Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[cascardo/linux.git] / net / wireless / nl80211.c
index 4809f4d..fd111e2 100644 (file)
@@ -848,13 +848,21 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
        struct nlattr *key;
        struct cfg80211_cached_keys *result;
        int rem, err, def = 0;
+       bool have_key = false;
+
+       nla_for_each_nested(key, keys, rem) {
+               have_key = true;
+               break;
+       }
+
+       if (!have_key)
+               return NULL;
 
        result = kzalloc(sizeof(*result), GFP_KERNEL);
        if (!result)
                return ERR_PTR(-ENOMEM);
 
        result->def = -1;
-       result->defmgmt = -1;
 
        nla_for_each_nested(key, keys, rem) {
                memset(&parse, 0, sizeof(parse));
@@ -866,7 +874,7 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
                err = -EINVAL;
                if (!parse.p.key)
                        goto error;
-               if (parse.idx < 0 || parse.idx > 4)
+               if (parse.idx < 0 || parse.idx > 3)
                        goto error;
                if (parse.def) {
                        if (def)
@@ -881,16 +889,24 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
                                                     parse.idx, false, NULL);
                if (err)
                        goto error;
+               if (parse.p.cipher != WLAN_CIPHER_SUITE_WEP40 &&
+                   parse.p.cipher != WLAN_CIPHER_SUITE_WEP104) {
+                       err = -EINVAL;
+                       goto error;
+               }
                result->params[parse.idx].cipher = parse.p.cipher;
                result->params[parse.idx].key_len = parse.p.key_len;
                result->params[parse.idx].key = result->data[parse.idx];
                memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len);
 
-               if (parse.p.cipher == WLAN_CIPHER_SUITE_WEP40 ||
-                   parse.p.cipher == WLAN_CIPHER_SUITE_WEP104) {
-                       if (no_ht)
-                               *no_ht = true;
-               }
+               /* must be WEP key if we got here */
+               if (no_ht)
+                       *no_ht = true;
+       }
+
+       if (result->def < 0) {
+               err = -EINVAL;
+               goto error;
        }
 
        return result;
@@ -2525,10 +2541,35 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
        int if_idx = 0;
        int wp_start = cb->args[0];
        int if_start = cb->args[1];
+       int filter_wiphy = -1;
        struct cfg80211_registered_device *rdev;
        struct wireless_dev *wdev;
 
        rtnl_lock();
+       if (!cb->args[2]) {
+               struct nl80211_dump_wiphy_state state = {
+                       .filter_wiphy = -1,
+               };
+               int ret;
+
+               ret = nl80211_dump_wiphy_parse(skb, cb, &state);
+               if (ret)
+                       return ret;
+
+               filter_wiphy = state.filter_wiphy;
+
+               /*
+                * if filtering, set cb->args[2] to +1 since 0 is the default
+                * value needed to determine that parsing is necessary.
+                */
+               if (filter_wiphy >= 0)
+                       cb->args[2] = filter_wiphy + 1;
+               else
+                       cb->args[2] = -1;
+       } else if (cb->args[2] > 0) {
+               filter_wiphy = cb->args[2] - 1;
+       }
+
        list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
                if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
                        continue;
@@ -2536,6 +2577,10 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
                        wp_idx++;
                        continue;
                }
+
+               if (filter_wiphy >= 0 && filter_wiphy != rdev->wiphy_idx)
+                       continue;
+
                if_idx = 0;
 
                list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
@@ -2751,7 +2796,7 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct vif_params params;
        struct wireless_dev *wdev;
-       struct sk_buff *msg, *event;
+       struct sk_buff *msg;
        int err;
        enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
        u32 flags;
@@ -2855,20 +2900,15 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
                return -ENOBUFS;
        }
 
-       event = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
-       if (event) {
-               if (nl80211_send_iface(event, 0, 0, 0,
-                                      rdev, wdev, false) < 0) {
-                       nlmsg_free(event);
-                       goto out;
-               }
-
-               genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy),
-                                       event, 0, NL80211_MCGRP_CONFIG,
-                                       GFP_KERNEL);
-       }
+       /*
+        * For wdevs which have no associated netdev object (e.g. of type
+        * NL80211_IFTYPE_P2P_DEVICE), emit the NEW_INTERFACE event here.
+        * For all other types, the event will be generated from the
+        * netdev notifier
+        */
+       if (!wdev->netdev)
+               nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE);
 
-out:
        return genlmsg_reply(msg, info);
 }
 
@@ -2876,18 +2916,10 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct wireless_dev *wdev = info->user_ptr[1];
-       struct sk_buff *msg;
-       int status;
 
        if (!rdev->ops->del_virtual_intf)
                return -EOPNOTSUPP;
 
-       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
-       if (msg && nl80211_send_iface(msg, 0, 0, 0, rdev, wdev, true) < 0) {
-               nlmsg_free(msg);
-               msg = NULL;
-       }
-
        /*
         * If we remove a wireless device without a netdev then clear
         * user_ptr[1] so that nl80211_post_doit won't dereference it
@@ -2898,15 +2930,7 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
        if (!wdev->netdev)
                info->user_ptr[1] = NULL;
 
-       status = rdev_del_virtual_intf(rdev, wdev);
-       if (status >= 0 && msg)
-               genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy),
-                                       msg, 0, NL80211_MCGRP_CONFIG,
-                                       GFP_KERNEL);
-       else
-               nlmsg_free(msg);
-
-       return status;
+       return rdev_del_virtual_intf(rdev, wdev);
 }
 
 static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info)
@@ -5374,6 +5398,18 @@ static int nl80211_check_s32(const struct nlattr *nla, s32 min, s32 max, s32 *ou
        return 0;
 }
 
+static int nl80211_check_power_mode(const struct nlattr *nla,
+                                   enum nl80211_mesh_power_mode min,
+                                   enum nl80211_mesh_power_mode max,
+                                   enum nl80211_mesh_power_mode *out)
+{
+       u32 val = nla_get_u32(nla);
+       if (val < min || val > max)
+               return -EINVAL;
+       *out = val;
+       return 0;
+}
+
 static int nl80211_parse_mesh_config(struct genl_info *info,
                                     struct mesh_config *cfg,
                                     u32 *mask_out)
@@ -5518,7 +5554,7 @@ do {                                                                          \
                                  NL80211_MESH_POWER_ACTIVE,
                                  NL80211_MESH_POWER_MAX,
                                  mask, NL80211_MESHCONF_POWER_MODE,
-                                 nl80211_check_u32);
+                                 nl80211_check_power_mode);
        FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration,
                                  0, 65535, mask,
                                  NL80211_MESHCONF_AWAKE_WINDOW, nl80211_check_u16);
@@ -7368,7 +7404,7 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
                    (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 ||
                     key.p.key_len != WLAN_KEY_LEN_WEP104))
                        return -EINVAL;
-               if (key.idx > 4)
+               if (key.idx > 3)
                        return -EINVAL;
        } else {
                key.p.key_len = 0;
@@ -7773,12 +7809,13 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
 
        ibss.beacon_interval = 100;
 
-       if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
+       if (info->attrs[NL80211_ATTR_BEACON_INTERVAL])
                ibss.beacon_interval =
                        nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
-               if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000)
-                       return -EINVAL;
-       }
+
+       err = cfg80211_validate_beacon_int(rdev, ibss.beacon_interval);
+       if (err)
+               return err;
 
        if (!rdev->ops->join_ibss)
                return -EOPNOTSUPP;
@@ -7985,6 +8022,8 @@ __cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev,
        }
 
        data = nla_nest_start(skb, attr);
+       if (!data)
+               goto nla_put_failure;
 
        ((void **)skb->cb)[0] = rdev;
        ((void **)skb->cb)[1] = hdr;
@@ -9252,9 +9291,10 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
        if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
                setup.beacon_interval =
                        nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
-               if (setup.beacon_interval < 10 ||
-                   setup.beacon_interval > 10000)
-                       return -EINVAL;
+
+               err = cfg80211_validate_beacon_int(rdev, setup.beacon_interval);
+               if (err)
+                       return err;
        }
 
        if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) {
@@ -9413,18 +9453,27 @@ static int nl80211_send_wowlan_nd(struct sk_buff *msg,
        if (!freqs)
                return -ENOBUFS;
 
-       for (i = 0; i < req->n_channels; i++)
-               nla_put_u32(msg, i, req->channels[i]->center_freq);
+       for (i = 0; i < req->n_channels; i++) {
+               if (nla_put_u32(msg, i, req->channels[i]->center_freq))
+                       return -ENOBUFS;
+       }
 
        nla_nest_end(msg, freqs);
 
        if (req->n_match_sets) {
                matches = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_MATCH);
+               if (!matches)
+                       return -ENOBUFS;
+
                for (i = 0; i < req->n_match_sets; i++) {
                        match = nla_nest_start(msg, i);
-                       nla_put(msg, NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
-                               req->match_sets[i].ssid.ssid_len,
-                               req->match_sets[i].ssid.ssid);
+                       if (!match)
+                               return -ENOBUFS;
+
+                       if (nla_put(msg, NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
+                                   req->match_sets[i].ssid.ssid_len,
+                                   req->match_sets[i].ssid.ssid))
+                               return -ENOBUFS;
                        nla_nest_end(msg, match);
                }
                nla_nest_end(msg, matches);
@@ -9436,6 +9485,9 @@ static int nl80211_send_wowlan_nd(struct sk_buff *msg,
 
        for (i = 0; i < req->n_scan_plans; i++) {
                scan_plan = nla_nest_start(msg, i + 1);
+               if (!scan_plan)
+                       return -ENOBUFS;
+
                if (!scan_plan ||
                    nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_INTERVAL,
                                req->scan_plans[i].interval) ||
@@ -11847,6 +11899,29 @@ void nl80211_notify_wiphy(struct cfg80211_registered_device *rdev,
                                NL80211_MCGRP_CONFIG, GFP_KERNEL);
 }
 
+void nl80211_notify_iface(struct cfg80211_registered_device *rdev,
+                               struct wireless_dev *wdev,
+                               enum nl80211_commands cmd)
+{
+       struct sk_buff *msg;
+
+       WARN_ON(cmd != NL80211_CMD_NEW_INTERFACE &&
+               cmd != NL80211_CMD_DEL_INTERFACE);
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return;
+
+       if (nl80211_send_iface(msg, 0, 0, 0, rdev, wdev,
+                              cmd == NL80211_CMD_DEL_INTERFACE) < 0) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       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,
                                struct cfg80211_registered_device *rdev)
 {