blob: cf4c7947f336f5e9008a38f2b63b1581673d3f9f [file] [log] [blame]
Johannes Berg55682962007-09-20 13:09:35 -04001/*
2 * This is the new netlink-based wireless configuration interface.
3 *
Jouni Malinen026331c2010-02-15 12:53:10 +02004 * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
Johannes Berg55682962007-09-20 13:09:35 -04005 */
6
7#include <linux/if.h>
8#include <linux/module.h>
9#include <linux/err.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090010#include <linux/slab.h>
Johannes Berg55682962007-09-20 13:09:35 -040011#include <linux/list.h>
12#include <linux/if_ether.h>
13#include <linux/ieee80211.h>
14#include <linux/nl80211.h>
15#include <linux/rtnetlink.h>
16#include <linux/netlink.h>
Johannes Berg2a519312009-02-10 21:25:55 +010017#include <linux/etherdevice.h>
Johannes Berg463d0182009-07-14 00:33:35 +020018#include <net/net_namespace.h>
Johannes Berg55682962007-09-20 13:09:35 -040019#include <net/genetlink.h>
20#include <net/cfg80211.h>
Johannes Berg463d0182009-07-14 00:33:35 +020021#include <net/sock.h>
Johannes Berg2a0e0472013-01-23 22:57:40 +010022#include <net/inet_connection_sock.h>
Johannes Berg55682962007-09-20 13:09:35 -040023#include "core.h"
24#include "nl80211.h"
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -070025#include "reg.h"
Hila Gonene35e4d22012-06-27 17:19:42 +030026#include "rdev-ops.h"
Johannes Berg55682962007-09-20 13:09:35 -040027
Jouni Malinen5fb628e2011-08-10 23:54:35 +030028static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
29 struct genl_info *info,
30 struct cfg80211_crypto_settings *settings,
31 int cipher_limit);
32
Johannes Berg4c476992010-10-04 21:36:35 +020033static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
34 struct genl_info *info);
35static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
36 struct genl_info *info);
37
Johannes Berg55682962007-09-20 13:09:35 -040038/* the netlink family */
39static struct genl_family nl80211_fam = {
40 .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
41 .name = "nl80211", /* have users key off the name instead */
42 .hdrsize = 0, /* no private header */
43 .version = 1, /* no particular meaning now */
44 .maxattr = NL80211_ATTR_MAX,
Johannes Berg463d0182009-07-14 00:33:35 +020045 .netnsok = true,
Johannes Berg4c476992010-10-04 21:36:35 +020046 .pre_doit = nl80211_pre_doit,
47 .post_doit = nl80211_post_doit,
Johannes Berg55682962007-09-20 13:09:35 -040048};
49
Johannes Berg89a54e42012-06-15 14:33:17 +020050/* returns ERR_PTR values */
51static struct wireless_dev *
52__cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs)
Johannes Berg55682962007-09-20 13:09:35 -040053{
Johannes Berg89a54e42012-06-15 14:33:17 +020054 struct cfg80211_registered_device *rdev;
55 struct wireless_dev *result = NULL;
56 bool have_ifidx = attrs[NL80211_ATTR_IFINDEX];
57 bool have_wdev_id = attrs[NL80211_ATTR_WDEV];
58 u64 wdev_id;
59 int wiphy_idx = -1;
60 int ifidx = -1;
Johannes Berg55682962007-09-20 13:09:35 -040061
Johannes Berg89a54e42012-06-15 14:33:17 +020062 assert_cfg80211_lock();
Johannes Berg55682962007-09-20 13:09:35 -040063
Johannes Berg89a54e42012-06-15 14:33:17 +020064 if (!have_ifidx && !have_wdev_id)
65 return ERR_PTR(-EINVAL);
Johannes Berg55682962007-09-20 13:09:35 -040066
Johannes Berg89a54e42012-06-15 14:33:17 +020067 if (have_ifidx)
68 ifidx = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
69 if (have_wdev_id) {
70 wdev_id = nla_get_u64(attrs[NL80211_ATTR_WDEV]);
71 wiphy_idx = wdev_id >> 32;
Johannes Berg55682962007-09-20 13:09:35 -040072 }
73
Johannes Berg89a54e42012-06-15 14:33:17 +020074 list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
75 struct wireless_dev *wdev;
76
77 if (wiphy_net(&rdev->wiphy) != netns)
78 continue;
79
80 if (have_wdev_id && rdev->wiphy_idx != wiphy_idx)
81 continue;
82
83 mutex_lock(&rdev->devlist_mtx);
84 list_for_each_entry(wdev, &rdev->wdev_list, list) {
85 if (have_ifidx && wdev->netdev &&
86 wdev->netdev->ifindex == ifidx) {
87 result = wdev;
88 break;
89 }
90 if (have_wdev_id && wdev->identifier == (u32)wdev_id) {
91 result = wdev;
92 break;
93 }
94 }
95 mutex_unlock(&rdev->devlist_mtx);
96
97 if (result)
98 break;
99 }
100
101 if (result)
102 return result;
103 return ERR_PTR(-ENODEV);
Johannes Berg55682962007-09-20 13:09:35 -0400104}
105
Johannes Berga9455402012-06-15 13:32:49 +0200106static struct cfg80211_registered_device *
Johannes Berg878d9ec2012-06-15 14:18:32 +0200107__cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
Johannes Berga9455402012-06-15 13:32:49 +0200108{
Johannes Berg7fee4772012-06-15 14:09:58 +0200109 struct cfg80211_registered_device *rdev = NULL, *tmp;
110 struct net_device *netdev;
Johannes Berga9455402012-06-15 13:32:49 +0200111
112 assert_cfg80211_lock();
113
Johannes Berg878d9ec2012-06-15 14:18:32 +0200114 if (!attrs[NL80211_ATTR_WIPHY] &&
Johannes Berg89a54e42012-06-15 14:33:17 +0200115 !attrs[NL80211_ATTR_IFINDEX] &&
116 !attrs[NL80211_ATTR_WDEV])
Johannes Berg7fee4772012-06-15 14:09:58 +0200117 return ERR_PTR(-EINVAL);
118
Johannes Berg878d9ec2012-06-15 14:18:32 +0200119 if (attrs[NL80211_ATTR_WIPHY])
Johannes Berg7fee4772012-06-15 14:09:58 +0200120 rdev = cfg80211_rdev_by_wiphy_idx(
Johannes Berg878d9ec2012-06-15 14:18:32 +0200121 nla_get_u32(attrs[NL80211_ATTR_WIPHY]));
Johannes Berga9455402012-06-15 13:32:49 +0200122
Johannes Berg89a54e42012-06-15 14:33:17 +0200123 if (attrs[NL80211_ATTR_WDEV]) {
124 u64 wdev_id = nla_get_u64(attrs[NL80211_ATTR_WDEV]);
125 struct wireless_dev *wdev;
126 bool found = false;
127
128 tmp = cfg80211_rdev_by_wiphy_idx(wdev_id >> 32);
129 if (tmp) {
130 /* make sure wdev exists */
131 mutex_lock(&tmp->devlist_mtx);
132 list_for_each_entry(wdev, &tmp->wdev_list, list) {
133 if (wdev->identifier != (u32)wdev_id)
134 continue;
135 found = true;
136 break;
137 }
138 mutex_unlock(&tmp->devlist_mtx);
139
140 if (!found)
141 tmp = NULL;
142
143 if (rdev && tmp != rdev)
144 return ERR_PTR(-EINVAL);
145 rdev = tmp;
146 }
147 }
148
Johannes Berg878d9ec2012-06-15 14:18:32 +0200149 if (attrs[NL80211_ATTR_IFINDEX]) {
150 int ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
Johannes Berg4f7eff12012-06-15 14:14:22 +0200151 netdev = dev_get_by_index(netns, ifindex);
Johannes Berg7fee4772012-06-15 14:09:58 +0200152 if (netdev) {
153 if (netdev->ieee80211_ptr)
154 tmp = wiphy_to_dev(
155 netdev->ieee80211_ptr->wiphy);
156 else
157 tmp = NULL;
158
159 dev_put(netdev);
160
161 /* not wireless device -- return error */
162 if (!tmp)
163 return ERR_PTR(-EINVAL);
164
165 /* mismatch -- return error */
166 if (rdev && tmp != rdev)
167 return ERR_PTR(-EINVAL);
168
169 rdev = tmp;
Johannes Berga9455402012-06-15 13:32:49 +0200170 }
Johannes Berga9455402012-06-15 13:32:49 +0200171 }
172
Johannes Berg4f7eff12012-06-15 14:14:22 +0200173 if (!rdev)
174 return ERR_PTR(-ENODEV);
Johannes Berga9455402012-06-15 13:32:49 +0200175
Johannes Berg4f7eff12012-06-15 14:14:22 +0200176 if (netns != wiphy_net(&rdev->wiphy))
177 return ERR_PTR(-ENODEV);
178
179 return rdev;
Johannes Berga9455402012-06-15 13:32:49 +0200180}
181
182/*
183 * This function returns a pointer to the driver
184 * that the genl_info item that is passed refers to.
185 * If successful, it returns non-NULL and also locks
186 * the driver's mutex!
187 *
188 * This means that you need to call cfg80211_unlock_rdev()
189 * before being allowed to acquire &cfg80211_mutex!
190 *
191 * This is necessary because we need to lock the global
192 * mutex to get an item off the list safely, and then
193 * we lock the rdev mutex so it doesn't go away under us.
194 *
195 * We don't want to keep cfg80211_mutex locked
196 * for all the time in order to allow requests on
197 * other interfaces to go through at the same time.
198 *
199 * The result of this can be a PTR_ERR and hence must
200 * be checked with IS_ERR() for errors.
201 */
202static struct cfg80211_registered_device *
Johannes Berg4f7eff12012-06-15 14:14:22 +0200203cfg80211_get_dev_from_info(struct net *netns, struct genl_info *info)
Johannes Berga9455402012-06-15 13:32:49 +0200204{
205 struct cfg80211_registered_device *rdev;
206
207 mutex_lock(&cfg80211_mutex);
Johannes Berg878d9ec2012-06-15 14:18:32 +0200208 rdev = __cfg80211_rdev_from_attrs(netns, info->attrs);
Johannes Berga9455402012-06-15 13:32:49 +0200209
210 /* if it is not an error we grab the lock on
211 * it to assure it won't be going away while
212 * we operate on it */
213 if (!IS_ERR(rdev))
214 mutex_lock(&rdev->mtx);
215
216 mutex_unlock(&cfg80211_mutex);
217
218 return rdev;
219}
220
Johannes Berg55682962007-09-20 13:09:35 -0400221/* policy for the attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +0000222static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
Johannes Berg55682962007-09-20 13:09:35 -0400223 [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
224 [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
David S. Miller079e24e2009-05-26 21:15:00 -0700225 .len = 20-1 },
Jouni Malinen31888482008-10-30 16:59:24 +0200226 [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
Johannes Berg3d9d1d62012-11-08 23:14:50 +0100227
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200228 [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
Sujith094d05d2008-12-12 11:57:43 +0530229 [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
Johannes Berg3d9d1d62012-11-08 23:14:50 +0100230 [NL80211_ATTR_CHANNEL_WIDTH] = { .type = NLA_U32 },
231 [NL80211_ATTR_CENTER_FREQ1] = { .type = NLA_U32 },
232 [NL80211_ATTR_CENTER_FREQ2] = { .type = NLA_U32 },
233
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200234 [NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 },
235 [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
236 [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
237 [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 },
Lukáš Turek81077e82009-12-21 22:50:47 +0100238 [NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 },
Johannes Berg55682962007-09-20 13:09:35 -0400239
240 [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
241 [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
242 [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
Johannes Berg41ade002007-12-19 02:03:29 +0100243
Eliad Pellere007b852011-11-24 18:13:56 +0200244 [NL80211_ATTR_MAC] = { .len = ETH_ALEN },
245 [NL80211_ATTR_PREV_BSSID] = { .len = ETH_ALEN },
Johannes Berg41ade002007-12-19 02:03:29 +0100246
Johannes Bergb9454e82009-07-08 13:29:08 +0200247 [NL80211_ATTR_KEY] = { .type = NLA_NESTED, },
Johannes Berg41ade002007-12-19 02:03:29 +0100248 [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
249 .len = WLAN_MAX_KEY_LEN },
250 [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
251 [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
252 [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
Jouni Malinen81962262011-11-02 23:36:31 +0200253 [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 },
Johannes Berge31b8212010-10-05 19:39:30 +0200254 [NL80211_ATTR_KEY_TYPE] = { .type = NLA_U32 },
Johannes Berged1b6cc2007-12-19 02:03:32 +0100255
256 [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
257 [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
258 [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY,
259 .len = IEEE80211_MAX_DATA_LEN },
260 [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY,
261 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg5727ef12007-12-19 02:03:34 +0100262 [NL80211_ATTR_STA_AID] = { .type = NLA_U16 },
263 [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED },
264 [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 },
265 [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY,
266 .len = NL80211_MAX_SUPP_RATES },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100267 [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
Johannes Berg5727ef12007-12-19 02:03:34 +0100268 [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
Johannes Berg0a9542e2008-10-15 11:54:04 +0200269 [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100270 [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +0800271 .len = IEEE80211_MAX_MESH_ID_LEN },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100272 [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300273
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -0700274 [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 },
275 [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED },
276
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300277 [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
278 [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
279 [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
Jouni Malinen90c97a02008-10-30 16:59:22 +0200280 [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
281 .len = NL80211_MAX_SUPP_RATES },
Helmut Schaa50b12f52010-11-19 12:40:25 +0100282 [NL80211_ATTR_BSS_HT_OPMODE] = { .type = NLA_U16 },
Jouni Malinen36aedc902008-08-25 11:58:58 +0300283
Javier Cardona24bdd9f2010-12-16 17:37:48 -0800284 [NL80211_ATTR_MESH_CONFIG] = { .type = NLA_NESTED },
Javier Cardona15d5dda2011-04-07 15:08:28 -0700285 [NL80211_ATTR_SUPPORT_MESH_AUTH] = { .type = NLA_FLAG },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -0700286
Johannes Berg6c739412011-11-03 09:27:01 +0100287 [NL80211_ATTR_HT_CAPABILITY] = { .len = NL80211_HT_CAPABILITY_LEN },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +0200288
289 [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
290 [NL80211_ATTR_IE] = { .type = NLA_BINARY,
291 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg2a519312009-02-10 21:25:55 +0100292 [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
293 [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
Jouni Malinen636a5d32009-03-19 13:39:22 +0200294
295 [NL80211_ATTR_SSID] = { .type = NLA_BINARY,
296 .len = IEEE80211_MAX_SSID_LEN },
297 [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
298 [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
Johannes Berg04a773a2009-04-19 21:24:32 +0200299 [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
Jouni Malinen1965c852009-04-22 21:38:25 +0300300 [NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG },
Jouni Malinendc6382c2009-05-06 22:09:37 +0300301 [NL80211_ATTR_USE_MFP] = { .type = NLA_U32 },
Johannes Bergeccb8e82009-05-11 21:57:56 +0300302 [NL80211_ATTR_STA_FLAGS2] = {
303 .len = sizeof(struct nl80211_sta_flag_update),
304 },
Jouni Malinen3f77316c2009-05-11 21:57:57 +0300305 [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG },
Johannes Bergc0692b82010-08-27 14:26:53 +0300306 [NL80211_ATTR_CONTROL_PORT_ETHERTYPE] = { .type = NLA_U16 },
307 [NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT] = { .type = NLA_FLAG },
Samuel Ortizb23aa672009-07-01 21:26:54 +0200308 [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG },
309 [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 },
310 [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
Johannes Berg463d0182009-07-14 00:33:35 +0200311 [NL80211_ATTR_PID] = { .type = NLA_U32 },
Felix Fietkau8b787642009-11-10 18:53:10 +0100312 [NL80211_ATTR_4ADDR] = { .type = NLA_U8 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100313 [NL80211_ATTR_PMKID] = { .type = NLA_BINARY,
314 .len = WLAN_PMKID_LEN },
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100315 [NL80211_ATTR_DURATION] = { .type = NLA_U32 },
316 [NL80211_ATTR_COOKIE] = { .type = NLA_U64 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +0200317 [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED },
Jouni Malinen026331c2010-02-15 12:53:10 +0200318 [NL80211_ATTR_FRAME] = { .type = NLA_BINARY,
319 .len = IEEE80211_MAX_DATA_LEN },
320 [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, },
Kalle Valoffb9eb32010-02-17 17:58:10 +0200321 [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +0200322 [NL80211_ATTR_CQM] = { .type = NLA_NESTED, },
Jouni Malinend5cdfac2010-04-04 09:37:19 +0300323 [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG },
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +0200324 [NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 },
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +0300325 [NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 },
326 [NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 },
Johannes Berg2e161f72010-08-12 15:38:38 +0200327 [NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 },
Bruno Randolfafe0cbf2010-11-10 12:50:50 +0900328 [NL80211_ATTR_WIPHY_ANTENNA_TX] = { .type = NLA_U32 },
329 [NL80211_ATTR_WIPHY_ANTENNA_RX] = { .type = NLA_U32 },
Felix Fietkau885a46d2010-11-11 15:07:22 +0100330 [NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 },
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100331 [NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG },
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100332 [NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
Johannes Bergff1b6e62011-05-04 15:37:28 +0200333 [NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED },
Javier Cardona9c3990a2011-05-03 16:57:11 -0700334 [NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 },
Luciano Coelhobbe6ad62011-05-11 17:09:37 +0300335 [NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 },
Johannes Berge5497d72011-07-05 16:35:40 +0200336 [NL80211_ATTR_REKEY_DATA] = { .type = NLA_NESTED },
Johannes Berg34850ab2011-07-18 18:08:35 +0200337 [NL80211_ATTR_SCAN_SUPP_RATES] = { .type = NLA_NESTED },
Jouni Malinen32e9de82011-08-10 23:53:31 +0300338 [NL80211_ATTR_HIDDEN_SSID] = { .type = NLA_U32 },
Jouni Malinen9946ecf2011-08-10 23:55:56 +0300339 [NL80211_ATTR_IE_PROBE_RESP] = { .type = NLA_BINARY,
340 .len = IEEE80211_MAX_DATA_LEN },
341 [NL80211_ATTR_IE_ASSOC_RESP] = { .type = NLA_BINARY,
342 .len = IEEE80211_MAX_DATA_LEN },
Vivek Natarajanf4b34b52011-08-29 14:23:03 +0530343 [NL80211_ATTR_ROAM_SUPPORT] = { .type = NLA_FLAG },
Luciano Coelhoa1f1c212011-08-31 16:01:48 +0300344 [NL80211_ATTR_SCHED_SCAN_MATCH] = { .type = NLA_NESTED },
Rajkumar Manoharane9f935e2011-09-25 14:53:30 +0530345 [NL80211_ATTR_TX_NO_CCK_RATE] = { .type = NLA_FLAG },
Arik Nemtsov109086c2011-09-28 14:12:50 +0300346 [NL80211_ATTR_TDLS_ACTION] = { .type = NLA_U8 },
347 [NL80211_ATTR_TDLS_DIALOG_TOKEN] = { .type = NLA_U8 },
348 [NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 },
349 [NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG },
350 [NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG },
Johannes Berge247bd902011-11-04 11:18:21 +0100351 [NL80211_ATTR_DONT_WAIT_FOR_ACK] = { .type = NLA_FLAG },
Arik Nemtsov00f740e2011-11-10 11:28:56 +0200352 [NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY,
353 .len = IEEE80211_MAX_DATA_LEN },
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -0700354 [NL80211_ATTR_DFS_REGION] = { .type = NLA_U8 },
Ben Greear7e7c8922011-11-18 11:31:59 -0800355 [NL80211_ATTR_DISABLE_HT] = { .type = NLA_FLAG },
356 [NL80211_ATTR_HT_CAPABILITY_MASK] = {
357 .len = NL80211_HT_CAPABILITY_LEN
358 },
Simon Wunderlich1d9d9212011-11-18 14:20:43 +0100359 [NL80211_ATTR_NOACK_MAP] = { .type = NLA_U16 },
Vasanthakumar Thiagarajan1b658f12012-03-02 15:50:02 +0530360 [NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 },
Bala Shanmugam4486ea92012-03-07 17:27:12 +0530361 [NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 },
Johannes Berg89a54e42012-06-15 14:33:17 +0200362 [NL80211_ATTR_WDEV] = { .type = NLA_U64 },
Luis R. Rodriguez57b5ce02012-07-12 11:49:18 -0700363 [NL80211_ATTR_USER_REG_HINT_TYPE] = { .type = NLA_U32 },
Jouni Malinene39e5b52012-09-30 19:29:39 +0300364 [NL80211_ATTR_SAE_DATA] = { .type = NLA_BINARY, },
Mahesh Palivelaf461be3e2012-10-11 08:04:52 +0000365 [NL80211_ATTR_VHT_CAPABILITY] = { .len = NL80211_VHT_CAPABILITY_LEN },
Sam Lefflered4737712012-10-11 21:03:31 -0700366 [NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 },
Johannes Berg53cabad2012-11-14 15:17:28 +0100367 [NL80211_ATTR_P2P_CTWINDOW] = { .type = NLA_U8 },
368 [NL80211_ATTR_P2P_OPPPS] = { .type = NLA_U8 },
Vasanthakumar Thiagarajan77765ea2013-01-18 11:18:45 +0530369 [NL80211_ATTR_ACL_POLICY] = {. type = NLA_U32 },
370 [NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED },
Jouni Malinen9d62a982013-02-14 21:10:13 +0200371 [NL80211_ATTR_STA_CAPABILITY] = { .type = NLA_U16 },
372 [NL80211_ATTR_STA_EXT_CAPABILITY] = { .type = NLA_BINARY, },
Johannes Berg55682962007-09-20 13:09:35 -0400373};
374
Johannes Berge31b8212010-10-05 19:39:30 +0200375/* policy for the key attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +0000376static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = {
Johannes Bergfffd0932009-07-08 14:22:54 +0200377 [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN },
Johannes Bergb9454e82009-07-08 13:29:08 +0200378 [NL80211_KEY_IDX] = { .type = NLA_U8 },
379 [NL80211_KEY_CIPHER] = { .type = NLA_U32 },
Jouni Malinen81962262011-11-02 23:36:31 +0200380 [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 },
Johannes Bergb9454e82009-07-08 13:29:08 +0200381 [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG },
382 [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
Johannes Berge31b8212010-10-05 19:39:30 +0200383 [NL80211_KEY_TYPE] = { .type = NLA_U32 },
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100384 [NL80211_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
385};
386
387/* policy for the key default flags */
388static const struct nla_policy
389nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = {
390 [NL80211_KEY_DEFAULT_TYPE_UNICAST] = { .type = NLA_FLAG },
391 [NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG },
Johannes Bergb9454e82009-07-08 13:29:08 +0200392};
393
Johannes Bergff1b6e62011-05-04 15:37:28 +0200394/* policy for WoWLAN attributes */
395static const struct nla_policy
396nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
397 [NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG },
398 [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG },
399 [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG },
400 [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED },
Johannes Berg77dbbb12011-07-13 10:48:55 +0200401 [NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE] = { .type = NLA_FLAG },
402 [NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG },
403 [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG },
404 [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG },
Johannes Berg2a0e0472013-01-23 22:57:40 +0100405 [NL80211_WOWLAN_TRIG_TCP_CONNECTION] = { .type = NLA_NESTED },
406};
407
408static const struct nla_policy
409nl80211_wowlan_tcp_policy[NUM_NL80211_WOWLAN_TCP] = {
410 [NL80211_WOWLAN_TCP_SRC_IPV4] = { .type = NLA_U32 },
411 [NL80211_WOWLAN_TCP_DST_IPV4] = { .type = NLA_U32 },
412 [NL80211_WOWLAN_TCP_DST_MAC] = { .len = ETH_ALEN },
413 [NL80211_WOWLAN_TCP_SRC_PORT] = { .type = NLA_U16 },
414 [NL80211_WOWLAN_TCP_DST_PORT] = { .type = NLA_U16 },
415 [NL80211_WOWLAN_TCP_DATA_PAYLOAD] = { .len = 1 },
416 [NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ] = {
417 .len = sizeof(struct nl80211_wowlan_tcp_data_seq)
418 },
419 [NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN] = {
420 .len = sizeof(struct nl80211_wowlan_tcp_data_token)
421 },
422 [NL80211_WOWLAN_TCP_DATA_INTERVAL] = { .type = NLA_U32 },
423 [NL80211_WOWLAN_TCP_WAKE_PAYLOAD] = { .len = 1 },
424 [NL80211_WOWLAN_TCP_WAKE_MASK] = { .len = 1 },
Johannes Bergff1b6e62011-05-04 15:37:28 +0200425};
426
Johannes Berge5497d72011-07-05 16:35:40 +0200427/* policy for GTK rekey offload attributes */
428static const struct nla_policy
429nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = {
430 [NL80211_REKEY_DATA_KEK] = { .len = NL80211_KEK_LEN },
431 [NL80211_REKEY_DATA_KCK] = { .len = NL80211_KCK_LEN },
432 [NL80211_REKEY_DATA_REPLAY_CTR] = { .len = NL80211_REPLAY_CTR_LEN },
433};
434
Luciano Coelhoa1f1c212011-08-31 16:01:48 +0300435static const struct nla_policy
436nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = {
Johannes Berg4a4ab0d2012-06-13 11:17:11 +0200437 [NL80211_SCHED_SCAN_MATCH_ATTR_SSID] = { .type = NLA_BINARY,
Luciano Coelhoa1f1c212011-08-31 16:01:48 +0300438 .len = IEEE80211_MAX_SSID_LEN },
Thomas Pedersen88e920b2012-06-21 11:09:54 -0700439 [NL80211_SCHED_SCAN_MATCH_ATTR_RSSI] = { .type = NLA_U32 },
Luciano Coelhoa1f1c212011-08-31 16:01:48 +0300440};
441
Holger Schuriga0438972009-11-11 11:30:02 +0100442/* ifidx get helper */
443static int nl80211_get_ifidx(struct netlink_callback *cb)
444{
445 int res;
446
447 res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
448 nl80211_fam.attrbuf, nl80211_fam.maxattr,
449 nl80211_policy);
450 if (res)
451 return res;
452
453 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
454 return -EINVAL;
455
456 res = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
457 if (!res)
458 return -EINVAL;
459 return res;
460}
461
Johannes Berg67748892010-10-04 21:14:06 +0200462static int nl80211_prepare_netdev_dump(struct sk_buff *skb,
463 struct netlink_callback *cb,
464 struct cfg80211_registered_device **rdev,
465 struct net_device **dev)
466{
467 int ifidx = cb->args[0];
468 int err;
469
470 if (!ifidx)
471 ifidx = nl80211_get_ifidx(cb);
472 if (ifidx < 0)
473 return ifidx;
474
475 cb->args[0] = ifidx;
476
477 rtnl_lock();
478
479 *dev = __dev_get_by_index(sock_net(skb->sk), ifidx);
480 if (!*dev) {
481 err = -ENODEV;
482 goto out_rtnl;
483 }
484
485 *rdev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Felix Fietkau3cc25e52010-10-31 15:31:54 +0100486 if (IS_ERR(*rdev)) {
487 err = PTR_ERR(*rdev);
Johannes Berg67748892010-10-04 21:14:06 +0200488 goto out_rtnl;
489 }
490
491 return 0;
492 out_rtnl:
493 rtnl_unlock();
494 return err;
495}
496
497static void nl80211_finish_netdev_dump(struct cfg80211_registered_device *rdev)
498{
499 cfg80211_unlock_rdev(rdev);
500 rtnl_unlock();
501}
502
Johannes Bergf4a11bb2009-03-27 12:40:28 +0100503/* IE validation */
504static bool is_valid_ie_attr(const struct nlattr *attr)
505{
506 const u8 *pos;
507 int len;
508
509 if (!attr)
510 return true;
511
512 pos = nla_data(attr);
513 len = nla_len(attr);
514
515 while (len) {
516 u8 elemlen;
517
518 if (len < 2)
519 return false;
520 len -= 2;
521
522 elemlen = pos[1];
523 if (elemlen > len)
524 return false;
525
526 len -= elemlen;
527 pos += 2 + elemlen;
528 }
529
530 return true;
531}
532
Johannes Berg55682962007-09-20 13:09:35 -0400533/* message building helper */
Eric W. Biederman15e47302012-09-07 20:12:54 +0000534static inline void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
Johannes Berg55682962007-09-20 13:09:35 -0400535 int flags, u8 cmd)
536{
537 /* since there is no private header just add the generic one */
Eric W. Biederman15e47302012-09-07 20:12:54 +0000538 return genlmsg_put(skb, portid, seq, &nl80211_fam, flags, cmd);
Johannes Berg55682962007-09-20 13:09:35 -0400539}
540
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400541static int nl80211_msg_put_channel(struct sk_buff *msg,
542 struct ieee80211_channel *chan)
543{
David S. Miller9360ffd2012-03-29 04:41:26 -0400544 if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_FREQ,
545 chan->center_freq))
546 goto nla_put_failure;
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400547
David S. Miller9360ffd2012-03-29 04:41:26 -0400548 if ((chan->flags & IEEE80211_CHAN_DISABLED) &&
549 nla_put_flag(msg, NL80211_FREQUENCY_ATTR_DISABLED))
550 goto nla_put_failure;
551 if ((chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
552 nla_put_flag(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN))
553 goto nla_put_failure;
554 if ((chan->flags & IEEE80211_CHAN_NO_IBSS) &&
555 nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IBSS))
556 goto nla_put_failure;
Johannes Berg1c33a052013-02-18 23:44:38 +0100557 if ((chan->flags & IEEE80211_CHAN_RADAR) &&
558 nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
559 goto nla_put_failure;
Johannes Berg50640f12012-12-12 17:59:39 +0100560 if ((chan->flags & IEEE80211_CHAN_NO_HT40MINUS) &&
561 nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_MINUS))
562 goto nla_put_failure;
563 if ((chan->flags & IEEE80211_CHAN_NO_HT40PLUS) &&
564 nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_PLUS))
565 goto nla_put_failure;
566 if ((chan->flags & IEEE80211_CHAN_NO_80MHZ) &&
567 nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_80MHZ))
568 goto nla_put_failure;
569 if ((chan->flags & IEEE80211_CHAN_NO_160MHZ) &&
570 nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_160MHZ))
571 goto nla_put_failure;
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400572
David S. Miller9360ffd2012-03-29 04:41:26 -0400573 if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
574 DBM_TO_MBM(chan->max_power)))
575 goto nla_put_failure;
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400576
577 return 0;
578
579 nla_put_failure:
580 return -ENOBUFS;
581}
582
Johannes Berg55682962007-09-20 13:09:35 -0400583/* netlink command implementations */
584
Johannes Bergb9454e82009-07-08 13:29:08 +0200585struct key_parse {
586 struct key_params p;
587 int idx;
Johannes Berge31b8212010-10-05 19:39:30 +0200588 int type;
Johannes Bergb9454e82009-07-08 13:29:08 +0200589 bool def, defmgmt;
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100590 bool def_uni, def_multi;
Johannes Bergb9454e82009-07-08 13:29:08 +0200591};
592
593static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k)
594{
595 struct nlattr *tb[NL80211_KEY_MAX + 1];
596 int err = nla_parse_nested(tb, NL80211_KEY_MAX, key,
597 nl80211_key_policy);
598 if (err)
599 return err;
600
601 k->def = !!tb[NL80211_KEY_DEFAULT];
602 k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT];
603
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100604 if (k->def) {
605 k->def_uni = true;
606 k->def_multi = true;
607 }
608 if (k->defmgmt)
609 k->def_multi = true;
610
Johannes Bergb9454e82009-07-08 13:29:08 +0200611 if (tb[NL80211_KEY_IDX])
612 k->idx = nla_get_u8(tb[NL80211_KEY_IDX]);
613
614 if (tb[NL80211_KEY_DATA]) {
615 k->p.key = nla_data(tb[NL80211_KEY_DATA]);
616 k->p.key_len = nla_len(tb[NL80211_KEY_DATA]);
617 }
618
619 if (tb[NL80211_KEY_SEQ]) {
620 k->p.seq = nla_data(tb[NL80211_KEY_SEQ]);
621 k->p.seq_len = nla_len(tb[NL80211_KEY_SEQ]);
622 }
623
624 if (tb[NL80211_KEY_CIPHER])
625 k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]);
626
Johannes Berge31b8212010-10-05 19:39:30 +0200627 if (tb[NL80211_KEY_TYPE]) {
628 k->type = nla_get_u32(tb[NL80211_KEY_TYPE]);
629 if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
630 return -EINVAL;
631 }
632
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100633 if (tb[NL80211_KEY_DEFAULT_TYPES]) {
634 struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
Johannes Berg2da8f412012-01-20 13:52:37 +0100635 err = nla_parse_nested(kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1,
636 tb[NL80211_KEY_DEFAULT_TYPES],
637 nl80211_key_default_policy);
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100638 if (err)
639 return err;
640
641 k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST];
642 k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
643 }
644
Johannes Bergb9454e82009-07-08 13:29:08 +0200645 return 0;
646}
647
648static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k)
649{
650 if (info->attrs[NL80211_ATTR_KEY_DATA]) {
651 k->p.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
652 k->p.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
653 }
654
655 if (info->attrs[NL80211_ATTR_KEY_SEQ]) {
656 k->p.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]);
657 k->p.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]);
658 }
659
660 if (info->attrs[NL80211_ATTR_KEY_IDX])
661 k->idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
662
663 if (info->attrs[NL80211_ATTR_KEY_CIPHER])
664 k->p.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
665
666 k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT];
667 k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT];
668
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100669 if (k->def) {
670 k->def_uni = true;
671 k->def_multi = true;
672 }
673 if (k->defmgmt)
674 k->def_multi = true;
675
Johannes Berge31b8212010-10-05 19:39:30 +0200676 if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
677 k->type = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
678 if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
679 return -EINVAL;
680 }
681
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100682 if (info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES]) {
683 struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
684 int err = nla_parse_nested(
685 kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1,
686 info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES],
687 nl80211_key_default_policy);
688 if (err)
689 return err;
690
691 k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST];
692 k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
693 }
694
Johannes Bergb9454e82009-07-08 13:29:08 +0200695 return 0;
696}
697
698static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
699{
700 int err;
701
702 memset(k, 0, sizeof(*k));
703 k->idx = -1;
Johannes Berge31b8212010-10-05 19:39:30 +0200704 k->type = -1;
Johannes Bergb9454e82009-07-08 13:29:08 +0200705
706 if (info->attrs[NL80211_ATTR_KEY])
707 err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k);
708 else
709 err = nl80211_parse_key_old(info, k);
710
711 if (err)
712 return err;
713
714 if (k->def && k->defmgmt)
715 return -EINVAL;
716
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100717 if (k->defmgmt) {
718 if (k->def_uni || !k->def_multi)
719 return -EINVAL;
720 }
721
Johannes Bergb9454e82009-07-08 13:29:08 +0200722 if (k->idx != -1) {
723 if (k->defmgmt) {
724 if (k->idx < 4 || k->idx > 5)
725 return -EINVAL;
726 } else if (k->def) {
727 if (k->idx < 0 || k->idx > 3)
728 return -EINVAL;
729 } else {
730 if (k->idx < 0 || k->idx > 5)
731 return -EINVAL;
732 }
733 }
734
735 return 0;
736}
737
Johannes Bergfffd0932009-07-08 14:22:54 +0200738static struct cfg80211_cached_keys *
739nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
Sujith Manoharande7044e2012-10-18 10:19:28 +0530740 struct nlattr *keys, bool *no_ht)
Johannes Bergfffd0932009-07-08 14:22:54 +0200741{
742 struct key_parse parse;
743 struct nlattr *key;
744 struct cfg80211_cached_keys *result;
745 int rem, err, def = 0;
746
747 result = kzalloc(sizeof(*result), GFP_KERNEL);
748 if (!result)
749 return ERR_PTR(-ENOMEM);
750
751 result->def = -1;
752 result->defmgmt = -1;
753
754 nla_for_each_nested(key, keys, rem) {
755 memset(&parse, 0, sizeof(parse));
756 parse.idx = -1;
757
758 err = nl80211_parse_key_new(key, &parse);
759 if (err)
760 goto error;
761 err = -EINVAL;
762 if (!parse.p.key)
763 goto error;
764 if (parse.idx < 0 || parse.idx > 4)
765 goto error;
766 if (parse.def) {
767 if (def)
768 goto error;
769 def = 1;
770 result->def = parse.idx;
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100771 if (!parse.def_uni || !parse.def_multi)
772 goto error;
Johannes Bergfffd0932009-07-08 14:22:54 +0200773 } else if (parse.defmgmt)
774 goto error;
775 err = cfg80211_validate_key_settings(rdev, &parse.p,
Johannes Berge31b8212010-10-05 19:39:30 +0200776 parse.idx, false, NULL);
Johannes Bergfffd0932009-07-08 14:22:54 +0200777 if (err)
778 goto error;
779 result->params[parse.idx].cipher = parse.p.cipher;
780 result->params[parse.idx].key_len = parse.p.key_len;
781 result->params[parse.idx].key = result->data[parse.idx];
782 memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len);
Sujith Manoharande7044e2012-10-18 10:19:28 +0530783
784 if (parse.p.cipher == WLAN_CIPHER_SUITE_WEP40 ||
785 parse.p.cipher == WLAN_CIPHER_SUITE_WEP104) {
786 if (no_ht)
787 *no_ht = true;
788 }
Johannes Bergfffd0932009-07-08 14:22:54 +0200789 }
790
791 return result;
792 error:
793 kfree(result);
794 return ERR_PTR(err);
795}
796
797static int nl80211_key_allowed(struct wireless_dev *wdev)
798{
799 ASSERT_WDEV_LOCK(wdev);
800
Johannes Bergfffd0932009-07-08 14:22:54 +0200801 switch (wdev->iftype) {
802 case NL80211_IFTYPE_AP:
803 case NL80211_IFTYPE_AP_VLAN:
Johannes Berg074ac8d2010-09-16 14:58:22 +0200804 case NL80211_IFTYPE_P2P_GO:
Thomas Pedersenff973af2011-05-03 16:57:12 -0700805 case NL80211_IFTYPE_MESH_POINT:
Johannes Bergfffd0932009-07-08 14:22:54 +0200806 break;
807 case NL80211_IFTYPE_ADHOC:
808 if (!wdev->current_bss)
809 return -ENOLINK;
810 break;
811 case NL80211_IFTYPE_STATION:
Johannes Berg074ac8d2010-09-16 14:58:22 +0200812 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Bergfffd0932009-07-08 14:22:54 +0200813 if (wdev->sme_state != CFG80211_SME_CONNECTED)
814 return -ENOLINK;
815 break;
816 default:
817 return -EINVAL;
818 }
819
820 return 0;
821}
822
Johannes Berg7527a782011-05-13 10:58:57 +0200823static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes)
824{
825 struct nlattr *nl_modes = nla_nest_start(msg, attr);
826 int i;
827
828 if (!nl_modes)
829 goto nla_put_failure;
830
831 i = 0;
832 while (ifmodes) {
David S. Miller9360ffd2012-03-29 04:41:26 -0400833 if ((ifmodes & 1) && nla_put_flag(msg, i))
834 goto nla_put_failure;
Johannes Berg7527a782011-05-13 10:58:57 +0200835 ifmodes >>= 1;
836 i++;
837 }
838
839 nla_nest_end(msg, nl_modes);
840 return 0;
841
842nla_put_failure:
843 return -ENOBUFS;
844}
845
846static int nl80211_put_iface_combinations(struct wiphy *wiphy,
847 struct sk_buff *msg)
848{
849 struct nlattr *nl_combis;
850 int i, j;
851
852 nl_combis = nla_nest_start(msg,
853 NL80211_ATTR_INTERFACE_COMBINATIONS);
854 if (!nl_combis)
855 goto nla_put_failure;
856
857 for (i = 0; i < wiphy->n_iface_combinations; i++) {
858 const struct ieee80211_iface_combination *c;
859 struct nlattr *nl_combi, *nl_limits;
860
861 c = &wiphy->iface_combinations[i];
862
863 nl_combi = nla_nest_start(msg, i + 1);
864 if (!nl_combi)
865 goto nla_put_failure;
866
867 nl_limits = nla_nest_start(msg, NL80211_IFACE_COMB_LIMITS);
868 if (!nl_limits)
869 goto nla_put_failure;
870
871 for (j = 0; j < c->n_limits; j++) {
872 struct nlattr *nl_limit;
873
874 nl_limit = nla_nest_start(msg, j + 1);
875 if (!nl_limit)
876 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -0400877 if (nla_put_u32(msg, NL80211_IFACE_LIMIT_MAX,
878 c->limits[j].max))
879 goto nla_put_failure;
Johannes Berg7527a782011-05-13 10:58:57 +0200880 if (nl80211_put_iftypes(msg, NL80211_IFACE_LIMIT_TYPES,
881 c->limits[j].types))
882 goto nla_put_failure;
883 nla_nest_end(msg, nl_limit);
884 }
885
886 nla_nest_end(msg, nl_limits);
887
David S. Miller9360ffd2012-03-29 04:41:26 -0400888 if (c->beacon_int_infra_match &&
889 nla_put_flag(msg, NL80211_IFACE_COMB_STA_AP_BI_MATCH))
890 goto nla_put_failure;
891 if (nla_put_u32(msg, NL80211_IFACE_COMB_NUM_CHANNELS,
892 c->num_different_channels) ||
893 nla_put_u32(msg, NL80211_IFACE_COMB_MAXNUM,
894 c->max_interfaces))
895 goto nla_put_failure;
Johannes Berg7527a782011-05-13 10:58:57 +0200896
897 nla_nest_end(msg, nl_combi);
898 }
899
900 nla_nest_end(msg, nl_combis);
901
902 return 0;
903nla_put_failure:
904 return -ENOBUFS;
905}
906
Johannes Berg2a0e0472013-01-23 22:57:40 +0100907#ifdef CONFIG_PM
908static int nl80211_send_wowlan_tcp_caps(struct cfg80211_registered_device *rdev,
909 struct sk_buff *msg)
910{
911 const struct wiphy_wowlan_tcp_support *tcp = rdev->wiphy.wowlan.tcp;
912 struct nlattr *nl_tcp;
913
914 if (!tcp)
915 return 0;
916
917 nl_tcp = nla_nest_start(msg, NL80211_WOWLAN_TRIG_TCP_CONNECTION);
918 if (!nl_tcp)
919 return -ENOBUFS;
920
921 if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD,
922 tcp->data_payload_max))
923 return -ENOBUFS;
924
925 if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD,
926 tcp->data_payload_max))
927 return -ENOBUFS;
928
929 if (tcp->seq && nla_put_flag(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ))
930 return -ENOBUFS;
931
932 if (tcp->tok && nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN,
933 sizeof(*tcp->tok), tcp->tok))
934 return -ENOBUFS;
935
936 if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_INTERVAL,
937 tcp->data_interval_max))
938 return -ENOBUFS;
939
940 if (nla_put_u32(msg, NL80211_WOWLAN_TCP_WAKE_PAYLOAD,
941 tcp->wake_payload_max))
942 return -ENOBUFS;
943
944 nla_nest_end(msg, nl_tcp);
945 return 0;
946}
947#endif
948
Eric W. Biederman15e47302012-09-07 20:12:54 +0000949static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flags,
Johannes Berg55682962007-09-20 13:09:35 -0400950 struct cfg80211_registered_device *dev)
951{
952 void *hdr;
Johannes Bergee688b002008-01-24 19:38:39 +0100953 struct nlattr *nl_bands, *nl_band;
954 struct nlattr *nl_freqs, *nl_freq;
955 struct nlattr *nl_rates, *nl_rate;
Johannes Berg8fdc6212009-03-14 09:34:01 +0100956 struct nlattr *nl_cmds;
Johannes Bergee688b002008-01-24 19:38:39 +0100957 enum ieee80211_band band;
958 struct ieee80211_channel *chan;
959 struct ieee80211_rate *rate;
960 int i;
Johannes Berg2e161f72010-08-12 15:38:38 +0200961 const struct ieee80211_txrx_stypes *mgmt_stypes =
962 dev->wiphy.mgmt_stypes;
Johannes Berg55682962007-09-20 13:09:35 -0400963
Eric W. Biederman15e47302012-09-07 20:12:54 +0000964 hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_WIPHY);
Johannes Berg55682962007-09-20 13:09:35 -0400965 if (!hdr)
966 return -1;
967
David S. Miller9360ffd2012-03-29 04:41:26 -0400968 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx) ||
969 nla_put_string(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy)) ||
970 nla_put_u32(msg, NL80211_ATTR_GENERATION,
971 cfg80211_rdev_list_generation) ||
972 nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
973 dev->wiphy.retry_short) ||
974 nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
975 dev->wiphy.retry_long) ||
976 nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
977 dev->wiphy.frag_threshold) ||
978 nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
979 dev->wiphy.rts_threshold) ||
980 nla_put_u8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
981 dev->wiphy.coverage_class) ||
982 nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
983 dev->wiphy.max_scan_ssids) ||
984 nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
985 dev->wiphy.max_sched_scan_ssids) ||
986 nla_put_u16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
987 dev->wiphy.max_scan_ie_len) ||
988 nla_put_u16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
989 dev->wiphy.max_sched_scan_ie_len) ||
990 nla_put_u8(msg, NL80211_ATTR_MAX_MATCH_SETS,
991 dev->wiphy.max_match_sets))
992 goto nla_put_failure;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200993
David S. Miller9360ffd2012-03-29 04:41:26 -0400994 if ((dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) &&
995 nla_put_flag(msg, NL80211_ATTR_SUPPORT_IBSS_RSN))
996 goto nla_put_failure;
997 if ((dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) &&
998 nla_put_flag(msg, NL80211_ATTR_SUPPORT_MESH_AUTH))
999 goto nla_put_failure;
1000 if ((dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
1001 nla_put_flag(msg, NL80211_ATTR_SUPPORT_AP_UAPSD))
1002 goto nla_put_failure;
1003 if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) &&
1004 nla_put_flag(msg, NL80211_ATTR_ROAM_SUPPORT))
1005 goto nla_put_failure;
1006 if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
1007 nla_put_flag(msg, NL80211_ATTR_TDLS_SUPPORT))
1008 goto nla_put_failure;
1009 if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
1010 nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))
1011 goto nla_put_failure;
Johannes Bergf5ea9122009-08-07 16:17:38 +02001012
David S. Miller9360ffd2012-03-29 04:41:26 -04001013 if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES,
1014 sizeof(u32) * dev->wiphy.n_cipher_suites,
1015 dev->wiphy.cipher_suites))
1016 goto nla_put_failure;
Johannes Bergee688b002008-01-24 19:38:39 +01001017
David S. Miller9360ffd2012-03-29 04:41:26 -04001018 if (nla_put_u8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
1019 dev->wiphy.max_num_pmkids))
1020 goto nla_put_failure;
Vivek Natarajanf4b34b52011-08-29 14:23:03 +05301021
David S. Miller9360ffd2012-03-29 04:41:26 -04001022 if ((dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
1023 nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE))
1024 goto nla_put_failure;
Johannes Berg25e47c12009-04-02 20:14:06 +02001025
David S. Miller9360ffd2012-03-29 04:41:26 -04001026 if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
1027 dev->wiphy.available_antennas_tx) ||
1028 nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
1029 dev->wiphy.available_antennas_rx))
1030 goto nla_put_failure;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01001031
David S. Miller9360ffd2012-03-29 04:41:26 -04001032 if ((dev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) &&
1033 nla_put_u32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD,
1034 dev->wiphy.probe_resp_offload))
1035 goto nla_put_failure;
Arik Nemtsov87bbbe22011-11-10 11:28:55 +02001036
Bruno Randolf7f531e02010-12-16 11:30:22 +09001037 if ((dev->wiphy.available_antennas_tx ||
1038 dev->wiphy.available_antennas_rx) && dev->ops->get_antenna) {
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001039 u32 tx_ant = 0, rx_ant = 0;
1040 int res;
Hila Gonene35e4d22012-06-27 17:19:42 +03001041 res = rdev_get_antenna(dev, &tx_ant, &rx_ant);
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001042 if (!res) {
David S. Miller9360ffd2012-03-29 04:41:26 -04001043 if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX,
1044 tx_ant) ||
1045 nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX,
1046 rx_ant))
1047 goto nla_put_failure;
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001048 }
1049 }
1050
Johannes Berg7527a782011-05-13 10:58:57 +02001051 if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
1052 dev->wiphy.interface_modes))
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -07001053 goto nla_put_failure;
1054
Johannes Bergee688b002008-01-24 19:38:39 +01001055 nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
1056 if (!nl_bands)
1057 goto nla_put_failure;
1058
1059 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
1060 if (!dev->wiphy.bands[band])
1061 continue;
1062
1063 nl_band = nla_nest_start(msg, band);
1064 if (!nl_band)
1065 goto nla_put_failure;
1066
Johannes Bergd51626d2008-10-09 12:20:13 +02001067 /* add HT info */
David S. Miller9360ffd2012-03-29 04:41:26 -04001068 if (dev->wiphy.bands[band]->ht_cap.ht_supported &&
1069 (nla_put(msg, NL80211_BAND_ATTR_HT_MCS_SET,
1070 sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
1071 &dev->wiphy.bands[band]->ht_cap.mcs) ||
1072 nla_put_u16(msg, NL80211_BAND_ATTR_HT_CAPA,
1073 dev->wiphy.bands[band]->ht_cap.cap) ||
1074 nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
1075 dev->wiphy.bands[band]->ht_cap.ampdu_factor) ||
1076 nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
1077 dev->wiphy.bands[band]->ht_cap.ampdu_density)))
1078 goto nla_put_failure;
Johannes Bergd51626d2008-10-09 12:20:13 +02001079
Mahesh Palivelabf0c111e2012-06-22 07:27:46 +00001080 /* add VHT info */
1081 if (dev->wiphy.bands[band]->vht_cap.vht_supported &&
1082 (nla_put(msg, NL80211_BAND_ATTR_VHT_MCS_SET,
1083 sizeof(dev->wiphy.bands[band]->vht_cap.vht_mcs),
1084 &dev->wiphy.bands[band]->vht_cap.vht_mcs) ||
1085 nla_put_u32(msg, NL80211_BAND_ATTR_VHT_CAPA,
1086 dev->wiphy.bands[band]->vht_cap.cap)))
1087 goto nla_put_failure;
1088
Johannes Bergee688b002008-01-24 19:38:39 +01001089 /* add frequencies */
1090 nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
1091 if (!nl_freqs)
1092 goto nla_put_failure;
1093
1094 for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
1095 nl_freq = nla_nest_start(msg, i);
1096 if (!nl_freq)
1097 goto nla_put_failure;
1098
1099 chan = &dev->wiphy.bands[band]->channels[i];
Johannes Bergee688b002008-01-24 19:38:39 +01001100
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -04001101 if (nl80211_msg_put_channel(msg, chan))
1102 goto nla_put_failure;
Jouni Malinene2f367f262008-11-21 19:01:30 +02001103
Johannes Bergee688b002008-01-24 19:38:39 +01001104 nla_nest_end(msg, nl_freq);
1105 }
1106
1107 nla_nest_end(msg, nl_freqs);
1108
1109 /* add bitrates */
1110 nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
1111 if (!nl_rates)
1112 goto nla_put_failure;
1113
1114 for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
1115 nl_rate = nla_nest_start(msg, i);
1116 if (!nl_rate)
1117 goto nla_put_failure;
1118
1119 rate = &dev->wiphy.bands[band]->bitrates[i];
David S. Miller9360ffd2012-03-29 04:41:26 -04001120 if (nla_put_u32(msg, NL80211_BITRATE_ATTR_RATE,
1121 rate->bitrate))
1122 goto nla_put_failure;
1123 if ((rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
1124 nla_put_flag(msg,
1125 NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE))
1126 goto nla_put_failure;
Johannes Bergee688b002008-01-24 19:38:39 +01001127
1128 nla_nest_end(msg, nl_rate);
1129 }
1130
1131 nla_nest_end(msg, nl_rates);
1132
1133 nla_nest_end(msg, nl_band);
1134 }
1135 nla_nest_end(msg, nl_bands);
1136
Johannes Berg8fdc6212009-03-14 09:34:01 +01001137 nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
1138 if (!nl_cmds)
1139 goto nla_put_failure;
1140
1141 i = 0;
1142#define CMD(op, n) \
1143 do { \
1144 if (dev->ops->op) { \
1145 i++; \
David S. Miller9360ffd2012-03-29 04:41:26 -04001146 if (nla_put_u32(msg, i, NL80211_CMD_ ## n)) \
1147 goto nla_put_failure; \
Johannes Berg8fdc6212009-03-14 09:34:01 +01001148 } \
1149 } while (0)
1150
1151 CMD(add_virtual_intf, NEW_INTERFACE);
1152 CMD(change_virtual_intf, SET_INTERFACE);
1153 CMD(add_key, NEW_KEY);
Johannes Berg88600202012-02-13 15:17:18 +01001154 CMD(start_ap, START_AP);
Johannes Berg8fdc6212009-03-14 09:34:01 +01001155 CMD(add_station, NEW_STATION);
1156 CMD(add_mpath, NEW_MPATH);
Javier Cardona24bdd9f2010-12-16 17:37:48 -08001157 CMD(update_mesh_config, SET_MESH_CONFIG);
Johannes Berg8fdc6212009-03-14 09:34:01 +01001158 CMD(change_bss, SET_BSS);
Jouni Malinen636a5d32009-03-19 13:39:22 +02001159 CMD(auth, AUTHENTICATE);
1160 CMD(assoc, ASSOCIATE);
1161 CMD(deauth, DEAUTHENTICATE);
1162 CMD(disassoc, DISASSOCIATE);
Johannes Berg04a773a2009-04-19 21:24:32 +02001163 CMD(join_ibss, JOIN_IBSS);
Johannes Berg29cbe682010-12-03 09:20:44 +01001164 CMD(join_mesh, JOIN_MESH);
Samuel Ortiz67fbb162009-11-24 23:59:15 +01001165 CMD(set_pmksa, SET_PMKSA);
1166 CMD(del_pmksa, DEL_PMKSA);
1167 CMD(flush_pmksa, FLUSH_PMKSA);
Johannes Berg7c4ef712011-11-18 15:33:48 +01001168 if (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)
1169 CMD(remain_on_channel, REMAIN_ON_CHANNEL);
Jouni Malinen13ae75b2009-12-29 12:59:45 +02001170 CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
Johannes Berg2e161f72010-08-12 15:38:38 +02001171 CMD(mgmt_tx, FRAME);
Johannes Bergf7ca38d2010-11-25 10:02:29 +01001172 CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL);
Johannes Berg5be83de2009-11-19 00:56:28 +01001173 if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
Johannes Berg463d0182009-07-14 00:33:35 +02001174 i++;
David S. Miller9360ffd2012-03-29 04:41:26 -04001175 if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS))
1176 goto nla_put_failure;
Johannes Berg463d0182009-07-14 00:33:35 +02001177 }
Johannes Berge8c9bd52012-06-06 08:18:22 +02001178 if (dev->ops->set_monitor_channel || dev->ops->start_ap ||
Johannes Bergcc1d2802012-05-16 23:50:20 +02001179 dev->ops->join_mesh) {
Johannes Bergaa430da2012-05-16 23:50:18 +02001180 i++;
1181 if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL))
1182 goto nla_put_failure;
1183 }
Bill Jordane8347eb2010-10-01 13:54:28 -04001184 CMD(set_wds_peer, SET_WDS_PEER);
Arik Nemtsov109086c2011-09-28 14:12:50 +03001185 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) {
1186 CMD(tdls_mgmt, TDLS_MGMT);
1187 CMD(tdls_oper, TDLS_OPER);
1188 }
Luciano Coelho807f8a82011-05-11 17:09:35 +03001189 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
1190 CMD(sched_scan_start, START_SCHED_SCAN);
Johannes Berg7f6cf312011-11-04 11:18:15 +01001191 CMD(probe_client, PROBE_CLIENT);
Simon Wunderlich1d9d9212011-11-18 14:20:43 +01001192 CMD(set_noack_map, SET_NOACK_MAP);
Johannes Berg5e7602302011-11-04 11:18:17 +01001193 if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) {
1194 i++;
David S. Miller9360ffd2012-03-29 04:41:26 -04001195 if (nla_put_u32(msg, i, NL80211_CMD_REGISTER_BEACONS))
1196 goto nla_put_failure;
Johannes Berg5e7602302011-11-04 11:18:17 +01001197 }
Johannes Berg98104fde2012-06-16 00:19:54 +02001198 CMD(start_p2p_device, START_P2P_DEVICE);
Antonio Quartullif4e583c2012-11-02 13:27:48 +01001199 CMD(set_mcast_rate, SET_MCAST_RATE);
Johannes Berg8fdc6212009-03-14 09:34:01 +01001200
Kalle Valo4745fc02011-11-17 19:06:10 +02001201#ifdef CONFIG_NL80211_TESTMODE
1202 CMD(testmode_cmd, TESTMODE);
1203#endif
1204
Johannes Berg8fdc6212009-03-14 09:34:01 +01001205#undef CMD
Samuel Ortizb23aa672009-07-01 21:26:54 +02001206
Johannes Berg6829c872009-07-02 09:13:27 +02001207 if (dev->ops->connect || dev->ops->auth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +02001208 i++;
David S. Miller9360ffd2012-03-29 04:41:26 -04001209 if (nla_put_u32(msg, i, NL80211_CMD_CONNECT))
1210 goto nla_put_failure;
Samuel Ortizb23aa672009-07-01 21:26:54 +02001211 }
1212
Johannes Berg6829c872009-07-02 09:13:27 +02001213 if (dev->ops->disconnect || dev->ops->deauth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +02001214 i++;
David S. Miller9360ffd2012-03-29 04:41:26 -04001215 if (nla_put_u32(msg, i, NL80211_CMD_DISCONNECT))
1216 goto nla_put_failure;
Samuel Ortizb23aa672009-07-01 21:26:54 +02001217 }
1218
Johannes Berg8fdc6212009-03-14 09:34:01 +01001219 nla_nest_end(msg, nl_cmds);
1220
Johannes Berg7c4ef712011-11-18 15:33:48 +01001221 if (dev->ops->remain_on_channel &&
David S. Miller9360ffd2012-03-29 04:41:26 -04001222 (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) &&
1223 nla_put_u32(msg, NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
1224 dev->wiphy.max_remain_on_channel_duration))
1225 goto nla_put_failure;
Johannes Berga2939112010-12-14 17:54:28 +01001226
David S. Miller9360ffd2012-03-29 04:41:26 -04001227 if ((dev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX) &&
1228 nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK))
1229 goto nla_put_failure;
Johannes Bergf7ca38d2010-11-25 10:02:29 +01001230
Johannes Berg2e161f72010-08-12 15:38:38 +02001231 if (mgmt_stypes) {
1232 u16 stypes;
1233 struct nlattr *nl_ftypes, *nl_ifs;
1234 enum nl80211_iftype ift;
1235
1236 nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES);
1237 if (!nl_ifs)
1238 goto nla_put_failure;
1239
1240 for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
1241 nl_ftypes = nla_nest_start(msg, ift);
1242 if (!nl_ftypes)
1243 goto nla_put_failure;
1244 i = 0;
1245 stypes = mgmt_stypes[ift].tx;
1246 while (stypes) {
David S. Miller9360ffd2012-03-29 04:41:26 -04001247 if ((stypes & 1) &&
1248 nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
1249 (i << 4) | IEEE80211_FTYPE_MGMT))
1250 goto nla_put_failure;
Johannes Berg2e161f72010-08-12 15:38:38 +02001251 stypes >>= 1;
1252 i++;
1253 }
1254 nla_nest_end(msg, nl_ftypes);
1255 }
1256
Johannes Berg74b70a42010-08-24 12:15:53 +02001257 nla_nest_end(msg, nl_ifs);
1258
Johannes Berg2e161f72010-08-12 15:38:38 +02001259 nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES);
1260 if (!nl_ifs)
1261 goto nla_put_failure;
1262
1263 for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
1264 nl_ftypes = nla_nest_start(msg, ift);
1265 if (!nl_ftypes)
1266 goto nla_put_failure;
1267 i = 0;
1268 stypes = mgmt_stypes[ift].rx;
1269 while (stypes) {
David S. Miller9360ffd2012-03-29 04:41:26 -04001270 if ((stypes & 1) &&
1271 nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
1272 (i << 4) | IEEE80211_FTYPE_MGMT))
1273 goto nla_put_failure;
Johannes Berg2e161f72010-08-12 15:38:38 +02001274 stypes >>= 1;
1275 i++;
1276 }
1277 nla_nest_end(msg, nl_ftypes);
1278 }
1279 nla_nest_end(msg, nl_ifs);
1280 }
1281
Johannes Bergdfb89c52012-06-27 09:23:48 +02001282#ifdef CONFIG_PM
Johannes Bergff1b6e62011-05-04 15:37:28 +02001283 if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) {
1284 struct nlattr *nl_wowlan;
1285
1286 nl_wowlan = nla_nest_start(msg,
1287 NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
1288 if (!nl_wowlan)
1289 goto nla_put_failure;
1290
David S. Miller9360ffd2012-03-29 04:41:26 -04001291 if (((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY) &&
1292 nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
1293 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT) &&
1294 nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
1295 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) &&
1296 nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
1297 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&
1298 nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED)) ||
1299 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
1300 nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
1301 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&
1302 nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
1303 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
1304 nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
1305 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
1306 nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
1307 goto nla_put_failure;
Johannes Bergff1b6e62011-05-04 15:37:28 +02001308 if (dev->wiphy.wowlan.n_patterns) {
1309 struct nl80211_wowlan_pattern_support pat = {
1310 .max_patterns = dev->wiphy.wowlan.n_patterns,
1311 .min_pattern_len =
1312 dev->wiphy.wowlan.pattern_min_len,
1313 .max_pattern_len =
1314 dev->wiphy.wowlan.pattern_max_len,
Amitkumar Karwarbb92d192013-02-12 12:16:26 -08001315 .max_pkt_offset =
1316 dev->wiphy.wowlan.max_pkt_offset,
Johannes Bergff1b6e62011-05-04 15:37:28 +02001317 };
David S. Miller9360ffd2012-03-29 04:41:26 -04001318 if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
1319 sizeof(pat), &pat))
1320 goto nla_put_failure;
Johannes Bergff1b6e62011-05-04 15:37:28 +02001321 }
1322
Johannes Berg2a0e0472013-01-23 22:57:40 +01001323 if (nl80211_send_wowlan_tcp_caps(dev, msg))
1324 goto nla_put_failure;
1325
Johannes Bergff1b6e62011-05-04 15:37:28 +02001326 nla_nest_end(msg, nl_wowlan);
1327 }
Johannes Bergdfb89c52012-06-27 09:23:48 +02001328#endif
Johannes Bergff1b6e62011-05-04 15:37:28 +02001329
Johannes Berg7527a782011-05-13 10:58:57 +02001330 if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
1331 dev->wiphy.software_iftypes))
1332 goto nla_put_failure;
1333
1334 if (nl80211_put_iface_combinations(&dev->wiphy, msg))
1335 goto nla_put_failure;
1336
David S. Miller9360ffd2012-03-29 04:41:26 -04001337 if ((dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) &&
1338 nla_put_u32(msg, NL80211_ATTR_DEVICE_AP_SME,
1339 dev->wiphy.ap_sme_capa))
1340 goto nla_put_failure;
Johannes Berg562a7482011-11-07 12:39:33 +01001341
David S. Miller9360ffd2012-03-29 04:41:26 -04001342 if (nla_put_u32(msg, NL80211_ATTR_FEATURE_FLAGS,
1343 dev->wiphy.features))
1344 goto nla_put_failure;
Johannes Berg1f074bd2011-11-06 14:13:33 +01001345
David S. Miller9360ffd2012-03-29 04:41:26 -04001346 if (dev->wiphy.ht_capa_mod_mask &&
1347 nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK,
1348 sizeof(*dev->wiphy.ht_capa_mod_mask),
1349 dev->wiphy.ht_capa_mod_mask))
1350 goto nla_put_failure;
Ben Greear7e7c8922011-11-18 11:31:59 -08001351
Vasanthakumar Thiagarajan77765ea2013-01-18 11:18:45 +05301352 if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME &&
1353 dev->wiphy.max_acl_mac_addrs &&
1354 nla_put_u32(msg, NL80211_ATTR_MAC_ACL_MAX,
1355 dev->wiphy.max_acl_mac_addrs))
1356 goto nla_put_failure;
1357
Johannes Berga50df0c2013-02-11 14:20:05 +01001358 if (dev->wiphy.extended_capabilities &&
1359 (nla_put(msg, NL80211_ATTR_EXT_CAPA,
1360 dev->wiphy.extended_capabilities_len,
1361 dev->wiphy.extended_capabilities) ||
1362 nla_put(msg, NL80211_ATTR_EXT_CAPA_MASK,
1363 dev->wiphy.extended_capabilities_len,
1364 dev->wiphy.extended_capabilities_mask)))
1365 goto nla_put_failure;
1366
Johannes Berg55682962007-09-20 13:09:35 -04001367 return genlmsg_end(msg, hdr);
1368
1369 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001370 genlmsg_cancel(msg, hdr);
1371 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -04001372}
1373
1374static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
1375{
1376 int idx = 0;
1377 int start = cb->args[0];
1378 struct cfg80211_registered_device *dev;
1379
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001380 mutex_lock(&cfg80211_mutex);
Johannes Berg79c97e92009-07-07 03:56:12 +02001381 list_for_each_entry(dev, &cfg80211_rdev_list, list) {
Johannes Berg463d0182009-07-14 00:33:35 +02001382 if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
1383 continue;
Julius Volzb4637272008-07-08 14:02:19 +02001384 if (++idx <= start)
Johannes Berg55682962007-09-20 13:09:35 -04001385 continue;
Eric W. Biederman15e47302012-09-07 20:12:54 +00001386 if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).portid,
Johannes Berg55682962007-09-20 13:09:35 -04001387 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Julius Volzb4637272008-07-08 14:02:19 +02001388 dev) < 0) {
1389 idx--;
Johannes Berg55682962007-09-20 13:09:35 -04001390 break;
Julius Volzb4637272008-07-08 14:02:19 +02001391 }
Johannes Berg55682962007-09-20 13:09:35 -04001392 }
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001393 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -04001394
1395 cb->args[0] = idx;
1396
1397 return skb->len;
1398}
1399
1400static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
1401{
1402 struct sk_buff *msg;
Johannes Berg4c476992010-10-04 21:36:35 +02001403 struct cfg80211_registered_device *dev = info->user_ptr[0];
Johannes Berg55682962007-09-20 13:09:35 -04001404
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001405 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04001406 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02001407 return -ENOMEM;
Johannes Berg55682962007-09-20 13:09:35 -04001408
Eric W. Biederman15e47302012-09-07 20:12:54 +00001409 if (nl80211_send_wiphy(msg, info->snd_portid, info->snd_seq, 0, dev) < 0) {
Johannes Berg4c476992010-10-04 21:36:35 +02001410 nlmsg_free(msg);
1411 return -ENOBUFS;
1412 }
Johannes Berg55682962007-09-20 13:09:35 -04001413
Johannes Berg134e6372009-07-10 09:51:34 +00001414 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -04001415}
1416
Jouni Malinen31888482008-10-30 16:59:24 +02001417static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
1418 [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
1419 [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
1420 [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
1421 [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
1422 [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
1423};
1424
1425static int parse_txq_params(struct nlattr *tb[],
1426 struct ieee80211_txq_params *txq_params)
1427{
Johannes Berga3304b02012-03-28 11:04:24 +02001428 if (!tb[NL80211_TXQ_ATTR_AC] || !tb[NL80211_TXQ_ATTR_TXOP] ||
Jouni Malinen31888482008-10-30 16:59:24 +02001429 !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
1430 !tb[NL80211_TXQ_ATTR_AIFS])
1431 return -EINVAL;
1432
Johannes Berga3304b02012-03-28 11:04:24 +02001433 txq_params->ac = nla_get_u8(tb[NL80211_TXQ_ATTR_AC]);
Jouni Malinen31888482008-10-30 16:59:24 +02001434 txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
1435 txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
1436 txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
1437 txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
1438
Johannes Berga3304b02012-03-28 11:04:24 +02001439 if (txq_params->ac >= NL80211_NUM_ACS)
1440 return -EINVAL;
1441
Jouni Malinen31888482008-10-30 16:59:24 +02001442 return 0;
1443}
1444
Johannes Bergf444de02010-05-05 15:25:02 +02001445static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
1446{
1447 /*
Johannes Bergcc1d2802012-05-16 23:50:20 +02001448 * You can only set the channel explicitly for WDS interfaces,
1449 * all others have their channel managed via their respective
1450 * "establish a connection" command (connect, join, ...)
1451 *
1452 * For AP/GO and mesh mode, the channel can be set with the
1453 * channel userspace API, but is only stored and passed to the
1454 * low-level driver when the AP starts or the mesh is joined.
1455 * This is for backward compatibility, userspace can also give
1456 * the channel in the start-ap or join-mesh commands instead.
Johannes Bergf444de02010-05-05 15:25:02 +02001457 *
1458 * Monitors are special as they are normally slaved to
Johannes Berge8c9bd52012-06-06 08:18:22 +02001459 * whatever else is going on, so they have their own special
1460 * operation to set the monitor channel if possible.
Johannes Bergf444de02010-05-05 15:25:02 +02001461 */
1462 return !wdev ||
1463 wdev->iftype == NL80211_IFTYPE_AP ||
Johannes Bergf444de02010-05-05 15:25:02 +02001464 wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
Johannes Berg074ac8d2010-09-16 14:58:22 +02001465 wdev->iftype == NL80211_IFTYPE_MONITOR ||
1466 wdev->iftype == NL80211_IFTYPE_P2P_GO;
Johannes Bergf444de02010-05-05 15:25:02 +02001467}
1468
Johannes Berg683b6d32012-11-08 21:25:48 +01001469static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
1470 struct genl_info *info,
1471 struct cfg80211_chan_def *chandef)
1472{
Mahesh Paliveladbeca2e2012-11-29 14:11:07 +05301473 u32 control_freq;
Johannes Berg683b6d32012-11-08 21:25:48 +01001474
1475 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
1476 return -EINVAL;
1477
1478 control_freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
1479
1480 chandef->chan = ieee80211_get_channel(&rdev->wiphy, control_freq);
Johannes Berg3d9d1d62012-11-08 23:14:50 +01001481 chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
1482 chandef->center_freq1 = control_freq;
1483 chandef->center_freq2 = 0;
Johannes Berg683b6d32012-11-08 21:25:48 +01001484
1485 /* Primary channel not allowed */
1486 if (!chandef->chan || chandef->chan->flags & IEEE80211_CHAN_DISABLED)
1487 return -EINVAL;
1488
Johannes Berg3d9d1d62012-11-08 23:14:50 +01001489 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
1490 enum nl80211_channel_type chantype;
Johannes Berg683b6d32012-11-08 21:25:48 +01001491
Johannes Berg3d9d1d62012-11-08 23:14:50 +01001492 chantype = nla_get_u32(
1493 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
1494
1495 switch (chantype) {
1496 case NL80211_CHAN_NO_HT:
1497 case NL80211_CHAN_HT20:
1498 case NL80211_CHAN_HT40PLUS:
1499 case NL80211_CHAN_HT40MINUS:
1500 cfg80211_chandef_create(chandef, chandef->chan,
1501 chantype);
1502 break;
1503 default:
Johannes Berg683b6d32012-11-08 21:25:48 +01001504 return -EINVAL;
Johannes Berg683b6d32012-11-08 21:25:48 +01001505 }
Johannes Berg3d9d1d62012-11-08 23:14:50 +01001506 } else if (info->attrs[NL80211_ATTR_CHANNEL_WIDTH]) {
1507 chandef->width =
1508 nla_get_u32(info->attrs[NL80211_ATTR_CHANNEL_WIDTH]);
1509 if (info->attrs[NL80211_ATTR_CENTER_FREQ1])
1510 chandef->center_freq1 =
1511 nla_get_u32(
1512 info->attrs[NL80211_ATTR_CENTER_FREQ1]);
1513 if (info->attrs[NL80211_ATTR_CENTER_FREQ2])
1514 chandef->center_freq2 =
1515 nla_get_u32(
1516 info->attrs[NL80211_ATTR_CENTER_FREQ2]);
1517 }
1518
Johannes Berg9f5e8f62012-11-22 16:59:45 +01001519 if (!cfg80211_chandef_valid(chandef))
Johannes Berg3d9d1d62012-11-08 23:14:50 +01001520 return -EINVAL;
1521
Johannes Berg9f5e8f62012-11-22 16:59:45 +01001522 if (!cfg80211_chandef_usable(&rdev->wiphy, chandef,
1523 IEEE80211_CHAN_DISABLED))
Johannes Berg3d9d1d62012-11-08 23:14:50 +01001524 return -EINVAL;
Johannes Berg3d9d1d62012-11-08 23:14:50 +01001525
Johannes Berg683b6d32012-11-08 21:25:48 +01001526 return 0;
1527}
1528
Johannes Bergf444de02010-05-05 15:25:02 +02001529static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
1530 struct wireless_dev *wdev,
1531 struct genl_info *info)
1532{
Johannes Berg683b6d32012-11-08 21:25:48 +01001533 struct cfg80211_chan_def chandef;
Johannes Bergf444de02010-05-05 15:25:02 +02001534 int result;
Johannes Berge8c9bd52012-06-06 08:18:22 +02001535 enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR;
1536
1537 if (wdev)
1538 iftype = wdev->iftype;
Johannes Bergf444de02010-05-05 15:25:02 +02001539
Johannes Bergf444de02010-05-05 15:25:02 +02001540 if (!nl80211_can_set_dev_channel(wdev))
1541 return -EOPNOTSUPP;
1542
Johannes Berg683b6d32012-11-08 21:25:48 +01001543 result = nl80211_parse_chandef(rdev, info, &chandef);
1544 if (result)
1545 return result;
Johannes Bergf444de02010-05-05 15:25:02 +02001546
1547 mutex_lock(&rdev->devlist_mtx);
Johannes Berge8c9bd52012-06-06 08:18:22 +02001548 switch (iftype) {
Johannes Bergaa430da2012-05-16 23:50:18 +02001549 case NL80211_IFTYPE_AP:
1550 case NL80211_IFTYPE_P2P_GO:
1551 if (wdev->beacon_interval) {
1552 result = -EBUSY;
1553 break;
1554 }
Johannes Berg683b6d32012-11-08 21:25:48 +01001555 if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef)) {
Johannes Bergaa430da2012-05-16 23:50:18 +02001556 result = -EINVAL;
1557 break;
1558 }
Johannes Berg683b6d32012-11-08 21:25:48 +01001559 wdev->preset_chandef = chandef;
Johannes Bergaa430da2012-05-16 23:50:18 +02001560 result = 0;
1561 break;
Johannes Bergcc1d2802012-05-16 23:50:20 +02001562 case NL80211_IFTYPE_MESH_POINT:
Johannes Berg683b6d32012-11-08 21:25:48 +01001563 result = cfg80211_set_mesh_channel(rdev, wdev, &chandef);
Johannes Bergcc1d2802012-05-16 23:50:20 +02001564 break;
Johannes Berge8c9bd52012-06-06 08:18:22 +02001565 case NL80211_IFTYPE_MONITOR:
Johannes Berg683b6d32012-11-08 21:25:48 +01001566 result = cfg80211_set_monitor_channel(rdev, &chandef);
Johannes Berge8c9bd52012-06-06 08:18:22 +02001567 break;
Johannes Bergaa430da2012-05-16 23:50:18 +02001568 default:
Johannes Berge8c9bd52012-06-06 08:18:22 +02001569 result = -EINVAL;
Johannes Bergf444de02010-05-05 15:25:02 +02001570 }
1571 mutex_unlock(&rdev->devlist_mtx);
1572
1573 return result;
1574}
1575
1576static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info)
1577{
Johannes Berg4c476992010-10-04 21:36:35 +02001578 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1579 struct net_device *netdev = info->user_ptr[1];
Johannes Bergf444de02010-05-05 15:25:02 +02001580
Johannes Berg4c476992010-10-04 21:36:35 +02001581 return __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info);
Johannes Bergf444de02010-05-05 15:25:02 +02001582}
1583
Bill Jordane8347eb2010-10-01 13:54:28 -04001584static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info)
1585{
Johannes Berg43b19952010-10-07 13:10:30 +02001586 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1587 struct net_device *dev = info->user_ptr[1];
1588 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Berg388ac772010-10-07 13:11:09 +02001589 const u8 *bssid;
Bill Jordane8347eb2010-10-01 13:54:28 -04001590
1591 if (!info->attrs[NL80211_ATTR_MAC])
1592 return -EINVAL;
1593
Johannes Berg43b19952010-10-07 13:10:30 +02001594 if (netif_running(dev))
1595 return -EBUSY;
Bill Jordane8347eb2010-10-01 13:54:28 -04001596
Johannes Berg43b19952010-10-07 13:10:30 +02001597 if (!rdev->ops->set_wds_peer)
1598 return -EOPNOTSUPP;
Bill Jordane8347eb2010-10-01 13:54:28 -04001599
Johannes Berg43b19952010-10-07 13:10:30 +02001600 if (wdev->iftype != NL80211_IFTYPE_WDS)
1601 return -EOPNOTSUPP;
Bill Jordane8347eb2010-10-01 13:54:28 -04001602
1603 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Hila Gonene35e4d22012-06-27 17:19:42 +03001604 return rdev_set_wds_peer(rdev, dev, bssid);
Bill Jordane8347eb2010-10-01 13:54:28 -04001605}
1606
1607
Johannes Berg55682962007-09-20 13:09:35 -04001608static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
1609{
1610 struct cfg80211_registered_device *rdev;
Johannes Bergf444de02010-05-05 15:25:02 +02001611 struct net_device *netdev = NULL;
1612 struct wireless_dev *wdev;
Bill Jordana1e567c2010-09-10 11:22:32 -04001613 int result = 0, rem_txq_params = 0;
Jouni Malinen31888482008-10-30 16:59:24 +02001614 struct nlattr *nl_txq_params;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001615 u32 changed;
1616 u8 retry_short = 0, retry_long = 0;
1617 u32 frag_threshold = 0, rts_threshold = 0;
Lukáš Turek81077e82009-12-21 22:50:47 +01001618 u8 coverage_class = 0;
Johannes Berg55682962007-09-20 13:09:35 -04001619
Johannes Bergf444de02010-05-05 15:25:02 +02001620 /*
1621 * Try to find the wiphy and netdev. Normally this
1622 * function shouldn't need the netdev, but this is
1623 * done for backward compatibility -- previously
1624 * setting the channel was done per wiphy, but now
1625 * it is per netdev. Previous userland like hostapd
1626 * also passed a netdev to set_wiphy, so that it is
1627 * possible to let that go to the right netdev!
1628 */
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001629 mutex_lock(&cfg80211_mutex);
1630
Johannes Bergf444de02010-05-05 15:25:02 +02001631 if (info->attrs[NL80211_ATTR_IFINDEX]) {
1632 int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
1633
1634 netdev = dev_get_by_index(genl_info_net(info), ifindex);
1635 if (netdev && netdev->ieee80211_ptr) {
1636 rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy);
1637 mutex_lock(&rdev->mtx);
1638 } else
1639 netdev = NULL;
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001640 }
1641
Johannes Bergf444de02010-05-05 15:25:02 +02001642 if (!netdev) {
Johannes Berg878d9ec2012-06-15 14:18:32 +02001643 rdev = __cfg80211_rdev_from_attrs(genl_info_net(info),
1644 info->attrs);
Johannes Bergf444de02010-05-05 15:25:02 +02001645 if (IS_ERR(rdev)) {
1646 mutex_unlock(&cfg80211_mutex);
Johannes Berg4c476992010-10-04 21:36:35 +02001647 return PTR_ERR(rdev);
Johannes Bergf444de02010-05-05 15:25:02 +02001648 }
1649 wdev = NULL;
1650 netdev = NULL;
1651 result = 0;
1652
1653 mutex_lock(&rdev->mtx);
Johannes Berg71fe96b2012-10-24 10:04:58 +02001654 } else
Johannes Bergf444de02010-05-05 15:25:02 +02001655 wdev = netdev->ieee80211_ptr;
Johannes Bergf444de02010-05-05 15:25:02 +02001656
1657 /*
1658 * end workaround code, by now the rdev is available
1659 * and locked, and wdev may or may not be NULL.
1660 */
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001661
1662 if (info->attrs[NL80211_ATTR_WIPHY_NAME])
Jouni Malinen31888482008-10-30 16:59:24 +02001663 result = cfg80211_dev_rename(
1664 rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001665
1666 mutex_unlock(&cfg80211_mutex);
1667
1668 if (result)
1669 goto bad_res;
Johannes Berg55682962007-09-20 13:09:35 -04001670
Jouni Malinen31888482008-10-30 16:59:24 +02001671 if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
1672 struct ieee80211_txq_params txq_params;
1673 struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
1674
1675 if (!rdev->ops->set_txq_params) {
1676 result = -EOPNOTSUPP;
1677 goto bad_res;
1678 }
1679
Eliad Pellerf70f01c2011-09-25 20:06:53 +03001680 if (!netdev) {
1681 result = -EINVAL;
1682 goto bad_res;
1683 }
1684
Johannes Berg133a3ff2011-11-03 14:50:13 +01001685 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
1686 netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
1687 result = -EINVAL;
1688 goto bad_res;
1689 }
1690
Johannes Berg2b5f8b02012-04-02 10:51:55 +02001691 if (!netif_running(netdev)) {
1692 result = -ENETDOWN;
1693 goto bad_res;
1694 }
1695
Jouni Malinen31888482008-10-30 16:59:24 +02001696 nla_for_each_nested(nl_txq_params,
1697 info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
1698 rem_txq_params) {
1699 nla_parse(tb, NL80211_TXQ_ATTR_MAX,
1700 nla_data(nl_txq_params),
1701 nla_len(nl_txq_params),
1702 txq_params_policy);
1703 result = parse_txq_params(tb, &txq_params);
1704 if (result)
1705 goto bad_res;
1706
Hila Gonene35e4d22012-06-27 17:19:42 +03001707 result = rdev_set_txq_params(rdev, netdev,
1708 &txq_params);
Jouni Malinen31888482008-10-30 16:59:24 +02001709 if (result)
1710 goto bad_res;
1711 }
1712 }
1713
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001714 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Johannes Berg71fe96b2012-10-24 10:04:58 +02001715 result = __nl80211_set_channel(rdev,
1716 nl80211_can_set_dev_channel(wdev) ? wdev : NULL,
1717 info);
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001718 if (result)
1719 goto bad_res;
1720 }
1721
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +03001722 if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) {
Johannes Bergc8442112012-10-24 10:17:18 +02001723 struct wireless_dev *txp_wdev = wdev;
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +03001724 enum nl80211_tx_power_setting type;
1725 int idx, mbm = 0;
1726
Johannes Bergc8442112012-10-24 10:17:18 +02001727 if (!(rdev->wiphy.features & NL80211_FEATURE_VIF_TXPOWER))
1728 txp_wdev = NULL;
1729
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +03001730 if (!rdev->ops->set_tx_power) {
Jiri Slaby60ea3852010-07-07 15:02:46 +02001731 result = -EOPNOTSUPP;
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +03001732 goto bad_res;
1733 }
1734
1735 idx = NL80211_ATTR_WIPHY_TX_POWER_SETTING;
1736 type = nla_get_u32(info->attrs[idx]);
1737
1738 if (!info->attrs[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] &&
1739 (type != NL80211_TX_POWER_AUTOMATIC)) {
1740 result = -EINVAL;
1741 goto bad_res;
1742 }
1743
1744 if (type != NL80211_TX_POWER_AUTOMATIC) {
1745 idx = NL80211_ATTR_WIPHY_TX_POWER_LEVEL;
1746 mbm = nla_get_u32(info->attrs[idx]);
1747 }
1748
Johannes Bergc8442112012-10-24 10:17:18 +02001749 result = rdev_set_tx_power(rdev, txp_wdev, type, mbm);
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +03001750 if (result)
1751 goto bad_res;
1752 }
1753
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001754 if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] &&
1755 info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) {
1756 u32 tx_ant, rx_ant;
Bruno Randolf7f531e02010-12-16 11:30:22 +09001757 if ((!rdev->wiphy.available_antennas_tx &&
1758 !rdev->wiphy.available_antennas_rx) ||
1759 !rdev->ops->set_antenna) {
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001760 result = -EOPNOTSUPP;
1761 goto bad_res;
1762 }
1763
1764 tx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]);
1765 rx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]);
1766
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001767 /* reject antenna configurations which don't match the
Bruno Randolf7f531e02010-12-16 11:30:22 +09001768 * available antenna masks, except for the "all" mask */
1769 if ((~tx_ant && (tx_ant & ~rdev->wiphy.available_antennas_tx)) ||
1770 (~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx))) {
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001771 result = -EINVAL;
1772 goto bad_res;
1773 }
1774
Bruno Randolf7f531e02010-12-16 11:30:22 +09001775 tx_ant = tx_ant & rdev->wiphy.available_antennas_tx;
1776 rx_ant = rx_ant & rdev->wiphy.available_antennas_rx;
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001777
Hila Gonene35e4d22012-06-27 17:19:42 +03001778 result = rdev_set_antenna(rdev, tx_ant, rx_ant);
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001779 if (result)
1780 goto bad_res;
1781 }
1782
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001783 changed = 0;
1784
1785 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {
1786 retry_short = nla_get_u8(
1787 info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]);
1788 if (retry_short == 0) {
1789 result = -EINVAL;
1790 goto bad_res;
1791 }
1792 changed |= WIPHY_PARAM_RETRY_SHORT;
1793 }
1794
1795 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {
1796 retry_long = nla_get_u8(
1797 info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]);
1798 if (retry_long == 0) {
1799 result = -EINVAL;
1800 goto bad_res;
1801 }
1802 changed |= WIPHY_PARAM_RETRY_LONG;
1803 }
1804
1805 if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
1806 frag_threshold = nla_get_u32(
1807 info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
1808 if (frag_threshold < 256) {
1809 result = -EINVAL;
1810 goto bad_res;
1811 }
1812 if (frag_threshold != (u32) -1) {
1813 /*
1814 * Fragments (apart from the last one) are required to
1815 * have even length. Make the fragmentation code
1816 * simpler by stripping LSB should someone try to use
1817 * odd threshold value.
1818 */
1819 frag_threshold &= ~0x1;
1820 }
1821 changed |= WIPHY_PARAM_FRAG_THRESHOLD;
1822 }
1823
1824 if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
1825 rts_threshold = nla_get_u32(
1826 info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
1827 changed |= WIPHY_PARAM_RTS_THRESHOLD;
1828 }
1829
Lukáš Turek81077e82009-12-21 22:50:47 +01001830 if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) {
1831 coverage_class = nla_get_u8(
1832 info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]);
1833 changed |= WIPHY_PARAM_COVERAGE_CLASS;
1834 }
1835
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001836 if (changed) {
1837 u8 old_retry_short, old_retry_long;
1838 u32 old_frag_threshold, old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001839 u8 old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001840
1841 if (!rdev->ops->set_wiphy_params) {
1842 result = -EOPNOTSUPP;
1843 goto bad_res;
1844 }
1845
1846 old_retry_short = rdev->wiphy.retry_short;
1847 old_retry_long = rdev->wiphy.retry_long;
1848 old_frag_threshold = rdev->wiphy.frag_threshold;
1849 old_rts_threshold = rdev->wiphy.rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001850 old_coverage_class = rdev->wiphy.coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001851
1852 if (changed & WIPHY_PARAM_RETRY_SHORT)
1853 rdev->wiphy.retry_short = retry_short;
1854 if (changed & WIPHY_PARAM_RETRY_LONG)
1855 rdev->wiphy.retry_long = retry_long;
1856 if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
1857 rdev->wiphy.frag_threshold = frag_threshold;
1858 if (changed & WIPHY_PARAM_RTS_THRESHOLD)
1859 rdev->wiphy.rts_threshold = rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001860 if (changed & WIPHY_PARAM_COVERAGE_CLASS)
1861 rdev->wiphy.coverage_class = coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001862
Hila Gonene35e4d22012-06-27 17:19:42 +03001863 result = rdev_set_wiphy_params(rdev, changed);
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001864 if (result) {
1865 rdev->wiphy.retry_short = old_retry_short;
1866 rdev->wiphy.retry_long = old_retry_long;
1867 rdev->wiphy.frag_threshold = old_frag_threshold;
1868 rdev->wiphy.rts_threshold = old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001869 rdev->wiphy.coverage_class = old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001870 }
1871 }
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001872
Johannes Berg306d6112008-12-08 12:39:04 +01001873 bad_res:
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001874 mutex_unlock(&rdev->mtx);
Johannes Bergf444de02010-05-05 15:25:02 +02001875 if (netdev)
1876 dev_put(netdev);
Johannes Berg55682962007-09-20 13:09:35 -04001877 return result;
1878}
1879
Johannes Berg71bbc992012-06-15 15:30:18 +02001880static inline u64 wdev_id(struct wireless_dev *wdev)
1881{
1882 return (u64)wdev->identifier |
1883 ((u64)wiphy_to_dev(wdev->wiphy)->wiphy_idx << 32);
1884}
Johannes Berg55682962007-09-20 13:09:35 -04001885
Johannes Berg683b6d32012-11-08 21:25:48 +01001886static int nl80211_send_chandef(struct sk_buff *msg,
1887 struct cfg80211_chan_def *chandef)
1888{
Johannes Berg9f5e8f62012-11-22 16:59:45 +01001889 WARN_ON(!cfg80211_chandef_valid(chandef));
Johannes Berg3d9d1d62012-11-08 23:14:50 +01001890
Johannes Berg683b6d32012-11-08 21:25:48 +01001891 if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
1892 chandef->chan->center_freq))
1893 return -ENOBUFS;
Johannes Berg3d9d1d62012-11-08 23:14:50 +01001894 switch (chandef->width) {
1895 case NL80211_CHAN_WIDTH_20_NOHT:
1896 case NL80211_CHAN_WIDTH_20:
1897 case NL80211_CHAN_WIDTH_40:
1898 if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
1899 cfg80211_get_chandef_type(chandef)))
1900 return -ENOBUFS;
1901 break;
1902 default:
1903 break;
1904 }
1905 if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, chandef->width))
1906 return -ENOBUFS;
1907 if (nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1, chandef->center_freq1))
1908 return -ENOBUFS;
1909 if (chandef->center_freq2 &&
1910 nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2, chandef->center_freq2))
Johannes Berg683b6d32012-11-08 21:25:48 +01001911 return -ENOBUFS;
1912 return 0;
1913}
1914
Eric W. Biederman15e47302012-09-07 20:12:54 +00001915static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
Johannes Bergd7264052009-04-19 16:23:20 +02001916 struct cfg80211_registered_device *rdev,
Johannes Berg72fb2ab2012-06-15 17:52:47 +02001917 struct wireless_dev *wdev)
Johannes Berg55682962007-09-20 13:09:35 -04001918{
Johannes Berg72fb2ab2012-06-15 17:52:47 +02001919 struct net_device *dev = wdev->netdev;
Johannes Berg55682962007-09-20 13:09:35 -04001920 void *hdr;
1921
Eric W. Biederman15e47302012-09-07 20:12:54 +00001922 hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_INTERFACE);
Johannes Berg55682962007-09-20 13:09:35 -04001923 if (!hdr)
1924 return -1;
1925
Johannes Berg72fb2ab2012-06-15 17:52:47 +02001926 if (dev &&
1927 (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
Johannes Berg98104fde2012-06-16 00:19:54 +02001928 nla_put_string(msg, NL80211_ATTR_IFNAME, dev->name)))
Johannes Berg72fb2ab2012-06-15 17:52:47 +02001929 goto nla_put_failure;
1930
1931 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
1932 nla_put_u32(msg, NL80211_ATTR_IFTYPE, wdev->iftype) ||
Johannes Berg71bbc992012-06-15 15:30:18 +02001933 nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) ||
Johannes Berg98104fde2012-06-16 00:19:54 +02001934 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, wdev_address(wdev)) ||
David S. Miller9360ffd2012-03-29 04:41:26 -04001935 nla_put_u32(msg, NL80211_ATTR_GENERATION,
1936 rdev->devlist_generation ^
1937 (cfg80211_rdev_list_generation << 2)))
1938 goto nla_put_failure;
Johannes Bergf5ea9122009-08-07 16:17:38 +02001939
Johannes Berg5b7ccaf2012-07-12 19:45:08 +02001940 if (rdev->ops->get_channel) {
Johannes Berg683b6d32012-11-08 21:25:48 +01001941 int ret;
1942 struct cfg80211_chan_def chandef;
Johannes Berg5b7ccaf2012-07-12 19:45:08 +02001943
Johannes Berg683b6d32012-11-08 21:25:48 +01001944 ret = rdev_get_channel(rdev, wdev, &chandef);
1945 if (ret == 0) {
1946 if (nl80211_send_chandef(msg, &chandef))
1947 goto nla_put_failure;
1948 }
Pontus Fuchsd91df0e2012-04-03 16:39:58 +02001949 }
1950
Antonio Quartullib84e7a02012-11-07 12:52:20 +01001951 if (wdev->ssid_len) {
1952 if (nla_put(msg, NL80211_ATTR_SSID, wdev->ssid_len, wdev->ssid))
1953 goto nla_put_failure;
1954 }
1955
Johannes Berg55682962007-09-20 13:09:35 -04001956 return genlmsg_end(msg, hdr);
1957
1958 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001959 genlmsg_cancel(msg, hdr);
1960 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -04001961}
1962
1963static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
1964{
1965 int wp_idx = 0;
1966 int if_idx = 0;
1967 int wp_start = cb->args[0];
1968 int if_start = cb->args[1];
Johannes Bergf5ea9122009-08-07 16:17:38 +02001969 struct cfg80211_registered_device *rdev;
Johannes Berg55682962007-09-20 13:09:35 -04001970 struct wireless_dev *wdev;
1971
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001972 mutex_lock(&cfg80211_mutex);
Johannes Bergf5ea9122009-08-07 16:17:38 +02001973 list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
1974 if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
Johannes Berg463d0182009-07-14 00:33:35 +02001975 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001976 if (wp_idx < wp_start) {
1977 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001978 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001979 }
Johannes Berg55682962007-09-20 13:09:35 -04001980 if_idx = 0;
1981
Johannes Bergf5ea9122009-08-07 16:17:38 +02001982 mutex_lock(&rdev->devlist_mtx);
Johannes Berg89a54e42012-06-15 14:33:17 +02001983 list_for_each_entry(wdev, &rdev->wdev_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +02001984 if (if_idx < if_start) {
1985 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001986 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001987 }
Eric W. Biederman15e47302012-09-07 20:12:54 +00001988 if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).portid,
Johannes Berg55682962007-09-20 13:09:35 -04001989 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Berg72fb2ab2012-06-15 17:52:47 +02001990 rdev, wdev) < 0) {
Johannes Bergf5ea9122009-08-07 16:17:38 +02001991 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001992 goto out;
1993 }
1994 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001995 }
Johannes Bergf5ea9122009-08-07 16:17:38 +02001996 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001997
1998 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001999 }
Johannes Bergbba95fe2008-07-29 13:22:51 +02002000 out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002001 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -04002002
2003 cb->args[0] = wp_idx;
2004 cb->args[1] = if_idx;
2005
2006 return skb->len;
2007}
2008
2009static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
2010{
2011 struct sk_buff *msg;
Johannes Berg4c476992010-10-04 21:36:35 +02002012 struct cfg80211_registered_device *dev = info->user_ptr[0];
Johannes Berg72fb2ab2012-06-15 17:52:47 +02002013 struct wireless_dev *wdev = info->user_ptr[1];
Johannes Berg55682962007-09-20 13:09:35 -04002014
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002015 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04002016 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02002017 return -ENOMEM;
Johannes Berg55682962007-09-20 13:09:35 -04002018
Eric W. Biederman15e47302012-09-07 20:12:54 +00002019 if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
Johannes Berg72fb2ab2012-06-15 17:52:47 +02002020 dev, wdev) < 0) {
Johannes Berg4c476992010-10-04 21:36:35 +02002021 nlmsg_free(msg);
2022 return -ENOBUFS;
2023 }
Johannes Berg55682962007-09-20 13:09:35 -04002024
Johannes Berg134e6372009-07-10 09:51:34 +00002025 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -04002026}
2027
Michael Wu66f7ac52008-01-31 19:48:22 +01002028static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
2029 [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
2030 [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
2031 [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
2032 [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
2033 [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
2034};
2035
2036static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
2037{
2038 struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
2039 int flag;
2040
2041 *mntrflags = 0;
2042
2043 if (!nla)
2044 return -EINVAL;
2045
2046 if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX,
2047 nla, mntr_flags_policy))
2048 return -EINVAL;
2049
2050 for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
2051 if (flags[flag])
2052 *mntrflags |= (1<<flag);
2053
2054 return 0;
2055}
2056
Johannes Berg9bc383d2009-11-19 11:55:19 +01002057static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev,
Johannes Bergad4bb6f2009-11-19 00:56:30 +01002058 struct net_device *netdev, u8 use_4addr,
2059 enum nl80211_iftype iftype)
Johannes Berg9bc383d2009-11-19 11:55:19 +01002060{
Johannes Bergad4bb6f2009-11-19 00:56:30 +01002061 if (!use_4addr) {
Jiri Pirkof350a0a82010-06-15 06:50:45 +00002062 if (netdev && (netdev->priv_flags & IFF_BRIDGE_PORT))
Johannes Bergad4bb6f2009-11-19 00:56:30 +01002063 return -EBUSY;
Johannes Berg9bc383d2009-11-19 11:55:19 +01002064 return 0;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01002065 }
Johannes Berg9bc383d2009-11-19 11:55:19 +01002066
2067 switch (iftype) {
2068 case NL80211_IFTYPE_AP_VLAN:
2069 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_AP)
2070 return 0;
2071 break;
2072 case NL80211_IFTYPE_STATION:
2073 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION)
2074 return 0;
2075 break;
2076 default:
2077 break;
2078 }
2079
2080 return -EOPNOTSUPP;
2081}
2082
Johannes Berg55682962007-09-20 13:09:35 -04002083static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
2084{
Johannes Berg4c476992010-10-04 21:36:35 +02002085 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002086 struct vif_params params;
Johannes Berge36d56b2009-06-09 21:04:43 +02002087 int err;
Johannes Berg04a773a2009-04-19 21:24:32 +02002088 enum nl80211_iftype otype, ntype;
Johannes Berg4c476992010-10-04 21:36:35 +02002089 struct net_device *dev = info->user_ptr[1];
Johannes Berg92ffe052008-09-16 20:39:36 +02002090 u32 _flags, *flags = NULL;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01002091 bool change = false;
Johannes Berg55682962007-09-20 13:09:35 -04002092
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002093 memset(&params, 0, sizeof(params));
2094
Johannes Berg04a773a2009-04-19 21:24:32 +02002095 otype = ntype = dev->ieee80211_ptr->iftype;
Johannes Berg55682962007-09-20 13:09:35 -04002096
Johannes Berg723b0382008-09-16 20:22:09 +02002097 if (info->attrs[NL80211_ATTR_IFTYPE]) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +01002098 ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
Johannes Berg04a773a2009-04-19 21:24:32 +02002099 if (otype != ntype)
Johannes Bergac7f9cf2009-03-21 17:07:59 +01002100 change = true;
Johannes Berg4c476992010-10-04 21:36:35 +02002101 if (ntype > NL80211_IFTYPE_MAX)
2102 return -EINVAL;
Johannes Berg723b0382008-09-16 20:22:09 +02002103 }
2104
Johannes Berg92ffe052008-09-16 20:39:36 +02002105 if (info->attrs[NL80211_ATTR_MESH_ID]) {
Johannes Berg29cbe682010-12-03 09:20:44 +01002106 struct wireless_dev *wdev = dev->ieee80211_ptr;
2107
Johannes Berg4c476992010-10-04 21:36:35 +02002108 if (ntype != NL80211_IFTYPE_MESH_POINT)
2109 return -EINVAL;
Johannes Berg29cbe682010-12-03 09:20:44 +01002110 if (netif_running(dev))
2111 return -EBUSY;
2112
2113 wdev_lock(wdev);
2114 BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
2115 IEEE80211_MAX_MESH_ID_LEN);
2116 wdev->mesh_id_up_len =
2117 nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
2118 memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
2119 wdev->mesh_id_up_len);
2120 wdev_unlock(wdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002121 }
2122
Felix Fietkau8b787642009-11-10 18:53:10 +01002123 if (info->attrs[NL80211_ATTR_4ADDR]) {
2124 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
2125 change = true;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01002126 err = nl80211_valid_4addr(rdev, dev, params.use_4addr, ntype);
Johannes Berg9bc383d2009-11-19 11:55:19 +01002127 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02002128 return err;
Felix Fietkau8b787642009-11-10 18:53:10 +01002129 } else {
2130 params.use_4addr = -1;
2131 }
2132
Johannes Berg92ffe052008-09-16 20:39:36 +02002133 if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
Johannes Berg4c476992010-10-04 21:36:35 +02002134 if (ntype != NL80211_IFTYPE_MONITOR)
2135 return -EINVAL;
Johannes Berg92ffe052008-09-16 20:39:36 +02002136 err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
2137 &_flags);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01002138 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02002139 return err;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01002140
2141 flags = &_flags;
2142 change = true;
Johannes Berg92ffe052008-09-16 20:39:36 +02002143 }
Johannes Berg3b858752009-03-12 09:55:09 +01002144
Johannes Bergac7f9cf2009-03-21 17:07:59 +01002145 if (change)
Johannes Berg3d54d252009-08-21 14:51:05 +02002146 err = cfg80211_change_iface(rdev, dev, ntype, flags, &params);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01002147 else
2148 err = 0;
Johannes Berg60719ff2008-09-16 14:55:09 +02002149
Johannes Berg9bc383d2009-11-19 11:55:19 +01002150 if (!err && params.use_4addr != -1)
2151 dev->ieee80211_ptr->use_4addr = params.use_4addr;
2152
Johannes Berg55682962007-09-20 13:09:35 -04002153 return err;
2154}
2155
2156static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
2157{
Johannes Berg4c476992010-10-04 21:36:35 +02002158 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002159 struct vif_params params;
Johannes Berg84efbb82012-06-16 00:00:26 +02002160 struct wireless_dev *wdev;
Johannes Berg1c90f9d2012-06-16 00:05:37 +02002161 struct sk_buff *msg;
Johannes Berg55682962007-09-20 13:09:35 -04002162 int err;
2163 enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
Michael Wu66f7ac52008-01-31 19:48:22 +01002164 u32 flags;
Johannes Berg55682962007-09-20 13:09:35 -04002165
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002166 memset(&params, 0, sizeof(params));
2167
Johannes Berg55682962007-09-20 13:09:35 -04002168 if (!info->attrs[NL80211_ATTR_IFNAME])
2169 return -EINVAL;
2170
2171 if (info->attrs[NL80211_ATTR_IFTYPE]) {
2172 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
2173 if (type > NL80211_IFTYPE_MAX)
2174 return -EINVAL;
2175 }
2176
Johannes Berg79c97e92009-07-07 03:56:12 +02002177 if (!rdev->ops->add_virtual_intf ||
Johannes Berg4c476992010-10-04 21:36:35 +02002178 !(rdev->wiphy.interface_modes & (1 << type)))
2179 return -EOPNOTSUPP;
Johannes Berg55682962007-09-20 13:09:35 -04002180
Arend van Spriel1c18f142013-01-08 10:17:27 +01002181 if (type == NL80211_IFTYPE_P2P_DEVICE && info->attrs[NL80211_ATTR_MAC]) {
2182 nla_memcpy(params.macaddr, info->attrs[NL80211_ATTR_MAC],
2183 ETH_ALEN);
2184 if (!is_valid_ether_addr(params.macaddr))
2185 return -EADDRNOTAVAIL;
2186 }
2187
Johannes Berg9bc383d2009-11-19 11:55:19 +01002188 if (info->attrs[NL80211_ATTR_4ADDR]) {
Felix Fietkau8b787642009-11-10 18:53:10 +01002189 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
Johannes Bergad4bb6f2009-11-19 00:56:30 +01002190 err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type);
Johannes Berg9bc383d2009-11-19 11:55:19 +01002191 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02002192 return err;
Johannes Berg9bc383d2009-11-19 11:55:19 +01002193 }
Felix Fietkau8b787642009-11-10 18:53:10 +01002194
Johannes Berg1c90f9d2012-06-16 00:05:37 +02002195 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
2196 if (!msg)
2197 return -ENOMEM;
2198
Michael Wu66f7ac52008-01-31 19:48:22 +01002199 err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
2200 info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
2201 &flags);
Hila Gonene35e4d22012-06-27 17:19:42 +03002202 wdev = rdev_add_virtual_intf(rdev,
2203 nla_data(info->attrs[NL80211_ATTR_IFNAME]),
2204 type, err ? NULL : &flags, &params);
Johannes Berg1c90f9d2012-06-16 00:05:37 +02002205 if (IS_ERR(wdev)) {
2206 nlmsg_free(msg);
Johannes Berg84efbb82012-06-16 00:00:26 +02002207 return PTR_ERR(wdev);
Johannes Berg1c90f9d2012-06-16 00:05:37 +02002208 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002209
Johannes Berg98104fde2012-06-16 00:19:54 +02002210 switch (type) {
2211 case NL80211_IFTYPE_MESH_POINT:
2212 if (!info->attrs[NL80211_ATTR_MESH_ID])
2213 break;
Johannes Berg29cbe682010-12-03 09:20:44 +01002214 wdev_lock(wdev);
2215 BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
2216 IEEE80211_MAX_MESH_ID_LEN);
2217 wdev->mesh_id_up_len =
2218 nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
2219 memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
2220 wdev->mesh_id_up_len);
2221 wdev_unlock(wdev);
Johannes Berg98104fde2012-06-16 00:19:54 +02002222 break;
2223 case NL80211_IFTYPE_P2P_DEVICE:
2224 /*
2225 * P2P Device doesn't have a netdev, so doesn't go
2226 * through the netdev notifier and must be added here
2227 */
2228 mutex_init(&wdev->mtx);
2229 INIT_LIST_HEAD(&wdev->event_list);
2230 spin_lock_init(&wdev->event_lock);
2231 INIT_LIST_HEAD(&wdev->mgmt_registrations);
2232 spin_lock_init(&wdev->mgmt_registrations_lock);
2233
2234 mutex_lock(&rdev->devlist_mtx);
2235 wdev->identifier = ++rdev->wdev_id;
2236 list_add_rcu(&wdev->list, &rdev->wdev_list);
2237 rdev->devlist_generation++;
2238 mutex_unlock(&rdev->devlist_mtx);
2239 break;
2240 default:
2241 break;
Johannes Berg29cbe682010-12-03 09:20:44 +01002242 }
2243
Eric W. Biederman15e47302012-09-07 20:12:54 +00002244 if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
Johannes Berg1c90f9d2012-06-16 00:05:37 +02002245 rdev, wdev) < 0) {
2246 nlmsg_free(msg);
2247 return -ENOBUFS;
2248 }
2249
2250 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -04002251}
2252
2253static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
2254{
Johannes Berg4c476992010-10-04 21:36:35 +02002255 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg84efbb82012-06-16 00:00:26 +02002256 struct wireless_dev *wdev = info->user_ptr[1];
Johannes Berg55682962007-09-20 13:09:35 -04002257
Johannes Berg4c476992010-10-04 21:36:35 +02002258 if (!rdev->ops->del_virtual_intf)
2259 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002260
Johannes Berg84efbb82012-06-16 00:00:26 +02002261 /*
2262 * If we remove a wireless device without a netdev then clear
2263 * user_ptr[1] so that nl80211_post_doit won't dereference it
2264 * to check if it needs to do dev_put(). Otherwise it crashes
2265 * since the wdev has been freed, unlike with a netdev where
2266 * we need the dev_put() for the netdev to really be freed.
2267 */
2268 if (!wdev->netdev)
2269 info->user_ptr[1] = NULL;
2270
Hila Gonene35e4d22012-06-27 17:19:42 +03002271 return rdev_del_virtual_intf(rdev, wdev);
Johannes Berg55682962007-09-20 13:09:35 -04002272}
2273
Simon Wunderlich1d9d9212011-11-18 14:20:43 +01002274static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info)
2275{
2276 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2277 struct net_device *dev = info->user_ptr[1];
2278 u16 noack_map;
2279
2280 if (!info->attrs[NL80211_ATTR_NOACK_MAP])
2281 return -EINVAL;
2282
2283 if (!rdev->ops->set_noack_map)
2284 return -EOPNOTSUPP;
2285
2286 noack_map = nla_get_u16(info->attrs[NL80211_ATTR_NOACK_MAP]);
2287
Hila Gonene35e4d22012-06-27 17:19:42 +03002288 return rdev_set_noack_map(rdev, dev, noack_map);
Simon Wunderlich1d9d9212011-11-18 14:20:43 +01002289}
2290
Johannes Berg41ade002007-12-19 02:03:29 +01002291struct get_key_cookie {
2292 struct sk_buff *msg;
2293 int error;
Johannes Bergb9454e82009-07-08 13:29:08 +02002294 int idx;
Johannes Berg41ade002007-12-19 02:03:29 +01002295};
2296
2297static void get_key_callback(void *c, struct key_params *params)
2298{
Johannes Bergb9454e82009-07-08 13:29:08 +02002299 struct nlattr *key;
Johannes Berg41ade002007-12-19 02:03:29 +01002300 struct get_key_cookie *cookie = c;
2301
David S. Miller9360ffd2012-03-29 04:41:26 -04002302 if ((params->key &&
2303 nla_put(cookie->msg, NL80211_ATTR_KEY_DATA,
2304 params->key_len, params->key)) ||
2305 (params->seq &&
2306 nla_put(cookie->msg, NL80211_ATTR_KEY_SEQ,
2307 params->seq_len, params->seq)) ||
2308 (params->cipher &&
2309 nla_put_u32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
2310 params->cipher)))
2311 goto nla_put_failure;
Johannes Berg41ade002007-12-19 02:03:29 +01002312
Johannes Bergb9454e82009-07-08 13:29:08 +02002313 key = nla_nest_start(cookie->msg, NL80211_ATTR_KEY);
2314 if (!key)
2315 goto nla_put_failure;
2316
David S. Miller9360ffd2012-03-29 04:41:26 -04002317 if ((params->key &&
2318 nla_put(cookie->msg, NL80211_KEY_DATA,
2319 params->key_len, params->key)) ||
2320 (params->seq &&
2321 nla_put(cookie->msg, NL80211_KEY_SEQ,
2322 params->seq_len, params->seq)) ||
2323 (params->cipher &&
2324 nla_put_u32(cookie->msg, NL80211_KEY_CIPHER,
2325 params->cipher)))
2326 goto nla_put_failure;
Johannes Bergb9454e82009-07-08 13:29:08 +02002327
David S. Miller9360ffd2012-03-29 04:41:26 -04002328 if (nla_put_u8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx))
2329 goto nla_put_failure;
Johannes Bergb9454e82009-07-08 13:29:08 +02002330
2331 nla_nest_end(cookie->msg, key);
2332
Johannes Berg41ade002007-12-19 02:03:29 +01002333 return;
2334 nla_put_failure:
2335 cookie->error = 1;
2336}
2337
2338static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
2339{
Johannes Berg4c476992010-10-04 21:36:35 +02002340 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg41ade002007-12-19 02:03:29 +01002341 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002342 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01002343 u8 key_idx = 0;
Johannes Berge31b8212010-10-05 19:39:30 +02002344 const u8 *mac_addr = NULL;
2345 bool pairwise;
Johannes Berg41ade002007-12-19 02:03:29 +01002346 struct get_key_cookie cookie = {
2347 .error = 0,
2348 };
2349 void *hdr;
2350 struct sk_buff *msg;
2351
2352 if (info->attrs[NL80211_ATTR_KEY_IDX])
2353 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
2354
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02002355 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +01002356 return -EINVAL;
2357
2358 if (info->attrs[NL80211_ATTR_MAC])
2359 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2360
Johannes Berge31b8212010-10-05 19:39:30 +02002361 pairwise = !!mac_addr;
2362 if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
2363 u32 kt = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
2364 if (kt >= NUM_NL80211_KEYTYPES)
2365 return -EINVAL;
2366 if (kt != NL80211_KEYTYPE_GROUP &&
2367 kt != NL80211_KEYTYPE_PAIRWISE)
2368 return -EINVAL;
2369 pairwise = kt == NL80211_KEYTYPE_PAIRWISE;
2370 }
2371
Johannes Berg4c476992010-10-04 21:36:35 +02002372 if (!rdev->ops->get_key)
2373 return -EOPNOTSUPP;
Johannes Berg41ade002007-12-19 02:03:29 +01002374
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002375 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02002376 if (!msg)
2377 return -ENOMEM;
Johannes Berg41ade002007-12-19 02:03:29 +01002378
Eric W. Biederman15e47302012-09-07 20:12:54 +00002379 hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
Johannes Berg41ade002007-12-19 02:03:29 +01002380 NL80211_CMD_NEW_KEY);
Johannes Berg4c476992010-10-04 21:36:35 +02002381 if (IS_ERR(hdr))
2382 return PTR_ERR(hdr);
Johannes Berg41ade002007-12-19 02:03:29 +01002383
2384 cookie.msg = msg;
Johannes Bergb9454e82009-07-08 13:29:08 +02002385 cookie.idx = key_idx;
Johannes Berg41ade002007-12-19 02:03:29 +01002386
David S. Miller9360ffd2012-03-29 04:41:26 -04002387 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
2388 nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_idx))
2389 goto nla_put_failure;
2390 if (mac_addr &&
2391 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr))
2392 goto nla_put_failure;
Johannes Berg41ade002007-12-19 02:03:29 +01002393
Johannes Berge31b8212010-10-05 19:39:30 +02002394 if (pairwise && mac_addr &&
2395 !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
2396 return -ENOENT;
2397
Hila Gonene35e4d22012-06-27 17:19:42 +03002398 err = rdev_get_key(rdev, dev, key_idx, pairwise, mac_addr, &cookie,
2399 get_key_callback);
Johannes Berg41ade002007-12-19 02:03:29 +01002400
2401 if (err)
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03002402 goto free_msg;
Johannes Berg41ade002007-12-19 02:03:29 +01002403
2404 if (cookie.error)
2405 goto nla_put_failure;
2406
2407 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02002408 return genlmsg_reply(msg, info);
Johannes Berg41ade002007-12-19 02:03:29 +01002409
2410 nla_put_failure:
2411 err = -ENOBUFS;
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03002412 free_msg:
Johannes Berg41ade002007-12-19 02:03:29 +01002413 nlmsg_free(msg);
Johannes Berg41ade002007-12-19 02:03:29 +01002414 return err;
2415}
2416
2417static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
2418{
Johannes Berg4c476992010-10-04 21:36:35 +02002419 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergb9454e82009-07-08 13:29:08 +02002420 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01002421 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002422 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01002423
Johannes Bergb9454e82009-07-08 13:29:08 +02002424 err = nl80211_parse_key(info, &key);
2425 if (err)
2426 return err;
2427
2428 if (key.idx < 0)
Johannes Berg41ade002007-12-19 02:03:29 +01002429 return -EINVAL;
2430
Johannes Bergb9454e82009-07-08 13:29:08 +02002431 /* only support setting default key */
2432 if (!key.def && !key.defmgmt)
Johannes Berg41ade002007-12-19 02:03:29 +01002433 return -EINVAL;
2434
Johannes Bergfffd0932009-07-08 14:22:54 +02002435 wdev_lock(dev->ieee80211_ptr);
Johannes Bergdbd2fd62010-12-09 19:58:59 +01002436
2437 if (key.def) {
2438 if (!rdev->ops->set_default_key) {
2439 err = -EOPNOTSUPP;
2440 goto out;
2441 }
2442
2443 err = nl80211_key_allowed(dev->ieee80211_ptr);
2444 if (err)
2445 goto out;
2446
Hila Gonene35e4d22012-06-27 17:19:42 +03002447 err = rdev_set_default_key(rdev, dev, key.idx,
Johannes Bergdbd2fd62010-12-09 19:58:59 +01002448 key.def_uni, key.def_multi);
2449
2450 if (err)
2451 goto out;
Johannes Bergfffd0932009-07-08 14:22:54 +02002452
Johannes Berg3d23e342009-09-29 23:27:28 +02002453#ifdef CONFIG_CFG80211_WEXT
Johannes Bergdbd2fd62010-12-09 19:58:59 +01002454 dev->ieee80211_ptr->wext.default_key = key.idx;
Johannes Berg08645122009-05-11 13:54:58 +02002455#endif
Johannes Bergdbd2fd62010-12-09 19:58:59 +01002456 } else {
2457 if (key.def_uni || !key.def_multi) {
2458 err = -EINVAL;
2459 goto out;
2460 }
2461
2462 if (!rdev->ops->set_default_mgmt_key) {
2463 err = -EOPNOTSUPP;
2464 goto out;
2465 }
2466
2467 err = nl80211_key_allowed(dev->ieee80211_ptr);
2468 if (err)
2469 goto out;
2470
Hila Gonene35e4d22012-06-27 17:19:42 +03002471 err = rdev_set_default_mgmt_key(rdev, dev, key.idx);
Johannes Bergdbd2fd62010-12-09 19:58:59 +01002472 if (err)
2473 goto out;
2474
2475#ifdef CONFIG_CFG80211_WEXT
2476 dev->ieee80211_ptr->wext.default_mgmt_key = key.idx;
2477#endif
2478 }
2479
2480 out:
Johannes Bergfffd0932009-07-08 14:22:54 +02002481 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01002482
Johannes Berg41ade002007-12-19 02:03:29 +01002483 return err;
2484}
2485
2486static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
2487{
Johannes Berg4c476992010-10-04 21:36:35 +02002488 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergfffd0932009-07-08 14:22:54 +02002489 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002490 struct net_device *dev = info->user_ptr[1];
Johannes Bergb9454e82009-07-08 13:29:08 +02002491 struct key_parse key;
Johannes Berge31b8212010-10-05 19:39:30 +02002492 const u8 *mac_addr = NULL;
Johannes Berg41ade002007-12-19 02:03:29 +01002493
Johannes Bergb9454e82009-07-08 13:29:08 +02002494 err = nl80211_parse_key(info, &key);
2495 if (err)
2496 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01002497
Johannes Bergb9454e82009-07-08 13:29:08 +02002498 if (!key.p.key)
Johannes Berg41ade002007-12-19 02:03:29 +01002499 return -EINVAL;
2500
Johannes Berg41ade002007-12-19 02:03:29 +01002501 if (info->attrs[NL80211_ATTR_MAC])
2502 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2503
Johannes Berge31b8212010-10-05 19:39:30 +02002504 if (key.type == -1) {
2505 if (mac_addr)
2506 key.type = NL80211_KEYTYPE_PAIRWISE;
2507 else
2508 key.type = NL80211_KEYTYPE_GROUP;
2509 }
2510
2511 /* for now */
2512 if (key.type != NL80211_KEYTYPE_PAIRWISE &&
2513 key.type != NL80211_KEYTYPE_GROUP)
2514 return -EINVAL;
2515
Johannes Berg4c476992010-10-04 21:36:35 +02002516 if (!rdev->ops->add_key)
2517 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002518
Johannes Berge31b8212010-10-05 19:39:30 +02002519 if (cfg80211_validate_key_settings(rdev, &key.p, key.idx,
2520 key.type == NL80211_KEYTYPE_PAIRWISE,
2521 mac_addr))
Johannes Berg4c476992010-10-04 21:36:35 +02002522 return -EINVAL;
Johannes Bergfffd0932009-07-08 14:22:54 +02002523
2524 wdev_lock(dev->ieee80211_ptr);
2525 err = nl80211_key_allowed(dev->ieee80211_ptr);
2526 if (!err)
Hila Gonene35e4d22012-06-27 17:19:42 +03002527 err = rdev_add_key(rdev, dev, key.idx,
2528 key.type == NL80211_KEYTYPE_PAIRWISE,
2529 mac_addr, &key.p);
Johannes Bergfffd0932009-07-08 14:22:54 +02002530 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01002531
Johannes Berg41ade002007-12-19 02:03:29 +01002532 return err;
2533}
2534
2535static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
2536{
Johannes Berg4c476992010-10-04 21:36:35 +02002537 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg41ade002007-12-19 02:03:29 +01002538 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002539 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01002540 u8 *mac_addr = NULL;
Johannes Bergb9454e82009-07-08 13:29:08 +02002541 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01002542
Johannes Bergb9454e82009-07-08 13:29:08 +02002543 err = nl80211_parse_key(info, &key);
2544 if (err)
2545 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01002546
2547 if (info->attrs[NL80211_ATTR_MAC])
2548 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2549
Johannes Berge31b8212010-10-05 19:39:30 +02002550 if (key.type == -1) {
2551 if (mac_addr)
2552 key.type = NL80211_KEYTYPE_PAIRWISE;
2553 else
2554 key.type = NL80211_KEYTYPE_GROUP;
2555 }
2556
2557 /* for now */
2558 if (key.type != NL80211_KEYTYPE_PAIRWISE &&
2559 key.type != NL80211_KEYTYPE_GROUP)
2560 return -EINVAL;
2561
Johannes Berg4c476992010-10-04 21:36:35 +02002562 if (!rdev->ops->del_key)
2563 return -EOPNOTSUPP;
Johannes Berg41ade002007-12-19 02:03:29 +01002564
Johannes Bergfffd0932009-07-08 14:22:54 +02002565 wdev_lock(dev->ieee80211_ptr);
2566 err = nl80211_key_allowed(dev->ieee80211_ptr);
Johannes Berge31b8212010-10-05 19:39:30 +02002567
2568 if (key.type == NL80211_KEYTYPE_PAIRWISE && mac_addr &&
2569 !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
2570 err = -ENOENT;
2571
Johannes Bergfffd0932009-07-08 14:22:54 +02002572 if (!err)
Hila Gonene35e4d22012-06-27 17:19:42 +03002573 err = rdev_del_key(rdev, dev, key.idx,
2574 key.type == NL80211_KEYTYPE_PAIRWISE,
2575 mac_addr);
Johannes Berg41ade002007-12-19 02:03:29 +01002576
Johannes Berg3d23e342009-09-29 23:27:28 +02002577#ifdef CONFIG_CFG80211_WEXT
Johannes Berg08645122009-05-11 13:54:58 +02002578 if (!err) {
Johannes Bergb9454e82009-07-08 13:29:08 +02002579 if (key.idx == dev->ieee80211_ptr->wext.default_key)
Johannes Berg08645122009-05-11 13:54:58 +02002580 dev->ieee80211_ptr->wext.default_key = -1;
Johannes Bergb9454e82009-07-08 13:29:08 +02002581 else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key)
Johannes Berg08645122009-05-11 13:54:58 +02002582 dev->ieee80211_ptr->wext.default_mgmt_key = -1;
2583 }
2584#endif
Johannes Bergfffd0932009-07-08 14:22:54 +02002585 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg08645122009-05-11 13:54:58 +02002586
Johannes Berg41ade002007-12-19 02:03:29 +01002587 return err;
2588}
2589
Vasanthakumar Thiagarajan77765ea2013-01-18 11:18:45 +05302590/* This function returns an error or the number of nested attributes */
2591static int validate_acl_mac_addrs(struct nlattr *nl_attr)
2592{
2593 struct nlattr *attr;
2594 int n_entries = 0, tmp;
2595
2596 nla_for_each_nested(attr, nl_attr, tmp) {
2597 if (nla_len(attr) != ETH_ALEN)
2598 return -EINVAL;
2599
2600 n_entries++;
2601 }
2602
2603 return n_entries;
2604}
2605
2606/*
2607 * This function parses ACL information and allocates memory for ACL data.
2608 * On successful return, the calling function is responsible to free the
2609 * ACL buffer returned by this function.
2610 */
2611static struct cfg80211_acl_data *parse_acl_data(struct wiphy *wiphy,
2612 struct genl_info *info)
2613{
2614 enum nl80211_acl_policy acl_policy;
2615 struct nlattr *attr;
2616 struct cfg80211_acl_data *acl;
2617 int i = 0, n_entries, tmp;
2618
2619 if (!wiphy->max_acl_mac_addrs)
2620 return ERR_PTR(-EOPNOTSUPP);
2621
2622 if (!info->attrs[NL80211_ATTR_ACL_POLICY])
2623 return ERR_PTR(-EINVAL);
2624
2625 acl_policy = nla_get_u32(info->attrs[NL80211_ATTR_ACL_POLICY]);
2626 if (acl_policy != NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED &&
2627 acl_policy != NL80211_ACL_POLICY_DENY_UNLESS_LISTED)
2628 return ERR_PTR(-EINVAL);
2629
2630 if (!info->attrs[NL80211_ATTR_MAC_ADDRS])
2631 return ERR_PTR(-EINVAL);
2632
2633 n_entries = validate_acl_mac_addrs(info->attrs[NL80211_ATTR_MAC_ADDRS]);
2634 if (n_entries < 0)
2635 return ERR_PTR(n_entries);
2636
2637 if (n_entries > wiphy->max_acl_mac_addrs)
2638 return ERR_PTR(-ENOTSUPP);
2639
2640 acl = kzalloc(sizeof(*acl) + (sizeof(struct mac_address) * n_entries),
2641 GFP_KERNEL);
2642 if (!acl)
2643 return ERR_PTR(-ENOMEM);
2644
2645 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_MAC_ADDRS], tmp) {
2646 memcpy(acl->mac_addrs[i].addr, nla_data(attr), ETH_ALEN);
2647 i++;
2648 }
2649
2650 acl->n_acl_entries = n_entries;
2651 acl->acl_policy = acl_policy;
2652
2653 return acl;
2654}
2655
2656static int nl80211_set_mac_acl(struct sk_buff *skb, struct genl_info *info)
2657{
2658 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2659 struct net_device *dev = info->user_ptr[1];
2660 struct cfg80211_acl_data *acl;
2661 int err;
2662
2663 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
2664 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2665 return -EOPNOTSUPP;
2666
2667 if (!dev->ieee80211_ptr->beacon_interval)
2668 return -EINVAL;
2669
2670 acl = parse_acl_data(&rdev->wiphy, info);
2671 if (IS_ERR(acl))
2672 return PTR_ERR(acl);
2673
2674 err = rdev_set_mac_acl(rdev, dev, acl);
2675
2676 kfree(acl);
2677
2678 return err;
2679}
2680
Johannes Berg88600202012-02-13 15:17:18 +01002681static int nl80211_parse_beacon(struct genl_info *info,
2682 struct cfg80211_beacon_data *bcn)
Johannes Berged1b6cc2007-12-19 02:03:32 +01002683{
Johannes Berg88600202012-02-13 15:17:18 +01002684 bool haveinfo = false;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002685
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002686 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]) ||
2687 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]) ||
2688 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_PROBE_RESP]) ||
2689 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]))
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002690 return -EINVAL;
2691
Johannes Berg88600202012-02-13 15:17:18 +01002692 memset(bcn, 0, sizeof(*bcn));
Johannes Berged1b6cc2007-12-19 02:03:32 +01002693
Johannes Berged1b6cc2007-12-19 02:03:32 +01002694 if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
Johannes Berg88600202012-02-13 15:17:18 +01002695 bcn->head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
2696 bcn->head_len = nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
2697 if (!bcn->head_len)
2698 return -EINVAL;
2699 haveinfo = true;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002700 }
2701
2702 if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
Johannes Berg88600202012-02-13 15:17:18 +01002703 bcn->tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
2704 bcn->tail_len =
Johannes Berged1b6cc2007-12-19 02:03:32 +01002705 nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
Johannes Berg88600202012-02-13 15:17:18 +01002706 haveinfo = true;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002707 }
2708
Johannes Berg4c476992010-10-04 21:36:35 +02002709 if (!haveinfo)
2710 return -EINVAL;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002711
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002712 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg88600202012-02-13 15:17:18 +01002713 bcn->beacon_ies = nla_data(info->attrs[NL80211_ATTR_IE]);
2714 bcn->beacon_ies_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002715 }
2716
2717 if (info->attrs[NL80211_ATTR_IE_PROBE_RESP]) {
Johannes Berg88600202012-02-13 15:17:18 +01002718 bcn->proberesp_ies =
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002719 nla_data(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
Johannes Berg88600202012-02-13 15:17:18 +01002720 bcn->proberesp_ies_len =
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002721 nla_len(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
2722 }
2723
2724 if (info->attrs[NL80211_ATTR_IE_ASSOC_RESP]) {
Johannes Berg88600202012-02-13 15:17:18 +01002725 bcn->assocresp_ies =
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002726 nla_data(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
Johannes Berg88600202012-02-13 15:17:18 +01002727 bcn->assocresp_ies_len =
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002728 nla_len(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
2729 }
2730
Arik Nemtsov00f740e2011-11-10 11:28:56 +02002731 if (info->attrs[NL80211_ATTR_PROBE_RESP]) {
Johannes Berg88600202012-02-13 15:17:18 +01002732 bcn->probe_resp =
Arik Nemtsov00f740e2011-11-10 11:28:56 +02002733 nla_data(info->attrs[NL80211_ATTR_PROBE_RESP]);
Johannes Berg88600202012-02-13 15:17:18 +01002734 bcn->probe_resp_len =
Arik Nemtsov00f740e2011-11-10 11:28:56 +02002735 nla_len(info->attrs[NL80211_ATTR_PROBE_RESP]);
2736 }
2737
Johannes Berg88600202012-02-13 15:17:18 +01002738 return 0;
2739}
2740
Felix Fietkau46c1dd02012-06-19 02:50:57 +02002741static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev,
2742 struct cfg80211_ap_settings *params)
2743{
2744 struct wireless_dev *wdev;
2745 bool ret = false;
2746
2747 mutex_lock(&rdev->devlist_mtx);
2748
Johannes Berg89a54e42012-06-15 14:33:17 +02002749 list_for_each_entry(wdev, &rdev->wdev_list, list) {
Felix Fietkau46c1dd02012-06-19 02:50:57 +02002750 if (wdev->iftype != NL80211_IFTYPE_AP &&
2751 wdev->iftype != NL80211_IFTYPE_P2P_GO)
2752 continue;
2753
Johannes Berg683b6d32012-11-08 21:25:48 +01002754 if (!wdev->preset_chandef.chan)
Felix Fietkau46c1dd02012-06-19 02:50:57 +02002755 continue;
2756
Johannes Berg683b6d32012-11-08 21:25:48 +01002757 params->chandef = wdev->preset_chandef;
Felix Fietkau46c1dd02012-06-19 02:50:57 +02002758 ret = true;
2759 break;
2760 }
2761
2762 mutex_unlock(&rdev->devlist_mtx);
2763
2764 return ret;
2765}
2766
Jouni Malinene39e5b52012-09-30 19:29:39 +03002767static bool nl80211_valid_auth_type(struct cfg80211_registered_device *rdev,
2768 enum nl80211_auth_type auth_type,
2769 enum nl80211_commands cmd)
2770{
2771 if (auth_type > NL80211_AUTHTYPE_MAX)
2772 return false;
2773
2774 switch (cmd) {
2775 case NL80211_CMD_AUTHENTICATE:
2776 if (!(rdev->wiphy.features & NL80211_FEATURE_SAE) &&
2777 auth_type == NL80211_AUTHTYPE_SAE)
2778 return false;
2779 return true;
2780 case NL80211_CMD_CONNECT:
2781 case NL80211_CMD_START_AP:
2782 /* SAE not supported yet */
2783 if (auth_type == NL80211_AUTHTYPE_SAE)
2784 return false;
2785 return true;
2786 default:
2787 return false;
2788 }
2789}
2790
Johannes Berg88600202012-02-13 15:17:18 +01002791static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
2792{
2793 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2794 struct net_device *dev = info->user_ptr[1];
2795 struct wireless_dev *wdev = dev->ieee80211_ptr;
2796 struct cfg80211_ap_settings params;
2797 int err;
Simon Wunderlich04f39042013-02-08 18:16:19 +01002798 u8 radar_detect_width = 0;
Johannes Berg88600202012-02-13 15:17:18 +01002799
2800 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
2801 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2802 return -EOPNOTSUPP;
2803
2804 if (!rdev->ops->start_ap)
2805 return -EOPNOTSUPP;
2806
2807 if (wdev->beacon_interval)
2808 return -EALREADY;
2809
2810 memset(&params, 0, sizeof(params));
2811
2812 /* these are required for START_AP */
2813 if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
2814 !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
2815 !info->attrs[NL80211_ATTR_BEACON_HEAD])
2816 return -EINVAL;
2817
2818 err = nl80211_parse_beacon(info, &params.beacon);
2819 if (err)
2820 return err;
2821
2822 params.beacon_interval =
2823 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
2824 params.dtim_period =
2825 nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
2826
2827 err = cfg80211_validate_beacon_int(rdev, params.beacon_interval);
2828 if (err)
2829 return err;
2830
2831 /*
2832 * In theory, some of these attributes should be required here
2833 * but since they were not used when the command was originally
2834 * added, keep them optional for old user space programs to let
2835 * them continue to work with drivers that do not need the
2836 * additional information -- drivers must check!
2837 */
2838 if (info->attrs[NL80211_ATTR_SSID]) {
2839 params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
2840 params.ssid_len =
2841 nla_len(info->attrs[NL80211_ATTR_SSID]);
2842 if (params.ssid_len == 0 ||
2843 params.ssid_len > IEEE80211_MAX_SSID_LEN)
2844 return -EINVAL;
2845 }
2846
2847 if (info->attrs[NL80211_ATTR_HIDDEN_SSID]) {
2848 params.hidden_ssid = nla_get_u32(
2849 info->attrs[NL80211_ATTR_HIDDEN_SSID]);
2850 if (params.hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE &&
2851 params.hidden_ssid != NL80211_HIDDEN_SSID_ZERO_LEN &&
2852 params.hidden_ssid != NL80211_HIDDEN_SSID_ZERO_CONTENTS)
2853 return -EINVAL;
2854 }
2855
2856 params.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
2857
2858 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
2859 params.auth_type = nla_get_u32(
2860 info->attrs[NL80211_ATTR_AUTH_TYPE]);
Jouni Malinene39e5b52012-09-30 19:29:39 +03002861 if (!nl80211_valid_auth_type(rdev, params.auth_type,
2862 NL80211_CMD_START_AP))
Johannes Berg88600202012-02-13 15:17:18 +01002863 return -EINVAL;
2864 } else
2865 params.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
2866
2867 err = nl80211_crypto_settings(rdev, info, &params.crypto,
2868 NL80211_MAX_NR_CIPHER_SUITES);
2869 if (err)
2870 return err;
2871
Vasanthakumar Thiagarajan1b658f12012-03-02 15:50:02 +05302872 if (info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]) {
2873 if (!(rdev->wiphy.features & NL80211_FEATURE_INACTIVITY_TIMER))
2874 return -EOPNOTSUPP;
2875 params.inactivity_timeout = nla_get_u16(
2876 info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]);
2877 }
2878
Johannes Berg53cabad2012-11-14 15:17:28 +01002879 if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) {
2880 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2881 return -EINVAL;
2882 params.p2p_ctwindow =
2883 nla_get_u8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]);
2884 if (params.p2p_ctwindow > 127)
2885 return -EINVAL;
2886 if (params.p2p_ctwindow != 0 &&
2887 !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN))
2888 return -EINVAL;
2889 }
2890
2891 if (info->attrs[NL80211_ATTR_P2P_OPPPS]) {
2892 u8 tmp;
2893
2894 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2895 return -EINVAL;
2896 tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]);
2897 if (tmp > 1)
2898 return -EINVAL;
2899 params.p2p_opp_ps = tmp;
2900 if (params.p2p_opp_ps != 0 &&
2901 !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS))
2902 return -EINVAL;
2903 }
2904
Johannes Bergaa430da2012-05-16 23:50:18 +02002905 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Johannes Berg683b6d32012-11-08 21:25:48 +01002906 err = nl80211_parse_chandef(rdev, info, &params.chandef);
2907 if (err)
2908 return err;
2909 } else if (wdev->preset_chandef.chan) {
2910 params.chandef = wdev->preset_chandef;
Felix Fietkau46c1dd02012-06-19 02:50:57 +02002911 } else if (!nl80211_get_ap_channel(rdev, &params))
Johannes Bergaa430da2012-05-16 23:50:18 +02002912 return -EINVAL;
2913
Johannes Berg683b6d32012-11-08 21:25:48 +01002914 if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef))
Johannes Bergaa430da2012-05-16 23:50:18 +02002915 return -EINVAL;
2916
Simon Wunderlich04f39042013-02-08 18:16:19 +01002917 err = cfg80211_chandef_dfs_required(wdev->wiphy, &params.chandef);
2918 if (err < 0)
2919 return err;
2920 if (err) {
2921 radar_detect_width = BIT(params.chandef.width);
2922 params.radar_required = true;
2923 }
2924
Michal Kaziore4e32452012-06-29 12:47:08 +02002925 mutex_lock(&rdev->devlist_mtx);
Simon Wunderlich04f39042013-02-08 18:16:19 +01002926 err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
2927 params.chandef.chan,
2928 CHAN_MODE_SHARED,
2929 radar_detect_width);
Michal Kaziore4e32452012-06-29 12:47:08 +02002930 mutex_unlock(&rdev->devlist_mtx);
2931
2932 if (err)
2933 return err;
2934
Vasanthakumar Thiagarajan77765ea2013-01-18 11:18:45 +05302935 if (info->attrs[NL80211_ATTR_ACL_POLICY]) {
2936 params.acl = parse_acl_data(&rdev->wiphy, info);
2937 if (IS_ERR(params.acl))
2938 return PTR_ERR(params.acl);
2939 }
2940
Hila Gonene35e4d22012-06-27 17:19:42 +03002941 err = rdev_start_ap(rdev, dev, &params);
Felix Fietkau46c1dd02012-06-19 02:50:57 +02002942 if (!err) {
Johannes Berg683b6d32012-11-08 21:25:48 +01002943 wdev->preset_chandef = params.chandef;
Johannes Berg88600202012-02-13 15:17:18 +01002944 wdev->beacon_interval = params.beacon_interval;
Johannes Berg683b6d32012-11-08 21:25:48 +01002945 wdev->channel = params.chandef.chan;
Antonio Quartulli06e191e2012-11-07 12:52:19 +01002946 wdev->ssid_len = params.ssid_len;
2947 memcpy(wdev->ssid, params.ssid, wdev->ssid_len);
Felix Fietkau46c1dd02012-06-19 02:50:57 +02002948 }
Vasanthakumar Thiagarajan77765ea2013-01-18 11:18:45 +05302949
2950 kfree(params.acl);
2951
Johannes Berg56d18932011-05-09 18:41:15 +02002952 return err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002953}
2954
Johannes Berg88600202012-02-13 15:17:18 +01002955static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
2956{
2957 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2958 struct net_device *dev = info->user_ptr[1];
2959 struct wireless_dev *wdev = dev->ieee80211_ptr;
2960 struct cfg80211_beacon_data params;
2961 int err;
2962
2963 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
2964 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2965 return -EOPNOTSUPP;
2966
2967 if (!rdev->ops->change_beacon)
2968 return -EOPNOTSUPP;
2969
2970 if (!wdev->beacon_interval)
2971 return -EINVAL;
2972
2973 err = nl80211_parse_beacon(info, &params);
2974 if (err)
2975 return err;
2976
Hila Gonene35e4d22012-06-27 17:19:42 +03002977 return rdev_change_beacon(rdev, dev, &params);
Johannes Berg88600202012-02-13 15:17:18 +01002978}
2979
2980static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info)
Johannes Berged1b6cc2007-12-19 02:03:32 +01002981{
Johannes Berg4c476992010-10-04 21:36:35 +02002982 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2983 struct net_device *dev = info->user_ptr[1];
Johannes Berged1b6cc2007-12-19 02:03:32 +01002984
Michal Kazior60771782012-06-29 12:46:56 +02002985 return cfg80211_stop_ap(rdev, dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01002986}
2987
Johannes Berg5727ef12007-12-19 02:03:34 +01002988static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
2989 [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
2990 [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
2991 [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
Jouni Malinen0e467242009-05-11 21:57:55 +03002992 [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG },
Javier Cardonab39c48f2011-04-07 15:08:30 -07002993 [NL80211_STA_FLAG_AUTHENTICATED] = { .type = NLA_FLAG },
Johannes Bergd83023d2011-12-14 09:29:15 +01002994 [NL80211_STA_FLAG_TDLS_PEER] = { .type = NLA_FLAG },
Johannes Berg5727ef12007-12-19 02:03:34 +01002995};
2996
Johannes Bergeccb8e82009-05-11 21:57:56 +03002997static int parse_station_flags(struct genl_info *info,
Johannes Bergbdd3ae32012-01-02 13:30:03 +01002998 enum nl80211_iftype iftype,
Johannes Bergeccb8e82009-05-11 21:57:56 +03002999 struct station_parameters *params)
Johannes Berg5727ef12007-12-19 02:03:34 +01003000{
3001 struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
Johannes Bergeccb8e82009-05-11 21:57:56 +03003002 struct nlattr *nla;
Johannes Berg5727ef12007-12-19 02:03:34 +01003003 int flag;
3004
Johannes Bergeccb8e82009-05-11 21:57:56 +03003005 /*
3006 * Try parsing the new attribute first so userspace
3007 * can specify both for older kernels.
3008 */
3009 nla = info->attrs[NL80211_ATTR_STA_FLAGS2];
3010 if (nla) {
3011 struct nl80211_sta_flag_update *sta_flags;
Johannes Berg5727ef12007-12-19 02:03:34 +01003012
Johannes Bergeccb8e82009-05-11 21:57:56 +03003013 sta_flags = nla_data(nla);
3014 params->sta_flags_mask = sta_flags->mask;
3015 params->sta_flags_set = sta_flags->set;
3016 if ((params->sta_flags_mask |
3017 params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID))
3018 return -EINVAL;
3019 return 0;
3020 }
3021
3022 /* if present, parse the old attribute */
3023
3024 nla = info->attrs[NL80211_ATTR_STA_FLAGS];
Johannes Berg5727ef12007-12-19 02:03:34 +01003025 if (!nla)
3026 return 0;
3027
3028 if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX,
3029 nla, sta_flags_policy))
3030 return -EINVAL;
3031
Johannes Bergbdd3ae32012-01-02 13:30:03 +01003032 /*
3033 * Only allow certain flags for interface types so that
3034 * other attributes are silently ignored. Remember that
3035 * this is backward compatibility code with old userspace
3036 * and shouldn't be hit in other cases anyway.
3037 */
3038 switch (iftype) {
3039 case NL80211_IFTYPE_AP:
3040 case NL80211_IFTYPE_AP_VLAN:
3041 case NL80211_IFTYPE_P2P_GO:
3042 params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHORIZED) |
3043 BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
3044 BIT(NL80211_STA_FLAG_WME) |
3045 BIT(NL80211_STA_FLAG_MFP);
3046 break;
3047 case NL80211_IFTYPE_P2P_CLIENT:
3048 case NL80211_IFTYPE_STATION:
3049 params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHORIZED) |
3050 BIT(NL80211_STA_FLAG_TDLS_PEER);
3051 break;
3052 case NL80211_IFTYPE_MESH_POINT:
3053 params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHENTICATED) |
3054 BIT(NL80211_STA_FLAG_MFP) |
3055 BIT(NL80211_STA_FLAG_AUTHORIZED);
3056 default:
3057 return -EINVAL;
3058 }
Johannes Berg5727ef12007-12-19 02:03:34 +01003059
Johannes Berg3383b5a2012-05-10 20:14:43 +02003060 for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++) {
3061 if (flags[flag]) {
Johannes Bergeccb8e82009-05-11 21:57:56 +03003062 params->sta_flags_set |= (1<<flag);
Johannes Berg5727ef12007-12-19 02:03:34 +01003063
Johannes Berg3383b5a2012-05-10 20:14:43 +02003064 /* no longer support new API additions in old API */
3065 if (flag > NL80211_STA_FLAG_MAX_OLD_API)
3066 return -EINVAL;
3067 }
3068 }
3069
Johannes Berg5727ef12007-12-19 02:03:34 +01003070 return 0;
3071}
3072
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01003073static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
3074 int attr)
3075{
3076 struct nlattr *rate;
Vladimir Kondratiev8eb41c82012-07-05 14:25:49 +03003077 u32 bitrate;
3078 u16 bitrate_compat;
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01003079
3080 rate = nla_nest_start(msg, attr);
3081 if (!rate)
Johannes Bergdb9c64c2012-11-09 14:56:41 +01003082 return false;
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01003083
3084 /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */
3085 bitrate = cfg80211_calculate_bitrate(info);
Vladimir Kondratiev8eb41c82012-07-05 14:25:49 +03003086 /* report 16-bit bitrate only if we can */
3087 bitrate_compat = bitrate < (1UL << 16) ? bitrate : 0;
Johannes Bergdb9c64c2012-11-09 14:56:41 +01003088 if (bitrate > 0 &&
3089 nla_put_u32(msg, NL80211_RATE_INFO_BITRATE32, bitrate))
3090 return false;
3091 if (bitrate_compat > 0 &&
3092 nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate_compat))
3093 return false;
3094
3095 if (info->flags & RATE_INFO_FLAGS_MCS) {
3096 if (nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs))
3097 return false;
3098 if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH &&
3099 nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH))
3100 return false;
3101 if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&
3102 nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))
3103 return false;
3104 } else if (info->flags & RATE_INFO_FLAGS_VHT_MCS) {
3105 if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_MCS, info->mcs))
3106 return false;
3107 if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_NSS, info->nss))
3108 return false;
3109 if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH &&
3110 nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH))
3111 return false;
3112 if (info->flags & RATE_INFO_FLAGS_80_MHZ_WIDTH &&
3113 nla_put_flag(msg, NL80211_RATE_INFO_80_MHZ_WIDTH))
3114 return false;
3115 if (info->flags & RATE_INFO_FLAGS_80P80_MHZ_WIDTH &&
3116 nla_put_flag(msg, NL80211_RATE_INFO_80P80_MHZ_WIDTH))
3117 return false;
3118 if (info->flags & RATE_INFO_FLAGS_160_MHZ_WIDTH &&
3119 nla_put_flag(msg, NL80211_RATE_INFO_160_MHZ_WIDTH))
3120 return false;
3121 if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&
3122 nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))
3123 return false;
3124 }
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01003125
3126 nla_nest_end(msg, rate);
3127 return true;
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01003128}
3129
Eric W. Biederman15e47302012-09-07 20:12:54 +00003130static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,
John W. Linville66266b32012-03-15 13:25:41 -04003131 int flags,
3132 struct cfg80211_registered_device *rdev,
3133 struct net_device *dev,
Johannes Berg98b62182009-12-23 13:15:44 +01003134 const u8 *mac_addr, struct station_info *sinfo)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003135{
3136 void *hdr;
Paul Stewartf4263c92011-03-31 09:25:41 -07003137 struct nlattr *sinfoattr, *bss_param;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003138
Eric W. Biederman15e47302012-09-07 20:12:54 +00003139 hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_STATION);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003140 if (!hdr)
3141 return -1;
3142
David S. Miller9360ffd2012-03-29 04:41:26 -04003143 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
3144 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr) ||
3145 nla_put_u32(msg, NL80211_ATTR_GENERATION, sinfo->generation))
3146 goto nla_put_failure;
Johannes Bergf5ea9122009-08-07 16:17:38 +02003147
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003148 sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
3149 if (!sinfoattr)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003150 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -04003151 if ((sinfo->filled & STATION_INFO_CONNECTED_TIME) &&
3152 nla_put_u32(msg, NL80211_STA_INFO_CONNECTED_TIME,
3153 sinfo->connected_time))
3154 goto nla_put_failure;
3155 if ((sinfo->filled & STATION_INFO_INACTIVE_TIME) &&
3156 nla_put_u32(msg, NL80211_STA_INFO_INACTIVE_TIME,
3157 sinfo->inactive_time))
3158 goto nla_put_failure;
Vladimir Kondratiev42745e02013-02-04 13:53:11 +02003159 if ((sinfo->filled & (STATION_INFO_RX_BYTES |
3160 STATION_INFO_RX_BYTES64)) &&
David S. Miller9360ffd2012-03-29 04:41:26 -04003161 nla_put_u32(msg, NL80211_STA_INFO_RX_BYTES,
Vladimir Kondratiev42745e02013-02-04 13:53:11 +02003162 (u32)sinfo->rx_bytes))
3163 goto nla_put_failure;
3164 if ((sinfo->filled & (STATION_INFO_TX_BYTES |
3165 NL80211_STA_INFO_TX_BYTES64)) &&
3166 nla_put_u32(msg, NL80211_STA_INFO_TX_BYTES,
3167 (u32)sinfo->tx_bytes))
3168 goto nla_put_failure;
3169 if ((sinfo->filled & STATION_INFO_RX_BYTES64) &&
3170 nla_put_u64(msg, NL80211_STA_INFO_RX_BYTES64,
David S. Miller9360ffd2012-03-29 04:41:26 -04003171 sinfo->rx_bytes))
3172 goto nla_put_failure;
Vladimir Kondratiev42745e02013-02-04 13:53:11 +02003173 if ((sinfo->filled & STATION_INFO_TX_BYTES64) &&
3174 nla_put_u64(msg, NL80211_STA_INFO_TX_BYTES64,
David S. Miller9360ffd2012-03-29 04:41:26 -04003175 sinfo->tx_bytes))
3176 goto nla_put_failure;
3177 if ((sinfo->filled & STATION_INFO_LLID) &&
3178 nla_put_u16(msg, NL80211_STA_INFO_LLID, sinfo->llid))
3179 goto nla_put_failure;
3180 if ((sinfo->filled & STATION_INFO_PLID) &&
3181 nla_put_u16(msg, NL80211_STA_INFO_PLID, sinfo->plid))
3182 goto nla_put_failure;
3183 if ((sinfo->filled & STATION_INFO_PLINK_STATE) &&
3184 nla_put_u8(msg, NL80211_STA_INFO_PLINK_STATE,
3185 sinfo->plink_state))
3186 goto nla_put_failure;
John W. Linville66266b32012-03-15 13:25:41 -04003187 switch (rdev->wiphy.signal_type) {
3188 case CFG80211_SIGNAL_TYPE_MBM:
David S. Miller9360ffd2012-03-29 04:41:26 -04003189 if ((sinfo->filled & STATION_INFO_SIGNAL) &&
3190 nla_put_u8(msg, NL80211_STA_INFO_SIGNAL,
3191 sinfo->signal))
3192 goto nla_put_failure;
3193 if ((sinfo->filled & STATION_INFO_SIGNAL_AVG) &&
3194 nla_put_u8(msg, NL80211_STA_INFO_SIGNAL_AVG,
3195 sinfo->signal_avg))
3196 goto nla_put_failure;
John W. Linville66266b32012-03-15 13:25:41 -04003197 break;
3198 default:
3199 break;
3200 }
Henning Rogge420e7fa2008-12-11 22:04:19 +01003201 if (sinfo->filled & STATION_INFO_TX_BITRATE) {
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01003202 if (!nl80211_put_sta_rate(msg, &sinfo->txrate,
3203 NL80211_STA_INFO_TX_BITRATE))
Henning Rogge420e7fa2008-12-11 22:04:19 +01003204 goto nla_put_failure;
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01003205 }
3206 if (sinfo->filled & STATION_INFO_RX_BITRATE) {
3207 if (!nl80211_put_sta_rate(msg, &sinfo->rxrate,
3208 NL80211_STA_INFO_RX_BITRATE))
3209 goto nla_put_failure;
Henning Rogge420e7fa2008-12-11 22:04:19 +01003210 }
David S. Miller9360ffd2012-03-29 04:41:26 -04003211 if ((sinfo->filled & STATION_INFO_RX_PACKETS) &&
3212 nla_put_u32(msg, NL80211_STA_INFO_RX_PACKETS,
3213 sinfo->rx_packets))
3214 goto nla_put_failure;
3215 if ((sinfo->filled & STATION_INFO_TX_PACKETS) &&
3216 nla_put_u32(msg, NL80211_STA_INFO_TX_PACKETS,
3217 sinfo->tx_packets))
3218 goto nla_put_failure;
3219 if ((sinfo->filled & STATION_INFO_TX_RETRIES) &&
3220 nla_put_u32(msg, NL80211_STA_INFO_TX_RETRIES,
3221 sinfo->tx_retries))
3222 goto nla_put_failure;
3223 if ((sinfo->filled & STATION_INFO_TX_FAILED) &&
3224 nla_put_u32(msg, NL80211_STA_INFO_TX_FAILED,
3225 sinfo->tx_failed))
3226 goto nla_put_failure;
3227 if ((sinfo->filled & STATION_INFO_BEACON_LOSS_COUNT) &&
3228 nla_put_u32(msg, NL80211_STA_INFO_BEACON_LOSS,
3229 sinfo->beacon_loss_count))
3230 goto nla_put_failure;
Marco Porsch3b1c5a52013-01-07 16:04:52 +01003231 if ((sinfo->filled & STATION_INFO_LOCAL_PM) &&
3232 nla_put_u32(msg, NL80211_STA_INFO_LOCAL_PM,
3233 sinfo->local_pm))
3234 goto nla_put_failure;
3235 if ((sinfo->filled & STATION_INFO_PEER_PM) &&
3236 nla_put_u32(msg, NL80211_STA_INFO_PEER_PM,
3237 sinfo->peer_pm))
3238 goto nla_put_failure;
3239 if ((sinfo->filled & STATION_INFO_NONPEER_PM) &&
3240 nla_put_u32(msg, NL80211_STA_INFO_NONPEER_PM,
3241 sinfo->nonpeer_pm))
3242 goto nla_put_failure;
Paul Stewartf4263c92011-03-31 09:25:41 -07003243 if (sinfo->filled & STATION_INFO_BSS_PARAM) {
3244 bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM);
3245 if (!bss_param)
3246 goto nla_put_failure;
3247
David S. Miller9360ffd2012-03-29 04:41:26 -04003248 if (((sinfo->bss_param.flags & BSS_PARAM_FLAGS_CTS_PROT) &&
3249 nla_put_flag(msg, NL80211_STA_BSS_PARAM_CTS_PROT)) ||
3250 ((sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_PREAMBLE) &&
3251 nla_put_flag(msg, NL80211_STA_BSS_PARAM_SHORT_PREAMBLE)) ||
3252 ((sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_SLOT_TIME) &&
3253 nla_put_flag(msg, NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME)) ||
3254 nla_put_u8(msg, NL80211_STA_BSS_PARAM_DTIM_PERIOD,
3255 sinfo->bss_param.dtim_period) ||
3256 nla_put_u16(msg, NL80211_STA_BSS_PARAM_BEACON_INTERVAL,
3257 sinfo->bss_param.beacon_interval))
3258 goto nla_put_failure;
Paul Stewartf4263c92011-03-31 09:25:41 -07003259
3260 nla_nest_end(msg, bss_param);
3261 }
David S. Miller9360ffd2012-03-29 04:41:26 -04003262 if ((sinfo->filled & STATION_INFO_STA_FLAGS) &&
3263 nla_put(msg, NL80211_STA_INFO_STA_FLAGS,
3264 sizeof(struct nl80211_sta_flag_update),
3265 &sinfo->sta_flags))
3266 goto nla_put_failure;
John W. Linville7eab0f62012-04-12 14:25:14 -04003267 if ((sinfo->filled & STATION_INFO_T_OFFSET) &&
3268 nla_put_u64(msg, NL80211_STA_INFO_T_OFFSET,
3269 sinfo->t_offset))
3270 goto nla_put_failure;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003271 nla_nest_end(msg, sinfoattr);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003272
David S. Miller9360ffd2012-03-29 04:41:26 -04003273 if ((sinfo->filled & STATION_INFO_ASSOC_REQ_IES) &&
3274 nla_put(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len,
3275 sinfo->assoc_req_ies))
3276 goto nla_put_failure;
Jouni Malinen50d3dfb2011-08-08 12:11:52 +03003277
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003278 return genlmsg_end(msg, hdr);
3279
3280 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07003281 genlmsg_cancel(msg, hdr);
3282 return -EMSGSIZE;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003283}
3284
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003285static int nl80211_dump_station(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02003286 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003287{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003288 struct station_info sinfo;
3289 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003290 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003291 u8 mac_addr[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02003292 int sta_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003293 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003294
Johannes Berg67748892010-10-04 21:14:06 +02003295 err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
3296 if (err)
3297 return err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003298
3299 if (!dev->ops->dump_station) {
Jouni Malineneec60b02009-03-20 21:21:19 +02003300 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003301 goto out_err;
3302 }
3303
Johannes Bergbba95fe2008-07-29 13:22:51 +02003304 while (1) {
Jouni Malinenf612ced2011-08-11 11:46:22 +03003305 memset(&sinfo, 0, sizeof(sinfo));
Hila Gonene35e4d22012-06-27 17:19:42 +03003306 err = rdev_dump_station(dev, netdev, sta_idx,
3307 mac_addr, &sinfo);
Johannes Bergbba95fe2008-07-29 13:22:51 +02003308 if (err == -ENOENT)
3309 break;
3310 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01003311 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003312
3313 if (nl80211_send_station(skb,
Eric W. Biederman15e47302012-09-07 20:12:54 +00003314 NETLINK_CB(cb->skb).portid,
Johannes Bergbba95fe2008-07-29 13:22:51 +02003315 cb->nlh->nlmsg_seq, NLM_F_MULTI,
John W. Linville66266b32012-03-15 13:25:41 -04003316 dev, netdev, mac_addr,
Johannes Bergbba95fe2008-07-29 13:22:51 +02003317 &sinfo) < 0)
3318 goto out;
3319
3320 sta_idx++;
3321 }
3322
3323
3324 out:
3325 cb->args[1] = sta_idx;
3326 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003327 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02003328 nl80211_finish_netdev_dump(dev);
Johannes Bergbba95fe2008-07-29 13:22:51 +02003329
3330 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003331}
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003332
Johannes Berg5727ef12007-12-19 02:03:34 +01003333static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
3334{
Johannes Berg4c476992010-10-04 21:36:35 +02003335 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3336 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003337 struct station_info sinfo;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003338 struct sk_buff *msg;
3339 u8 *mac_addr = NULL;
Johannes Berg4c476992010-10-04 21:36:35 +02003340 int err;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003341
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003342 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003343
3344 if (!info->attrs[NL80211_ATTR_MAC])
3345 return -EINVAL;
3346
3347 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
3348
Johannes Berg4c476992010-10-04 21:36:35 +02003349 if (!rdev->ops->get_station)
3350 return -EOPNOTSUPP;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003351
Hila Gonene35e4d22012-06-27 17:19:42 +03003352 err = rdev_get_station(rdev, dev, mac_addr, &sinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003353 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02003354 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003355
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07003356 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003357 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02003358 return -ENOMEM;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003359
Eric W. Biederman15e47302012-09-07 20:12:54 +00003360 if (nl80211_send_station(msg, info->snd_portid, info->snd_seq, 0,
John W. Linville66266b32012-03-15 13:25:41 -04003361 rdev, dev, mac_addr, &sinfo) < 0) {
Johannes Berg4c476992010-10-04 21:36:35 +02003362 nlmsg_free(msg);
3363 return -ENOBUFS;
3364 }
Johannes Bergfd5b74d2007-12-19 02:03:36 +01003365
Johannes Berg4c476992010-10-04 21:36:35 +02003366 return genlmsg_reply(msg, info);
Johannes Berg5727ef12007-12-19 02:03:34 +01003367}
3368
3369/*
Felix Fietkauc258d2d2009-11-11 17:23:31 +01003370 * Get vlan interface making sure it is running and on the right wiphy.
Johannes Berg5727ef12007-12-19 02:03:34 +01003371 */
Johannes Berg80b99892011-11-18 16:23:01 +01003372static struct net_device *get_vlan(struct genl_info *info,
3373 struct cfg80211_registered_device *rdev)
Johannes Berg5727ef12007-12-19 02:03:34 +01003374{
Johannes Berg463d0182009-07-14 00:33:35 +02003375 struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN];
Johannes Berg80b99892011-11-18 16:23:01 +01003376 struct net_device *v;
3377 int ret;
Johannes Berg5727ef12007-12-19 02:03:34 +01003378
Johannes Berg80b99892011-11-18 16:23:01 +01003379 if (!vlanattr)
3380 return NULL;
3381
3382 v = dev_get_by_index(genl_info_net(info), nla_get_u32(vlanattr));
3383 if (!v)
3384 return ERR_PTR(-ENODEV);
3385
3386 if (!v->ieee80211_ptr || v->ieee80211_ptr->wiphy != &rdev->wiphy) {
3387 ret = -EINVAL;
3388 goto error;
Johannes Berg5727ef12007-12-19 02:03:34 +01003389 }
Johannes Berg80b99892011-11-18 16:23:01 +01003390
3391 if (!netif_running(v)) {
3392 ret = -ENETDOWN;
3393 goto error;
3394 }
3395
3396 return v;
3397 error:
3398 dev_put(v);
3399 return ERR_PTR(ret);
Johannes Berg5727ef12007-12-19 02:03:34 +01003400}
3401
Jouni Malinendf881292013-02-14 21:10:54 +02003402static struct nla_policy
3403nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = {
3404 [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
3405 [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
3406};
3407
3408static int nl80211_set_station_tdls(struct genl_info *info,
3409 struct station_parameters *params)
3410{
Jouni Malinendf881292013-02-14 21:10:54 +02003411 struct nlattr *tb[NL80211_STA_WME_MAX + 1];
3412 struct nlattr *nla;
3413 int err;
3414
Jouni Malinendf881292013-02-14 21:10:54 +02003415 /* Dummy STA entry gets updated once the peer capabilities are known */
3416 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
3417 params->ht_capa =
3418 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
3419 if (info->attrs[NL80211_ATTR_VHT_CAPABILITY])
3420 params->vht_capa =
3421 nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
3422
3423 /* parse WME attributes if present */
3424 if (!info->attrs[NL80211_ATTR_STA_WME])
3425 return 0;
3426
3427 nla = info->attrs[NL80211_ATTR_STA_WME];
3428 err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla,
3429 nl80211_sta_wme_policy);
3430 if (err)
3431 return err;
3432
3433 if (tb[NL80211_STA_WME_UAPSD_QUEUES])
3434 params->uapsd_queues = nla_get_u8(
3435 tb[NL80211_STA_WME_UAPSD_QUEUES]);
3436 if (params->uapsd_queues & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
3437 return -EINVAL;
3438
3439 if (tb[NL80211_STA_WME_MAX_SP])
3440 params->max_sp = nla_get_u8(tb[NL80211_STA_WME_MAX_SP]);
3441
3442 if (params->max_sp & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
3443 return -EINVAL;
3444
3445 params->sta_modify_mask |= STATION_PARAM_APPLY_UAPSD;
3446
3447 return 0;
3448}
3449
Johannes Berg5727ef12007-12-19 02:03:34 +01003450static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
3451{
Johannes Berg4c476992010-10-04 21:36:35 +02003452 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg5727ef12007-12-19 02:03:34 +01003453 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02003454 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01003455 struct station_parameters params;
3456 u8 *mac_addr = NULL;
3457
3458 memset(&params, 0, sizeof(params));
3459
3460 params.listen_interval = -1;
Javier Cardona57cf8042011-05-13 10:45:43 -07003461 params.plink_state = -1;
Johannes Berg5727ef12007-12-19 02:03:34 +01003462
3463 if (info->attrs[NL80211_ATTR_STA_AID])
3464 return -EINVAL;
3465
3466 if (!info->attrs[NL80211_ATTR_MAC])
3467 return -EINVAL;
3468
3469 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
3470
3471 if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
3472 params.supported_rates =
3473 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
3474 params.supported_rates_len =
3475 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
3476 }
3477
Jouni Malinen9d62a982013-02-14 21:10:13 +02003478 if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) {
3479 params.capability =
3480 nla_get_u16(info->attrs[NL80211_ATTR_STA_CAPABILITY]);
3481 params.sta_modify_mask |= STATION_PARAM_APPLY_CAPABILITY;
3482 }
3483
3484 if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) {
3485 params.ext_capab =
3486 nla_data(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
3487 params.ext_capab_len =
3488 nla_len(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
3489 }
3490
Jouni Malinendf881292013-02-14 21:10:54 +02003491 if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
Johannes Bergba23d202012-12-27 17:32:09 +01003492 return -EINVAL;
Jouni Malinen36aedc902008-08-25 11:58:58 +03003493
Johannes Bergbdd90d52011-12-14 12:20:27 +01003494 if (!rdev->ops->change_station)
3495 return -EOPNOTSUPP;
3496
Johannes Bergbdd3ae32012-01-02 13:30:03 +01003497 if (parse_station_flags(info, dev->ieee80211_ptr->iftype, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01003498 return -EINVAL;
3499
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003500 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
3501 params.plink_action =
3502 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
3503
Javier Cardona9c3990a2011-05-03 16:57:11 -07003504 if (info->attrs[NL80211_ATTR_STA_PLINK_STATE])
3505 params.plink_state =
3506 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
3507
Marco Porsch3b1c5a52013-01-07 16:04:52 +01003508 if (info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]) {
3509 enum nl80211_mesh_power_mode pm = nla_get_u32(
3510 info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]);
3511
3512 if (pm <= NL80211_MESH_POWER_UNKNOWN ||
3513 pm > NL80211_MESH_POWER_MAX)
3514 return -EINVAL;
3515
3516 params.local_pm = pm;
3517 }
3518
Johannes Berga97f4422009-06-18 17:23:43 +02003519 switch (dev->ieee80211_ptr->iftype) {
3520 case NL80211_IFTYPE_AP:
3521 case NL80211_IFTYPE_AP_VLAN:
Johannes Berg074ac8d2010-09-16 14:58:22 +02003522 case NL80211_IFTYPE_P2P_GO:
Johannes Berga97f4422009-06-18 17:23:43 +02003523 /* disallow mesh-specific things */
3524 if (params.plink_action)
Johannes Bergbdd90d52011-12-14 12:20:27 +01003525 return -EINVAL;
Marco Porsch3b1c5a52013-01-07 16:04:52 +01003526 if (params.local_pm)
3527 return -EINVAL;
Johannes Bergbdd90d52011-12-14 12:20:27 +01003528
3529 /* TDLS can't be set, ... */
3530 if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
3531 return -EINVAL;
3532 /*
3533 * ... but don't bother the driver with it. This works around
3534 * a hostapd/wpa_supplicant issue -- it always includes the
3535 * TLDS_PEER flag in the mask even for AP mode.
3536 */
3537 params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
3538
3539 /* accept only the listed bits */
3540 if (params.sta_flags_mask &
3541 ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
Johannes Bergd582cff2012-10-26 17:53:44 +02003542 BIT(NL80211_STA_FLAG_AUTHENTICATED) |
3543 BIT(NL80211_STA_FLAG_ASSOCIATED) |
Johannes Bergbdd90d52011-12-14 12:20:27 +01003544 BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
3545 BIT(NL80211_STA_FLAG_WME) |
3546 BIT(NL80211_STA_FLAG_MFP)))
3547 return -EINVAL;
3548
Johannes Bergd582cff2012-10-26 17:53:44 +02003549 /* but authenticated/associated only if driver handles it */
3550 if (!(rdev->wiphy.features &
3551 NL80211_FEATURE_FULL_AP_CLIENT_STATE) &&
3552 params.sta_flags_mask &
3553 (BIT(NL80211_STA_FLAG_AUTHENTICATED) |
3554 BIT(NL80211_STA_FLAG_ASSOCIATED)))
3555 return -EINVAL;
3556
Johannes Bergba23d202012-12-27 17:32:09 +01003557 /* reject other things that can't change */
3558 if (params.supported_rates)
3559 return -EINVAL;
Jouni Malinen9d62a982013-02-14 21:10:13 +02003560 if (info->attrs[NL80211_ATTR_STA_CAPABILITY])
3561 return -EINVAL;
3562 if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY])
3563 return -EINVAL;
Jouni Malinendf881292013-02-14 21:10:54 +02003564 if (info->attrs[NL80211_ATTR_HT_CAPABILITY] ||
3565 info->attrs[NL80211_ATTR_VHT_CAPABILITY])
3566 return -EINVAL;
Johannes Bergba23d202012-12-27 17:32:09 +01003567
Johannes Bergbdd90d52011-12-14 12:20:27 +01003568 /* must be last in here for error handling */
3569 params.vlan = get_vlan(info, rdev);
3570 if (IS_ERR(params.vlan))
3571 return PTR_ERR(params.vlan);
Johannes Berga97f4422009-06-18 17:23:43 +02003572 break;
Johannes Berg074ac8d2010-09-16 14:58:22 +02003573 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Berga97f4422009-06-18 17:23:43 +02003574 case NL80211_IFTYPE_STATION:
Johannes Bergbdd90d52011-12-14 12:20:27 +01003575 /*
3576 * Don't allow userspace to change the TDLS_PEER flag,
3577 * but silently ignore attempts to change it since we
3578 * don't have state here to verify that it doesn't try
3579 * to change the flag.
3580 */
3581 params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
Jouni Malinendf881292013-02-14 21:10:54 +02003582 /* Include parameters for TDLS peer (driver will check) */
3583 err = nl80211_set_station_tdls(info, &params);
3584 if (err)
3585 return err;
3586 /* disallow things sta doesn't support */
3587 if (params.plink_action)
3588 return -EINVAL;
3589 if (params.local_pm)
3590 return -EINVAL;
3591 /* reject any changes other than AUTHORIZED or WME (for TDLS) */
3592 if (params.sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
3593 BIT(NL80211_STA_FLAG_WME)))
3594 return -EINVAL;
3595 break;
Antonio Quartulli267335d2012-01-31 20:25:47 +01003596 case NL80211_IFTYPE_ADHOC:
3597 /* disallow things sta doesn't support */
3598 if (params.plink_action)
3599 return -EINVAL;
Marco Porsch3b1c5a52013-01-07 16:04:52 +01003600 if (params.local_pm)
3601 return -EINVAL;
Jouni Malinendf881292013-02-14 21:10:54 +02003602 if (info->attrs[NL80211_ATTR_HT_CAPABILITY] ||
3603 info->attrs[NL80211_ATTR_VHT_CAPABILITY])
3604 return -EINVAL;
Johannes Bergbdd90d52011-12-14 12:20:27 +01003605 /* reject any changes other than AUTHORIZED */
3606 if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
3607 return -EINVAL;
Johannes Berga97f4422009-06-18 17:23:43 +02003608 break;
3609 case NL80211_IFTYPE_MESH_POINT:
3610 /* disallow things mesh doesn't support */
3611 if (params.vlan)
Johannes Bergbdd90d52011-12-14 12:20:27 +01003612 return -EINVAL;
Johannes Bergba23d202012-12-27 17:32:09 +01003613 if (params.supported_rates)
Johannes Bergbdd90d52011-12-14 12:20:27 +01003614 return -EINVAL;
Jouni Malinen9d62a982013-02-14 21:10:13 +02003615 if (info->attrs[NL80211_ATTR_STA_CAPABILITY])
3616 return -EINVAL;
3617 if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY])
3618 return -EINVAL;
Jouni Malinendf881292013-02-14 21:10:54 +02003619 if (info->attrs[NL80211_ATTR_HT_CAPABILITY] ||
3620 info->attrs[NL80211_ATTR_VHT_CAPABILITY])
3621 return -EINVAL;
Johannes Bergbdd90d52011-12-14 12:20:27 +01003622 /*
3623 * No special handling for TDLS here -- the userspace
3624 * mesh code doesn't have this bug.
3625 */
Javier Cardonab39c48f2011-04-07 15:08:30 -07003626 if (params.sta_flags_mask &
3627 ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
Thomas Pedersen84298282011-05-03 16:57:13 -07003628 BIT(NL80211_STA_FLAG_MFP) |
Javier Cardonab39c48f2011-04-07 15:08:30 -07003629 BIT(NL80211_STA_FLAG_AUTHORIZED)))
Johannes Bergbdd90d52011-12-14 12:20:27 +01003630 return -EINVAL;
Johannes Berga97f4422009-06-18 17:23:43 +02003631 break;
3632 default:
Johannes Bergbdd90d52011-12-14 12:20:27 +01003633 return -EOPNOTSUPP;
Johannes Berg034d6552009-05-27 10:35:29 +02003634 }
3635
Johannes Bergbdd90d52011-12-14 12:20:27 +01003636 /* be aware of params.vlan when changing code here */
Johannes Berg5727ef12007-12-19 02:03:34 +01003637
Hila Gonene35e4d22012-06-27 17:19:42 +03003638 err = rdev_change_station(rdev, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01003639
Johannes Berg5727ef12007-12-19 02:03:34 +01003640 if (params.vlan)
3641 dev_put(params.vlan);
Johannes Berg3b858752009-03-12 09:55:09 +01003642
Johannes Berg5727ef12007-12-19 02:03:34 +01003643 return err;
3644}
3645
3646static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
3647{
Johannes Berg4c476992010-10-04 21:36:35 +02003648 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg5727ef12007-12-19 02:03:34 +01003649 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02003650 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01003651 struct station_parameters params;
3652 u8 *mac_addr = NULL;
3653
3654 memset(&params, 0, sizeof(params));
3655
3656 if (!info->attrs[NL80211_ATTR_MAC])
3657 return -EINVAL;
3658
Johannes Berg5727ef12007-12-19 02:03:34 +01003659 if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
3660 return -EINVAL;
3661
3662 if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
3663 return -EINVAL;
3664
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02003665 if (!info->attrs[NL80211_ATTR_STA_AID])
3666 return -EINVAL;
3667
Johannes Berg5727ef12007-12-19 02:03:34 +01003668 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
3669 params.supported_rates =
3670 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
3671 params.supported_rates_len =
3672 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
3673 params.listen_interval =
3674 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
Johannes Berg51b50fb2009-05-24 16:42:30 +02003675
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02003676 params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
3677 if (!params.aid || params.aid > IEEE80211_MAX_AID)
3678 return -EINVAL;
Johannes Berg51b50fb2009-05-24 16:42:30 +02003679
Jouni Malinen9d62a982013-02-14 21:10:13 +02003680 if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) {
3681 params.capability =
3682 nla_get_u16(info->attrs[NL80211_ATTR_STA_CAPABILITY]);
3683 params.sta_modify_mask |= STATION_PARAM_APPLY_CAPABILITY;
3684 }
3685
3686 if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) {
3687 params.ext_capab =
3688 nla_data(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
3689 params.ext_capab_len =
3690 nla_len(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
3691 }
3692
Jouni Malinen36aedc902008-08-25 11:58:58 +03003693 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
3694 params.ht_capa =
3695 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
Johannes Berg5727ef12007-12-19 02:03:34 +01003696
Mahesh Palivelaf461be3e2012-10-11 08:04:52 +00003697 if (info->attrs[NL80211_ATTR_VHT_CAPABILITY])
3698 params.vht_capa =
3699 nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
3700
Javier Cardona96b78df2011-04-07 15:08:33 -07003701 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
3702 params.plink_action =
3703 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
3704
Johannes Bergbdd90d52011-12-14 12:20:27 +01003705 if (!rdev->ops->add_station)
3706 return -EOPNOTSUPP;
3707
Johannes Bergbdd3ae32012-01-02 13:30:03 +01003708 if (parse_station_flags(info, dev->ieee80211_ptr->iftype, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01003709 return -EINVAL;
3710
Johannes Bergbdd90d52011-12-14 12:20:27 +01003711 switch (dev->ieee80211_ptr->iftype) {
3712 case NL80211_IFTYPE_AP:
3713 case NL80211_IFTYPE_AP_VLAN:
3714 case NL80211_IFTYPE_P2P_GO:
3715 /* parse WME attributes if sta is WME capable */
3716 if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
3717 (params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)) &&
3718 info->attrs[NL80211_ATTR_STA_WME]) {
3719 struct nlattr *tb[NL80211_STA_WME_MAX + 1];
3720 struct nlattr *nla;
Eliad Pellerc75786c2011-08-23 14:37:46 +03003721
Johannes Bergbdd90d52011-12-14 12:20:27 +01003722 nla = info->attrs[NL80211_ATTR_STA_WME];
3723 err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla,
3724 nl80211_sta_wme_policy);
3725 if (err)
3726 return err;
Eliad Pellerc75786c2011-08-23 14:37:46 +03003727
Johannes Bergbdd90d52011-12-14 12:20:27 +01003728 if (tb[NL80211_STA_WME_UAPSD_QUEUES])
3729 params.uapsd_queues =
3730 nla_get_u8(tb[NL80211_STA_WME_UAPSD_QUEUES]);
3731 if (params.uapsd_queues &
3732 ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
3733 return -EINVAL;
3734
3735 if (tb[NL80211_STA_WME_MAX_SP])
3736 params.max_sp =
3737 nla_get_u8(tb[NL80211_STA_WME_MAX_SP]);
3738
3739 if (params.max_sp &
3740 ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
3741 return -EINVAL;
3742
3743 params.sta_modify_mask |= STATION_PARAM_APPLY_UAPSD;
3744 }
3745 /* TDLS peers cannot be added */
3746 if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
Johannes Berg4319e192011-09-07 11:50:48 +02003747 return -EINVAL;
Johannes Bergbdd90d52011-12-14 12:20:27 +01003748 /* but don't bother the driver with it */
3749 params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
Eliad Pellerc75786c2011-08-23 14:37:46 +03003750
Johannes Bergd582cff2012-10-26 17:53:44 +02003751 /* allow authenticated/associated only if driver handles it */
3752 if (!(rdev->wiphy.features &
3753 NL80211_FEATURE_FULL_AP_CLIENT_STATE) &&
3754 params.sta_flags_mask &
3755 (BIT(NL80211_STA_FLAG_AUTHENTICATED) |
3756 BIT(NL80211_STA_FLAG_ASSOCIATED)))
3757 return -EINVAL;
3758
Johannes Bergbdd90d52011-12-14 12:20:27 +01003759 /* must be last in here for error handling */
3760 params.vlan = get_vlan(info, rdev);
3761 if (IS_ERR(params.vlan))
3762 return PTR_ERR(params.vlan);
3763 break;
3764 case NL80211_IFTYPE_MESH_POINT:
Johannes Bergd582cff2012-10-26 17:53:44 +02003765 /* associated is disallowed */
3766 if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED))
3767 return -EINVAL;
Johannes Bergbdd90d52011-12-14 12:20:27 +01003768 /* TDLS peers cannot be added */
3769 if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
Johannes Berg4319e192011-09-07 11:50:48 +02003770 return -EINVAL;
Johannes Bergbdd90d52011-12-14 12:20:27 +01003771 break;
3772 case NL80211_IFTYPE_STATION:
Johannes Bergd582cff2012-10-26 17:53:44 +02003773 /* associated is disallowed */
3774 if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED))
3775 return -EINVAL;
Johannes Bergbdd90d52011-12-14 12:20:27 +01003776 /* Only TDLS peers can be added */
3777 if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
3778 return -EINVAL;
3779 /* Can only add if TDLS ... */
3780 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS))
3781 return -EOPNOTSUPP;
3782 /* ... with external setup is supported */
3783 if (!(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))
3784 return -EOPNOTSUPP;
3785 break;
3786 default:
3787 return -EOPNOTSUPP;
Eliad Pellerc75786c2011-08-23 14:37:46 +03003788 }
3789
Johannes Bergbdd90d52011-12-14 12:20:27 +01003790 /* be aware of params.vlan when changing code here */
Johannes Berg5727ef12007-12-19 02:03:34 +01003791
Hila Gonene35e4d22012-06-27 17:19:42 +03003792 err = rdev_add_station(rdev, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01003793
Johannes Berg5727ef12007-12-19 02:03:34 +01003794 if (params.vlan)
3795 dev_put(params.vlan);
Johannes Berg5727ef12007-12-19 02:03:34 +01003796 return err;
3797}
3798
3799static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
3800{
Johannes Berg4c476992010-10-04 21:36:35 +02003801 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3802 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01003803 u8 *mac_addr = NULL;
3804
3805 if (info->attrs[NL80211_ATTR_MAC])
3806 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
3807
Johannes Berge80cf852009-05-11 14:43:13 +02003808 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Marco Porschd5d9de02010-03-30 10:00:16 +02003809 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02003810 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02003811 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
3812 return -EINVAL;
Johannes Berge80cf852009-05-11 14:43:13 +02003813
Johannes Berg4c476992010-10-04 21:36:35 +02003814 if (!rdev->ops->del_station)
3815 return -EOPNOTSUPP;
Johannes Berg5727ef12007-12-19 02:03:34 +01003816
Hila Gonene35e4d22012-06-27 17:19:42 +03003817 return rdev_del_station(rdev, dev, mac_addr);
Johannes Berg5727ef12007-12-19 02:03:34 +01003818}
3819
Eric W. Biederman15e47302012-09-07 20:12:54 +00003820static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003821 int flags, struct net_device *dev,
3822 u8 *dst, u8 *next_hop,
3823 struct mpath_info *pinfo)
3824{
3825 void *hdr;
3826 struct nlattr *pinfoattr;
3827
Eric W. Biederman15e47302012-09-07 20:12:54 +00003828 hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_STATION);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003829 if (!hdr)
3830 return -1;
3831
David S. Miller9360ffd2012-03-29 04:41:26 -04003832 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
3833 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dst) ||
3834 nla_put(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop) ||
3835 nla_put_u32(msg, NL80211_ATTR_GENERATION, pinfo->generation))
3836 goto nla_put_failure;
Johannes Bergf5ea9122009-08-07 16:17:38 +02003837
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003838 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
3839 if (!pinfoattr)
3840 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -04003841 if ((pinfo->filled & MPATH_INFO_FRAME_QLEN) &&
3842 nla_put_u32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
3843 pinfo->frame_qlen))
3844 goto nla_put_failure;
3845 if (((pinfo->filled & MPATH_INFO_SN) &&
3846 nla_put_u32(msg, NL80211_MPATH_INFO_SN, pinfo->sn)) ||
3847 ((pinfo->filled & MPATH_INFO_METRIC) &&
3848 nla_put_u32(msg, NL80211_MPATH_INFO_METRIC,
3849 pinfo->metric)) ||
3850 ((pinfo->filled & MPATH_INFO_EXPTIME) &&
3851 nla_put_u32(msg, NL80211_MPATH_INFO_EXPTIME,
3852 pinfo->exptime)) ||
3853 ((pinfo->filled & MPATH_INFO_FLAGS) &&
3854 nla_put_u8(msg, NL80211_MPATH_INFO_FLAGS,
3855 pinfo->flags)) ||
3856 ((pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT) &&
3857 nla_put_u32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
3858 pinfo->discovery_timeout)) ||
3859 ((pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES) &&
3860 nla_put_u8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
3861 pinfo->discovery_retries)))
3862 goto nla_put_failure;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003863
3864 nla_nest_end(msg, pinfoattr);
3865
3866 return genlmsg_end(msg, hdr);
3867
3868 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07003869 genlmsg_cancel(msg, hdr);
3870 return -EMSGSIZE;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003871}
3872
3873static int nl80211_dump_mpath(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02003874 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003875{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003876 struct mpath_info pinfo;
3877 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003878 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003879 u8 dst[ETH_ALEN];
3880 u8 next_hop[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02003881 int path_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003882 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003883
Johannes Berg67748892010-10-04 21:14:06 +02003884 err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
3885 if (err)
3886 return err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003887
3888 if (!dev->ops->dump_mpath) {
Jouni Malineneec60b02009-03-20 21:21:19 +02003889 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003890 goto out_err;
3891 }
3892
Jouni Malineneec60b02009-03-20 21:21:19 +02003893 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
3894 err = -EOPNOTSUPP;
Roel Kluin0448b5f2009-08-22 21:15:49 +02003895 goto out_err;
Jouni Malineneec60b02009-03-20 21:21:19 +02003896 }
3897
Johannes Bergbba95fe2008-07-29 13:22:51 +02003898 while (1) {
Hila Gonene35e4d22012-06-27 17:19:42 +03003899 err = rdev_dump_mpath(dev, netdev, path_idx, dst, next_hop,
3900 &pinfo);
Johannes Bergbba95fe2008-07-29 13:22:51 +02003901 if (err == -ENOENT)
3902 break;
3903 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01003904 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003905
Eric W. Biederman15e47302012-09-07 20:12:54 +00003906 if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).portid,
Johannes Bergbba95fe2008-07-29 13:22:51 +02003907 cb->nlh->nlmsg_seq, NLM_F_MULTI,
3908 netdev, dst, next_hop,
3909 &pinfo) < 0)
3910 goto out;
3911
3912 path_idx++;
3913 }
3914
3915
3916 out:
3917 cb->args[1] = path_idx;
3918 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003919 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02003920 nl80211_finish_netdev_dump(dev);
Johannes Bergbba95fe2008-07-29 13:22:51 +02003921 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003922}
3923
3924static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
3925{
Johannes Berg4c476992010-10-04 21:36:35 +02003926 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003927 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02003928 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003929 struct mpath_info pinfo;
3930 struct sk_buff *msg;
3931 u8 *dst = NULL;
3932 u8 next_hop[ETH_ALEN];
3933
3934 memset(&pinfo, 0, sizeof(pinfo));
3935
3936 if (!info->attrs[NL80211_ATTR_MAC])
3937 return -EINVAL;
3938
3939 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
3940
Johannes Berg4c476992010-10-04 21:36:35 +02003941 if (!rdev->ops->get_mpath)
3942 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01003943
Johannes Berg4c476992010-10-04 21:36:35 +02003944 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
3945 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02003946
Hila Gonene35e4d22012-06-27 17:19:42 +03003947 err = rdev_get_mpath(rdev, dev, dst, next_hop, &pinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003948 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02003949 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003950
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07003951 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003952 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02003953 return -ENOMEM;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003954
Eric W. Biederman15e47302012-09-07 20:12:54 +00003955 if (nl80211_send_mpath(msg, info->snd_portid, info->snd_seq, 0,
Johannes Berg4c476992010-10-04 21:36:35 +02003956 dev, dst, next_hop, &pinfo) < 0) {
3957 nlmsg_free(msg);
3958 return -ENOBUFS;
3959 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003960
Johannes Berg4c476992010-10-04 21:36:35 +02003961 return genlmsg_reply(msg, info);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003962}
3963
3964static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
3965{
Johannes Berg4c476992010-10-04 21:36:35 +02003966 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3967 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003968 u8 *dst = NULL;
3969 u8 *next_hop = NULL;
3970
3971 if (!info->attrs[NL80211_ATTR_MAC])
3972 return -EINVAL;
3973
3974 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
3975 return -EINVAL;
3976
3977 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
3978 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
3979
Johannes Berg4c476992010-10-04 21:36:35 +02003980 if (!rdev->ops->change_mpath)
3981 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01003982
Johannes Berg4c476992010-10-04 21:36:35 +02003983 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
3984 return -EOPNOTSUPP;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003985
Hila Gonene35e4d22012-06-27 17:19:42 +03003986 return rdev_change_mpath(rdev, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003987}
Johannes Berg4c476992010-10-04 21:36:35 +02003988
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003989static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
3990{
Johannes Berg4c476992010-10-04 21:36:35 +02003991 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3992 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003993 u8 *dst = NULL;
3994 u8 *next_hop = NULL;
3995
3996 if (!info->attrs[NL80211_ATTR_MAC])
3997 return -EINVAL;
3998
3999 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
4000 return -EINVAL;
4001
4002 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
4003 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
4004
Johannes Berg4c476992010-10-04 21:36:35 +02004005 if (!rdev->ops->add_mpath)
4006 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01004007
Johannes Berg4c476992010-10-04 21:36:35 +02004008 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
4009 return -EOPNOTSUPP;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004010
Hila Gonene35e4d22012-06-27 17:19:42 +03004011 return rdev_add_mpath(rdev, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004012}
4013
4014static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
4015{
Johannes Berg4c476992010-10-04 21:36:35 +02004016 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4017 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004018 u8 *dst = NULL;
4019
4020 if (info->attrs[NL80211_ATTR_MAC])
4021 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
4022
Johannes Berg4c476992010-10-04 21:36:35 +02004023 if (!rdev->ops->del_mpath)
4024 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01004025
Hila Gonene35e4d22012-06-27 17:19:42 +03004026 return rdev_del_mpath(rdev, dev, dst);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004027}
4028
Jouni Malinen9f1ba902008-08-07 20:07:01 +03004029static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
4030{
Johannes Berg4c476992010-10-04 21:36:35 +02004031 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4032 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9f1ba902008-08-07 20:07:01 +03004033 struct bss_parameters params;
4034
4035 memset(&params, 0, sizeof(params));
4036 /* default to not changing parameters */
4037 params.use_cts_prot = -1;
4038 params.use_short_preamble = -1;
4039 params.use_short_slot_time = -1;
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +02004040 params.ap_isolate = -1;
Helmut Schaa50b12f52010-11-19 12:40:25 +01004041 params.ht_opmode = -1;
Johannes Berg53cabad2012-11-14 15:17:28 +01004042 params.p2p_ctwindow = -1;
4043 params.p2p_opp_ps = -1;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03004044
4045 if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
4046 params.use_cts_prot =
4047 nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
4048 if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
4049 params.use_short_preamble =
4050 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
4051 if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
4052 params.use_short_slot_time =
4053 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
Jouni Malinen90c97a02008-10-30 16:59:22 +02004054 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
4055 params.basic_rates =
4056 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
4057 params.basic_rates_len =
4058 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
4059 }
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +02004060 if (info->attrs[NL80211_ATTR_AP_ISOLATE])
4061 params.ap_isolate = !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]);
Helmut Schaa50b12f52010-11-19 12:40:25 +01004062 if (info->attrs[NL80211_ATTR_BSS_HT_OPMODE])
4063 params.ht_opmode =
4064 nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03004065
Johannes Berg53cabad2012-11-14 15:17:28 +01004066 if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) {
4067 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
4068 return -EINVAL;
4069 params.p2p_ctwindow =
4070 nla_get_s8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]);
4071 if (params.p2p_ctwindow < 0)
4072 return -EINVAL;
4073 if (params.p2p_ctwindow != 0 &&
4074 !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN))
4075 return -EINVAL;
4076 }
4077
4078 if (info->attrs[NL80211_ATTR_P2P_OPPPS]) {
4079 u8 tmp;
4080
4081 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
4082 return -EINVAL;
4083 tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]);
4084 if (tmp > 1)
4085 return -EINVAL;
4086 params.p2p_opp_ps = tmp;
4087 if (params.p2p_opp_ps &&
4088 !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS))
4089 return -EINVAL;
4090 }
4091
Johannes Berg4c476992010-10-04 21:36:35 +02004092 if (!rdev->ops->change_bss)
4093 return -EOPNOTSUPP;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03004094
Johannes Berg074ac8d2010-09-16 14:58:22 +02004095 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg4c476992010-10-04 21:36:35 +02004096 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
4097 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004098
Hila Gonene35e4d22012-06-27 17:19:42 +03004099 return rdev_change_bss(rdev, dev, &params);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03004100}
4101
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00004102static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004103 [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
4104 [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
4105 [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
4106 [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
4107 [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
4108 [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
4109};
4110
4111static int parse_reg_rule(struct nlattr *tb[],
4112 struct ieee80211_reg_rule *reg_rule)
4113{
4114 struct ieee80211_freq_range *freq_range = &reg_rule->freq_range;
4115 struct ieee80211_power_rule *power_rule = &reg_rule->power_rule;
4116
4117 if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
4118 return -EINVAL;
4119 if (!tb[NL80211_ATTR_FREQ_RANGE_START])
4120 return -EINVAL;
4121 if (!tb[NL80211_ATTR_FREQ_RANGE_END])
4122 return -EINVAL;
4123 if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
4124 return -EINVAL;
4125 if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
4126 return -EINVAL;
4127
4128 reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
4129
4130 freq_range->start_freq_khz =
4131 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
4132 freq_range->end_freq_khz =
4133 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
4134 freq_range->max_bandwidth_khz =
4135 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
4136
4137 power_rule->max_eirp =
4138 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
4139
4140 if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
4141 power_rule->max_antenna_gain =
4142 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
4143
4144 return 0;
4145}
4146
4147static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
4148{
4149 int r;
4150 char *data = NULL;
Luis R. Rodriguez57b5ce02012-07-12 11:49:18 -07004151 enum nl80211_user_reg_hint_type user_reg_hint_type;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004152
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05004153 /*
4154 * You should only get this when cfg80211 hasn't yet initialized
4155 * completely when built-in to the kernel right between the time
4156 * window between nl80211_init() and regulatory_init(), if that is
4157 * even possible.
4158 */
Johannes Berg458f4f92012-12-06 15:47:38 +01004159 if (unlikely(!rcu_access_pointer(cfg80211_regdomain)))
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05004160 return -EINPROGRESS;
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05004161
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05004162 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
4163 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004164
4165 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
4166
Luis R. Rodriguez57b5ce02012-07-12 11:49:18 -07004167 if (info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE])
4168 user_reg_hint_type =
4169 nla_get_u32(info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE]);
4170 else
4171 user_reg_hint_type = NL80211_USER_REG_HINT_USER;
4172
4173 switch (user_reg_hint_type) {
4174 case NL80211_USER_REG_HINT_USER:
4175 case NL80211_USER_REG_HINT_CELL_BASE:
4176 break;
4177 default:
4178 return -EINVAL;
4179 }
4180
4181 r = regulatory_hint_user(data, user_reg_hint_type);
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05004182
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004183 return r;
4184}
4185
Javier Cardona24bdd9f2010-12-16 17:37:48 -08004186static int nl80211_get_mesh_config(struct sk_buff *skb,
Johannes Berg29cbe682010-12-03 09:20:44 +01004187 struct genl_info *info)
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004188{
Johannes Berg4c476992010-10-04 21:36:35 +02004189 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg4c476992010-10-04 21:36:35 +02004190 struct net_device *dev = info->user_ptr[1];
Johannes Berg29cbe682010-12-03 09:20:44 +01004191 struct wireless_dev *wdev = dev->ieee80211_ptr;
4192 struct mesh_config cur_params;
4193 int err = 0;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004194 void *hdr;
4195 struct nlattr *pinfoattr;
4196 struct sk_buff *msg;
4197
Johannes Berg29cbe682010-12-03 09:20:44 +01004198 if (wdev->iftype != NL80211_IFTYPE_MESH_POINT)
4199 return -EOPNOTSUPP;
4200
Javier Cardona24bdd9f2010-12-16 17:37:48 -08004201 if (!rdev->ops->get_mesh_config)
Johannes Berg4c476992010-10-04 21:36:35 +02004202 return -EOPNOTSUPP;
Jouni Malinenf3f92582009-03-20 17:57:36 +02004203
Johannes Berg29cbe682010-12-03 09:20:44 +01004204 wdev_lock(wdev);
4205 /* If not connected, get default parameters */
4206 if (!wdev->mesh_id_len)
4207 memcpy(&cur_params, &default_mesh_config, sizeof(cur_params));
4208 else
Hila Gonene35e4d22012-06-27 17:19:42 +03004209 err = rdev_get_mesh_config(rdev, dev, &cur_params);
Johannes Berg29cbe682010-12-03 09:20:44 +01004210 wdev_unlock(wdev);
4211
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004212 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02004213 return err;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004214
4215 /* Draw up a netlink message to send back */
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07004216 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02004217 if (!msg)
4218 return -ENOMEM;
Eric W. Biederman15e47302012-09-07 20:12:54 +00004219 hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
Javier Cardona24bdd9f2010-12-16 17:37:48 -08004220 NL80211_CMD_GET_MESH_CONFIG);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004221 if (!hdr)
Julia Lawallefe1cf02011-01-28 15:17:11 +01004222 goto out;
Javier Cardona24bdd9f2010-12-16 17:37:48 -08004223 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004224 if (!pinfoattr)
4225 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -04004226 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
4227 nla_put_u16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
4228 cur_params.dot11MeshRetryTimeout) ||
4229 nla_put_u16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
4230 cur_params.dot11MeshConfirmTimeout) ||
4231 nla_put_u16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
4232 cur_params.dot11MeshHoldingTimeout) ||
4233 nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
4234 cur_params.dot11MeshMaxPeerLinks) ||
4235 nla_put_u8(msg, NL80211_MESHCONF_MAX_RETRIES,
4236 cur_params.dot11MeshMaxRetries) ||
4237 nla_put_u8(msg, NL80211_MESHCONF_TTL,
4238 cur_params.dot11MeshTTL) ||
4239 nla_put_u8(msg, NL80211_MESHCONF_ELEMENT_TTL,
4240 cur_params.element_ttl) ||
4241 nla_put_u8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
4242 cur_params.auto_open_plinks) ||
John W. Linville7eab0f62012-04-12 14:25:14 -04004243 nla_put_u32(msg, NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
4244 cur_params.dot11MeshNbrOffsetMaxNeighbor) ||
David S. Miller9360ffd2012-03-29 04:41:26 -04004245 nla_put_u8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
4246 cur_params.dot11MeshHWMPmaxPREQretries) ||
4247 nla_put_u32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
4248 cur_params.path_refresh_time) ||
4249 nla_put_u16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
4250 cur_params.min_discovery_timeout) ||
4251 nla_put_u32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
4252 cur_params.dot11MeshHWMPactivePathTimeout) ||
4253 nla_put_u16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
4254 cur_params.dot11MeshHWMPpreqMinInterval) ||
4255 nla_put_u16(msg, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
4256 cur_params.dot11MeshHWMPperrMinInterval) ||
4257 nla_put_u16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
4258 cur_params.dot11MeshHWMPnetDiameterTraversalTime) ||
4259 nla_put_u8(msg, NL80211_MESHCONF_HWMP_ROOTMODE,
4260 cur_params.dot11MeshHWMPRootMode) ||
4261 nla_put_u16(msg, NL80211_MESHCONF_HWMP_RANN_INTERVAL,
4262 cur_params.dot11MeshHWMPRannInterval) ||
4263 nla_put_u8(msg, NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
4264 cur_params.dot11MeshGateAnnouncementProtocol) ||
4265 nla_put_u8(msg, NL80211_MESHCONF_FORWARDING,
4266 cur_params.dot11MeshForwarding) ||
4267 nla_put_u32(msg, NL80211_MESHCONF_RSSI_THRESHOLD,
Ashok Nagarajan70c33ea2012-04-30 14:20:32 -07004268 cur_params.rssi_threshold) ||
4269 nla_put_u32(msg, NL80211_MESHCONF_HT_OPMODE,
Chun-Yeow Yeohac1073a2012-06-14 02:06:06 +08004270 cur_params.ht_opmode) ||
4271 nla_put_u32(msg, NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
4272 cur_params.dot11MeshHWMPactivePathToRootTimeout) ||
4273 nla_put_u16(msg, NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
Chun-Yeow Yeoh728b19e2012-06-14 02:06:10 +08004274 cur_params.dot11MeshHWMProotInterval) ||
4275 nla_put_u16(msg, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
Marco Porsch3b1c5a52013-01-07 16:04:52 +01004276 cur_params.dot11MeshHWMPconfirmationInterval) ||
4277 nla_put_u32(msg, NL80211_MESHCONF_POWER_MODE,
4278 cur_params.power_mode) ||
4279 nla_put_u16(msg, NL80211_MESHCONF_AWAKE_WINDOW,
4280 cur_params.dot11MeshAwakeWindowDuration))
David S. Miller9360ffd2012-03-29 04:41:26 -04004281 goto nla_put_failure;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004282 nla_nest_end(msg, pinfoattr);
4283 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02004284 return genlmsg_reply(msg, info);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004285
Johannes Berg3b858752009-03-12 09:55:09 +01004286 nla_put_failure:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004287 genlmsg_cancel(msg, hdr);
Julia Lawallefe1cf02011-01-28 15:17:11 +01004288 out:
Yuri Ershovd080e272010-06-29 15:08:07 +04004289 nlmsg_free(msg);
Johannes Berg4c476992010-10-04 21:36:35 +02004290 return -ENOBUFS;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004291}
4292
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00004293static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = {
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004294 [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
4295 [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
4296 [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
4297 [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
4298 [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
4299 [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
Javier Cardona45904f22010-12-03 09:20:40 +01004300 [NL80211_MESHCONF_ELEMENT_TTL] = { .type = NLA_U8 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004301 [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
Javier Cardonad299a1f2012-03-31 11:31:33 -07004302 [NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR] = { .type = NLA_U32 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004303 [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
4304 [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
4305 [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
4306 [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
4307 [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
Thomas Pedersendca7e942011-11-24 17:15:24 -08004308 [NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL] = { .type = NLA_U16 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004309 [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
Javier Cardona699403d2011-08-09 16:45:09 -07004310 [NL80211_MESHCONF_HWMP_ROOTMODE] = { .type = NLA_U8 },
Javier Cardona0507e152011-08-09 16:45:10 -07004311 [NL80211_MESHCONF_HWMP_RANN_INTERVAL] = { .type = NLA_U16 },
Javier Cardona16dd7262011-08-09 16:45:11 -07004312 [NL80211_MESHCONF_GATE_ANNOUNCEMENTS] = { .type = NLA_U8 },
Chun-Yeow Yeoh94f90652012-01-21 01:02:16 +08004313 [NL80211_MESHCONF_FORWARDING] = { .type = NLA_U8 },
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004314 [NL80211_MESHCONF_RSSI_THRESHOLD] = { .type = NLA_U32 },
4315 [NL80211_MESHCONF_HT_OPMODE] = { .type = NLA_U16 },
Chun-Yeow Yeohac1073a2012-06-14 02:06:06 +08004316 [NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT] = { .type = NLA_U32 },
4317 [NL80211_MESHCONF_HWMP_ROOT_INTERVAL] = { .type = NLA_U16 },
Chun-Yeow Yeoh728b19e2012-06-14 02:06:10 +08004318 [NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL] = { .type = NLA_U16 },
Marco Porsch3b1c5a52013-01-07 16:04:52 +01004319 [NL80211_MESHCONF_POWER_MODE] = { .type = NLA_U32 },
4320 [NL80211_MESHCONF_AWAKE_WINDOW] = { .type = NLA_U16 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004321};
4322
Javier Cardonac80d5452010-12-16 17:37:49 -08004323static const struct nla_policy
4324 nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = {
Javier Cardonad299a1f2012-03-31 11:31:33 -07004325 [NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC] = { .type = NLA_U8 },
Javier Cardonac80d5452010-12-16 17:37:49 -08004326 [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 },
4327 [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 },
Javier Cardona15d5dda2011-04-07 15:08:28 -07004328 [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG },
Javier Cardona581a8b02011-04-07 15:08:27 -07004329 [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004330 .len = IEEE80211_MAX_DATA_LEN },
Javier Cardonab130e5c2011-05-03 16:57:07 -07004331 [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG },
Javier Cardonac80d5452010-12-16 17:37:49 -08004332};
4333
Javier Cardona24bdd9f2010-12-16 17:37:48 -08004334static int nl80211_parse_mesh_config(struct genl_info *info,
Johannes Bergbd90fdc2010-12-03 09:20:43 +01004335 struct mesh_config *cfg,
4336 u32 *mask_out)
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004337{
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004338 struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
Johannes Bergbd90fdc2010-12-03 09:20:43 +01004339 u32 mask = 0;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004340
Marco Porschea54fba2013-01-07 16:04:48 +01004341#define FILL_IN_MESH_PARAM_IF_SET(tb, cfg, param, min, max, mask, attr, fn) \
4342do { \
4343 if (tb[attr]) { \
4344 if (fn(tb[attr]) < min || fn(tb[attr]) > max) \
4345 return -EINVAL; \
4346 cfg->param = fn(tb[attr]); \
4347 mask |= (1 << (attr - 1)); \
4348 } \
4349} while (0)
Johannes Bergbd90fdc2010-12-03 09:20:43 +01004350
4351
Javier Cardona24bdd9f2010-12-16 17:37:48 -08004352 if (!info->attrs[NL80211_ATTR_MESH_CONFIG])
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004353 return -EINVAL;
4354 if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
Javier Cardona24bdd9f2010-12-16 17:37:48 -08004355 info->attrs[NL80211_ATTR_MESH_CONFIG],
Johannes Bergbd90fdc2010-12-03 09:20:43 +01004356 nl80211_meshconf_params_policy))
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004357 return -EINVAL;
4358
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004359 /* This makes sure that there aren't more than 32 mesh config
4360 * parameters (otherwise our bitfield scheme would not work.) */
4361 BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
4362
4363 /* Fill in the params struct */
Marco Porschea54fba2013-01-07 16:04:48 +01004364 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout, 1, 255,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004365 mask, NL80211_MESHCONF_RETRY_TIMEOUT,
4366 nla_get_u16);
Marco Porschea54fba2013-01-07 16:04:48 +01004367 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout, 1, 255,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004368 mask, NL80211_MESHCONF_CONFIRM_TIMEOUT,
4369 nla_get_u16);
Marco Porschea54fba2013-01-07 16:04:48 +01004370 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout, 1, 255,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004371 mask, NL80211_MESHCONF_HOLDING_TIMEOUT,
4372 nla_get_u16);
Marco Porschea54fba2013-01-07 16:04:48 +01004373 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks, 0, 255,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004374 mask, NL80211_MESHCONF_MAX_PEER_LINKS,
4375 nla_get_u16);
Marco Porschea54fba2013-01-07 16:04:48 +01004376 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries, 0, 16,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004377 mask, NL80211_MESHCONF_MAX_RETRIES,
4378 nla_get_u8);
Marco Porschea54fba2013-01-07 16:04:48 +01004379 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL, 1, 255,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004380 mask, NL80211_MESHCONF_TTL, nla_get_u8);
Marco Porschea54fba2013-01-07 16:04:48 +01004381 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl, 1, 255,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004382 mask, NL80211_MESHCONF_ELEMENT_TTL,
4383 nla_get_u8);
Marco Porschea54fba2013-01-07 16:04:48 +01004384 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks, 0, 1,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004385 mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
4386 nla_get_u8);
Marco Porschea54fba2013-01-07 16:04:48 +01004387 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNbrOffsetMaxNeighbor,
4388 1, 255, mask,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004389 NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
4390 nla_get_u32);
Marco Porschea54fba2013-01-07 16:04:48 +01004391 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries, 0, 255,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004392 mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
4393 nla_get_u8);
Marco Porschea54fba2013-01-07 16:04:48 +01004394 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time, 1, 65535,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004395 mask, NL80211_MESHCONF_PATH_REFRESH_TIME,
4396 nla_get_u32);
Marco Porschea54fba2013-01-07 16:04:48 +01004397 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout, 1, 65535,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004398 mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
4399 nla_get_u16);
Marco Porschea54fba2013-01-07 16:04:48 +01004400 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
4401 1, 65535, mask,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004402 NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
4403 nla_get_u32);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004404 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
Marco Porschea54fba2013-01-07 16:04:48 +01004405 1, 65535, mask,
4406 NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004407 nla_get_u16);
Thomas Pedersendca7e942011-11-24 17:15:24 -08004408 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPperrMinInterval,
Marco Porschea54fba2013-01-07 16:04:48 +01004409 1, 65535, mask,
4410 NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004411 nla_get_u16);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004412 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
Marco Porschea54fba2013-01-07 16:04:48 +01004413 dot11MeshHWMPnetDiameterTraversalTime,
4414 1, 65535, mask,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004415 NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
4416 nla_get_u16);
Marco Porschea54fba2013-01-07 16:04:48 +01004417 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRootMode, 0, 4,
4418 mask, NL80211_MESHCONF_HWMP_ROOTMODE,
4419 nla_get_u8);
4420 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRannInterval, 1, 65535,
4421 mask, NL80211_MESHCONF_HWMP_RANN_INTERVAL,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004422 nla_get_u16);
Rui Paulo63c57232009-11-09 23:46:57 +00004423 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
Marco Porschea54fba2013-01-07 16:04:48 +01004424 dot11MeshGateAnnouncementProtocol, 0, 1,
4425 mask, NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004426 nla_get_u8);
Marco Porschea54fba2013-01-07 16:04:48 +01004427 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding, 0, 1,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004428 mask, NL80211_MESHCONF_FORWARDING,
4429 nla_get_u8);
Marco Porschea54fba2013-01-07 16:04:48 +01004430 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, 1, 255,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004431 mask, NL80211_MESHCONF_RSSI_THRESHOLD,
4432 nla_get_u32);
Marco Porschea54fba2013-01-07 16:04:48 +01004433 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, ht_opmode, 0, 16,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08004434 mask, NL80211_MESHCONF_HT_OPMODE,
4435 nla_get_u16);
Chun-Yeow Yeohac1073a2012-06-14 02:06:06 +08004436 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathToRootTimeout,
Marco Porschea54fba2013-01-07 16:04:48 +01004437 1, 65535, mask,
Chun-Yeow Yeohac1073a2012-06-14 02:06:06 +08004438 NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
4439 nla_get_u32);
Marco Porschea54fba2013-01-07 16:04:48 +01004440 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMProotInterval, 1, 65535,
Chun-Yeow Yeohac1073a2012-06-14 02:06:06 +08004441 mask, NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
4442 nla_get_u16);
Chun-Yeow Yeoh728b19e2012-06-14 02:06:10 +08004443 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
Marco Porschea54fba2013-01-07 16:04:48 +01004444 dot11MeshHWMPconfirmationInterval,
4445 1, 65535, mask,
Chun-Yeow Yeoh728b19e2012-06-14 02:06:10 +08004446 NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
4447 nla_get_u16);
Marco Porsch3b1c5a52013-01-07 16:04:52 +01004448 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, power_mode,
4449 NL80211_MESH_POWER_ACTIVE,
4450 NL80211_MESH_POWER_MAX,
4451 mask, NL80211_MESHCONF_POWER_MODE,
4452 nla_get_u32);
4453 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration,
4454 0, 65535, mask,
4455 NL80211_MESHCONF_AWAKE_WINDOW, nla_get_u16);
Johannes Bergbd90fdc2010-12-03 09:20:43 +01004456 if (mask_out)
4457 *mask_out = mask;
Javier Cardonac80d5452010-12-16 17:37:49 -08004458
Johannes Bergbd90fdc2010-12-03 09:20:43 +01004459 return 0;
4460
4461#undef FILL_IN_MESH_PARAM_IF_SET
4462}
4463
Javier Cardonac80d5452010-12-16 17:37:49 -08004464static int nl80211_parse_mesh_setup(struct genl_info *info,
4465 struct mesh_setup *setup)
4466{
4467 struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1];
4468
4469 if (!info->attrs[NL80211_ATTR_MESH_SETUP])
4470 return -EINVAL;
4471 if (nla_parse_nested(tb, NL80211_MESH_SETUP_ATTR_MAX,
4472 info->attrs[NL80211_ATTR_MESH_SETUP],
4473 nl80211_mesh_setup_params_policy))
4474 return -EINVAL;
4475
Javier Cardonad299a1f2012-03-31 11:31:33 -07004476 if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC])
4477 setup->sync_method =
4478 (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC])) ?
4479 IEEE80211_SYNC_METHOD_VENDOR :
4480 IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET;
4481
Javier Cardonac80d5452010-12-16 17:37:49 -08004482 if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])
4483 setup->path_sel_proto =
4484 (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])) ?
4485 IEEE80211_PATH_PROTOCOL_VENDOR :
4486 IEEE80211_PATH_PROTOCOL_HWMP;
4487
4488 if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])
4489 setup->path_metric =
4490 (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])) ?
4491 IEEE80211_PATH_METRIC_VENDOR :
4492 IEEE80211_PATH_METRIC_AIRTIME;
4493
Javier Cardona581a8b02011-04-07 15:08:27 -07004494
4495 if (tb[NL80211_MESH_SETUP_IE]) {
Javier Cardonac80d5452010-12-16 17:37:49 -08004496 struct nlattr *ieattr =
Javier Cardona581a8b02011-04-07 15:08:27 -07004497 tb[NL80211_MESH_SETUP_IE];
Javier Cardonac80d5452010-12-16 17:37:49 -08004498 if (!is_valid_ie_attr(ieattr))
4499 return -EINVAL;
Javier Cardona581a8b02011-04-07 15:08:27 -07004500 setup->ie = nla_data(ieattr);
4501 setup->ie_len = nla_len(ieattr);
Javier Cardonac80d5452010-12-16 17:37:49 -08004502 }
Javier Cardonab130e5c2011-05-03 16:57:07 -07004503 setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]);
4504 setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]);
Javier Cardonac80d5452010-12-16 17:37:49 -08004505
4506 return 0;
4507}
4508
Javier Cardona24bdd9f2010-12-16 17:37:48 -08004509static int nl80211_update_mesh_config(struct sk_buff *skb,
Johannes Berg29cbe682010-12-03 09:20:44 +01004510 struct genl_info *info)
Johannes Bergbd90fdc2010-12-03 09:20:43 +01004511{
4512 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4513 struct net_device *dev = info->user_ptr[1];
Johannes Berg29cbe682010-12-03 09:20:44 +01004514 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Bergbd90fdc2010-12-03 09:20:43 +01004515 struct mesh_config cfg;
4516 u32 mask;
4517 int err;
4518
Johannes Berg29cbe682010-12-03 09:20:44 +01004519 if (wdev->iftype != NL80211_IFTYPE_MESH_POINT)
4520 return -EOPNOTSUPP;
4521
Javier Cardona24bdd9f2010-12-16 17:37:48 -08004522 if (!rdev->ops->update_mesh_config)
Johannes Bergbd90fdc2010-12-03 09:20:43 +01004523 return -EOPNOTSUPP;
4524
Javier Cardona24bdd9f2010-12-16 17:37:48 -08004525 err = nl80211_parse_mesh_config(info, &cfg, &mask);
Johannes Bergbd90fdc2010-12-03 09:20:43 +01004526 if (err)
4527 return err;
4528
Johannes Berg29cbe682010-12-03 09:20:44 +01004529 wdev_lock(wdev);
4530 if (!wdev->mesh_id_len)
4531 err = -ENOLINK;
4532
4533 if (!err)
Hila Gonene35e4d22012-06-27 17:19:42 +03004534 err = rdev_update_mesh_config(rdev, dev, mask, &cfg);
Johannes Berg29cbe682010-12-03 09:20:44 +01004535
4536 wdev_unlock(wdev);
4537
4538 return err;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004539}
4540
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004541static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
4542{
Johannes Berg458f4f92012-12-06 15:47:38 +01004543 const struct ieee80211_regdomain *regdom;
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004544 struct sk_buff *msg;
4545 void *hdr = NULL;
4546 struct nlattr *nl_reg_rules;
4547 unsigned int i;
4548 int err = -EINVAL;
4549
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05004550 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004551
4552 if (!cfg80211_regdomain)
4553 goto out;
4554
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07004555 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004556 if (!msg) {
4557 err = -ENOBUFS;
4558 goto out;
4559 }
4560
Eric W. Biederman15e47302012-09-07 20:12:54 +00004561 hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004562 NL80211_CMD_GET_REG);
4563 if (!hdr)
Julia Lawallefe1cf02011-01-28 15:17:11 +01004564 goto put_failure;
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004565
Luis R. Rodriguez57b5ce02012-07-12 11:49:18 -07004566 if (reg_last_request_cell_base() &&
4567 nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
4568 NL80211_USER_REG_HINT_CELL_BASE))
4569 goto nla_put_failure;
4570
Johannes Berg458f4f92012-12-06 15:47:38 +01004571 rcu_read_lock();
4572 regdom = rcu_dereference(cfg80211_regdomain);
4573
4574 if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, regdom->alpha2) ||
4575 (regdom->dfs_region &&
4576 nla_put_u8(msg, NL80211_ATTR_DFS_REGION, regdom->dfs_region)))
4577 goto nla_put_failure_rcu;
4578
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004579 nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
4580 if (!nl_reg_rules)
Johannes Berg458f4f92012-12-06 15:47:38 +01004581 goto nla_put_failure_rcu;
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004582
Johannes Berg458f4f92012-12-06 15:47:38 +01004583 for (i = 0; i < regdom->n_reg_rules; i++) {
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004584 struct nlattr *nl_reg_rule;
4585 const struct ieee80211_reg_rule *reg_rule;
4586 const struct ieee80211_freq_range *freq_range;
4587 const struct ieee80211_power_rule *power_rule;
4588
Johannes Berg458f4f92012-12-06 15:47:38 +01004589 reg_rule = &regdom->reg_rules[i];
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004590 freq_range = &reg_rule->freq_range;
4591 power_rule = &reg_rule->power_rule;
4592
4593 nl_reg_rule = nla_nest_start(msg, i);
4594 if (!nl_reg_rule)
Johannes Berg458f4f92012-12-06 15:47:38 +01004595 goto nla_put_failure_rcu;
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004596
David S. Miller9360ffd2012-03-29 04:41:26 -04004597 if (nla_put_u32(msg, NL80211_ATTR_REG_RULE_FLAGS,
4598 reg_rule->flags) ||
4599 nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_START,
4600 freq_range->start_freq_khz) ||
4601 nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_END,
4602 freq_range->end_freq_khz) ||
4603 nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
4604 freq_range->max_bandwidth_khz) ||
4605 nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
4606 power_rule->max_antenna_gain) ||
4607 nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
4608 power_rule->max_eirp))
Johannes Berg458f4f92012-12-06 15:47:38 +01004609 goto nla_put_failure_rcu;
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004610
4611 nla_nest_end(msg, nl_reg_rule);
4612 }
Johannes Berg458f4f92012-12-06 15:47:38 +01004613 rcu_read_unlock();
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004614
4615 nla_nest_end(msg, nl_reg_rules);
4616
4617 genlmsg_end(msg, hdr);
Johannes Berg134e6372009-07-10 09:51:34 +00004618 err = genlmsg_reply(msg, info);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004619 goto out;
4620
Johannes Berg458f4f92012-12-06 15:47:38 +01004621nla_put_failure_rcu:
4622 rcu_read_unlock();
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004623nla_put_failure:
4624 genlmsg_cancel(msg, hdr);
Julia Lawallefe1cf02011-01-28 15:17:11 +01004625put_failure:
Yuri Ershovd080e272010-06-29 15:08:07 +04004626 nlmsg_free(msg);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004627 err = -EMSGSIZE;
4628out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05004629 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004630 return err;
4631}
4632
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004633static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
4634{
4635 struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
4636 struct nlattr *nl_reg_rule;
4637 char *alpha2 = NULL;
4638 int rem_reg_rules = 0, r = 0;
4639 u32 num_rules = 0, rule_idx = 0, size_of_regd;
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -07004640 u8 dfs_region = 0;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004641 struct ieee80211_regdomain *rd = NULL;
4642
4643 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
4644 return -EINVAL;
4645
4646 if (!info->attrs[NL80211_ATTR_REG_RULES])
4647 return -EINVAL;
4648
4649 alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
4650
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -07004651 if (info->attrs[NL80211_ATTR_DFS_REGION])
4652 dfs_region = nla_get_u8(info->attrs[NL80211_ATTR_DFS_REGION]);
4653
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004654 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
Johannes Berg1a919312012-12-03 17:21:11 +01004655 rem_reg_rules) {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004656 num_rules++;
4657 if (num_rules > NL80211_MAX_SUPP_REG_RULES)
Luis R. Rodriguez4776c6e2009-05-13 17:04:39 -04004658 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004659 }
4660
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004661 size_of_regd = sizeof(struct ieee80211_regdomain) +
Johannes Berg1a919312012-12-03 17:21:11 +01004662 num_rules * sizeof(struct ieee80211_reg_rule);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004663
4664 rd = kzalloc(size_of_regd, GFP_KERNEL);
Johannes Berg6913b492012-12-04 00:48:59 +01004665 if (!rd)
4666 return -ENOMEM;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004667
4668 rd->n_reg_rules = num_rules;
4669 rd->alpha2[0] = alpha2[0];
4670 rd->alpha2[1] = alpha2[1];
4671
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -07004672 /*
4673 * Disable DFS master mode if the DFS region was
4674 * not supported or known on this kernel.
4675 */
4676 if (reg_supported_dfs_region(dfs_region))
4677 rd->dfs_region = dfs_region;
4678
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004679 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
Johannes Berg1a919312012-12-03 17:21:11 +01004680 rem_reg_rules) {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004681 nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
Johannes Berg1a919312012-12-03 17:21:11 +01004682 nla_data(nl_reg_rule), nla_len(nl_reg_rule),
4683 reg_rule_policy);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004684 r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
4685 if (r)
4686 goto bad_reg;
4687
4688 rule_idx++;
4689
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04004690 if (rule_idx > NL80211_MAX_SUPP_REG_RULES) {
4691 r = -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004692 goto bad_reg;
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04004693 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004694 }
4695
Johannes Berg6913b492012-12-04 00:48:59 +01004696 mutex_lock(&cfg80211_mutex);
4697
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004698 r = set_regdom(rd);
Johannes Berg6913b492012-12-04 00:48:59 +01004699 /* set_regdom took ownership */
Johannes Berg1a919312012-12-03 17:21:11 +01004700 rd = NULL;
Johannes Berg6913b492012-12-04 00:48:59 +01004701 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004702
Johannes Bergd2372b32008-10-24 20:32:20 +02004703 bad_reg:
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004704 kfree(rd);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04004705 return r;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004706}
4707
Johannes Berg83f5e2c2009-06-17 17:41:49 +02004708static int validate_scan_freqs(struct nlattr *freqs)
4709{
4710 struct nlattr *attr1, *attr2;
4711 int n_channels = 0, tmp1, tmp2;
4712
4713 nla_for_each_nested(attr1, freqs, tmp1) {
4714 n_channels++;
4715 /*
4716 * Some hardware has a limited channel list for
4717 * scanning, and it is pretty much nonsensical
4718 * to scan for a channel twice, so disallow that
4719 * and don't require drivers to check that the
4720 * channel list they get isn't longer than what
4721 * they can scan, as long as they can scan all
4722 * the channels they registered at once.
4723 */
4724 nla_for_each_nested(attr2, freqs, tmp2)
4725 if (attr1 != attr2 &&
4726 nla_get_u32(attr1) == nla_get_u32(attr2))
4727 return 0;
4728 }
4729
4730 return n_channels;
4731}
4732
Johannes Berg2a519312009-02-10 21:25:55 +01004733static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
4734{
Johannes Berg4c476992010-10-04 21:36:35 +02004735 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergfd014282012-06-18 19:17:03 +02004736 struct wireless_dev *wdev = info->user_ptr[1];
Johannes Berg2a519312009-02-10 21:25:55 +01004737 struct cfg80211_scan_request *request;
Johannes Berg2a519312009-02-10 21:25:55 +01004738 struct nlattr *attr;
4739 struct wiphy *wiphy;
Johannes Berg83f5e2c2009-06-17 17:41:49 +02004740 int err, tmp, n_ssids = 0, n_channels, i;
Jouni Malinen70692ad2009-02-16 19:39:13 +02004741 size_t ie_len;
Johannes Berg2a519312009-02-10 21:25:55 +01004742
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004743 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4744 return -EINVAL;
4745
Johannes Berg79c97e92009-07-07 03:56:12 +02004746 wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01004747
Johannes Berg4c476992010-10-04 21:36:35 +02004748 if (!rdev->ops->scan)
4749 return -EOPNOTSUPP;
Johannes Berg2a519312009-02-10 21:25:55 +01004750
Johannes Berg4c476992010-10-04 21:36:35 +02004751 if (rdev->scan_req)
4752 return -EBUSY;
Johannes Berg2a519312009-02-10 21:25:55 +01004753
4754 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
Johannes Berg83f5e2c2009-06-17 17:41:49 +02004755 n_channels = validate_scan_freqs(
4756 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
Johannes Berg4c476992010-10-04 21:36:35 +02004757 if (!n_channels)
4758 return -EINVAL;
Johannes Berg2a519312009-02-10 21:25:55 +01004759 } else {
Johannes Berg34850ab2011-07-18 18:08:35 +02004760 enum ieee80211_band band;
Johannes Berg83f5e2c2009-06-17 17:41:49 +02004761 n_channels = 0;
4762
Johannes Berg2a519312009-02-10 21:25:55 +01004763 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
4764 if (wiphy->bands[band])
4765 n_channels += wiphy->bands[band]->n_channels;
4766 }
4767
4768 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
4769 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
4770 n_ssids++;
4771
Johannes Berg4c476992010-10-04 21:36:35 +02004772 if (n_ssids > wiphy->max_scan_ssids)
4773 return -EINVAL;
Johannes Berg2a519312009-02-10 21:25:55 +01004774
Jouni Malinen70692ad2009-02-16 19:39:13 +02004775 if (info->attrs[NL80211_ATTR_IE])
4776 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4777 else
4778 ie_len = 0;
4779
Johannes Berg4c476992010-10-04 21:36:35 +02004780 if (ie_len > wiphy->max_scan_ie_len)
4781 return -EINVAL;
Johannes Berg18a83652009-03-31 12:12:05 +02004782
Johannes Berg2a519312009-02-10 21:25:55 +01004783 request = kzalloc(sizeof(*request)
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03004784 + sizeof(*request->ssids) * n_ssids
4785 + sizeof(*request->channels) * n_channels
Jouni Malinen70692ad2009-02-16 19:39:13 +02004786 + ie_len, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02004787 if (!request)
4788 return -ENOMEM;
Johannes Berg2a519312009-02-10 21:25:55 +01004789
Johannes Berg2a519312009-02-10 21:25:55 +01004790 if (n_ssids)
Johannes Berg5ba63532009-08-07 17:54:07 +02004791 request->ssids = (void *)&request->channels[n_channels];
Johannes Berg2a519312009-02-10 21:25:55 +01004792 request->n_ssids = n_ssids;
Jouni Malinen70692ad2009-02-16 19:39:13 +02004793 if (ie_len) {
4794 if (request->ssids)
4795 request->ie = (void *)(request->ssids + n_ssids);
4796 else
4797 request->ie = (void *)(request->channels + n_channels);
4798 }
Johannes Berg2a519312009-02-10 21:25:55 +01004799
Johannes Berg584991d2009-11-02 13:32:03 +01004800 i = 0;
Johannes Berg2a519312009-02-10 21:25:55 +01004801 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
4802 /* user specified, bail out if channel not found */
Johannes Berg2a519312009-02-10 21:25:55 +01004803 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
Johannes Berg584991d2009-11-02 13:32:03 +01004804 struct ieee80211_channel *chan;
4805
4806 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
4807
4808 if (!chan) {
Johannes Berg2a519312009-02-10 21:25:55 +01004809 err = -EINVAL;
4810 goto out_free;
4811 }
Johannes Berg584991d2009-11-02 13:32:03 +01004812
4813 /* ignore disabled channels */
4814 if (chan->flags & IEEE80211_CHAN_DISABLED)
4815 continue;
4816
4817 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01004818 i++;
4819 }
4820 } else {
Johannes Berg34850ab2011-07-18 18:08:35 +02004821 enum ieee80211_band band;
4822
Johannes Berg2a519312009-02-10 21:25:55 +01004823 /* all channels */
Johannes Berg2a519312009-02-10 21:25:55 +01004824 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
4825 int j;
4826 if (!wiphy->bands[band])
4827 continue;
4828 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
Johannes Berg584991d2009-11-02 13:32:03 +01004829 struct ieee80211_channel *chan;
4830
4831 chan = &wiphy->bands[band]->channels[j];
4832
4833 if (chan->flags & IEEE80211_CHAN_DISABLED)
4834 continue;
4835
4836 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01004837 i++;
4838 }
4839 }
4840 }
4841
Johannes Berg584991d2009-11-02 13:32:03 +01004842 if (!i) {
4843 err = -EINVAL;
4844 goto out_free;
4845 }
4846
4847 request->n_channels = i;
4848
Johannes Berg2a519312009-02-10 21:25:55 +01004849 i = 0;
4850 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
4851 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
Luciano Coelho57a27e12011-06-07 20:42:26 +03004852 if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
Johannes Berg2a519312009-02-10 21:25:55 +01004853 err = -EINVAL;
4854 goto out_free;
4855 }
Luciano Coelho57a27e12011-06-07 20:42:26 +03004856 request->ssids[i].ssid_len = nla_len(attr);
Johannes Berg2a519312009-02-10 21:25:55 +01004857 memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
Johannes Berg2a519312009-02-10 21:25:55 +01004858 i++;
4859 }
4860 }
4861
Jouni Malinen70692ad2009-02-16 19:39:13 +02004862 if (info->attrs[NL80211_ATTR_IE]) {
4863 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Johannes Bergde95a542009-04-01 11:58:36 +02004864 memcpy((void *)request->ie,
4865 nla_data(info->attrs[NL80211_ATTR_IE]),
Jouni Malinen70692ad2009-02-16 19:39:13 +02004866 request->ie_len);
4867 }
4868
Johannes Berg34850ab2011-07-18 18:08:35 +02004869 for (i = 0; i < IEEE80211_NUM_BANDS; i++)
Johannes Berga401d2b2011-07-20 00:52:16 +02004870 if (wiphy->bands[i])
4871 request->rates[i] =
4872 (1 << wiphy->bands[i]->n_bitrates) - 1;
Johannes Berg34850ab2011-07-18 18:08:35 +02004873
4874 if (info->attrs[NL80211_ATTR_SCAN_SUPP_RATES]) {
4875 nla_for_each_nested(attr,
4876 info->attrs[NL80211_ATTR_SCAN_SUPP_RATES],
4877 tmp) {
4878 enum ieee80211_band band = nla_type(attr);
4879
Dan Carpenter84404622011-07-29 11:52:18 +03004880 if (band < 0 || band >= IEEE80211_NUM_BANDS) {
Johannes Berg34850ab2011-07-18 18:08:35 +02004881 err = -EINVAL;
4882 goto out_free;
4883 }
4884 err = ieee80211_get_ratemask(wiphy->bands[band],
4885 nla_data(attr),
4886 nla_len(attr),
4887 &request->rates[band]);
4888 if (err)
4889 goto out_free;
4890 }
4891 }
4892
Sam Leffler46856bb2012-10-11 21:03:32 -07004893 if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
Sam Lefflered4737712012-10-11 21:03:31 -07004894 request->flags = nla_get_u32(
4895 info->attrs[NL80211_ATTR_SCAN_FLAGS]);
Sam Leffler15d60302012-10-11 21:03:34 -07004896 if (((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
4897 !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) ||
4898 ((request->flags & NL80211_SCAN_FLAG_FLUSH) &&
4899 !(wiphy->features & NL80211_FEATURE_SCAN_FLUSH))) {
Sam Leffler46856bb2012-10-11 21:03:32 -07004900 err = -EOPNOTSUPP;
4901 goto out_free;
4902 }
4903 }
Sam Lefflered4737712012-10-11 21:03:31 -07004904
Rajkumar Manoharane9f935e2011-09-25 14:53:30 +05304905 request->no_cck =
4906 nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
4907
Johannes Bergfd014282012-06-18 19:17:03 +02004908 request->wdev = wdev;
Johannes Berg79c97e92009-07-07 03:56:12 +02004909 request->wiphy = &rdev->wiphy;
Sam Leffler15d60302012-10-11 21:03:34 -07004910 request->scan_start = jiffies;
Johannes Berg2a519312009-02-10 21:25:55 +01004911
Johannes Berg79c97e92009-07-07 03:56:12 +02004912 rdev->scan_req = request;
Hila Gonene35e4d22012-06-27 17:19:42 +03004913 err = rdev_scan(rdev, request);
Johannes Berg2a519312009-02-10 21:25:55 +01004914
Johannes Berg463d0182009-07-14 00:33:35 +02004915 if (!err) {
Johannes Bergfd014282012-06-18 19:17:03 +02004916 nl80211_send_scan_start(rdev, wdev);
4917 if (wdev->netdev)
4918 dev_hold(wdev->netdev);
Johannes Berg4c476992010-10-04 21:36:35 +02004919 } else {
Johannes Berg2a519312009-02-10 21:25:55 +01004920 out_free:
Johannes Berg79c97e92009-07-07 03:56:12 +02004921 rdev->scan_req = NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01004922 kfree(request);
4923 }
Johannes Berg3b858752009-03-12 09:55:09 +01004924
Johannes Berg2a519312009-02-10 21:25:55 +01004925 return err;
4926}
4927
Luciano Coelho807f8a82011-05-11 17:09:35 +03004928static int nl80211_start_sched_scan(struct sk_buff *skb,
4929 struct genl_info *info)
4930{
4931 struct cfg80211_sched_scan_request *request;
4932 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4933 struct net_device *dev = info->user_ptr[1];
Luciano Coelho807f8a82011-05-11 17:09:35 +03004934 struct nlattr *attr;
4935 struct wiphy *wiphy;
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03004936 int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i;
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03004937 u32 interval;
Luciano Coelho807f8a82011-05-11 17:09:35 +03004938 enum ieee80211_band band;
4939 size_t ie_len;
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03004940 struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];
Luciano Coelho807f8a82011-05-11 17:09:35 +03004941
4942 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
4943 !rdev->ops->sched_scan_start)
4944 return -EOPNOTSUPP;
4945
4946 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4947 return -EINVAL;
4948
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03004949 if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
4950 return -EINVAL;
4951
4952 interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
4953 if (interval == 0)
4954 return -EINVAL;
4955
Luciano Coelho807f8a82011-05-11 17:09:35 +03004956 wiphy = &rdev->wiphy;
4957
4958 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
4959 n_channels = validate_scan_freqs(
4960 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
4961 if (!n_channels)
4962 return -EINVAL;
4963 } else {
4964 n_channels = 0;
4965
4966 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
4967 if (wiphy->bands[band])
4968 n_channels += wiphy->bands[band]->n_channels;
4969 }
4970
4971 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
4972 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
4973 tmp)
4974 n_ssids++;
4975
Luciano Coelho93b6aa62011-07-13 14:57:28 +03004976 if (n_ssids > wiphy->max_sched_scan_ssids)
Luciano Coelho807f8a82011-05-11 17:09:35 +03004977 return -EINVAL;
4978
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03004979 if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH])
4980 nla_for_each_nested(attr,
4981 info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
4982 tmp)
4983 n_match_sets++;
4984
4985 if (n_match_sets > wiphy->max_match_sets)
4986 return -EINVAL;
4987
Luciano Coelho807f8a82011-05-11 17:09:35 +03004988 if (info->attrs[NL80211_ATTR_IE])
4989 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4990 else
4991 ie_len = 0;
4992
Luciano Coelho5a865ba2011-07-13 14:57:29 +03004993 if (ie_len > wiphy->max_sched_scan_ie_len)
Luciano Coelho807f8a82011-05-11 17:09:35 +03004994 return -EINVAL;
4995
Luciano Coelhoc10841c2011-06-30 08:32:41 +03004996 mutex_lock(&rdev->sched_scan_mtx);
4997
4998 if (rdev->sched_scan_req) {
4999 err = -EINPROGRESS;
5000 goto out;
5001 }
5002
Luciano Coelho807f8a82011-05-11 17:09:35 +03005003 request = kzalloc(sizeof(*request)
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03005004 + sizeof(*request->ssids) * n_ssids
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03005005 + sizeof(*request->match_sets) * n_match_sets
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03005006 + sizeof(*request->channels) * n_channels
Luciano Coelho807f8a82011-05-11 17:09:35 +03005007 + ie_len, GFP_KERNEL);
Luciano Coelhoc10841c2011-06-30 08:32:41 +03005008 if (!request) {
5009 err = -ENOMEM;
5010 goto out;
5011 }
Luciano Coelho807f8a82011-05-11 17:09:35 +03005012
5013 if (n_ssids)
5014 request->ssids = (void *)&request->channels[n_channels];
5015 request->n_ssids = n_ssids;
5016 if (ie_len) {
5017 if (request->ssids)
5018 request->ie = (void *)(request->ssids + n_ssids);
5019 else
5020 request->ie = (void *)(request->channels + n_channels);
5021 }
5022
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03005023 if (n_match_sets) {
5024 if (request->ie)
5025 request->match_sets = (void *)(request->ie + ie_len);
5026 else if (request->ssids)
5027 request->match_sets =
5028 (void *)(request->ssids + n_ssids);
5029 else
5030 request->match_sets =
5031 (void *)(request->channels + n_channels);
5032 }
5033 request->n_match_sets = n_match_sets;
5034
Luciano Coelho807f8a82011-05-11 17:09:35 +03005035 i = 0;
5036 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
5037 /* user specified, bail out if channel not found */
5038 nla_for_each_nested(attr,
5039 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES],
5040 tmp) {
5041 struct ieee80211_channel *chan;
5042
5043 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
5044
5045 if (!chan) {
5046 err = -EINVAL;
5047 goto out_free;
5048 }
5049
5050 /* ignore disabled channels */
5051 if (chan->flags & IEEE80211_CHAN_DISABLED)
5052 continue;
5053
5054 request->channels[i] = chan;
5055 i++;
5056 }
5057 } else {
5058 /* all channels */
5059 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
5060 int j;
5061 if (!wiphy->bands[band])
5062 continue;
5063 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
5064 struct ieee80211_channel *chan;
5065
5066 chan = &wiphy->bands[band]->channels[j];
5067
5068 if (chan->flags & IEEE80211_CHAN_DISABLED)
5069 continue;
5070
5071 request->channels[i] = chan;
5072 i++;
5073 }
5074 }
5075 }
5076
5077 if (!i) {
5078 err = -EINVAL;
5079 goto out_free;
5080 }
5081
5082 request->n_channels = i;
5083
5084 i = 0;
5085 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
5086 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
5087 tmp) {
Luciano Coelho57a27e12011-06-07 20:42:26 +03005088 if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
Luciano Coelho807f8a82011-05-11 17:09:35 +03005089 err = -EINVAL;
5090 goto out_free;
5091 }
Luciano Coelho57a27e12011-06-07 20:42:26 +03005092 request->ssids[i].ssid_len = nla_len(attr);
Luciano Coelho807f8a82011-05-11 17:09:35 +03005093 memcpy(request->ssids[i].ssid, nla_data(attr),
5094 nla_len(attr));
Luciano Coelho807f8a82011-05-11 17:09:35 +03005095 i++;
5096 }
5097 }
5098
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03005099 i = 0;
5100 if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
5101 nla_for_each_nested(attr,
5102 info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
5103 tmp) {
Thomas Pedersen88e920b2012-06-21 11:09:54 -07005104 struct nlattr *ssid, *rssi;
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03005105
5106 nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX,
5107 nla_data(attr), nla_len(attr),
5108 nl80211_match_policy);
Johannes Berg4a4ab0d2012-06-13 11:17:11 +02005109 ssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID];
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03005110 if (ssid) {
5111 if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) {
5112 err = -EINVAL;
5113 goto out_free;
5114 }
5115 memcpy(request->match_sets[i].ssid.ssid,
5116 nla_data(ssid), nla_len(ssid));
5117 request->match_sets[i].ssid.ssid_len =
5118 nla_len(ssid);
5119 }
Thomas Pedersen88e920b2012-06-21 11:09:54 -07005120 rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI];
5121 if (rssi)
5122 request->rssi_thold = nla_get_u32(rssi);
5123 else
5124 request->rssi_thold =
5125 NL80211_SCAN_RSSI_THOLD_OFF;
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03005126 i++;
5127 }
5128 }
5129
Luciano Coelho807f8a82011-05-11 17:09:35 +03005130 if (info->attrs[NL80211_ATTR_IE]) {
5131 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
5132 memcpy((void *)request->ie,
5133 nla_data(info->attrs[NL80211_ATTR_IE]),
5134 request->ie_len);
5135 }
5136
Sam Leffler46856bb2012-10-11 21:03:32 -07005137 if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
Sam Lefflered4737712012-10-11 21:03:31 -07005138 request->flags = nla_get_u32(
5139 info->attrs[NL80211_ATTR_SCAN_FLAGS]);
Sam Leffler15d60302012-10-11 21:03:34 -07005140 if (((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
5141 !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) ||
5142 ((request->flags & NL80211_SCAN_FLAG_FLUSH) &&
5143 !(wiphy->features & NL80211_FEATURE_SCAN_FLUSH))) {
Sam Leffler46856bb2012-10-11 21:03:32 -07005144 err = -EOPNOTSUPP;
5145 goto out_free;
5146 }
5147 }
Sam Lefflered4737712012-10-11 21:03:31 -07005148
Luciano Coelho807f8a82011-05-11 17:09:35 +03005149 request->dev = dev;
5150 request->wiphy = &rdev->wiphy;
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03005151 request->interval = interval;
Sam Leffler15d60302012-10-11 21:03:34 -07005152 request->scan_start = jiffies;
Luciano Coelho807f8a82011-05-11 17:09:35 +03005153
Hila Gonene35e4d22012-06-27 17:19:42 +03005154 err = rdev_sched_scan_start(rdev, dev, request);
Luciano Coelho807f8a82011-05-11 17:09:35 +03005155 if (!err) {
5156 rdev->sched_scan_req = request;
5157 nl80211_send_sched_scan(rdev, dev,
5158 NL80211_CMD_START_SCHED_SCAN);
5159 goto out;
5160 }
5161
5162out_free:
5163 kfree(request);
5164out:
Luciano Coelhoc10841c2011-06-30 08:32:41 +03005165 mutex_unlock(&rdev->sched_scan_mtx);
Luciano Coelho807f8a82011-05-11 17:09:35 +03005166 return err;
5167}
5168
5169static int nl80211_stop_sched_scan(struct sk_buff *skb,
5170 struct genl_info *info)
5171{
5172 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luciano Coelhoc10841c2011-06-30 08:32:41 +03005173 int err;
Luciano Coelho807f8a82011-05-11 17:09:35 +03005174
5175 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
5176 !rdev->ops->sched_scan_stop)
5177 return -EOPNOTSUPP;
5178
Luciano Coelhoc10841c2011-06-30 08:32:41 +03005179 mutex_lock(&rdev->sched_scan_mtx);
5180 err = __cfg80211_stop_sched_scan(rdev, false);
5181 mutex_unlock(&rdev->sched_scan_mtx);
5182
5183 return err;
Luciano Coelho807f8a82011-05-11 17:09:35 +03005184}
5185
Simon Wunderlich04f39042013-02-08 18:16:19 +01005186static int nl80211_start_radar_detection(struct sk_buff *skb,
5187 struct genl_info *info)
5188{
5189 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5190 struct net_device *dev = info->user_ptr[1];
5191 struct wireless_dev *wdev = dev->ieee80211_ptr;
5192 struct cfg80211_chan_def chandef;
5193 int err;
5194
5195 err = nl80211_parse_chandef(rdev, info, &chandef);
5196 if (err)
5197 return err;
5198
5199 if (wdev->cac_started)
5200 return -EBUSY;
5201
5202 err = cfg80211_chandef_dfs_required(wdev->wiphy, &chandef);
5203 if (err < 0)
5204 return err;
5205
5206 if (err == 0)
5207 return -EINVAL;
5208
5209 if (chandef.chan->dfs_state != NL80211_DFS_USABLE)
5210 return -EINVAL;
5211
5212 if (!rdev->ops->start_radar_detection)
5213 return -EOPNOTSUPP;
5214
5215 mutex_lock(&rdev->devlist_mtx);
5216 err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
5217 chandef.chan, CHAN_MODE_SHARED,
5218 BIT(chandef.width));
5219 if (err)
5220 goto err_locked;
5221
5222 err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef);
5223 if (!err) {
5224 wdev->channel = chandef.chan;
5225 wdev->cac_started = true;
5226 wdev->cac_start_time = jiffies;
5227 }
5228err_locked:
5229 mutex_unlock(&rdev->devlist_mtx);
5230
5231 return err;
5232}
5233
Johannes Berg9720bb32011-06-21 09:45:33 +02005234static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
5235 u32 seq, int flags,
Johannes Berg2a519312009-02-10 21:25:55 +01005236 struct cfg80211_registered_device *rdev,
Johannes Berg48ab9052009-07-10 18:42:31 +02005237 struct wireless_dev *wdev,
5238 struct cfg80211_internal_bss *intbss)
Johannes Berg2a519312009-02-10 21:25:55 +01005239{
Johannes Berg48ab9052009-07-10 18:42:31 +02005240 struct cfg80211_bss *res = &intbss->pub;
Johannes Berg9caf0362012-11-29 01:25:20 +01005241 const struct cfg80211_bss_ies *ies;
Johannes Berg2a519312009-02-10 21:25:55 +01005242 void *hdr;
5243 struct nlattr *bss;
Johannes Berg8cef2c92013-02-05 16:54:31 +01005244 bool tsf = false;
Johannes Berg48ab9052009-07-10 18:42:31 +02005245
5246 ASSERT_WDEV_LOCK(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01005247
Eric W. Biederman15e47302012-09-07 20:12:54 +00005248 hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).portid, seq, flags,
Johannes Berg2a519312009-02-10 21:25:55 +01005249 NL80211_CMD_NEW_SCAN_RESULTS);
5250 if (!hdr)
5251 return -1;
5252
Johannes Berg9720bb32011-06-21 09:45:33 +02005253 genl_dump_check_consistent(cb, hdr, &nl80211_fam);
5254
David S. Miller9360ffd2012-03-29 04:41:26 -04005255 if (nla_put_u32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation) ||
5256 nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex))
5257 goto nla_put_failure;
Johannes Berg2a519312009-02-10 21:25:55 +01005258
5259 bss = nla_nest_start(msg, NL80211_ATTR_BSS);
5260 if (!bss)
5261 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -04005262 if ((!is_zero_ether_addr(res->bssid) &&
Johannes Berg9caf0362012-11-29 01:25:20 +01005263 nla_put(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid)))
David S. Miller9360ffd2012-03-29 04:41:26 -04005264 goto nla_put_failure;
Johannes Berg9caf0362012-11-29 01:25:20 +01005265
5266 rcu_read_lock();
5267 ies = rcu_dereference(res->ies);
Johannes Berg8cef2c92013-02-05 16:54:31 +01005268 if (ies) {
5269 if (nla_put_u64(msg, NL80211_BSS_TSF, ies->tsf))
5270 goto fail_unlock_rcu;
5271 tsf = true;
5272 if (ies->len && nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS,
5273 ies->len, ies->data))
5274 goto fail_unlock_rcu;
Johannes Berg9caf0362012-11-29 01:25:20 +01005275 }
5276 ies = rcu_dereference(res->beacon_ies);
Johannes Berg8cef2c92013-02-05 16:54:31 +01005277 if (ies) {
5278 if (!tsf && nla_put_u64(msg, NL80211_BSS_TSF, ies->tsf))
5279 goto fail_unlock_rcu;
5280 if (ies->len && nla_put(msg, NL80211_BSS_BEACON_IES,
5281 ies->len, ies->data))
5282 goto fail_unlock_rcu;
Johannes Berg9caf0362012-11-29 01:25:20 +01005283 }
5284 rcu_read_unlock();
5285
David S. Miller9360ffd2012-03-29 04:41:26 -04005286 if (res->beacon_interval &&
5287 nla_put_u16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval))
5288 goto nla_put_failure;
5289 if (nla_put_u16(msg, NL80211_BSS_CAPABILITY, res->capability) ||
5290 nla_put_u32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq) ||
5291 nla_put_u32(msg, NL80211_BSS_SEEN_MS_AGO,
5292 jiffies_to_msecs(jiffies - intbss->ts)))
5293 goto nla_put_failure;
Johannes Berg2a519312009-02-10 21:25:55 +01005294
Johannes Berg77965c92009-02-18 18:45:06 +01005295 switch (rdev->wiphy.signal_type) {
Johannes Berg2a519312009-02-10 21:25:55 +01005296 case CFG80211_SIGNAL_TYPE_MBM:
David S. Miller9360ffd2012-03-29 04:41:26 -04005297 if (nla_put_u32(msg, NL80211_BSS_SIGNAL_MBM, res->signal))
5298 goto nla_put_failure;
Johannes Berg2a519312009-02-10 21:25:55 +01005299 break;
5300 case CFG80211_SIGNAL_TYPE_UNSPEC:
David S. Miller9360ffd2012-03-29 04:41:26 -04005301 if (nla_put_u8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal))
5302 goto nla_put_failure;
Johannes Berg2a519312009-02-10 21:25:55 +01005303 break;
5304 default:
5305 break;
5306 }
5307
Johannes Berg48ab9052009-07-10 18:42:31 +02005308 switch (wdev->iftype) {
Johannes Berg074ac8d2010-09-16 14:58:22 +02005309 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Berg48ab9052009-07-10 18:42:31 +02005310 case NL80211_IFTYPE_STATION:
David S. Miller9360ffd2012-03-29 04:41:26 -04005311 if (intbss == wdev->current_bss &&
5312 nla_put_u32(msg, NL80211_BSS_STATUS,
5313 NL80211_BSS_STATUS_ASSOCIATED))
5314 goto nla_put_failure;
Johannes Berg48ab9052009-07-10 18:42:31 +02005315 break;
5316 case NL80211_IFTYPE_ADHOC:
David S. Miller9360ffd2012-03-29 04:41:26 -04005317 if (intbss == wdev->current_bss &&
5318 nla_put_u32(msg, NL80211_BSS_STATUS,
5319 NL80211_BSS_STATUS_IBSS_JOINED))
5320 goto nla_put_failure;
Johannes Berg48ab9052009-07-10 18:42:31 +02005321 break;
5322 default:
5323 break;
5324 }
5325
Johannes Berg2a519312009-02-10 21:25:55 +01005326 nla_nest_end(msg, bss);
5327
5328 return genlmsg_end(msg, hdr);
5329
Johannes Berg8cef2c92013-02-05 16:54:31 +01005330 fail_unlock_rcu:
5331 rcu_read_unlock();
Johannes Berg2a519312009-02-10 21:25:55 +01005332 nla_put_failure:
5333 genlmsg_cancel(msg, hdr);
5334 return -EMSGSIZE;
5335}
5336
5337static int nl80211_dump_scan(struct sk_buff *skb,
5338 struct netlink_callback *cb)
5339{
Johannes Berg48ab9052009-07-10 18:42:31 +02005340 struct cfg80211_registered_device *rdev;
5341 struct net_device *dev;
Johannes Berg2a519312009-02-10 21:25:55 +01005342 struct cfg80211_internal_bss *scan;
Johannes Berg48ab9052009-07-10 18:42:31 +02005343 struct wireless_dev *wdev;
Johannes Berg2a519312009-02-10 21:25:55 +01005344 int start = cb->args[1], idx = 0;
5345 int err;
5346
Johannes Berg67748892010-10-04 21:14:06 +02005347 err = nl80211_prepare_netdev_dump(skb, cb, &rdev, &dev);
5348 if (err)
5349 return err;
Johannes Berg2a519312009-02-10 21:25:55 +01005350
Johannes Berg48ab9052009-07-10 18:42:31 +02005351 wdev = dev->ieee80211_ptr;
Johannes Berg2a519312009-02-10 21:25:55 +01005352
Johannes Berg48ab9052009-07-10 18:42:31 +02005353 wdev_lock(wdev);
5354 spin_lock_bh(&rdev->bss_lock);
5355 cfg80211_bss_expire(rdev);
5356
Johannes Berg9720bb32011-06-21 09:45:33 +02005357 cb->seq = rdev->bss_generation;
5358
Johannes Berg48ab9052009-07-10 18:42:31 +02005359 list_for_each_entry(scan, &rdev->bss_list, list) {
Johannes Berg2a519312009-02-10 21:25:55 +01005360 if (++idx <= start)
5361 continue;
Johannes Berg9720bb32011-06-21 09:45:33 +02005362 if (nl80211_send_bss(skb, cb,
Johannes Berg2a519312009-02-10 21:25:55 +01005363 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Berg48ab9052009-07-10 18:42:31 +02005364 rdev, wdev, scan) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01005365 idx--;
Johannes Berg67748892010-10-04 21:14:06 +02005366 break;
Johannes Berg2a519312009-02-10 21:25:55 +01005367 }
5368 }
5369
Johannes Berg48ab9052009-07-10 18:42:31 +02005370 spin_unlock_bh(&rdev->bss_lock);
5371 wdev_unlock(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01005372
5373 cb->args[1] = idx;
Johannes Berg67748892010-10-04 21:14:06 +02005374 nl80211_finish_netdev_dump(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01005375
Johannes Berg67748892010-10-04 21:14:06 +02005376 return skb->len;
Johannes Berg2a519312009-02-10 21:25:55 +01005377}
5378
Eric W. Biederman15e47302012-09-07 20:12:54 +00005379static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq,
Holger Schurig61fa7132009-11-11 12:25:40 +01005380 int flags, struct net_device *dev,
5381 struct survey_info *survey)
5382{
5383 void *hdr;
5384 struct nlattr *infoattr;
5385
Eric W. Biederman15e47302012-09-07 20:12:54 +00005386 hdr = nl80211hdr_put(msg, portid, seq, flags,
Holger Schurig61fa7132009-11-11 12:25:40 +01005387 NL80211_CMD_NEW_SURVEY_RESULTS);
5388 if (!hdr)
5389 return -ENOMEM;
5390
David S. Miller9360ffd2012-03-29 04:41:26 -04005391 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
5392 goto nla_put_failure;
Holger Schurig61fa7132009-11-11 12:25:40 +01005393
5394 infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO);
5395 if (!infoattr)
5396 goto nla_put_failure;
5397
David S. Miller9360ffd2012-03-29 04:41:26 -04005398 if (nla_put_u32(msg, NL80211_SURVEY_INFO_FREQUENCY,
5399 survey->channel->center_freq))
5400 goto nla_put_failure;
5401
5402 if ((survey->filled & SURVEY_INFO_NOISE_DBM) &&
5403 nla_put_u8(msg, NL80211_SURVEY_INFO_NOISE, survey->noise))
5404 goto nla_put_failure;
5405 if ((survey->filled & SURVEY_INFO_IN_USE) &&
5406 nla_put_flag(msg, NL80211_SURVEY_INFO_IN_USE))
5407 goto nla_put_failure;
5408 if ((survey->filled & SURVEY_INFO_CHANNEL_TIME) &&
5409 nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME,
5410 survey->channel_time))
5411 goto nla_put_failure;
5412 if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_BUSY) &&
5413 nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY,
5414 survey->channel_time_busy))
5415 goto nla_put_failure;
5416 if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) &&
5417 nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY,
5418 survey->channel_time_ext_busy))
5419 goto nla_put_failure;
5420 if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_RX) &&
5421 nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_RX,
5422 survey->channel_time_rx))
5423 goto nla_put_failure;
5424 if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_TX) &&
5425 nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_TX,
5426 survey->channel_time_tx))
5427 goto nla_put_failure;
Holger Schurig61fa7132009-11-11 12:25:40 +01005428
5429 nla_nest_end(msg, infoattr);
5430
5431 return genlmsg_end(msg, hdr);
5432
5433 nla_put_failure:
5434 genlmsg_cancel(msg, hdr);
5435 return -EMSGSIZE;
5436}
5437
5438static int nl80211_dump_survey(struct sk_buff *skb,
5439 struct netlink_callback *cb)
5440{
5441 struct survey_info survey;
5442 struct cfg80211_registered_device *dev;
5443 struct net_device *netdev;
Holger Schurig61fa7132009-11-11 12:25:40 +01005444 int survey_idx = cb->args[1];
5445 int res;
5446
Johannes Berg67748892010-10-04 21:14:06 +02005447 res = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
5448 if (res)
5449 return res;
Holger Schurig61fa7132009-11-11 12:25:40 +01005450
5451 if (!dev->ops->dump_survey) {
5452 res = -EOPNOTSUPP;
5453 goto out_err;
5454 }
5455
5456 while (1) {
Luis R. Rodriguez180cdc72011-05-27 07:24:02 -07005457 struct ieee80211_channel *chan;
5458
Hila Gonene35e4d22012-06-27 17:19:42 +03005459 res = rdev_dump_survey(dev, netdev, survey_idx, &survey);
Holger Schurig61fa7132009-11-11 12:25:40 +01005460 if (res == -ENOENT)
5461 break;
5462 if (res)
5463 goto out_err;
5464
Luis R. Rodriguez180cdc72011-05-27 07:24:02 -07005465 /* Survey without a channel doesn't make sense */
5466 if (!survey.channel) {
5467 res = -EINVAL;
5468 goto out;
5469 }
5470
5471 chan = ieee80211_get_channel(&dev->wiphy,
5472 survey.channel->center_freq);
5473 if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) {
5474 survey_idx++;
5475 continue;
5476 }
5477
Holger Schurig61fa7132009-11-11 12:25:40 +01005478 if (nl80211_send_survey(skb,
Eric W. Biederman15e47302012-09-07 20:12:54 +00005479 NETLINK_CB(cb->skb).portid,
Holger Schurig61fa7132009-11-11 12:25:40 +01005480 cb->nlh->nlmsg_seq, NLM_F_MULTI,
5481 netdev,
5482 &survey) < 0)
5483 goto out;
5484 survey_idx++;
5485 }
5486
5487 out:
5488 cb->args[1] = survey_idx;
5489 res = skb->len;
5490 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02005491 nl80211_finish_netdev_dump(dev);
Holger Schurig61fa7132009-11-11 12:25:40 +01005492 return res;
5493}
5494
Samuel Ortizb23aa672009-07-01 21:26:54 +02005495static bool nl80211_valid_wpa_versions(u32 wpa_versions)
5496{
5497 return !(wpa_versions & ~(NL80211_WPA_VERSION_1 |
5498 NL80211_WPA_VERSION_2));
5499}
5500
Jouni Malinen636a5d32009-03-19 13:39:22 +02005501static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
5502{
Johannes Berg4c476992010-10-04 21:36:35 +02005503 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5504 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02005505 struct ieee80211_channel *chan;
Jouni Malinene39e5b52012-09-30 19:29:39 +03005506 const u8 *bssid, *ssid, *ie = NULL, *sae_data = NULL;
5507 int err, ssid_len, ie_len = 0, sae_data_len = 0;
Johannes Berg19957bb2009-07-02 17:20:43 +02005508 enum nl80211_auth_type auth_type;
Johannes Bergfffd0932009-07-08 14:22:54 +02005509 struct key_parse key;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03005510 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02005511
Johannes Bergf4a11bb2009-03-27 12:40:28 +01005512 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
5513 return -EINVAL;
5514
5515 if (!info->attrs[NL80211_ATTR_MAC])
5516 return -EINVAL;
5517
Jouni Malinen17780922009-03-27 20:52:47 +02005518 if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
5519 return -EINVAL;
5520
Johannes Berg19957bb2009-07-02 17:20:43 +02005521 if (!info->attrs[NL80211_ATTR_SSID])
5522 return -EINVAL;
5523
5524 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
5525 return -EINVAL;
5526
Johannes Bergfffd0932009-07-08 14:22:54 +02005527 err = nl80211_parse_key(info, &key);
5528 if (err)
5529 return err;
5530
5531 if (key.idx >= 0) {
Johannes Berge31b8212010-10-05 19:39:30 +02005532 if (key.type != -1 && key.type != NL80211_KEYTYPE_GROUP)
5533 return -EINVAL;
Johannes Bergfffd0932009-07-08 14:22:54 +02005534 if (!key.p.key || !key.p.key_len)
5535 return -EINVAL;
5536 if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 ||
5537 key.p.key_len != WLAN_KEY_LEN_WEP40) &&
5538 (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 ||
5539 key.p.key_len != WLAN_KEY_LEN_WEP104))
5540 return -EINVAL;
5541 if (key.idx > 4)
5542 return -EINVAL;
5543 } else {
5544 key.p.key_len = 0;
5545 key.p.key = NULL;
5546 }
5547
Johannes Bergafea0b72010-08-10 09:46:42 +02005548 if (key.idx >= 0) {
5549 int i;
5550 bool ok = false;
5551 for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) {
5552 if (key.p.cipher == rdev->wiphy.cipher_suites[i]) {
5553 ok = true;
5554 break;
5555 }
5556 }
Johannes Berg4c476992010-10-04 21:36:35 +02005557 if (!ok)
5558 return -EINVAL;
Johannes Bergafea0b72010-08-10 09:46:42 +02005559 }
5560
Johannes Berg4c476992010-10-04 21:36:35 +02005561 if (!rdev->ops->auth)
5562 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02005563
Johannes Berg074ac8d2010-09-16 14:58:22 +02005564 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005565 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
5566 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02005567
Johannes Berg19957bb2009-07-02 17:20:43 +02005568 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg79c97e92009-07-07 03:56:12 +02005569 chan = ieee80211_get_channel(&rdev->wiphy,
Johannes Berg19957bb2009-07-02 17:20:43 +02005570 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
Johannes Berg4c476992010-10-04 21:36:35 +02005571 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
5572 return -EINVAL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02005573
Johannes Berg19957bb2009-07-02 17:20:43 +02005574 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
5575 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
5576
5577 if (info->attrs[NL80211_ATTR_IE]) {
5578 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
5579 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
5580 }
5581
5582 auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
Jouni Malinene39e5b52012-09-30 19:29:39 +03005583 if (!nl80211_valid_auth_type(rdev, auth_type, NL80211_CMD_AUTHENTICATE))
Johannes Berg4c476992010-10-04 21:36:35 +02005584 return -EINVAL;
Johannes Berg19957bb2009-07-02 17:20:43 +02005585
Jouni Malinene39e5b52012-09-30 19:29:39 +03005586 if (auth_type == NL80211_AUTHTYPE_SAE &&
5587 !info->attrs[NL80211_ATTR_SAE_DATA])
5588 return -EINVAL;
5589
5590 if (info->attrs[NL80211_ATTR_SAE_DATA]) {
5591 if (auth_type != NL80211_AUTHTYPE_SAE)
5592 return -EINVAL;
5593 sae_data = nla_data(info->attrs[NL80211_ATTR_SAE_DATA]);
5594 sae_data_len = nla_len(info->attrs[NL80211_ATTR_SAE_DATA]);
5595 /* need to include at least Auth Transaction and Status Code */
5596 if (sae_data_len < 4)
5597 return -EINVAL;
5598 }
5599
Jouni Malinend5cdfac2010-04-04 09:37:19 +03005600 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
5601
Johannes Berg95de8172012-01-20 13:55:25 +01005602 /*
5603 * Since we no longer track auth state, ignore
5604 * requests to only change local state.
5605 */
5606 if (local_state_change)
5607 return 0;
5608
Johannes Berg4c476992010-10-04 21:36:35 +02005609 return cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
5610 ssid, ssid_len, ie, ie_len,
Jouni Malinene39e5b52012-09-30 19:29:39 +03005611 key.p.key, key.p.key_len, key.idx,
5612 sae_data, sae_data_len);
Jouni Malinen636a5d32009-03-19 13:39:22 +02005613}
5614
Johannes Bergc0692b82010-08-27 14:26:53 +03005615static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
5616 struct genl_info *info,
Johannes Berg3dc27d22009-07-02 21:36:37 +02005617 struct cfg80211_crypto_settings *settings,
5618 int cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02005619{
Johannes Bergc0b2bbd2009-07-25 16:54:36 +02005620 memset(settings, 0, sizeof(*settings));
5621
Samuel Ortizb23aa672009-07-01 21:26:54 +02005622 settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT];
5623
Johannes Bergc0692b82010-08-27 14:26:53 +03005624 if (info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) {
5625 u16 proto;
5626 proto = nla_get_u16(
5627 info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]);
5628 settings->control_port_ethertype = cpu_to_be16(proto);
5629 if (!(rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
5630 proto != ETH_P_PAE)
5631 return -EINVAL;
5632 if (info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT])
5633 settings->control_port_no_encrypt = true;
5634 } else
5635 settings->control_port_ethertype = cpu_to_be16(ETH_P_PAE);
5636
Samuel Ortizb23aa672009-07-01 21:26:54 +02005637 if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) {
5638 void *data;
5639 int len, i;
5640
5641 data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
5642 len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
5643 settings->n_ciphers_pairwise = len / sizeof(u32);
5644
5645 if (len % sizeof(u32))
5646 return -EINVAL;
5647
Johannes Berg3dc27d22009-07-02 21:36:37 +02005648 if (settings->n_ciphers_pairwise > cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02005649 return -EINVAL;
5650
5651 memcpy(settings->ciphers_pairwise, data, len);
5652
5653 for (i = 0; i < settings->n_ciphers_pairwise; i++)
Jouni Malinen38ba3c52011-09-21 18:14:56 +03005654 if (!cfg80211_supported_cipher_suite(
5655 &rdev->wiphy,
Samuel Ortizb23aa672009-07-01 21:26:54 +02005656 settings->ciphers_pairwise[i]))
5657 return -EINVAL;
5658 }
5659
5660 if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) {
5661 settings->cipher_group =
5662 nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]);
Jouni Malinen38ba3c52011-09-21 18:14:56 +03005663 if (!cfg80211_supported_cipher_suite(&rdev->wiphy,
5664 settings->cipher_group))
Samuel Ortizb23aa672009-07-01 21:26:54 +02005665 return -EINVAL;
5666 }
5667
5668 if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) {
5669 settings->wpa_versions =
5670 nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]);
5671 if (!nl80211_valid_wpa_versions(settings->wpa_versions))
5672 return -EINVAL;
5673 }
5674
5675 if (info->attrs[NL80211_ATTR_AKM_SUITES]) {
5676 void *data;
Jouni Malinen6d302402011-09-21 18:11:33 +03005677 int len;
Samuel Ortizb23aa672009-07-01 21:26:54 +02005678
5679 data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]);
5680 len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]);
5681 settings->n_akm_suites = len / sizeof(u32);
5682
5683 if (len % sizeof(u32))
5684 return -EINVAL;
5685
Jouni Malinen1b9ca022011-09-21 16:13:07 +03005686 if (settings->n_akm_suites > NL80211_MAX_NR_AKM_SUITES)
5687 return -EINVAL;
5688
Samuel Ortizb23aa672009-07-01 21:26:54 +02005689 memcpy(settings->akm_suites, data, len);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005690 }
5691
5692 return 0;
5693}
5694
Jouni Malinen636a5d32009-03-19 13:39:22 +02005695static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
5696{
Johannes Berg4c476992010-10-04 21:36:35 +02005697 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5698 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02005699 struct cfg80211_crypto_settings crypto;
Johannes Bergf444de02010-05-05 15:25:02 +02005700 struct ieee80211_channel *chan;
Johannes Berg3e5d7642009-07-07 14:37:26 +02005701 const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +02005702 int err, ssid_len, ie_len = 0;
5703 bool use_mfp = false;
Ben Greear7e7c8922011-11-18 11:31:59 -08005704 u32 flags = 0;
5705 struct ieee80211_ht_cap *ht_capa = NULL;
5706 struct ieee80211_ht_cap *ht_capa_mask = NULL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02005707
Johannes Bergf4a11bb2009-03-27 12:40:28 +01005708 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
5709 return -EINVAL;
5710
5711 if (!info->attrs[NL80211_ATTR_MAC] ||
Johannes Berg19957bb2009-07-02 17:20:43 +02005712 !info->attrs[NL80211_ATTR_SSID] ||
5713 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
Johannes Bergf4a11bb2009-03-27 12:40:28 +01005714 return -EINVAL;
5715
Johannes Berg4c476992010-10-04 21:36:35 +02005716 if (!rdev->ops->assoc)
5717 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02005718
Johannes Berg074ac8d2010-09-16 14:58:22 +02005719 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005720 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
5721 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02005722
Johannes Berg19957bb2009-07-02 17:20:43 +02005723 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02005724
Johannes Berg19957bb2009-07-02 17:20:43 +02005725 chan = ieee80211_get_channel(&rdev->wiphy,
5726 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
Johannes Berg4c476992010-10-04 21:36:35 +02005727 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
5728 return -EINVAL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02005729
Johannes Berg19957bb2009-07-02 17:20:43 +02005730 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
5731 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02005732
5733 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02005734 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
5735 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02005736 }
5737
Jouni Malinendc6382c2009-05-06 22:09:37 +03005738 if (info->attrs[NL80211_ATTR_USE_MFP]) {
Johannes Berg4f5dadc2009-07-07 03:56:10 +02005739 enum nl80211_mfp mfp =
Jouni Malinendc6382c2009-05-06 22:09:37 +03005740 nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
Johannes Berg4f5dadc2009-07-07 03:56:10 +02005741 if (mfp == NL80211_MFP_REQUIRED)
Johannes Berg19957bb2009-07-02 17:20:43 +02005742 use_mfp = true;
Johannes Berg4c476992010-10-04 21:36:35 +02005743 else if (mfp != NL80211_MFP_NO)
5744 return -EINVAL;
Jouni Malinendc6382c2009-05-06 22:09:37 +03005745 }
5746
Johannes Berg3e5d7642009-07-07 14:37:26 +02005747 if (info->attrs[NL80211_ATTR_PREV_BSSID])
5748 prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
5749
Ben Greear7e7c8922011-11-18 11:31:59 -08005750 if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
5751 flags |= ASSOC_REQ_DISABLE_HT;
5752
5753 if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
5754 ht_capa_mask =
5755 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]);
5756
5757 if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
5758 if (!ht_capa_mask)
5759 return -EINVAL;
5760 ht_capa = nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
5761 }
5762
Johannes Bergc0692b82010-08-27 14:26:53 +03005763 err = nl80211_crypto_settings(rdev, info, &crypto, 1);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005764 if (!err)
Johannes Berg3e5d7642009-07-07 14:37:26 +02005765 err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
5766 ssid, ssid_len, ie, ie_len, use_mfp,
Ben Greear7e7c8922011-11-18 11:31:59 -08005767 &crypto, flags, ht_capa,
5768 ht_capa_mask);
Jouni Malinen636a5d32009-03-19 13:39:22 +02005769
Jouni Malinen636a5d32009-03-19 13:39:22 +02005770 return err;
5771}
5772
5773static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
5774{
Johannes Berg4c476992010-10-04 21:36:35 +02005775 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5776 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02005777 const u8 *ie = NULL, *bssid;
Johannes Berg4c476992010-10-04 21:36:35 +02005778 int ie_len = 0;
Johannes Berg19957bb2009-07-02 17:20:43 +02005779 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03005780 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02005781
Johannes Bergf4a11bb2009-03-27 12:40:28 +01005782 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
5783 return -EINVAL;
5784
5785 if (!info->attrs[NL80211_ATTR_MAC])
5786 return -EINVAL;
5787
5788 if (!info->attrs[NL80211_ATTR_REASON_CODE])
5789 return -EINVAL;
5790
Johannes Berg4c476992010-10-04 21:36:35 +02005791 if (!rdev->ops->deauth)
5792 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02005793
Johannes Berg074ac8d2010-09-16 14:58:22 +02005794 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005795 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
5796 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02005797
Johannes Berg19957bb2009-07-02 17:20:43 +02005798 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02005799
Johannes Berg19957bb2009-07-02 17:20:43 +02005800 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
5801 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01005802 /* Reason Code 0 is reserved */
Johannes Berg4c476992010-10-04 21:36:35 +02005803 return -EINVAL;
Jouni Malinen255e7372009-03-20 21:21:17 +02005804 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02005805
5806 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02005807 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
5808 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02005809 }
5810
Jouni Malinend5cdfac2010-04-04 09:37:19 +03005811 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
5812
Johannes Berg4c476992010-10-04 21:36:35 +02005813 return cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
5814 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02005815}
5816
5817static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
5818{
Johannes Berg4c476992010-10-04 21:36:35 +02005819 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5820 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02005821 const u8 *ie = NULL, *bssid;
Johannes Berg4c476992010-10-04 21:36:35 +02005822 int ie_len = 0;
Johannes Berg19957bb2009-07-02 17:20:43 +02005823 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03005824 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02005825
Johannes Bergf4a11bb2009-03-27 12:40:28 +01005826 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
5827 return -EINVAL;
5828
5829 if (!info->attrs[NL80211_ATTR_MAC])
5830 return -EINVAL;
5831
5832 if (!info->attrs[NL80211_ATTR_REASON_CODE])
5833 return -EINVAL;
5834
Johannes Berg4c476992010-10-04 21:36:35 +02005835 if (!rdev->ops->disassoc)
5836 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02005837
Johannes Berg074ac8d2010-09-16 14:58:22 +02005838 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005839 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
5840 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02005841
Johannes Berg19957bb2009-07-02 17:20:43 +02005842 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02005843
Johannes Berg19957bb2009-07-02 17:20:43 +02005844 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
5845 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01005846 /* Reason Code 0 is reserved */
Johannes Berg4c476992010-10-04 21:36:35 +02005847 return -EINVAL;
Jouni Malinen255e7372009-03-20 21:21:17 +02005848 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02005849
5850 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02005851 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
5852 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02005853 }
5854
Jouni Malinend5cdfac2010-04-04 09:37:19 +03005855 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
5856
Johannes Berg4c476992010-10-04 21:36:35 +02005857 return cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
5858 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02005859}
5860
Felix Fietkaudd5b4cc2010-11-22 20:58:24 +01005861static bool
5862nl80211_parse_mcast_rate(struct cfg80211_registered_device *rdev,
5863 int mcast_rate[IEEE80211_NUM_BANDS],
5864 int rateval)
5865{
5866 struct wiphy *wiphy = &rdev->wiphy;
5867 bool found = false;
5868 int band, i;
5869
5870 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
5871 struct ieee80211_supported_band *sband;
5872
5873 sband = wiphy->bands[band];
5874 if (!sband)
5875 continue;
5876
5877 for (i = 0; i < sband->n_bitrates; i++) {
5878 if (sband->bitrates[i].bitrate == rateval) {
5879 mcast_rate[band] = i + 1;
5880 found = true;
5881 break;
5882 }
5883 }
5884 }
5885
5886 return found;
5887}
5888
Johannes Berg04a773a2009-04-19 21:24:32 +02005889static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
5890{
Johannes Berg4c476992010-10-04 21:36:35 +02005891 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5892 struct net_device *dev = info->user_ptr[1];
Johannes Berg04a773a2009-04-19 21:24:32 +02005893 struct cfg80211_ibss_params ibss;
5894 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02005895 struct cfg80211_cached_keys *connkeys = NULL;
Johannes Berg04a773a2009-04-19 21:24:32 +02005896 int err;
5897
Johannes Berg8e30bc52009-04-22 17:45:38 +02005898 memset(&ibss, 0, sizeof(ibss));
5899
Johannes Berg04a773a2009-04-19 21:24:32 +02005900 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
5901 return -EINVAL;
5902
Johannes Berg683b6d32012-11-08 21:25:48 +01005903 if (!info->attrs[NL80211_ATTR_SSID] ||
Johannes Berg04a773a2009-04-19 21:24:32 +02005904 !nla_len(info->attrs[NL80211_ATTR_SSID]))
5905 return -EINVAL;
5906
Johannes Berg8e30bc52009-04-22 17:45:38 +02005907 ibss.beacon_interval = 100;
5908
5909 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
5910 ibss.beacon_interval =
5911 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
5912 if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000)
5913 return -EINVAL;
5914 }
5915
Johannes Berg4c476992010-10-04 21:36:35 +02005916 if (!rdev->ops->join_ibss)
5917 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02005918
Johannes Berg4c476992010-10-04 21:36:35 +02005919 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
5920 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02005921
Johannes Berg79c97e92009-07-07 03:56:12 +02005922 wiphy = &rdev->wiphy;
Johannes Berg04a773a2009-04-19 21:24:32 +02005923
Johannes Berg39193492011-09-16 13:45:25 +02005924 if (info->attrs[NL80211_ATTR_MAC]) {
Johannes Berg04a773a2009-04-19 21:24:32 +02005925 ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg39193492011-09-16 13:45:25 +02005926
5927 if (!is_valid_ether_addr(ibss.bssid))
5928 return -EINVAL;
5929 }
Johannes Berg04a773a2009-04-19 21:24:32 +02005930 ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
5931 ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
5932
5933 if (info->attrs[NL80211_ATTR_IE]) {
5934 ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
5935 ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
5936 }
5937
Johannes Berg683b6d32012-11-08 21:25:48 +01005938 err = nl80211_parse_chandef(rdev, info, &ibss.chandef);
5939 if (err)
5940 return err;
Alexander Simon54858ee5b2011-11-30 16:56:32 +01005941
Johannes Berg683b6d32012-11-08 21:25:48 +01005942 if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef))
Alexander Simon54858ee5b2011-11-30 16:56:32 +01005943 return -EINVAL;
5944
Johannes Bergdb9c64c2012-11-09 14:56:41 +01005945 if (ibss.chandef.width > NL80211_CHAN_WIDTH_40)
5946 return -EINVAL;
5947 if (ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
5948 !(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS))
Simon Wunderlichc04d6152012-11-29 18:37:22 +01005949 return -EINVAL;
Johannes Bergdb9c64c2012-11-09 14:56:41 +01005950
Johannes Berg04a773a2009-04-19 21:24:32 +02005951 ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
Johannes Bergfffd0932009-07-08 14:22:54 +02005952 ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
Johannes Berg04a773a2009-04-19 21:24:32 +02005953
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03005954 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
5955 u8 *rates =
5956 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
5957 int n_rates =
5958 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
5959 struct ieee80211_supported_band *sband =
Johannes Berg683b6d32012-11-08 21:25:48 +01005960 wiphy->bands[ibss.chandef.chan->band];
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03005961
Johannes Berg34850ab2011-07-18 18:08:35 +02005962 err = ieee80211_get_ratemask(sband, rates, n_rates,
5963 &ibss.basic_rates);
5964 if (err)
5965 return err;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03005966 }
Felix Fietkaudd5b4cc2010-11-22 20:58:24 +01005967
5968 if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
5969 !nl80211_parse_mcast_rate(rdev, ibss.mcast_rate,
5970 nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
5971 return -EINVAL;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03005972
Johannes Berg4c476992010-10-04 21:36:35 +02005973 if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
Sujith Manoharande7044e2012-10-18 10:19:28 +05305974 bool no_ht = false;
5975
Johannes Berg4c476992010-10-04 21:36:35 +02005976 connkeys = nl80211_parse_connkeys(rdev,
Sujith Manoharande7044e2012-10-18 10:19:28 +05305977 info->attrs[NL80211_ATTR_KEYS],
5978 &no_ht);
Johannes Berg4c476992010-10-04 21:36:35 +02005979 if (IS_ERR(connkeys))
5980 return PTR_ERR(connkeys);
Sujith Manoharande7044e2012-10-18 10:19:28 +05305981
Johannes Berg3d9d1d62012-11-08 23:14:50 +01005982 if ((ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) &&
5983 no_ht) {
Sujith Manoharande7044e2012-10-18 10:19:28 +05305984 kfree(connkeys);
5985 return -EINVAL;
5986 }
Johannes Berg4c476992010-10-04 21:36:35 +02005987 }
Johannes Berg04a773a2009-04-19 21:24:32 +02005988
Antonio Quartulli267335d2012-01-31 20:25:47 +01005989 ibss.control_port =
5990 nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT]);
5991
Johannes Berg4c476992010-10-04 21:36:35 +02005992 err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02005993 if (err)
5994 kfree(connkeys);
Johannes Berg04a773a2009-04-19 21:24:32 +02005995 return err;
5996}
5997
5998static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
5999{
Johannes Berg4c476992010-10-04 21:36:35 +02006000 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6001 struct net_device *dev = info->user_ptr[1];
Johannes Berg04a773a2009-04-19 21:24:32 +02006002
Johannes Berg4c476992010-10-04 21:36:35 +02006003 if (!rdev->ops->leave_ibss)
6004 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02006005
Johannes Berg4c476992010-10-04 21:36:35 +02006006 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
6007 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02006008
Johannes Berg4c476992010-10-04 21:36:35 +02006009 return cfg80211_leave_ibss(rdev, dev, false);
Johannes Berg04a773a2009-04-19 21:24:32 +02006010}
6011
Antonio Quartullif4e583c2012-11-02 13:27:48 +01006012static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info)
6013{
6014 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6015 struct net_device *dev = info->user_ptr[1];
6016 int mcast_rate[IEEE80211_NUM_BANDS];
6017 u32 nla_rate;
6018 int err;
6019
6020 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
6021 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
6022 return -EOPNOTSUPP;
6023
6024 if (!rdev->ops->set_mcast_rate)
6025 return -EOPNOTSUPP;
6026
6027 memset(mcast_rate, 0, sizeof(mcast_rate));
6028
6029 if (!info->attrs[NL80211_ATTR_MCAST_RATE])
6030 return -EINVAL;
6031
6032 nla_rate = nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE]);
6033 if (!nl80211_parse_mcast_rate(rdev, mcast_rate, nla_rate))
6034 return -EINVAL;
6035
6036 err = rdev->ops->set_mcast_rate(&rdev->wiphy, dev, mcast_rate);
6037
6038 return err;
6039}
6040
6041
Johannes Bergaff89a92009-07-01 21:26:51 +02006042#ifdef CONFIG_NL80211_TESTMODE
6043static struct genl_multicast_group nl80211_testmode_mcgrp = {
6044 .name = "testmode",
6045};
6046
6047static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
6048{
Johannes Berg4c476992010-10-04 21:36:35 +02006049 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergaff89a92009-07-01 21:26:51 +02006050 int err;
6051
6052 if (!info->attrs[NL80211_ATTR_TESTDATA])
6053 return -EINVAL;
6054
Johannes Bergaff89a92009-07-01 21:26:51 +02006055 err = -EOPNOTSUPP;
6056 if (rdev->ops->testmode_cmd) {
6057 rdev->testmode_info = info;
Hila Gonene35e4d22012-06-27 17:19:42 +03006058 err = rdev_testmode_cmd(rdev,
Johannes Bergaff89a92009-07-01 21:26:51 +02006059 nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
6060 nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
6061 rdev->testmode_info = NULL;
6062 }
6063
Johannes Bergaff89a92009-07-01 21:26:51 +02006064 return err;
6065}
6066
Wey-Yi Guy71063f02011-05-20 09:05:54 -07006067static int nl80211_testmode_dump(struct sk_buff *skb,
6068 struct netlink_callback *cb)
6069{
Johannes Berg00918d32011-12-13 17:22:05 +01006070 struct cfg80211_registered_device *rdev;
Wey-Yi Guy71063f02011-05-20 09:05:54 -07006071 int err;
6072 long phy_idx;
6073 void *data = NULL;
6074 int data_len = 0;
6075
6076 if (cb->args[0]) {
6077 /*
6078 * 0 is a valid index, but not valid for args[0],
6079 * so we need to offset by 1.
6080 */
6081 phy_idx = cb->args[0] - 1;
6082 } else {
6083 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
6084 nl80211_fam.attrbuf, nl80211_fam.maxattr,
6085 nl80211_policy);
6086 if (err)
6087 return err;
Johannes Berg00918d32011-12-13 17:22:05 +01006088
Johannes Berg2bd7e352012-06-15 14:23:16 +02006089 mutex_lock(&cfg80211_mutex);
6090 rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk),
6091 nl80211_fam.attrbuf);
6092 if (IS_ERR(rdev)) {
6093 mutex_unlock(&cfg80211_mutex);
6094 return PTR_ERR(rdev);
Johannes Berg00918d32011-12-13 17:22:05 +01006095 }
Johannes Berg2bd7e352012-06-15 14:23:16 +02006096 phy_idx = rdev->wiphy_idx;
6097 rdev = NULL;
6098 mutex_unlock(&cfg80211_mutex);
6099
Wey-Yi Guy71063f02011-05-20 09:05:54 -07006100 if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA])
6101 cb->args[1] =
6102 (long)nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA];
6103 }
6104
6105 if (cb->args[1]) {
6106 data = nla_data((void *)cb->args[1]);
6107 data_len = nla_len((void *)cb->args[1]);
6108 }
6109
6110 mutex_lock(&cfg80211_mutex);
Johannes Berg00918d32011-12-13 17:22:05 +01006111 rdev = cfg80211_rdev_by_wiphy_idx(phy_idx);
6112 if (!rdev) {
Wey-Yi Guy71063f02011-05-20 09:05:54 -07006113 mutex_unlock(&cfg80211_mutex);
6114 return -ENOENT;
6115 }
Johannes Berg00918d32011-12-13 17:22:05 +01006116 cfg80211_lock_rdev(rdev);
Wey-Yi Guy71063f02011-05-20 09:05:54 -07006117 mutex_unlock(&cfg80211_mutex);
6118
Johannes Berg00918d32011-12-13 17:22:05 +01006119 if (!rdev->ops->testmode_dump) {
Wey-Yi Guy71063f02011-05-20 09:05:54 -07006120 err = -EOPNOTSUPP;
6121 goto out_err;
6122 }
6123
6124 while (1) {
Eric W. Biederman15e47302012-09-07 20:12:54 +00006125 void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).portid,
Wey-Yi Guy71063f02011-05-20 09:05:54 -07006126 cb->nlh->nlmsg_seq, NLM_F_MULTI,
6127 NL80211_CMD_TESTMODE);
6128 struct nlattr *tmdata;
6129
David S. Miller9360ffd2012-03-29 04:41:26 -04006130 if (nla_put_u32(skb, NL80211_ATTR_WIPHY, phy_idx)) {
Wey-Yi Guy71063f02011-05-20 09:05:54 -07006131 genlmsg_cancel(skb, hdr);
6132 break;
6133 }
6134
6135 tmdata = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
6136 if (!tmdata) {
6137 genlmsg_cancel(skb, hdr);
6138 break;
6139 }
Hila Gonene35e4d22012-06-27 17:19:42 +03006140 err = rdev_testmode_dump(rdev, skb, cb, data, data_len);
Wey-Yi Guy71063f02011-05-20 09:05:54 -07006141 nla_nest_end(skb, tmdata);
6142
6143 if (err == -ENOBUFS || err == -ENOENT) {
6144 genlmsg_cancel(skb, hdr);
6145 break;
6146 } else if (err) {
6147 genlmsg_cancel(skb, hdr);
6148 goto out_err;
6149 }
6150
6151 genlmsg_end(skb, hdr);
6152 }
6153
6154 err = skb->len;
6155 /* see above */
6156 cb->args[0] = phy_idx + 1;
6157 out_err:
Johannes Berg00918d32011-12-13 17:22:05 +01006158 cfg80211_unlock_rdev(rdev);
Wey-Yi Guy71063f02011-05-20 09:05:54 -07006159 return err;
6160}
6161
Johannes Bergaff89a92009-07-01 21:26:51 +02006162static struct sk_buff *
6163__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
Eric W. Biederman15e47302012-09-07 20:12:54 +00006164 int approxlen, u32 portid, u32 seq, gfp_t gfp)
Johannes Bergaff89a92009-07-01 21:26:51 +02006165{
6166 struct sk_buff *skb;
6167 void *hdr;
6168 struct nlattr *data;
6169
6170 skb = nlmsg_new(approxlen + 100, gfp);
6171 if (!skb)
6172 return NULL;
6173
Eric W. Biederman15e47302012-09-07 20:12:54 +00006174 hdr = nl80211hdr_put(skb, portid, seq, 0, NL80211_CMD_TESTMODE);
Johannes Bergaff89a92009-07-01 21:26:51 +02006175 if (!hdr) {
6176 kfree_skb(skb);
6177 return NULL;
6178 }
6179
David S. Miller9360ffd2012-03-29 04:41:26 -04006180 if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx))
6181 goto nla_put_failure;
Johannes Bergaff89a92009-07-01 21:26:51 +02006182 data = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
6183
6184 ((void **)skb->cb)[0] = rdev;
6185 ((void **)skb->cb)[1] = hdr;
6186 ((void **)skb->cb)[2] = data;
6187
6188 return skb;
6189
6190 nla_put_failure:
6191 kfree_skb(skb);
6192 return NULL;
6193}
6194
6195struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
6196 int approxlen)
6197{
6198 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
6199
6200 if (WARN_ON(!rdev->testmode_info))
6201 return NULL;
6202
6203 return __cfg80211_testmode_alloc_skb(rdev, approxlen,
Eric W. Biederman15e47302012-09-07 20:12:54 +00006204 rdev->testmode_info->snd_portid,
Johannes Bergaff89a92009-07-01 21:26:51 +02006205 rdev->testmode_info->snd_seq,
6206 GFP_KERNEL);
6207}
6208EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb);
6209
6210int cfg80211_testmode_reply(struct sk_buff *skb)
6211{
6212 struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
6213 void *hdr = ((void **)skb->cb)[1];
6214 struct nlattr *data = ((void **)skb->cb)[2];
6215
6216 if (WARN_ON(!rdev->testmode_info)) {
6217 kfree_skb(skb);
6218 return -EINVAL;
6219 }
6220
6221 nla_nest_end(skb, data);
6222 genlmsg_end(skb, hdr);
6223 return genlmsg_reply(skb, rdev->testmode_info);
6224}
6225EXPORT_SYMBOL(cfg80211_testmode_reply);
6226
6227struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
6228 int approxlen, gfp_t gfp)
6229{
6230 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
6231
6232 return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp);
6233}
6234EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb);
6235
6236void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
6237{
6238 void *hdr = ((void **)skb->cb)[1];
6239 struct nlattr *data = ((void **)skb->cb)[2];
6240
6241 nla_nest_end(skb, data);
6242 genlmsg_end(skb, hdr);
6243 genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp);
6244}
6245EXPORT_SYMBOL(cfg80211_testmode_event);
6246#endif
6247
Samuel Ortizb23aa672009-07-01 21:26:54 +02006248static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
6249{
Johannes Berg4c476992010-10-04 21:36:35 +02006250 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6251 struct net_device *dev = info->user_ptr[1];
Samuel Ortizb23aa672009-07-01 21:26:54 +02006252 struct cfg80211_connect_params connect;
6253 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02006254 struct cfg80211_cached_keys *connkeys = NULL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02006255 int err;
6256
6257 memset(&connect, 0, sizeof(connect));
6258
6259 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
6260 return -EINVAL;
6261
6262 if (!info->attrs[NL80211_ATTR_SSID] ||
6263 !nla_len(info->attrs[NL80211_ATTR_SSID]))
6264 return -EINVAL;
6265
6266 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
6267 connect.auth_type =
6268 nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
Jouni Malinene39e5b52012-09-30 19:29:39 +03006269 if (!nl80211_valid_auth_type(rdev, connect.auth_type,
6270 NL80211_CMD_CONNECT))
Samuel Ortizb23aa672009-07-01 21:26:54 +02006271 return -EINVAL;
6272 } else
6273 connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
6274
6275 connect.privacy = info->attrs[NL80211_ATTR_PRIVACY];
6276
Johannes Bergc0692b82010-08-27 14:26:53 +03006277 err = nl80211_crypto_settings(rdev, info, &connect.crypto,
Johannes Berg3dc27d22009-07-02 21:36:37 +02006278 NL80211_MAX_NR_CIPHER_SUITES);
Samuel Ortizb23aa672009-07-01 21:26:54 +02006279 if (err)
6280 return err;
Samuel Ortizb23aa672009-07-01 21:26:54 +02006281
Johannes Berg074ac8d2010-09-16 14:58:22 +02006282 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02006283 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
6284 return -EOPNOTSUPP;
Samuel Ortizb23aa672009-07-01 21:26:54 +02006285
Johannes Berg79c97e92009-07-07 03:56:12 +02006286 wiphy = &rdev->wiphy;
Samuel Ortizb23aa672009-07-01 21:26:54 +02006287
Bala Shanmugam4486ea92012-03-07 17:27:12 +05306288 connect.bg_scan_period = -1;
6289 if (info->attrs[NL80211_ATTR_BG_SCAN_PERIOD] &&
6290 (wiphy->flags & WIPHY_FLAG_SUPPORTS_FW_ROAM)) {
6291 connect.bg_scan_period =
6292 nla_get_u16(info->attrs[NL80211_ATTR_BG_SCAN_PERIOD]);
6293 }
6294
Samuel Ortizb23aa672009-07-01 21:26:54 +02006295 if (info->attrs[NL80211_ATTR_MAC])
6296 connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
6297 connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
6298 connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
6299
6300 if (info->attrs[NL80211_ATTR_IE]) {
6301 connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
6302 connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
6303 }
6304
Jouni Malinencee00a92013-01-15 17:15:57 +02006305 if (info->attrs[NL80211_ATTR_USE_MFP]) {
6306 connect.mfp = nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
6307 if (connect.mfp != NL80211_MFP_REQUIRED &&
6308 connect.mfp != NL80211_MFP_NO)
6309 return -EINVAL;
6310 } else {
6311 connect.mfp = NL80211_MFP_NO;
6312 }
6313
Samuel Ortizb23aa672009-07-01 21:26:54 +02006314 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
6315 connect.channel =
6316 ieee80211_get_channel(wiphy,
6317 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
6318 if (!connect.channel ||
Johannes Berg4c476992010-10-04 21:36:35 +02006319 connect.channel->flags & IEEE80211_CHAN_DISABLED)
6320 return -EINVAL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02006321 }
6322
Johannes Bergfffd0932009-07-08 14:22:54 +02006323 if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
6324 connkeys = nl80211_parse_connkeys(rdev,
Sujith Manoharande7044e2012-10-18 10:19:28 +05306325 info->attrs[NL80211_ATTR_KEYS], NULL);
Johannes Berg4c476992010-10-04 21:36:35 +02006326 if (IS_ERR(connkeys))
6327 return PTR_ERR(connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02006328 }
6329
Ben Greear7e7c8922011-11-18 11:31:59 -08006330 if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
6331 connect.flags |= ASSOC_REQ_DISABLE_HT;
6332
6333 if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
6334 memcpy(&connect.ht_capa_mask,
6335 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
6336 sizeof(connect.ht_capa_mask));
6337
6338 if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
Wei Yongjunb4e4f472012-09-02 21:41:04 +08006339 if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) {
6340 kfree(connkeys);
Ben Greear7e7c8922011-11-18 11:31:59 -08006341 return -EINVAL;
Wei Yongjunb4e4f472012-09-02 21:41:04 +08006342 }
Ben Greear7e7c8922011-11-18 11:31:59 -08006343 memcpy(&connect.ht_capa,
6344 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
6345 sizeof(connect.ht_capa));
6346 }
6347
Johannes Bergfffd0932009-07-08 14:22:54 +02006348 err = cfg80211_connect(rdev, dev, &connect, connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02006349 if (err)
6350 kfree(connkeys);
Samuel Ortizb23aa672009-07-01 21:26:54 +02006351 return err;
6352}
6353
6354static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
6355{
Johannes Berg4c476992010-10-04 21:36:35 +02006356 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6357 struct net_device *dev = info->user_ptr[1];
Samuel Ortizb23aa672009-07-01 21:26:54 +02006358 u16 reason;
6359
6360 if (!info->attrs[NL80211_ATTR_REASON_CODE])
6361 reason = WLAN_REASON_DEAUTH_LEAVING;
6362 else
6363 reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
6364
6365 if (reason == 0)
6366 return -EINVAL;
6367
Johannes Berg074ac8d2010-09-16 14:58:22 +02006368 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02006369 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
6370 return -EOPNOTSUPP;
Samuel Ortizb23aa672009-07-01 21:26:54 +02006371
Johannes Berg4c476992010-10-04 21:36:35 +02006372 return cfg80211_disconnect(rdev, dev, reason, true);
Samuel Ortizb23aa672009-07-01 21:26:54 +02006373}
6374
Johannes Berg463d0182009-07-14 00:33:35 +02006375static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
6376{
Johannes Berg4c476992010-10-04 21:36:35 +02006377 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg463d0182009-07-14 00:33:35 +02006378 struct net *net;
6379 int err;
6380 u32 pid;
6381
6382 if (!info->attrs[NL80211_ATTR_PID])
6383 return -EINVAL;
6384
6385 pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
6386
Johannes Berg463d0182009-07-14 00:33:35 +02006387 net = get_net_ns_by_pid(pid);
Johannes Berg4c476992010-10-04 21:36:35 +02006388 if (IS_ERR(net))
6389 return PTR_ERR(net);
Johannes Berg463d0182009-07-14 00:33:35 +02006390
6391 err = 0;
6392
6393 /* check if anything to do */
Johannes Berg4c476992010-10-04 21:36:35 +02006394 if (!net_eq(wiphy_net(&rdev->wiphy), net))
6395 err = cfg80211_switch_netns(rdev, net);
Johannes Berg463d0182009-07-14 00:33:35 +02006396
Johannes Berg463d0182009-07-14 00:33:35 +02006397 put_net(net);
Johannes Berg463d0182009-07-14 00:33:35 +02006398 return err;
6399}
6400
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006401static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info)
6402{
Johannes Berg4c476992010-10-04 21:36:35 +02006403 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006404 int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev,
6405 struct cfg80211_pmksa *pmksa) = NULL;
Johannes Berg4c476992010-10-04 21:36:35 +02006406 struct net_device *dev = info->user_ptr[1];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006407 struct cfg80211_pmksa pmksa;
6408
6409 memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
6410
6411 if (!info->attrs[NL80211_ATTR_MAC])
6412 return -EINVAL;
6413
6414 if (!info->attrs[NL80211_ATTR_PMKID])
6415 return -EINVAL;
6416
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006417 pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]);
6418 pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
6419
Johannes Berg074ac8d2010-09-16 14:58:22 +02006420 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02006421 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
6422 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006423
6424 switch (info->genlhdr->cmd) {
6425 case NL80211_CMD_SET_PMKSA:
6426 rdev_ops = rdev->ops->set_pmksa;
6427 break;
6428 case NL80211_CMD_DEL_PMKSA:
6429 rdev_ops = rdev->ops->del_pmksa;
6430 break;
6431 default:
6432 WARN_ON(1);
6433 break;
6434 }
6435
Johannes Berg4c476992010-10-04 21:36:35 +02006436 if (!rdev_ops)
6437 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006438
Johannes Berg4c476992010-10-04 21:36:35 +02006439 return rdev_ops(&rdev->wiphy, dev, &pmksa);
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006440}
6441
6442static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info)
6443{
Johannes Berg4c476992010-10-04 21:36:35 +02006444 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6445 struct net_device *dev = info->user_ptr[1];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006446
Johannes Berg074ac8d2010-09-16 14:58:22 +02006447 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02006448 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
6449 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006450
Johannes Berg4c476992010-10-04 21:36:35 +02006451 if (!rdev->ops->flush_pmksa)
6452 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006453
Hila Gonene35e4d22012-06-27 17:19:42 +03006454 return rdev_flush_pmksa(rdev, dev);
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006455}
6456
Arik Nemtsov109086c2011-09-28 14:12:50 +03006457static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
6458{
6459 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6460 struct net_device *dev = info->user_ptr[1];
6461 u8 action_code, dialog_token;
6462 u16 status_code;
6463 u8 *peer;
6464
6465 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
6466 !rdev->ops->tdls_mgmt)
6467 return -EOPNOTSUPP;
6468
6469 if (!info->attrs[NL80211_ATTR_TDLS_ACTION] ||
6470 !info->attrs[NL80211_ATTR_STATUS_CODE] ||
6471 !info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN] ||
6472 !info->attrs[NL80211_ATTR_IE] ||
6473 !info->attrs[NL80211_ATTR_MAC])
6474 return -EINVAL;
6475
6476 peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
6477 action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]);
6478 status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]);
6479 dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]);
6480
Hila Gonene35e4d22012-06-27 17:19:42 +03006481 return rdev_tdls_mgmt(rdev, dev, peer, action_code,
6482 dialog_token, status_code,
6483 nla_data(info->attrs[NL80211_ATTR_IE]),
6484 nla_len(info->attrs[NL80211_ATTR_IE]));
Arik Nemtsov109086c2011-09-28 14:12:50 +03006485}
6486
6487static int nl80211_tdls_oper(struct sk_buff *skb, struct genl_info *info)
6488{
6489 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6490 struct net_device *dev = info->user_ptr[1];
6491 enum nl80211_tdls_operation operation;
6492 u8 *peer;
6493
6494 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
6495 !rdev->ops->tdls_oper)
6496 return -EOPNOTSUPP;
6497
6498 if (!info->attrs[NL80211_ATTR_TDLS_OPERATION] ||
6499 !info->attrs[NL80211_ATTR_MAC])
6500 return -EINVAL;
6501
6502 operation = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_OPERATION]);
6503 peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
6504
Hila Gonene35e4d22012-06-27 17:19:42 +03006505 return rdev_tdls_oper(rdev, dev, peer, operation);
Arik Nemtsov109086c2011-09-28 14:12:50 +03006506}
6507
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006508static int nl80211_remain_on_channel(struct sk_buff *skb,
6509 struct genl_info *info)
6510{
Johannes Berg4c476992010-10-04 21:36:35 +02006511 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg71bbc992012-06-15 15:30:18 +02006512 struct wireless_dev *wdev = info->user_ptr[1];
Johannes Berg683b6d32012-11-08 21:25:48 +01006513 struct cfg80211_chan_def chandef;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006514 struct sk_buff *msg;
6515 void *hdr;
6516 u64 cookie;
Johannes Berg683b6d32012-11-08 21:25:48 +01006517 u32 duration;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006518 int err;
6519
6520 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
6521 !info->attrs[NL80211_ATTR_DURATION])
6522 return -EINVAL;
6523
6524 duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
6525
Johannes Berg7c4ef712011-11-18 15:33:48 +01006526 if (!rdev->ops->remain_on_channel ||
6527 !(rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL))
Johannes Berg4c476992010-10-04 21:36:35 +02006528 return -EOPNOTSUPP;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006529
Johannes Bergebf348f2012-06-01 12:50:54 +02006530 /*
6531 * We should be on that channel for at least a minimum amount of
6532 * time (10ms) but no longer than the driver supports.
6533 */
6534 if (duration < NL80211_MIN_REMAIN_ON_CHANNEL_TIME ||
6535 duration > rdev->wiphy.max_remain_on_channel_duration)
6536 return -EINVAL;
6537
Johannes Berg683b6d32012-11-08 21:25:48 +01006538 err = nl80211_parse_chandef(rdev, info, &chandef);
6539 if (err)
6540 return err;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006541
6542 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02006543 if (!msg)
6544 return -ENOMEM;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006545
Eric W. Biederman15e47302012-09-07 20:12:54 +00006546 hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006547 NL80211_CMD_REMAIN_ON_CHANNEL);
6548
6549 if (IS_ERR(hdr)) {
6550 err = PTR_ERR(hdr);
6551 goto free_msg;
6552 }
6553
Johannes Berg683b6d32012-11-08 21:25:48 +01006554 err = rdev_remain_on_channel(rdev, wdev, chandef.chan,
6555 duration, &cookie);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006556
6557 if (err)
6558 goto free_msg;
6559
David S. Miller9360ffd2012-03-29 04:41:26 -04006560 if (nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie))
6561 goto nla_put_failure;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006562
6563 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02006564
6565 return genlmsg_reply(msg, info);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006566
6567 nla_put_failure:
6568 err = -ENOBUFS;
6569 free_msg:
6570 nlmsg_free(msg);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006571 return err;
6572}
6573
6574static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,
6575 struct genl_info *info)
6576{
Johannes Berg4c476992010-10-04 21:36:35 +02006577 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg71bbc992012-06-15 15:30:18 +02006578 struct wireless_dev *wdev = info->user_ptr[1];
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006579 u64 cookie;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006580
6581 if (!info->attrs[NL80211_ATTR_COOKIE])
6582 return -EINVAL;
6583
Johannes Berg4c476992010-10-04 21:36:35 +02006584 if (!rdev->ops->cancel_remain_on_channel)
6585 return -EOPNOTSUPP;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006586
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006587 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
6588
Hila Gonene35e4d22012-06-27 17:19:42 +03006589 return rdev_cancel_remain_on_channel(rdev, wdev, cookie);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006590}
6591
Jouni Malinen13ae75b2009-12-29 12:59:45 +02006592static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
6593 u8 *rates, u8 rates_len)
6594{
6595 u8 i;
6596 u32 mask = 0;
6597
6598 for (i = 0; i < rates_len; i++) {
6599 int rate = (rates[i] & 0x7f) * 5;
6600 int ridx;
6601 for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
6602 struct ieee80211_rate *srate =
6603 &sband->bitrates[ridx];
6604 if (rate == srate->bitrate) {
6605 mask |= 1 << ridx;
6606 break;
6607 }
6608 }
6609 if (ridx == sband->n_bitrates)
6610 return 0; /* rate not found */
6611 }
6612
6613 return mask;
6614}
6615
Simon Wunderlich24db78c2012-01-28 17:25:32 +01006616static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband,
6617 u8 *rates, u8 rates_len,
6618 u8 mcs[IEEE80211_HT_MCS_MASK_LEN])
6619{
6620 u8 i;
6621
6622 memset(mcs, 0, IEEE80211_HT_MCS_MASK_LEN);
6623
6624 for (i = 0; i < rates_len; i++) {
6625 int ridx, rbit;
6626
6627 ridx = rates[i] / 8;
6628 rbit = BIT(rates[i] % 8);
6629
6630 /* check validity */
Dan Carpenter910570b52012-02-01 10:42:11 +03006631 if ((ridx < 0) || (ridx >= IEEE80211_HT_MCS_MASK_LEN))
Simon Wunderlich24db78c2012-01-28 17:25:32 +01006632 return false;
6633
6634 /* check availability */
6635 if (sband->ht_cap.mcs.rx_mask[ridx] & rbit)
6636 mcs[ridx] |= rbit;
6637 else
6638 return false;
6639 }
6640
6641 return true;
6642}
6643
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00006644static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
Jouni Malinen13ae75b2009-12-29 12:59:45 +02006645 [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
6646 .len = NL80211_MAX_SUPP_RATES },
Simon Wunderlich24db78c2012-01-28 17:25:32 +01006647 [NL80211_TXRATE_MCS] = { .type = NLA_BINARY,
6648 .len = NL80211_MAX_SUPP_HT_RATES },
Jouni Malinen13ae75b2009-12-29 12:59:45 +02006649};
6650
6651static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
6652 struct genl_info *info)
6653{
6654 struct nlattr *tb[NL80211_TXRATE_MAX + 1];
Johannes Berg4c476992010-10-04 21:36:35 +02006655 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Jouni Malinen13ae75b2009-12-29 12:59:45 +02006656 struct cfg80211_bitrate_mask mask;
Johannes Berg4c476992010-10-04 21:36:35 +02006657 int rem, i;
6658 struct net_device *dev = info->user_ptr[1];
Jouni Malinen13ae75b2009-12-29 12:59:45 +02006659 struct nlattr *tx_rates;
6660 struct ieee80211_supported_band *sband;
6661
6662 if (info->attrs[NL80211_ATTR_TX_RATES] == NULL)
6663 return -EINVAL;
6664
Johannes Berg4c476992010-10-04 21:36:35 +02006665 if (!rdev->ops->set_bitrate_mask)
6666 return -EOPNOTSUPP;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02006667
6668 memset(&mask, 0, sizeof(mask));
6669 /* Default to all rates enabled */
6670 for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
6671 sband = rdev->wiphy.bands[i];
6672 mask.control[i].legacy =
6673 sband ? (1 << sband->n_bitrates) - 1 : 0;
Simon Wunderlich24db78c2012-01-28 17:25:32 +01006674 if (sband)
6675 memcpy(mask.control[i].mcs,
6676 sband->ht_cap.mcs.rx_mask,
6677 sizeof(mask.control[i].mcs));
6678 else
6679 memset(mask.control[i].mcs, 0,
6680 sizeof(mask.control[i].mcs));
Jouni Malinen13ae75b2009-12-29 12:59:45 +02006681 }
6682
6683 /*
6684 * The nested attribute uses enum nl80211_band as the index. This maps
6685 * directly to the enum ieee80211_band values used in cfg80211.
6686 */
Simon Wunderlich24db78c2012-01-28 17:25:32 +01006687 BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8);
Jouni Malinen13ae75b2009-12-29 12:59:45 +02006688 nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem)
6689 {
6690 enum ieee80211_band band = nla_type(tx_rates);
Johannes Berg4c476992010-10-04 21:36:35 +02006691 if (band < 0 || band >= IEEE80211_NUM_BANDS)
6692 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02006693 sband = rdev->wiphy.bands[band];
Johannes Berg4c476992010-10-04 21:36:35 +02006694 if (sband == NULL)
6695 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02006696 nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
6697 nla_len(tx_rates), nl80211_txattr_policy);
6698 if (tb[NL80211_TXRATE_LEGACY]) {
6699 mask.control[band].legacy = rateset_to_mask(
6700 sband,
6701 nla_data(tb[NL80211_TXRATE_LEGACY]),
6702 nla_len(tb[NL80211_TXRATE_LEGACY]));
Bala Shanmugam218d2e22012-04-20 19:12:58 +05306703 if ((mask.control[band].legacy == 0) &&
6704 nla_len(tb[NL80211_TXRATE_LEGACY]))
6705 return -EINVAL;
Simon Wunderlich24db78c2012-01-28 17:25:32 +01006706 }
6707 if (tb[NL80211_TXRATE_MCS]) {
6708 if (!ht_rateset_to_mask(
6709 sband,
6710 nla_data(tb[NL80211_TXRATE_MCS]),
6711 nla_len(tb[NL80211_TXRATE_MCS]),
6712 mask.control[band].mcs))
6713 return -EINVAL;
6714 }
6715
6716 if (mask.control[band].legacy == 0) {
6717 /* don't allow empty legacy rates if HT
6718 * is not even supported. */
6719 if (!rdev->wiphy.bands[band]->ht_cap.ht_supported)
6720 return -EINVAL;
6721
6722 for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
6723 if (mask.control[band].mcs[i])
6724 break;
6725
6726 /* legacy and mcs rates may not be both empty */
6727 if (i == IEEE80211_HT_MCS_MASK_LEN)
Johannes Berg4c476992010-10-04 21:36:35 +02006728 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02006729 }
6730 }
6731
Hila Gonene35e4d22012-06-27 17:19:42 +03006732 return rdev_set_bitrate_mask(rdev, dev, NULL, &mask);
Jouni Malinen13ae75b2009-12-29 12:59:45 +02006733}
6734
Johannes Berg2e161f72010-08-12 15:38:38 +02006735static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
Jouni Malinen026331c2010-02-15 12:53:10 +02006736{
Johannes Berg4c476992010-10-04 21:36:35 +02006737 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg71bbc992012-06-15 15:30:18 +02006738 struct wireless_dev *wdev = info->user_ptr[1];
Johannes Berg2e161f72010-08-12 15:38:38 +02006739 u16 frame_type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION;
Jouni Malinen026331c2010-02-15 12:53:10 +02006740
6741 if (!info->attrs[NL80211_ATTR_FRAME_MATCH])
6742 return -EINVAL;
6743
Johannes Berg2e161f72010-08-12 15:38:38 +02006744 if (info->attrs[NL80211_ATTR_FRAME_TYPE])
6745 frame_type = nla_get_u16(info->attrs[NL80211_ATTR_FRAME_TYPE]);
Jouni Malinen026331c2010-02-15 12:53:10 +02006746
Johannes Berg71bbc992012-06-15 15:30:18 +02006747 switch (wdev->iftype) {
6748 case NL80211_IFTYPE_STATION:
6749 case NL80211_IFTYPE_ADHOC:
6750 case NL80211_IFTYPE_P2P_CLIENT:
6751 case NL80211_IFTYPE_AP:
6752 case NL80211_IFTYPE_AP_VLAN:
6753 case NL80211_IFTYPE_MESH_POINT:
6754 case NL80211_IFTYPE_P2P_GO:
Johannes Berg98104fde2012-06-16 00:19:54 +02006755 case NL80211_IFTYPE_P2P_DEVICE:
Johannes Berg71bbc992012-06-15 15:30:18 +02006756 break;
6757 default:
Johannes Berg4c476992010-10-04 21:36:35 +02006758 return -EOPNOTSUPP;
Johannes Berg71bbc992012-06-15 15:30:18 +02006759 }
Jouni Malinen026331c2010-02-15 12:53:10 +02006760
6761 /* not much point in registering if we can't reply */
Johannes Berg4c476992010-10-04 21:36:35 +02006762 if (!rdev->ops->mgmt_tx)
6763 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02006764
Eric W. Biederman15e47302012-09-07 20:12:54 +00006765 return cfg80211_mlme_register_mgmt(wdev, info->snd_portid, frame_type,
Jouni Malinen026331c2010-02-15 12:53:10 +02006766 nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]),
6767 nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]));
Jouni Malinen026331c2010-02-15 12:53:10 +02006768}
6769
Johannes Berg2e161f72010-08-12 15:38:38 +02006770static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
Jouni Malinen026331c2010-02-15 12:53:10 +02006771{
Johannes Berg4c476992010-10-04 21:36:35 +02006772 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg71bbc992012-06-15 15:30:18 +02006773 struct wireless_dev *wdev = info->user_ptr[1];
Johannes Berg683b6d32012-11-08 21:25:48 +01006774 struct cfg80211_chan_def chandef;
Jouni Malinen026331c2010-02-15 12:53:10 +02006775 int err;
Johannes Bergd64d3732011-11-10 09:44:46 +01006776 void *hdr = NULL;
Jouni Malinen026331c2010-02-15 12:53:10 +02006777 u64 cookie;
Johannes Berge247bd902011-11-04 11:18:21 +01006778 struct sk_buff *msg = NULL;
Johannes Bergf7ca38d2010-11-25 10:02:29 +01006779 unsigned int wait = 0;
Johannes Berge247bd902011-11-04 11:18:21 +01006780 bool offchan, no_cck, dont_wait_for_ack;
6781
6782 dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK];
Jouni Malinen026331c2010-02-15 12:53:10 +02006783
Johannes Berg683b6d32012-11-08 21:25:48 +01006784 if (!info->attrs[NL80211_ATTR_FRAME])
Jouni Malinen026331c2010-02-15 12:53:10 +02006785 return -EINVAL;
6786
Johannes Berg4c476992010-10-04 21:36:35 +02006787 if (!rdev->ops->mgmt_tx)
6788 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02006789
Johannes Berg71bbc992012-06-15 15:30:18 +02006790 switch (wdev->iftype) {
6791 case NL80211_IFTYPE_STATION:
6792 case NL80211_IFTYPE_ADHOC:
6793 case NL80211_IFTYPE_P2P_CLIENT:
6794 case NL80211_IFTYPE_AP:
6795 case NL80211_IFTYPE_AP_VLAN:
6796 case NL80211_IFTYPE_MESH_POINT:
6797 case NL80211_IFTYPE_P2P_GO:
Johannes Berg98104fde2012-06-16 00:19:54 +02006798 case NL80211_IFTYPE_P2P_DEVICE:
Johannes Berg71bbc992012-06-15 15:30:18 +02006799 break;
6800 default:
Johannes Berg4c476992010-10-04 21:36:35 +02006801 return -EOPNOTSUPP;
Johannes Berg71bbc992012-06-15 15:30:18 +02006802 }
Jouni Malinen026331c2010-02-15 12:53:10 +02006803
Johannes Bergf7ca38d2010-11-25 10:02:29 +01006804 if (info->attrs[NL80211_ATTR_DURATION]) {
Johannes Berg7c4ef712011-11-18 15:33:48 +01006805 if (!(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
Johannes Bergf7ca38d2010-11-25 10:02:29 +01006806 return -EINVAL;
6807 wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
Johannes Bergebf348f2012-06-01 12:50:54 +02006808
6809 /*
6810 * We should wait on the channel for at least a minimum amount
6811 * of time (10ms) but no longer than the driver supports.
6812 */
6813 if (wait < NL80211_MIN_REMAIN_ON_CHANNEL_TIME ||
6814 wait > rdev->wiphy.max_remain_on_channel_duration)
6815 return -EINVAL;
6816
Johannes Bergf7ca38d2010-11-25 10:02:29 +01006817 }
6818
Johannes Bergf7ca38d2010-11-25 10:02:29 +01006819 offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];
6820
Johannes Berg7c4ef712011-11-18 15:33:48 +01006821 if (offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
6822 return -EINVAL;
6823
Rajkumar Manoharane9f935e2011-09-25 14:53:30 +05306824 no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
6825
Johannes Berg683b6d32012-11-08 21:25:48 +01006826 err = nl80211_parse_chandef(rdev, info, &chandef);
6827 if (err)
6828 return err;
Jouni Malinen026331c2010-02-15 12:53:10 +02006829
Johannes Berge247bd902011-11-04 11:18:21 +01006830 if (!dont_wait_for_ack) {
6831 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
6832 if (!msg)
6833 return -ENOMEM;
Jouni Malinen026331c2010-02-15 12:53:10 +02006834
Eric W. Biederman15e47302012-09-07 20:12:54 +00006835 hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
Johannes Berge247bd902011-11-04 11:18:21 +01006836 NL80211_CMD_FRAME);
Jouni Malinen026331c2010-02-15 12:53:10 +02006837
Johannes Berge247bd902011-11-04 11:18:21 +01006838 if (IS_ERR(hdr)) {
6839 err = PTR_ERR(hdr);
6840 goto free_msg;
6841 }
Jouni Malinen026331c2010-02-15 12:53:10 +02006842 }
Johannes Berge247bd902011-11-04 11:18:21 +01006843
Johannes Berg683b6d32012-11-08 21:25:48 +01006844 err = cfg80211_mlme_mgmt_tx(rdev, wdev, chandef.chan, offchan, wait,
Johannes Berg2e161f72010-08-12 15:38:38 +02006845 nla_data(info->attrs[NL80211_ATTR_FRAME]),
6846 nla_len(info->attrs[NL80211_ATTR_FRAME]),
Johannes Berge247bd902011-11-04 11:18:21 +01006847 no_cck, dont_wait_for_ack, &cookie);
Jouni Malinen026331c2010-02-15 12:53:10 +02006848 if (err)
6849 goto free_msg;
6850
Johannes Berge247bd902011-11-04 11:18:21 +01006851 if (msg) {
David S. Miller9360ffd2012-03-29 04:41:26 -04006852 if (nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie))
6853 goto nla_put_failure;
Jouni Malinen026331c2010-02-15 12:53:10 +02006854
Johannes Berge247bd902011-11-04 11:18:21 +01006855 genlmsg_end(msg, hdr);
6856 return genlmsg_reply(msg, info);
6857 }
6858
6859 return 0;
Jouni Malinen026331c2010-02-15 12:53:10 +02006860
6861 nla_put_failure:
6862 err = -ENOBUFS;
6863 free_msg:
6864 nlmsg_free(msg);
Jouni Malinen026331c2010-02-15 12:53:10 +02006865 return err;
6866}
6867
Johannes Bergf7ca38d2010-11-25 10:02:29 +01006868static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *info)
6869{
6870 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg71bbc992012-06-15 15:30:18 +02006871 struct wireless_dev *wdev = info->user_ptr[1];
Johannes Bergf7ca38d2010-11-25 10:02:29 +01006872 u64 cookie;
6873
6874 if (!info->attrs[NL80211_ATTR_COOKIE])
6875 return -EINVAL;
6876
6877 if (!rdev->ops->mgmt_tx_cancel_wait)
6878 return -EOPNOTSUPP;
6879
Johannes Berg71bbc992012-06-15 15:30:18 +02006880 switch (wdev->iftype) {
6881 case NL80211_IFTYPE_STATION:
6882 case NL80211_IFTYPE_ADHOC:
6883 case NL80211_IFTYPE_P2P_CLIENT:
6884 case NL80211_IFTYPE_AP:
6885 case NL80211_IFTYPE_AP_VLAN:
6886 case NL80211_IFTYPE_P2P_GO:
Johannes Berg98104fde2012-06-16 00:19:54 +02006887 case NL80211_IFTYPE_P2P_DEVICE:
Johannes Berg71bbc992012-06-15 15:30:18 +02006888 break;
6889 default:
Johannes Bergf7ca38d2010-11-25 10:02:29 +01006890 return -EOPNOTSUPP;
Johannes Berg71bbc992012-06-15 15:30:18 +02006891 }
Johannes Bergf7ca38d2010-11-25 10:02:29 +01006892
6893 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
6894
Hila Gonene35e4d22012-06-27 17:19:42 +03006895 return rdev_mgmt_tx_cancel_wait(rdev, wdev, cookie);
Johannes Bergf7ca38d2010-11-25 10:02:29 +01006896}
6897
Kalle Valoffb9eb32010-02-17 17:58:10 +02006898static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info)
6899{
Johannes Berg4c476992010-10-04 21:36:35 +02006900 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Kalle Valoffb9eb32010-02-17 17:58:10 +02006901 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02006902 struct net_device *dev = info->user_ptr[1];
Kalle Valoffb9eb32010-02-17 17:58:10 +02006903 u8 ps_state;
6904 bool state;
6905 int err;
6906
Johannes Berg4c476992010-10-04 21:36:35 +02006907 if (!info->attrs[NL80211_ATTR_PS_STATE])
6908 return -EINVAL;
Kalle Valoffb9eb32010-02-17 17:58:10 +02006909
6910 ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]);
6911
Johannes Berg4c476992010-10-04 21:36:35 +02006912 if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED)
6913 return -EINVAL;
Kalle Valoffb9eb32010-02-17 17:58:10 +02006914
6915 wdev = dev->ieee80211_ptr;
6916
Johannes Berg4c476992010-10-04 21:36:35 +02006917 if (!rdev->ops->set_power_mgmt)
6918 return -EOPNOTSUPP;
Kalle Valoffb9eb32010-02-17 17:58:10 +02006919
6920 state = (ps_state == NL80211_PS_ENABLED) ? true : false;
6921
6922 if (state == wdev->ps)
Johannes Berg4c476992010-10-04 21:36:35 +02006923 return 0;
Kalle Valoffb9eb32010-02-17 17:58:10 +02006924
Hila Gonene35e4d22012-06-27 17:19:42 +03006925 err = rdev_set_power_mgmt(rdev, dev, state, wdev->ps_timeout);
Johannes Berg4c476992010-10-04 21:36:35 +02006926 if (!err)
6927 wdev->ps = state;
Kalle Valoffb9eb32010-02-17 17:58:10 +02006928 return err;
6929}
6930
6931static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)
6932{
Johannes Berg4c476992010-10-04 21:36:35 +02006933 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Kalle Valoffb9eb32010-02-17 17:58:10 +02006934 enum nl80211_ps_state ps_state;
6935 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02006936 struct net_device *dev = info->user_ptr[1];
Kalle Valoffb9eb32010-02-17 17:58:10 +02006937 struct sk_buff *msg;
6938 void *hdr;
6939 int err;
6940
Kalle Valoffb9eb32010-02-17 17:58:10 +02006941 wdev = dev->ieee80211_ptr;
6942
Johannes Berg4c476992010-10-04 21:36:35 +02006943 if (!rdev->ops->set_power_mgmt)
6944 return -EOPNOTSUPP;
Kalle Valoffb9eb32010-02-17 17:58:10 +02006945
6946 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02006947 if (!msg)
6948 return -ENOMEM;
Kalle Valoffb9eb32010-02-17 17:58:10 +02006949
Eric W. Biederman15e47302012-09-07 20:12:54 +00006950 hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
Kalle Valoffb9eb32010-02-17 17:58:10 +02006951 NL80211_CMD_GET_POWER_SAVE);
6952 if (!hdr) {
Johannes Berg4c476992010-10-04 21:36:35 +02006953 err = -ENOBUFS;
Kalle Valoffb9eb32010-02-17 17:58:10 +02006954 goto free_msg;
6955 }
6956
6957 if (wdev->ps)
6958 ps_state = NL80211_PS_ENABLED;
6959 else
6960 ps_state = NL80211_PS_DISABLED;
6961
David S. Miller9360ffd2012-03-29 04:41:26 -04006962 if (nla_put_u32(msg, NL80211_ATTR_PS_STATE, ps_state))
6963 goto nla_put_failure;
Kalle Valoffb9eb32010-02-17 17:58:10 +02006964
6965 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02006966 return genlmsg_reply(msg, info);
Kalle Valoffb9eb32010-02-17 17:58:10 +02006967
Johannes Berg4c476992010-10-04 21:36:35 +02006968 nla_put_failure:
Kalle Valoffb9eb32010-02-17 17:58:10 +02006969 err = -ENOBUFS;
Johannes Berg4c476992010-10-04 21:36:35 +02006970 free_msg:
Kalle Valoffb9eb32010-02-17 17:58:10 +02006971 nlmsg_free(msg);
Kalle Valoffb9eb32010-02-17 17:58:10 +02006972 return err;
6973}
6974
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006975static struct nla_policy
6976nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
6977 [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
6978 [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
6979 [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
Thomas Pedersen84f10702012-07-12 16:17:33 -07006980 [NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 },
6981 [NL80211_ATTR_CQM_TXE_PKTS] = { .type = NLA_U32 },
6982 [NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006983};
6984
Thomas Pedersen84f10702012-07-12 16:17:33 -07006985static int nl80211_set_cqm_txe(struct genl_info *info,
Johannes Bergd9d8b012012-11-26 12:51:52 +01006986 u32 rate, u32 pkts, u32 intvl)
Thomas Pedersen84f10702012-07-12 16:17:33 -07006987{
6988 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6989 struct wireless_dev *wdev;
6990 struct net_device *dev = info->user_ptr[1];
6991
Johannes Bergd9d8b012012-11-26 12:51:52 +01006992 if (rate > 100 || intvl > NL80211_CQM_TXE_MAX_INTVL)
Thomas Pedersen84f10702012-07-12 16:17:33 -07006993 return -EINVAL;
6994
6995 wdev = dev->ieee80211_ptr;
6996
6997 if (!rdev->ops->set_cqm_txe_config)
6998 return -EOPNOTSUPP;
6999
7000 if (wdev->iftype != NL80211_IFTYPE_STATION &&
7001 wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
7002 return -EOPNOTSUPP;
7003
Hila Gonene35e4d22012-06-27 17:19:42 +03007004 return rdev_set_cqm_txe_config(rdev, dev, rate, pkts, intvl);
Thomas Pedersen84f10702012-07-12 16:17:33 -07007005}
7006
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007007static int nl80211_set_cqm_rssi(struct genl_info *info,
7008 s32 threshold, u32 hysteresis)
7009{
Johannes Berg4c476992010-10-04 21:36:35 +02007010 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007011 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02007012 struct net_device *dev = info->user_ptr[1];
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007013
7014 if (threshold > 0)
7015 return -EINVAL;
7016
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007017 wdev = dev->ieee80211_ptr;
7018
Johannes Berg4c476992010-10-04 21:36:35 +02007019 if (!rdev->ops->set_cqm_rssi_config)
7020 return -EOPNOTSUPP;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007021
Johannes Berg074ac8d2010-09-16 14:58:22 +02007022 if (wdev->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02007023 wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
7024 return -EOPNOTSUPP;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007025
Hila Gonene35e4d22012-06-27 17:19:42 +03007026 return rdev_set_cqm_rssi_config(rdev, dev, threshold, hysteresis);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007027}
7028
7029static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
7030{
7031 struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1];
7032 struct nlattr *cqm;
7033 int err;
7034
7035 cqm = info->attrs[NL80211_ATTR_CQM];
7036 if (!cqm) {
7037 err = -EINVAL;
7038 goto out;
7039 }
7040
7041 err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
7042 nl80211_attr_cqm_policy);
7043 if (err)
7044 goto out;
7045
7046 if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
7047 attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
7048 s32 threshold;
7049 u32 hysteresis;
7050 threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
7051 hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
7052 err = nl80211_set_cqm_rssi(info, threshold, hysteresis);
Thomas Pedersen84f10702012-07-12 16:17:33 -07007053 } else if (attrs[NL80211_ATTR_CQM_TXE_RATE] &&
7054 attrs[NL80211_ATTR_CQM_TXE_PKTS] &&
7055 attrs[NL80211_ATTR_CQM_TXE_INTVL]) {
7056 u32 rate, pkts, intvl;
7057 rate = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_RATE]);
7058 pkts = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_PKTS]);
7059 intvl = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_INTVL]);
7060 err = nl80211_set_cqm_txe(info, rate, pkts, intvl);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007061 } else
7062 err = -EINVAL;
7063
7064out:
7065 return err;
7066}
7067
Johannes Berg29cbe682010-12-03 09:20:44 +01007068static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
7069{
7070 struct cfg80211_registered_device *rdev = info->user_ptr[0];
7071 struct net_device *dev = info->user_ptr[1];
7072 struct mesh_config cfg;
Javier Cardonac80d5452010-12-16 17:37:49 -08007073 struct mesh_setup setup;
Johannes Berg29cbe682010-12-03 09:20:44 +01007074 int err;
7075
7076 /* start with default */
7077 memcpy(&cfg, &default_mesh_config, sizeof(cfg));
Javier Cardonac80d5452010-12-16 17:37:49 -08007078 memcpy(&setup, &default_mesh_setup, sizeof(setup));
Johannes Berg29cbe682010-12-03 09:20:44 +01007079
Javier Cardona24bdd9f2010-12-16 17:37:48 -08007080 if (info->attrs[NL80211_ATTR_MESH_CONFIG]) {
Johannes Berg29cbe682010-12-03 09:20:44 +01007081 /* and parse parameters if given */
Javier Cardona24bdd9f2010-12-16 17:37:48 -08007082 err = nl80211_parse_mesh_config(info, &cfg, NULL);
Johannes Berg29cbe682010-12-03 09:20:44 +01007083 if (err)
7084 return err;
7085 }
7086
7087 if (!info->attrs[NL80211_ATTR_MESH_ID] ||
7088 !nla_len(info->attrs[NL80211_ATTR_MESH_ID]))
7089 return -EINVAL;
7090
Javier Cardonac80d5452010-12-16 17:37:49 -08007091 setup.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
7092 setup.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
7093
Chun-Yeow Yeoh4bb62342011-11-24 17:15:20 -08007094 if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
7095 !nl80211_parse_mcast_rate(rdev, setup.mcast_rate,
7096 nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
7097 return -EINVAL;
7098
Marco Porsch9bdbf042013-01-07 16:04:51 +01007099 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
7100 setup.beacon_interval =
7101 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
7102 if (setup.beacon_interval < 10 ||
7103 setup.beacon_interval > 10000)
7104 return -EINVAL;
7105 }
7106
7107 if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) {
7108 setup.dtim_period =
7109 nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
7110 if (setup.dtim_period < 1 || setup.dtim_period > 100)
7111 return -EINVAL;
7112 }
7113
Javier Cardonac80d5452010-12-16 17:37:49 -08007114 if (info->attrs[NL80211_ATTR_MESH_SETUP]) {
7115 /* parse additional setup parameters if given */
7116 err = nl80211_parse_mesh_setup(info, &setup);
7117 if (err)
7118 return err;
7119 }
7120
Johannes Bergcc1d2802012-05-16 23:50:20 +02007121 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Johannes Berg683b6d32012-11-08 21:25:48 +01007122 err = nl80211_parse_chandef(rdev, info, &setup.chandef);
7123 if (err)
7124 return err;
Johannes Bergcc1d2802012-05-16 23:50:20 +02007125 } else {
7126 /* cfg80211_join_mesh() will sort it out */
Johannes Berg683b6d32012-11-08 21:25:48 +01007127 setup.chandef.chan = NULL;
Johannes Bergcc1d2802012-05-16 23:50:20 +02007128 }
7129
Javier Cardonac80d5452010-12-16 17:37:49 -08007130 return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
Johannes Berg29cbe682010-12-03 09:20:44 +01007131}
7132
7133static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info)
7134{
7135 struct cfg80211_registered_device *rdev = info->user_ptr[0];
7136 struct net_device *dev = info->user_ptr[1];
7137
7138 return cfg80211_leave_mesh(rdev, dev);
7139}
7140
Johannes Bergdfb89c52012-06-27 09:23:48 +02007141#ifdef CONFIG_PM
Amitkumar Karwarbb92d192013-02-12 12:16:26 -08007142static int nl80211_send_wowlan_patterns(struct sk_buff *msg,
7143 struct cfg80211_registered_device *rdev)
7144{
7145 struct nlattr *nl_pats, *nl_pat;
7146 int i, pat_len;
7147
7148 if (!rdev->wowlan->n_patterns)
7149 return 0;
7150
7151 nl_pats = nla_nest_start(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN);
7152 if (!nl_pats)
7153 return -ENOBUFS;
7154
7155 for (i = 0; i < rdev->wowlan->n_patterns; i++) {
7156 nl_pat = nla_nest_start(msg, i + 1);
7157 if (!nl_pat)
7158 return -ENOBUFS;
7159 pat_len = rdev->wowlan->patterns[i].pattern_len;
7160 if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK,
7161 DIV_ROUND_UP(pat_len, 8),
7162 rdev->wowlan->patterns[i].mask) ||
7163 nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN,
7164 pat_len, rdev->wowlan->patterns[i].pattern) ||
7165 nla_put_u32(msg, NL80211_WOWLAN_PKTPAT_OFFSET,
7166 rdev->wowlan->patterns[i].pkt_offset))
7167 return -ENOBUFS;
7168 nla_nest_end(msg, nl_pat);
7169 }
7170 nla_nest_end(msg, nl_pats);
7171
7172 return 0;
7173}
7174
Johannes Berg2a0e0472013-01-23 22:57:40 +01007175static int nl80211_send_wowlan_tcp(struct sk_buff *msg,
7176 struct cfg80211_wowlan_tcp *tcp)
7177{
7178 struct nlattr *nl_tcp;
7179
7180 if (!tcp)
7181 return 0;
7182
7183 nl_tcp = nla_nest_start(msg, NL80211_WOWLAN_TRIG_TCP_CONNECTION);
7184 if (!nl_tcp)
7185 return -ENOBUFS;
7186
7187 if (nla_put_be32(msg, NL80211_WOWLAN_TCP_SRC_IPV4, tcp->src) ||
7188 nla_put_be32(msg, NL80211_WOWLAN_TCP_DST_IPV4, tcp->dst) ||
7189 nla_put(msg, NL80211_WOWLAN_TCP_DST_MAC, ETH_ALEN, tcp->dst_mac) ||
7190 nla_put_u16(msg, NL80211_WOWLAN_TCP_SRC_PORT, tcp->src_port) ||
7191 nla_put_u16(msg, NL80211_WOWLAN_TCP_DST_PORT, tcp->dst_port) ||
7192 nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD,
7193 tcp->payload_len, tcp->payload) ||
7194 nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_INTERVAL,
7195 tcp->data_interval) ||
7196 nla_put(msg, NL80211_WOWLAN_TCP_WAKE_PAYLOAD,
7197 tcp->wake_len, tcp->wake_data) ||
7198 nla_put(msg, NL80211_WOWLAN_TCP_WAKE_MASK,
7199 DIV_ROUND_UP(tcp->wake_len, 8), tcp->wake_mask))
7200 return -ENOBUFS;
7201
7202 if (tcp->payload_seq.len &&
7203 nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ,
7204 sizeof(tcp->payload_seq), &tcp->payload_seq))
7205 return -ENOBUFS;
7206
7207 if (tcp->payload_tok.len &&
7208 nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN,
7209 sizeof(tcp->payload_tok) + tcp->tokens_size,
7210 &tcp->payload_tok))
7211 return -ENOBUFS;
7212
7213 return 0;
7214}
7215
Johannes Bergff1b6e62011-05-04 15:37:28 +02007216static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
7217{
7218 struct cfg80211_registered_device *rdev = info->user_ptr[0];
7219 struct sk_buff *msg;
7220 void *hdr;
Johannes Berg2a0e0472013-01-23 22:57:40 +01007221 u32 size = NLMSG_DEFAULT_SIZE;
Johannes Bergff1b6e62011-05-04 15:37:28 +02007222
Johannes Berg2a0e0472013-01-23 22:57:40 +01007223 if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns &&
7224 !rdev->wiphy.wowlan.tcp)
Johannes Bergff1b6e62011-05-04 15:37:28 +02007225 return -EOPNOTSUPP;
7226
Johannes Berg2a0e0472013-01-23 22:57:40 +01007227 if (rdev->wowlan && rdev->wowlan->tcp) {
7228 /* adjust size to have room for all the data */
7229 size += rdev->wowlan->tcp->tokens_size +
7230 rdev->wowlan->tcp->payload_len +
7231 rdev->wowlan->tcp->wake_len +
7232 rdev->wowlan->tcp->wake_len / 8;
7233 }
7234
7235 msg = nlmsg_new(size, GFP_KERNEL);
Johannes Bergff1b6e62011-05-04 15:37:28 +02007236 if (!msg)
7237 return -ENOMEM;
7238
Eric W. Biederman15e47302012-09-07 20:12:54 +00007239 hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
Johannes Bergff1b6e62011-05-04 15:37:28 +02007240 NL80211_CMD_GET_WOWLAN);
7241 if (!hdr)
7242 goto nla_put_failure;
7243
7244 if (rdev->wowlan) {
7245 struct nlattr *nl_wowlan;
7246
7247 nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
7248 if (!nl_wowlan)
7249 goto nla_put_failure;
7250
David S. Miller9360ffd2012-03-29 04:41:26 -04007251 if ((rdev->wowlan->any &&
7252 nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
7253 (rdev->wowlan->disconnect &&
7254 nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
7255 (rdev->wowlan->magic_pkt &&
7256 nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
7257 (rdev->wowlan->gtk_rekey_failure &&
7258 nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
7259 (rdev->wowlan->eap_identity_req &&
7260 nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
7261 (rdev->wowlan->four_way_handshake &&
7262 nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
7263 (rdev->wowlan->rfkill_release &&
7264 nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
7265 goto nla_put_failure;
Johannes Berg2a0e0472013-01-23 22:57:40 +01007266
Amitkumar Karwarbb92d192013-02-12 12:16:26 -08007267 if (nl80211_send_wowlan_patterns(msg, rdev))
7268 goto nla_put_failure;
Johannes Berg2a0e0472013-01-23 22:57:40 +01007269
7270 if (nl80211_send_wowlan_tcp(msg, rdev->wowlan->tcp))
7271 goto nla_put_failure;
7272
Johannes Bergff1b6e62011-05-04 15:37:28 +02007273 nla_nest_end(msg, nl_wowlan);
7274 }
7275
7276 genlmsg_end(msg, hdr);
7277 return genlmsg_reply(msg, info);
7278
7279nla_put_failure:
7280 nlmsg_free(msg);
7281 return -ENOBUFS;
7282}
7283
Johannes Berg2a0e0472013-01-23 22:57:40 +01007284static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
7285 struct nlattr *attr,
7286 struct cfg80211_wowlan *trig)
7287{
7288 struct nlattr *tb[NUM_NL80211_WOWLAN_TCP];
7289 struct cfg80211_wowlan_tcp *cfg;
7290 struct nl80211_wowlan_tcp_data_token *tok = NULL;
7291 struct nl80211_wowlan_tcp_data_seq *seq = NULL;
7292 u32 size;
7293 u32 data_size, wake_size, tokens_size = 0, wake_mask_size;
7294 int err, port;
7295
7296 if (!rdev->wiphy.wowlan.tcp)
7297 return -EINVAL;
7298
7299 err = nla_parse(tb, MAX_NL80211_WOWLAN_TCP,
7300 nla_data(attr), nla_len(attr),
7301 nl80211_wowlan_tcp_policy);
7302 if (err)
7303 return err;
7304
7305 if (!tb[NL80211_WOWLAN_TCP_SRC_IPV4] ||
7306 !tb[NL80211_WOWLAN_TCP_DST_IPV4] ||
7307 !tb[NL80211_WOWLAN_TCP_DST_MAC] ||
7308 !tb[NL80211_WOWLAN_TCP_DST_PORT] ||
7309 !tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD] ||
7310 !tb[NL80211_WOWLAN_TCP_DATA_INTERVAL] ||
7311 !tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD] ||
7312 !tb[NL80211_WOWLAN_TCP_WAKE_MASK])
7313 return -EINVAL;
7314
7315 data_size = nla_len(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]);
7316 if (data_size > rdev->wiphy.wowlan.tcp->data_payload_max)
7317 return -EINVAL;
7318
7319 if (nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) >
7320 rdev->wiphy.wowlan.tcp->data_interval_max)
7321 return -EINVAL;
7322
7323 wake_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]);
7324 if (wake_size > rdev->wiphy.wowlan.tcp->wake_payload_max)
7325 return -EINVAL;
7326
7327 wake_mask_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_MASK]);
7328 if (wake_mask_size != DIV_ROUND_UP(wake_size, 8))
7329 return -EINVAL;
7330
7331 if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]) {
7332 u32 tokln = nla_len(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]);
7333
7334 tok = nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]);
7335 tokens_size = tokln - sizeof(*tok);
7336
7337 if (!tok->len || tokens_size % tok->len)
7338 return -EINVAL;
7339 if (!rdev->wiphy.wowlan.tcp->tok)
7340 return -EINVAL;
7341 if (tok->len > rdev->wiphy.wowlan.tcp->tok->max_len)
7342 return -EINVAL;
7343 if (tok->len < rdev->wiphy.wowlan.tcp->tok->min_len)
7344 return -EINVAL;
7345 if (tokens_size > rdev->wiphy.wowlan.tcp->tok->bufsize)
7346 return -EINVAL;
7347 if (tok->offset + tok->len > data_size)
7348 return -EINVAL;
7349 }
7350
7351 if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]) {
7352 seq = nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]);
7353 if (!rdev->wiphy.wowlan.tcp->seq)
7354 return -EINVAL;
7355 if (seq->len == 0 || seq->len > 4)
7356 return -EINVAL;
7357 if (seq->len + seq->offset > data_size)
7358 return -EINVAL;
7359 }
7360
7361 size = sizeof(*cfg);
7362 size += data_size;
7363 size += wake_size + wake_mask_size;
7364 size += tokens_size;
7365
7366 cfg = kzalloc(size, GFP_KERNEL);
7367 if (!cfg)
7368 return -ENOMEM;
7369 cfg->src = nla_get_be32(tb[NL80211_WOWLAN_TCP_SRC_IPV4]);
7370 cfg->dst = nla_get_be32(tb[NL80211_WOWLAN_TCP_DST_IPV4]);
7371 memcpy(cfg->dst_mac, nla_data(tb[NL80211_WOWLAN_TCP_DST_MAC]),
7372 ETH_ALEN);
7373 if (tb[NL80211_WOWLAN_TCP_SRC_PORT])
7374 port = nla_get_u16(tb[NL80211_WOWLAN_TCP_SRC_PORT]);
7375 else
7376 port = 0;
7377#ifdef CONFIG_INET
7378 /* allocate a socket and port for it and use it */
7379 err = __sock_create(wiphy_net(&rdev->wiphy), PF_INET, SOCK_STREAM,
7380 IPPROTO_TCP, &cfg->sock, 1);
7381 if (err) {
7382 kfree(cfg);
7383 return err;
7384 }
7385 if (inet_csk_get_port(cfg->sock->sk, port)) {
7386 sock_release(cfg->sock);
7387 kfree(cfg);
7388 return -EADDRINUSE;
7389 }
7390 cfg->src_port = inet_sk(cfg->sock->sk)->inet_num;
7391#else
7392 if (!port) {
7393 kfree(cfg);
7394 return -EINVAL;
7395 }
7396 cfg->src_port = port;
7397#endif
7398
7399 cfg->dst_port = nla_get_u16(tb[NL80211_WOWLAN_TCP_DST_PORT]);
7400 cfg->payload_len = data_size;
7401 cfg->payload = (u8 *)cfg + sizeof(*cfg) + tokens_size;
7402 memcpy((void *)cfg->payload,
7403 nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]),
7404 data_size);
7405 if (seq)
7406 cfg->payload_seq = *seq;
7407 cfg->data_interval = nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]);
7408 cfg->wake_len = wake_size;
7409 cfg->wake_data = (u8 *)cfg + sizeof(*cfg) + tokens_size + data_size;
7410 memcpy((void *)cfg->wake_data,
7411 nla_data(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]),
7412 wake_size);
7413 cfg->wake_mask = (u8 *)cfg + sizeof(*cfg) + tokens_size +
7414 data_size + wake_size;
7415 memcpy((void *)cfg->wake_mask,
7416 nla_data(tb[NL80211_WOWLAN_TCP_WAKE_MASK]),
7417 wake_mask_size);
7418 if (tok) {
7419 cfg->tokens_size = tokens_size;
7420 memcpy(&cfg->payload_tok, tok, sizeof(*tok) + tokens_size);
7421 }
7422
7423 trig->tcp = cfg;
7424
7425 return 0;
7426}
7427
Johannes Bergff1b6e62011-05-04 15:37:28 +02007428static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
7429{
7430 struct cfg80211_registered_device *rdev = info->user_ptr[0];
7431 struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG];
Johannes Bergff1b6e62011-05-04 15:37:28 +02007432 struct cfg80211_wowlan new_triggers = {};
Johannes Bergae33bd82012-07-12 16:25:02 +02007433 struct cfg80211_wowlan *ntrig;
Johannes Bergff1b6e62011-05-04 15:37:28 +02007434 struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan;
7435 int err, i;
Johannes Berg6d525632012-04-04 15:05:25 +02007436 bool prev_enabled = rdev->wowlan;
Johannes Bergff1b6e62011-05-04 15:37:28 +02007437
Johannes Berg2a0e0472013-01-23 22:57:40 +01007438 if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns &&
7439 !rdev->wiphy.wowlan.tcp)
Johannes Bergff1b6e62011-05-04 15:37:28 +02007440 return -EOPNOTSUPP;
7441
Johannes Bergae33bd82012-07-12 16:25:02 +02007442 if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) {
7443 cfg80211_rdev_free_wowlan(rdev);
7444 rdev->wowlan = NULL;
7445 goto set_wakeup;
7446 }
Johannes Bergff1b6e62011-05-04 15:37:28 +02007447
7448 err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG,
7449 nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
7450 nla_len(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
7451 nl80211_wowlan_policy);
7452 if (err)
7453 return err;
7454
7455 if (tb[NL80211_WOWLAN_TRIG_ANY]) {
7456 if (!(wowlan->flags & WIPHY_WOWLAN_ANY))
7457 return -EINVAL;
7458 new_triggers.any = true;
7459 }
7460
7461 if (tb[NL80211_WOWLAN_TRIG_DISCONNECT]) {
7462 if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT))
7463 return -EINVAL;
7464 new_triggers.disconnect = true;
7465 }
7466
7467 if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) {
7468 if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT))
7469 return -EINVAL;
7470 new_triggers.magic_pkt = true;
7471 }
7472
Johannes Berg77dbbb12011-07-13 10:48:55 +02007473 if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED])
7474 return -EINVAL;
7475
7476 if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) {
7477 if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE))
7478 return -EINVAL;
7479 new_triggers.gtk_rekey_failure = true;
7480 }
7481
7482 if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) {
7483 if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ))
7484 return -EINVAL;
7485 new_triggers.eap_identity_req = true;
7486 }
7487
7488 if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) {
7489 if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE))
7490 return -EINVAL;
7491 new_triggers.four_way_handshake = true;
7492 }
7493
7494 if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) {
7495 if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE))
7496 return -EINVAL;
7497 new_triggers.rfkill_release = true;
7498 }
7499
Johannes Bergff1b6e62011-05-04 15:37:28 +02007500 if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
7501 struct nlattr *pat;
7502 int n_patterns = 0;
Amitkumar Karwarbb92d192013-02-12 12:16:26 -08007503 int rem, pat_len, mask_len, pkt_offset;
Johannes Bergff1b6e62011-05-04 15:37:28 +02007504 struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT];
7505
7506 nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
7507 rem)
7508 n_patterns++;
7509 if (n_patterns > wowlan->n_patterns)
7510 return -EINVAL;
7511
7512 new_triggers.patterns = kcalloc(n_patterns,
7513 sizeof(new_triggers.patterns[0]),
7514 GFP_KERNEL);
7515 if (!new_triggers.patterns)
7516 return -ENOMEM;
7517
7518 new_triggers.n_patterns = n_patterns;
7519 i = 0;
7520
7521 nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
7522 rem) {
7523 nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT,
7524 nla_data(pat), nla_len(pat), NULL);
7525 err = -EINVAL;
7526 if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] ||
7527 !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN])
7528 goto error;
7529 pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]);
7530 mask_len = DIV_ROUND_UP(pat_len, 8);
7531 if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) !=
7532 mask_len)
7533 goto error;
7534 if (pat_len > wowlan->pattern_max_len ||
7535 pat_len < wowlan->pattern_min_len)
7536 goto error;
7537
Amitkumar Karwarbb92d192013-02-12 12:16:26 -08007538 if (!pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET])
7539 pkt_offset = 0;
7540 else
7541 pkt_offset = nla_get_u32(
7542 pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET]);
7543 if (pkt_offset > wowlan->max_pkt_offset)
7544 goto error;
7545 new_triggers.patterns[i].pkt_offset = pkt_offset;
7546
Johannes Bergff1b6e62011-05-04 15:37:28 +02007547 new_triggers.patterns[i].mask =
7548 kmalloc(mask_len + pat_len, GFP_KERNEL);
7549 if (!new_triggers.patterns[i].mask) {
7550 err = -ENOMEM;
7551 goto error;
7552 }
7553 new_triggers.patterns[i].pattern =
7554 new_triggers.patterns[i].mask + mask_len;
7555 memcpy(new_triggers.patterns[i].mask,
7556 nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]),
7557 mask_len);
7558 new_triggers.patterns[i].pattern_len = pat_len;
7559 memcpy(new_triggers.patterns[i].pattern,
7560 nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]),
7561 pat_len);
7562 i++;
7563 }
7564 }
7565
Johannes Berg2a0e0472013-01-23 22:57:40 +01007566 if (tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION]) {
7567 err = nl80211_parse_wowlan_tcp(
7568 rdev, tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION],
7569 &new_triggers);
7570 if (err)
7571 goto error;
7572 }
7573
Johannes Bergae33bd82012-07-12 16:25:02 +02007574 ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL);
7575 if (!ntrig) {
7576 err = -ENOMEM;
7577 goto error;
Johannes Bergff1b6e62011-05-04 15:37:28 +02007578 }
Johannes Bergae33bd82012-07-12 16:25:02 +02007579 cfg80211_rdev_free_wowlan(rdev);
7580 rdev->wowlan = ntrig;
Johannes Bergff1b6e62011-05-04 15:37:28 +02007581
Johannes Bergae33bd82012-07-12 16:25:02 +02007582 set_wakeup:
Johannes Berg6d525632012-04-04 15:05:25 +02007583 if (rdev->ops->set_wakeup && prev_enabled != !!rdev->wowlan)
Hila Gonene35e4d22012-06-27 17:19:42 +03007584 rdev_set_wakeup(rdev, rdev->wowlan);
Johannes Berg6d525632012-04-04 15:05:25 +02007585
Johannes Bergff1b6e62011-05-04 15:37:28 +02007586 return 0;
7587 error:
7588 for (i = 0; i < new_triggers.n_patterns; i++)
7589 kfree(new_triggers.patterns[i].mask);
7590 kfree(new_triggers.patterns);
Johannes Berg2a0e0472013-01-23 22:57:40 +01007591 if (new_triggers.tcp && new_triggers.tcp->sock)
7592 sock_release(new_triggers.tcp->sock);
7593 kfree(new_triggers.tcp);
Johannes Bergff1b6e62011-05-04 15:37:28 +02007594 return err;
7595}
Johannes Bergdfb89c52012-06-27 09:23:48 +02007596#endif
Johannes Bergff1b6e62011-05-04 15:37:28 +02007597
Johannes Berge5497d72011-07-05 16:35:40 +02007598static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)
7599{
7600 struct cfg80211_registered_device *rdev = info->user_ptr[0];
7601 struct net_device *dev = info->user_ptr[1];
7602 struct wireless_dev *wdev = dev->ieee80211_ptr;
7603 struct nlattr *tb[NUM_NL80211_REKEY_DATA];
7604 struct cfg80211_gtk_rekey_data rekey_data;
7605 int err;
7606
7607 if (!info->attrs[NL80211_ATTR_REKEY_DATA])
7608 return -EINVAL;
7609
7610 err = nla_parse(tb, MAX_NL80211_REKEY_DATA,
7611 nla_data(info->attrs[NL80211_ATTR_REKEY_DATA]),
7612 nla_len(info->attrs[NL80211_ATTR_REKEY_DATA]),
7613 nl80211_rekey_policy);
7614 if (err)
7615 return err;
7616
7617 if (nla_len(tb[NL80211_REKEY_DATA_REPLAY_CTR]) != NL80211_REPLAY_CTR_LEN)
7618 return -ERANGE;
7619 if (nla_len(tb[NL80211_REKEY_DATA_KEK]) != NL80211_KEK_LEN)
7620 return -ERANGE;
7621 if (nla_len(tb[NL80211_REKEY_DATA_KCK]) != NL80211_KCK_LEN)
7622 return -ERANGE;
7623
7624 memcpy(rekey_data.kek, nla_data(tb[NL80211_REKEY_DATA_KEK]),
7625 NL80211_KEK_LEN);
7626 memcpy(rekey_data.kck, nla_data(tb[NL80211_REKEY_DATA_KCK]),
7627 NL80211_KCK_LEN);
7628 memcpy(rekey_data.replay_ctr,
7629 nla_data(tb[NL80211_REKEY_DATA_REPLAY_CTR]),
7630 NL80211_REPLAY_CTR_LEN);
7631
7632 wdev_lock(wdev);
7633 if (!wdev->current_bss) {
7634 err = -ENOTCONN;
7635 goto out;
7636 }
7637
7638 if (!rdev->ops->set_rekey_data) {
7639 err = -EOPNOTSUPP;
7640 goto out;
7641 }
7642
Hila Gonene35e4d22012-06-27 17:19:42 +03007643 err = rdev_set_rekey_data(rdev, dev, &rekey_data);
Johannes Berge5497d72011-07-05 16:35:40 +02007644 out:
7645 wdev_unlock(wdev);
7646 return err;
7647}
7648
Johannes Berg28946da2011-11-04 11:18:12 +01007649static int nl80211_register_unexpected_frame(struct sk_buff *skb,
7650 struct genl_info *info)
7651{
7652 struct net_device *dev = info->user_ptr[1];
7653 struct wireless_dev *wdev = dev->ieee80211_ptr;
7654
7655 if (wdev->iftype != NL80211_IFTYPE_AP &&
7656 wdev->iftype != NL80211_IFTYPE_P2P_GO)
7657 return -EINVAL;
7658
Eric W. Biederman15e47302012-09-07 20:12:54 +00007659 if (wdev->ap_unexpected_nlportid)
Johannes Berg28946da2011-11-04 11:18:12 +01007660 return -EBUSY;
7661
Eric W. Biederman15e47302012-09-07 20:12:54 +00007662 wdev->ap_unexpected_nlportid = info->snd_portid;
Johannes Berg28946da2011-11-04 11:18:12 +01007663 return 0;
7664}
7665
Johannes Berg7f6cf312011-11-04 11:18:15 +01007666static int nl80211_probe_client(struct sk_buff *skb,
7667 struct genl_info *info)
7668{
7669 struct cfg80211_registered_device *rdev = info->user_ptr[0];
7670 struct net_device *dev = info->user_ptr[1];
7671 struct wireless_dev *wdev = dev->ieee80211_ptr;
7672 struct sk_buff *msg;
7673 void *hdr;
7674 const u8 *addr;
7675 u64 cookie;
7676 int err;
7677
7678 if (wdev->iftype != NL80211_IFTYPE_AP &&
7679 wdev->iftype != NL80211_IFTYPE_P2P_GO)
7680 return -EOPNOTSUPP;
7681
7682 if (!info->attrs[NL80211_ATTR_MAC])
7683 return -EINVAL;
7684
7685 if (!rdev->ops->probe_client)
7686 return -EOPNOTSUPP;
7687
7688 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
7689 if (!msg)
7690 return -ENOMEM;
7691
Eric W. Biederman15e47302012-09-07 20:12:54 +00007692 hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
Johannes Berg7f6cf312011-11-04 11:18:15 +01007693 NL80211_CMD_PROBE_CLIENT);
7694
7695 if (IS_ERR(hdr)) {
7696 err = PTR_ERR(hdr);
7697 goto free_msg;
7698 }
7699
7700 addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
7701
Hila Gonene35e4d22012-06-27 17:19:42 +03007702 err = rdev_probe_client(rdev, dev, addr, &cookie);
Johannes Berg7f6cf312011-11-04 11:18:15 +01007703 if (err)
7704 goto free_msg;
7705
David S. Miller9360ffd2012-03-29 04:41:26 -04007706 if (nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie))
7707 goto nla_put_failure;
Johannes Berg7f6cf312011-11-04 11:18:15 +01007708
7709 genlmsg_end(msg, hdr);
7710
7711 return genlmsg_reply(msg, info);
7712
7713 nla_put_failure:
7714 err = -ENOBUFS;
7715 free_msg:
7716 nlmsg_free(msg);
7717 return err;
7718}
7719
Johannes Berg5e7602302011-11-04 11:18:17 +01007720static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info)
7721{
7722 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Ben Greear37c73b52012-10-26 14:49:25 -07007723 struct cfg80211_beacon_registration *reg, *nreg;
7724 int rv;
Johannes Berg5e7602302011-11-04 11:18:17 +01007725
7726 if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS))
7727 return -EOPNOTSUPP;
7728
Ben Greear37c73b52012-10-26 14:49:25 -07007729 nreg = kzalloc(sizeof(*nreg), GFP_KERNEL);
7730 if (!nreg)
7731 return -ENOMEM;
Johannes Berg5e7602302011-11-04 11:18:17 +01007732
Ben Greear37c73b52012-10-26 14:49:25 -07007733 /* First, check if already registered. */
7734 spin_lock_bh(&rdev->beacon_registrations_lock);
7735 list_for_each_entry(reg, &rdev->beacon_registrations, list) {
7736 if (reg->nlportid == info->snd_portid) {
7737 rv = -EALREADY;
7738 goto out_err;
7739 }
7740 }
7741 /* Add it to the list */
7742 nreg->nlportid = info->snd_portid;
7743 list_add(&nreg->list, &rdev->beacon_registrations);
7744
7745 spin_unlock_bh(&rdev->beacon_registrations_lock);
Johannes Berg5e7602302011-11-04 11:18:17 +01007746
7747 return 0;
Ben Greear37c73b52012-10-26 14:49:25 -07007748out_err:
7749 spin_unlock_bh(&rdev->beacon_registrations_lock);
7750 kfree(nreg);
7751 return rv;
Johannes Berg5e7602302011-11-04 11:18:17 +01007752}
7753
Johannes Berg98104fde2012-06-16 00:19:54 +02007754static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info)
7755{
7756 struct cfg80211_registered_device *rdev = info->user_ptr[0];
7757 struct wireless_dev *wdev = info->user_ptr[1];
7758 int err;
7759
7760 if (!rdev->ops->start_p2p_device)
7761 return -EOPNOTSUPP;
7762
7763 if (wdev->iftype != NL80211_IFTYPE_P2P_DEVICE)
7764 return -EOPNOTSUPP;
7765
7766 if (wdev->p2p_started)
7767 return 0;
7768
7769 mutex_lock(&rdev->devlist_mtx);
7770 err = cfg80211_can_add_interface(rdev, wdev->iftype);
7771 mutex_unlock(&rdev->devlist_mtx);
7772 if (err)
7773 return err;
7774
Johannes Bergeeb126e2012-10-23 15:16:50 +02007775 err = rdev_start_p2p_device(rdev, wdev);
Johannes Berg98104fde2012-06-16 00:19:54 +02007776 if (err)
7777 return err;
7778
7779 wdev->p2p_started = true;
7780 mutex_lock(&rdev->devlist_mtx);
7781 rdev->opencount++;
7782 mutex_unlock(&rdev->devlist_mtx);
7783
7784 return 0;
7785}
7786
7787static int nl80211_stop_p2p_device(struct sk_buff *skb, struct genl_info *info)
7788{
7789 struct cfg80211_registered_device *rdev = info->user_ptr[0];
7790 struct wireless_dev *wdev = info->user_ptr[1];
7791
7792 if (wdev->iftype != NL80211_IFTYPE_P2P_DEVICE)
7793 return -EOPNOTSUPP;
7794
7795 if (!rdev->ops->stop_p2p_device)
7796 return -EOPNOTSUPP;
7797
7798 if (!wdev->p2p_started)
7799 return 0;
7800
Johannes Bergeeb126e2012-10-23 15:16:50 +02007801 rdev_stop_p2p_device(rdev, wdev);
Johannes Berg98104fde2012-06-16 00:19:54 +02007802 wdev->p2p_started = false;
7803
7804 mutex_lock(&rdev->devlist_mtx);
7805 rdev->opencount--;
7806 mutex_unlock(&rdev->devlist_mtx);
7807
7808 if (WARN_ON(rdev->scan_req && rdev->scan_req->wdev == wdev)) {
7809 rdev->scan_req->aborted = true;
7810 ___cfg80211_scan_done(rdev, true);
7811 }
7812
7813 return 0;
7814}
7815
Johannes Berg4c476992010-10-04 21:36:35 +02007816#define NL80211_FLAG_NEED_WIPHY 0x01
7817#define NL80211_FLAG_NEED_NETDEV 0x02
7818#define NL80211_FLAG_NEED_RTNL 0x04
Johannes Berg41265712010-10-04 21:14:05 +02007819#define NL80211_FLAG_CHECK_NETDEV_UP 0x08
7820#define NL80211_FLAG_NEED_NETDEV_UP (NL80211_FLAG_NEED_NETDEV |\
7821 NL80211_FLAG_CHECK_NETDEV_UP)
Johannes Berg1bf614e2012-06-15 15:23:36 +02007822#define NL80211_FLAG_NEED_WDEV 0x10
Johannes Berg98104fde2012-06-16 00:19:54 +02007823/* If a netdev is associated, it must be UP, P2P must be started */
Johannes Berg1bf614e2012-06-15 15:23:36 +02007824#define NL80211_FLAG_NEED_WDEV_UP (NL80211_FLAG_NEED_WDEV |\
7825 NL80211_FLAG_CHECK_NETDEV_UP)
Johannes Berg4c476992010-10-04 21:36:35 +02007826
7827static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
7828 struct genl_info *info)
7829{
7830 struct cfg80211_registered_device *rdev;
Johannes Berg89a54e42012-06-15 14:33:17 +02007831 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02007832 struct net_device *dev;
Johannes Berg4c476992010-10-04 21:36:35 +02007833 bool rtnl = ops->internal_flags & NL80211_FLAG_NEED_RTNL;
7834
7835 if (rtnl)
7836 rtnl_lock();
7837
7838 if (ops->internal_flags & NL80211_FLAG_NEED_WIPHY) {
Johannes Berg4f7eff12012-06-15 14:14:22 +02007839 rdev = cfg80211_get_dev_from_info(genl_info_net(info), info);
Johannes Berg4c476992010-10-04 21:36:35 +02007840 if (IS_ERR(rdev)) {
7841 if (rtnl)
7842 rtnl_unlock();
7843 return PTR_ERR(rdev);
7844 }
7845 info->user_ptr[0] = rdev;
Johannes Berg1bf614e2012-06-15 15:23:36 +02007846 } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV ||
7847 ops->internal_flags & NL80211_FLAG_NEED_WDEV) {
Johannes Berg89a54e42012-06-15 14:33:17 +02007848 mutex_lock(&cfg80211_mutex);
7849 wdev = __cfg80211_wdev_from_attrs(genl_info_net(info),
7850 info->attrs);
7851 if (IS_ERR(wdev)) {
7852 mutex_unlock(&cfg80211_mutex);
Johannes Berg4c476992010-10-04 21:36:35 +02007853 if (rtnl)
7854 rtnl_unlock();
Johannes Berg89a54e42012-06-15 14:33:17 +02007855 return PTR_ERR(wdev);
Johannes Berg4c476992010-10-04 21:36:35 +02007856 }
Johannes Berg89a54e42012-06-15 14:33:17 +02007857
Johannes Berg89a54e42012-06-15 14:33:17 +02007858 dev = wdev->netdev;
7859 rdev = wiphy_to_dev(wdev->wiphy);
7860
Johannes Berg1bf614e2012-06-15 15:23:36 +02007861 if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {
7862 if (!dev) {
7863 mutex_unlock(&cfg80211_mutex);
7864 if (rtnl)
7865 rtnl_unlock();
7866 return -EINVAL;
7867 }
7868
7869 info->user_ptr[1] = dev;
7870 } else {
7871 info->user_ptr[1] = wdev;
Johannes Berg41265712010-10-04 21:14:05 +02007872 }
Johannes Berg89a54e42012-06-15 14:33:17 +02007873
Johannes Berg1bf614e2012-06-15 15:23:36 +02007874 if (dev) {
7875 if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP &&
7876 !netif_running(dev)) {
7877 mutex_unlock(&cfg80211_mutex);
7878 if (rtnl)
7879 rtnl_unlock();
7880 return -ENETDOWN;
7881 }
7882
7883 dev_hold(dev);
Johannes Berg98104fde2012-06-16 00:19:54 +02007884 } else if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP) {
7885 if (!wdev->p2p_started) {
7886 mutex_unlock(&cfg80211_mutex);
7887 if (rtnl)
7888 rtnl_unlock();
7889 return -ENETDOWN;
7890 }
Johannes Berg1bf614e2012-06-15 15:23:36 +02007891 }
7892
Johannes Berg89a54e42012-06-15 14:33:17 +02007893 cfg80211_lock_rdev(rdev);
7894
7895 mutex_unlock(&cfg80211_mutex);
7896
Johannes Berg4c476992010-10-04 21:36:35 +02007897 info->user_ptr[0] = rdev;
Johannes Berg4c476992010-10-04 21:36:35 +02007898 }
7899
7900 return 0;
7901}
7902
7903static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
7904 struct genl_info *info)
7905{
7906 if (info->user_ptr[0])
7907 cfg80211_unlock_rdev(info->user_ptr[0]);
Johannes Berg1bf614e2012-06-15 15:23:36 +02007908 if (info->user_ptr[1]) {
7909 if (ops->internal_flags & NL80211_FLAG_NEED_WDEV) {
7910 struct wireless_dev *wdev = info->user_ptr[1];
7911
7912 if (wdev->netdev)
7913 dev_put(wdev->netdev);
7914 } else {
7915 dev_put(info->user_ptr[1]);
7916 }
7917 }
Johannes Berg4c476992010-10-04 21:36:35 +02007918 if (ops->internal_flags & NL80211_FLAG_NEED_RTNL)
7919 rtnl_unlock();
7920}
7921
Johannes Berg55682962007-09-20 13:09:35 -04007922static struct genl_ops nl80211_ops[] = {
7923 {
7924 .cmd = NL80211_CMD_GET_WIPHY,
7925 .doit = nl80211_get_wiphy,
7926 .dumpit = nl80211_dump_wiphy,
7927 .policy = nl80211_policy,
7928 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02007929 .internal_flags = NL80211_FLAG_NEED_WIPHY,
Johannes Berg55682962007-09-20 13:09:35 -04007930 },
7931 {
7932 .cmd = NL80211_CMD_SET_WIPHY,
7933 .doit = nl80211_set_wiphy,
7934 .policy = nl80211_policy,
7935 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02007936 .internal_flags = NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04007937 },
7938 {
7939 .cmd = NL80211_CMD_GET_INTERFACE,
7940 .doit = nl80211_get_interface,
7941 .dumpit = nl80211_dump_interface,
7942 .policy = nl80211_policy,
7943 /* can be retrieved by unprivileged users */
Johannes Berg72fb2ab2012-06-15 17:52:47 +02007944 .internal_flags = NL80211_FLAG_NEED_WDEV,
Johannes Berg55682962007-09-20 13:09:35 -04007945 },
7946 {
7947 .cmd = NL80211_CMD_SET_INTERFACE,
7948 .doit = nl80211_set_interface,
7949 .policy = nl80211_policy,
7950 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02007951 .internal_flags = NL80211_FLAG_NEED_NETDEV |
7952 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04007953 },
7954 {
7955 .cmd = NL80211_CMD_NEW_INTERFACE,
7956 .doit = nl80211_new_interface,
7957 .policy = nl80211_policy,
7958 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02007959 .internal_flags = NL80211_FLAG_NEED_WIPHY |
7960 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04007961 },
7962 {
7963 .cmd = NL80211_CMD_DEL_INTERFACE,
7964 .doit = nl80211_del_interface,
7965 .policy = nl80211_policy,
7966 .flags = GENL_ADMIN_PERM,
Johannes Berg84efbb82012-06-16 00:00:26 +02007967 .internal_flags = NL80211_FLAG_NEED_WDEV |
Johannes Berg4c476992010-10-04 21:36:35 +02007968 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04007969 },
Johannes Berg41ade002007-12-19 02:03:29 +01007970 {
7971 .cmd = NL80211_CMD_GET_KEY,
7972 .doit = nl80211_get_key,
7973 .policy = nl80211_policy,
7974 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02007975 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02007976 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01007977 },
7978 {
7979 .cmd = NL80211_CMD_SET_KEY,
7980 .doit = nl80211_set_key,
7981 .policy = nl80211_policy,
7982 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02007983 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02007984 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01007985 },
7986 {
7987 .cmd = NL80211_CMD_NEW_KEY,
7988 .doit = nl80211_new_key,
7989 .policy = nl80211_policy,
7990 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02007991 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02007992 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01007993 },
7994 {
7995 .cmd = NL80211_CMD_DEL_KEY,
7996 .doit = nl80211_del_key,
7997 .policy = nl80211_policy,
7998 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02007999 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008000 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01008001 },
Johannes Berged1b6cc2007-12-19 02:03:32 +01008002 {
8003 .cmd = NL80211_CMD_SET_BEACON,
8004 .policy = nl80211_policy,
8005 .flags = GENL_ADMIN_PERM,
Johannes Berg88600202012-02-13 15:17:18 +01008006 .doit = nl80211_set_beacon,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02008007 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008008 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01008009 },
8010 {
Johannes Berg88600202012-02-13 15:17:18 +01008011 .cmd = NL80211_CMD_START_AP,
Johannes Berged1b6cc2007-12-19 02:03:32 +01008012 .policy = nl80211_policy,
8013 .flags = GENL_ADMIN_PERM,
Johannes Berg88600202012-02-13 15:17:18 +01008014 .doit = nl80211_start_ap,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02008015 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008016 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01008017 },
8018 {
Johannes Berg88600202012-02-13 15:17:18 +01008019 .cmd = NL80211_CMD_STOP_AP,
Johannes Berged1b6cc2007-12-19 02:03:32 +01008020 .policy = nl80211_policy,
8021 .flags = GENL_ADMIN_PERM,
Johannes Berg88600202012-02-13 15:17:18 +01008022 .doit = nl80211_stop_ap,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02008023 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008024 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01008025 },
Johannes Berg5727ef12007-12-19 02:03:34 +01008026 {
8027 .cmd = NL80211_CMD_GET_STATION,
8028 .doit = nl80211_get_station,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01008029 .dumpit = nl80211_dump_station,
Johannes Berg5727ef12007-12-19 02:03:34 +01008030 .policy = nl80211_policy,
Johannes Berg4c476992010-10-04 21:36:35 +02008031 .internal_flags = NL80211_FLAG_NEED_NETDEV |
8032 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01008033 },
8034 {
8035 .cmd = NL80211_CMD_SET_STATION,
8036 .doit = nl80211_set_station,
8037 .policy = nl80211_policy,
8038 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02008039 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008040 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01008041 },
8042 {
8043 .cmd = NL80211_CMD_NEW_STATION,
8044 .doit = nl80211_new_station,
8045 .policy = nl80211_policy,
8046 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008047 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008048 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01008049 },
8050 {
8051 .cmd = NL80211_CMD_DEL_STATION,
8052 .doit = nl80211_del_station,
8053 .policy = nl80211_policy,
8054 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02008055 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008056 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01008057 },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01008058 {
8059 .cmd = NL80211_CMD_GET_MPATH,
8060 .doit = nl80211_get_mpath,
8061 .dumpit = nl80211_dump_mpath,
8062 .policy = nl80211_policy,
8063 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008064 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008065 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01008066 },
8067 {
8068 .cmd = NL80211_CMD_SET_MPATH,
8069 .doit = nl80211_set_mpath,
8070 .policy = nl80211_policy,
8071 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008072 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008073 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01008074 },
8075 {
8076 .cmd = NL80211_CMD_NEW_MPATH,
8077 .doit = nl80211_new_mpath,
8078 .policy = nl80211_policy,
8079 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008080 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008081 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01008082 },
8083 {
8084 .cmd = NL80211_CMD_DEL_MPATH,
8085 .doit = nl80211_del_mpath,
8086 .policy = nl80211_policy,
8087 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02008088 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008089 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01008090 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +03008091 {
8092 .cmd = NL80211_CMD_SET_BSS,
8093 .doit = nl80211_set_bss,
8094 .policy = nl80211_policy,
8095 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02008096 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008097 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9f1ba902008-08-07 20:07:01 +03008098 },
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07008099 {
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08008100 .cmd = NL80211_CMD_GET_REG,
8101 .doit = nl80211_get_reg,
8102 .policy = nl80211_policy,
8103 /* can be retrieved by unprivileged users */
8104 },
8105 {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07008106 .cmd = NL80211_CMD_SET_REG,
8107 .doit = nl80211_set_reg,
8108 .policy = nl80211_policy,
8109 .flags = GENL_ADMIN_PERM,
8110 },
8111 {
8112 .cmd = NL80211_CMD_REQ_SET_REG,
8113 .doit = nl80211_req_set_reg,
8114 .policy = nl80211_policy,
8115 .flags = GENL_ADMIN_PERM,
8116 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07008117 {
Javier Cardona24bdd9f2010-12-16 17:37:48 -08008118 .cmd = NL80211_CMD_GET_MESH_CONFIG,
8119 .doit = nl80211_get_mesh_config,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07008120 .policy = nl80211_policy,
8121 /* can be retrieved by unprivileged users */
Johannes Berg2b5f8b02012-04-02 10:51:55 +02008122 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008123 NL80211_FLAG_NEED_RTNL,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07008124 },
8125 {
Javier Cardona24bdd9f2010-12-16 17:37:48 -08008126 .cmd = NL80211_CMD_SET_MESH_CONFIG,
8127 .doit = nl80211_update_mesh_config,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07008128 .policy = nl80211_policy,
8129 .flags = GENL_ADMIN_PERM,
Johannes Berg29cbe682010-12-03 09:20:44 +01008130 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008131 NL80211_FLAG_NEED_RTNL,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07008132 },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +02008133 {
Johannes Berg2a519312009-02-10 21:25:55 +01008134 .cmd = NL80211_CMD_TRIGGER_SCAN,
8135 .doit = nl80211_trigger_scan,
8136 .policy = nl80211_policy,
8137 .flags = GENL_ADMIN_PERM,
Johannes Bergfd014282012-06-18 19:17:03 +02008138 .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008139 NL80211_FLAG_NEED_RTNL,
Johannes Berg2a519312009-02-10 21:25:55 +01008140 },
8141 {
8142 .cmd = NL80211_CMD_GET_SCAN,
8143 .policy = nl80211_policy,
8144 .dumpit = nl80211_dump_scan,
8145 },
Jouni Malinen636a5d32009-03-19 13:39:22 +02008146 {
Luciano Coelho807f8a82011-05-11 17:09:35 +03008147 .cmd = NL80211_CMD_START_SCHED_SCAN,
8148 .doit = nl80211_start_sched_scan,
8149 .policy = nl80211_policy,
8150 .flags = GENL_ADMIN_PERM,
8151 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
8152 NL80211_FLAG_NEED_RTNL,
8153 },
8154 {
8155 .cmd = NL80211_CMD_STOP_SCHED_SCAN,
8156 .doit = nl80211_stop_sched_scan,
8157 .policy = nl80211_policy,
8158 .flags = GENL_ADMIN_PERM,
8159 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
8160 NL80211_FLAG_NEED_RTNL,
8161 },
8162 {
Jouni Malinen636a5d32009-03-19 13:39:22 +02008163 .cmd = NL80211_CMD_AUTHENTICATE,
8164 .doit = nl80211_authenticate,
8165 .policy = nl80211_policy,
8166 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008167 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008168 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02008169 },
8170 {
8171 .cmd = NL80211_CMD_ASSOCIATE,
8172 .doit = nl80211_associate,
8173 .policy = nl80211_policy,
8174 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008175 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008176 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02008177 },
8178 {
8179 .cmd = NL80211_CMD_DEAUTHENTICATE,
8180 .doit = nl80211_deauthenticate,
8181 .policy = nl80211_policy,
8182 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008183 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008184 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02008185 },
8186 {
8187 .cmd = NL80211_CMD_DISASSOCIATE,
8188 .doit = nl80211_disassociate,
8189 .policy = nl80211_policy,
8190 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008191 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008192 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02008193 },
Johannes Berg04a773a2009-04-19 21:24:32 +02008194 {
8195 .cmd = NL80211_CMD_JOIN_IBSS,
8196 .doit = nl80211_join_ibss,
8197 .policy = nl80211_policy,
8198 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008199 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008200 NL80211_FLAG_NEED_RTNL,
Johannes Berg04a773a2009-04-19 21:24:32 +02008201 },
8202 {
8203 .cmd = NL80211_CMD_LEAVE_IBSS,
8204 .doit = nl80211_leave_ibss,
8205 .policy = nl80211_policy,
8206 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008207 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008208 NL80211_FLAG_NEED_RTNL,
Johannes Berg04a773a2009-04-19 21:24:32 +02008209 },
Johannes Bergaff89a92009-07-01 21:26:51 +02008210#ifdef CONFIG_NL80211_TESTMODE
8211 {
8212 .cmd = NL80211_CMD_TESTMODE,
8213 .doit = nl80211_testmode_do,
Wey-Yi Guy71063f02011-05-20 09:05:54 -07008214 .dumpit = nl80211_testmode_dump,
Johannes Bergaff89a92009-07-01 21:26:51 +02008215 .policy = nl80211_policy,
8216 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02008217 .internal_flags = NL80211_FLAG_NEED_WIPHY |
8218 NL80211_FLAG_NEED_RTNL,
Johannes Bergaff89a92009-07-01 21:26:51 +02008219 },
8220#endif
Samuel Ortizb23aa672009-07-01 21:26:54 +02008221 {
8222 .cmd = NL80211_CMD_CONNECT,
8223 .doit = nl80211_connect,
8224 .policy = nl80211_policy,
8225 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008226 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008227 NL80211_FLAG_NEED_RTNL,
Samuel Ortizb23aa672009-07-01 21:26:54 +02008228 },
8229 {
8230 .cmd = NL80211_CMD_DISCONNECT,
8231 .doit = nl80211_disconnect,
8232 .policy = nl80211_policy,
8233 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02008234 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008235 NL80211_FLAG_NEED_RTNL,
Samuel Ortizb23aa672009-07-01 21:26:54 +02008236 },
Johannes Berg463d0182009-07-14 00:33:35 +02008237 {
8238 .cmd = NL80211_CMD_SET_WIPHY_NETNS,
8239 .doit = nl80211_wiphy_netns,
8240 .policy = nl80211_policy,
8241 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02008242 .internal_flags = NL80211_FLAG_NEED_WIPHY |
8243 NL80211_FLAG_NEED_RTNL,
Johannes Berg463d0182009-07-14 00:33:35 +02008244 },
Holger Schurig61fa7132009-11-11 12:25:40 +01008245 {
8246 .cmd = NL80211_CMD_GET_SURVEY,
8247 .policy = nl80211_policy,
8248 .dumpit = nl80211_dump_survey,
8249 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +01008250 {
8251 .cmd = NL80211_CMD_SET_PMKSA,
8252 .doit = nl80211_setdel_pmksa,
8253 .policy = nl80211_policy,
8254 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02008255 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008256 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01008257 },
8258 {
8259 .cmd = NL80211_CMD_DEL_PMKSA,
8260 .doit = nl80211_setdel_pmksa,
8261 .policy = nl80211_policy,
8262 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02008263 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008264 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01008265 },
8266 {
8267 .cmd = NL80211_CMD_FLUSH_PMKSA,
8268 .doit = nl80211_flush_pmksa,
8269 .policy = nl80211_policy,
8270 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02008271 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008272 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01008273 },
Jouni Malinen9588bbd2009-12-23 13:15:41 +01008274 {
8275 .cmd = NL80211_CMD_REMAIN_ON_CHANNEL,
8276 .doit = nl80211_remain_on_channel,
8277 .policy = nl80211_policy,
8278 .flags = GENL_ADMIN_PERM,
Johannes Berg71bbc992012-06-15 15:30:18 +02008279 .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008280 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01008281 },
8282 {
8283 .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
8284 .doit = nl80211_cancel_remain_on_channel,
8285 .policy = nl80211_policy,
8286 .flags = GENL_ADMIN_PERM,
Johannes Berg71bbc992012-06-15 15:30:18 +02008287 .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008288 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01008289 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +02008290 {
8291 .cmd = NL80211_CMD_SET_TX_BITRATE_MASK,
8292 .doit = nl80211_set_tx_bitrate_mask,
8293 .policy = nl80211_policy,
8294 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02008295 .internal_flags = NL80211_FLAG_NEED_NETDEV |
8296 NL80211_FLAG_NEED_RTNL,
Jouni Malinen13ae75b2009-12-29 12:59:45 +02008297 },
Jouni Malinen026331c2010-02-15 12:53:10 +02008298 {
Johannes Berg2e161f72010-08-12 15:38:38 +02008299 .cmd = NL80211_CMD_REGISTER_FRAME,
8300 .doit = nl80211_register_mgmt,
Jouni Malinen026331c2010-02-15 12:53:10 +02008301 .policy = nl80211_policy,
8302 .flags = GENL_ADMIN_PERM,
Johannes Berg71bbc992012-06-15 15:30:18 +02008303 .internal_flags = NL80211_FLAG_NEED_WDEV |
Johannes Berg4c476992010-10-04 21:36:35 +02008304 NL80211_FLAG_NEED_RTNL,
Jouni Malinen026331c2010-02-15 12:53:10 +02008305 },
8306 {
Johannes Berg2e161f72010-08-12 15:38:38 +02008307 .cmd = NL80211_CMD_FRAME,
8308 .doit = nl80211_tx_mgmt,
Jouni Malinen026331c2010-02-15 12:53:10 +02008309 .policy = nl80211_policy,
8310 .flags = GENL_ADMIN_PERM,
Johannes Berg71bbc992012-06-15 15:30:18 +02008311 .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02008312 NL80211_FLAG_NEED_RTNL,
Jouni Malinen026331c2010-02-15 12:53:10 +02008313 },
Kalle Valoffb9eb32010-02-17 17:58:10 +02008314 {
Johannes Bergf7ca38d2010-11-25 10:02:29 +01008315 .cmd = NL80211_CMD_FRAME_WAIT_CANCEL,
8316 .doit = nl80211_tx_mgmt_cancel_wait,
8317 .policy = nl80211_policy,
8318 .flags = GENL_ADMIN_PERM,
Johannes Berg71bbc992012-06-15 15:30:18 +02008319 .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
Johannes Bergf7ca38d2010-11-25 10:02:29 +01008320 NL80211_FLAG_NEED_RTNL,
8321 },
8322 {
Kalle Valoffb9eb32010-02-17 17:58:10 +02008323 .cmd = NL80211_CMD_SET_POWER_SAVE,
8324 .doit = nl80211_set_power_save,
8325 .policy = nl80211_policy,
8326 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02008327 .internal_flags = NL80211_FLAG_NEED_NETDEV |
8328 NL80211_FLAG_NEED_RTNL,
Kalle Valoffb9eb32010-02-17 17:58:10 +02008329 },
8330 {
8331 .cmd = NL80211_CMD_GET_POWER_SAVE,
8332 .doit = nl80211_get_power_save,
8333 .policy = nl80211_policy,
8334 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02008335 .internal_flags = NL80211_FLAG_NEED_NETDEV |
8336 NL80211_FLAG_NEED_RTNL,
Kalle Valoffb9eb32010-02-17 17:58:10 +02008337 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02008338 {
8339 .cmd = NL80211_CMD_SET_CQM,
8340 .doit = nl80211_set_cqm,
8341 .policy = nl80211_policy,
8342 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02008343 .internal_flags = NL80211_FLAG_NEED_NETDEV |
8344 NL80211_FLAG_NEED_RTNL,
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02008345 },
Johannes Bergf444de02010-05-05 15:25:02 +02008346 {
8347 .cmd = NL80211_CMD_SET_CHANNEL,
8348 .doit = nl80211_set_channel,
8349 .policy = nl80211_policy,
8350 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02008351 .internal_flags = NL80211_FLAG_NEED_NETDEV |
8352 NL80211_FLAG_NEED_RTNL,
Johannes Bergf444de02010-05-05 15:25:02 +02008353 },
Bill Jordane8347eb2010-10-01 13:54:28 -04008354 {
8355 .cmd = NL80211_CMD_SET_WDS_PEER,
8356 .doit = nl80211_set_wds_peer,
8357 .policy = nl80211_policy,
8358 .flags = GENL_ADMIN_PERM,
Johannes Berg43b19952010-10-07 13:10:30 +02008359 .internal_flags = NL80211_FLAG_NEED_NETDEV |
8360 NL80211_FLAG_NEED_RTNL,
Bill Jordane8347eb2010-10-01 13:54:28 -04008361 },
Johannes Berg29cbe682010-12-03 09:20:44 +01008362 {
8363 .cmd = NL80211_CMD_JOIN_MESH,
8364 .doit = nl80211_join_mesh,
8365 .policy = nl80211_policy,
8366 .flags = GENL_ADMIN_PERM,
8367 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
8368 NL80211_FLAG_NEED_RTNL,
8369 },
8370 {
8371 .cmd = NL80211_CMD_LEAVE_MESH,
8372 .doit = nl80211_leave_mesh,
8373 .policy = nl80211_policy,
8374 .flags = GENL_ADMIN_PERM,
8375 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
8376 NL80211_FLAG_NEED_RTNL,
8377 },
Johannes Bergdfb89c52012-06-27 09:23:48 +02008378#ifdef CONFIG_PM
Johannes Bergff1b6e62011-05-04 15:37:28 +02008379 {
8380 .cmd = NL80211_CMD_GET_WOWLAN,
8381 .doit = nl80211_get_wowlan,
8382 .policy = nl80211_policy,
8383 /* can be retrieved by unprivileged users */
8384 .internal_flags = NL80211_FLAG_NEED_WIPHY |
8385 NL80211_FLAG_NEED_RTNL,
8386 },
8387 {
8388 .cmd = NL80211_CMD_SET_WOWLAN,
8389 .doit = nl80211_set_wowlan,
8390 .policy = nl80211_policy,
8391 .flags = GENL_ADMIN_PERM,
8392 .internal_flags = NL80211_FLAG_NEED_WIPHY |
8393 NL80211_FLAG_NEED_RTNL,
8394 },
Johannes Bergdfb89c52012-06-27 09:23:48 +02008395#endif
Johannes Berge5497d72011-07-05 16:35:40 +02008396 {
8397 .cmd = NL80211_CMD_SET_REKEY_OFFLOAD,
8398 .doit = nl80211_set_rekey_data,
8399 .policy = nl80211_policy,
8400 .flags = GENL_ADMIN_PERM,
8401 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
8402 NL80211_FLAG_NEED_RTNL,
8403 },
Arik Nemtsov109086c2011-09-28 14:12:50 +03008404 {
8405 .cmd = NL80211_CMD_TDLS_MGMT,
8406 .doit = nl80211_tdls_mgmt,
8407 .policy = nl80211_policy,
8408 .flags = GENL_ADMIN_PERM,
8409 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
8410 NL80211_FLAG_NEED_RTNL,
8411 },
8412 {
8413 .cmd = NL80211_CMD_TDLS_OPER,
8414 .doit = nl80211_tdls_oper,
8415 .policy = nl80211_policy,
8416 .flags = GENL_ADMIN_PERM,
8417 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
8418 NL80211_FLAG_NEED_RTNL,
8419 },
Johannes Berg28946da2011-11-04 11:18:12 +01008420 {
8421 .cmd = NL80211_CMD_UNEXPECTED_FRAME,
8422 .doit = nl80211_register_unexpected_frame,
8423 .policy = nl80211_policy,
8424 .flags = GENL_ADMIN_PERM,
8425 .internal_flags = NL80211_FLAG_NEED_NETDEV |
8426 NL80211_FLAG_NEED_RTNL,
8427 },
Johannes Berg7f6cf312011-11-04 11:18:15 +01008428 {
8429 .cmd = NL80211_CMD_PROBE_CLIENT,
8430 .doit = nl80211_probe_client,
8431 .policy = nl80211_policy,
8432 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02008433 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg7f6cf312011-11-04 11:18:15 +01008434 NL80211_FLAG_NEED_RTNL,
8435 },
Johannes Berg5e7602302011-11-04 11:18:17 +01008436 {
8437 .cmd = NL80211_CMD_REGISTER_BEACONS,
8438 .doit = nl80211_register_beacons,
8439 .policy = nl80211_policy,
8440 .flags = GENL_ADMIN_PERM,
8441 .internal_flags = NL80211_FLAG_NEED_WIPHY |
8442 NL80211_FLAG_NEED_RTNL,
8443 },
Simon Wunderlich1d9d9212011-11-18 14:20:43 +01008444 {
8445 .cmd = NL80211_CMD_SET_NOACK_MAP,
8446 .doit = nl80211_set_noack_map,
8447 .policy = nl80211_policy,
8448 .flags = GENL_ADMIN_PERM,
8449 .internal_flags = NL80211_FLAG_NEED_NETDEV |
8450 NL80211_FLAG_NEED_RTNL,
8451 },
Johannes Berg98104fde2012-06-16 00:19:54 +02008452 {
8453 .cmd = NL80211_CMD_START_P2P_DEVICE,
8454 .doit = nl80211_start_p2p_device,
8455 .policy = nl80211_policy,
8456 .flags = GENL_ADMIN_PERM,
8457 .internal_flags = NL80211_FLAG_NEED_WDEV |
8458 NL80211_FLAG_NEED_RTNL,
8459 },
8460 {
8461 .cmd = NL80211_CMD_STOP_P2P_DEVICE,
8462 .doit = nl80211_stop_p2p_device,
8463 .policy = nl80211_policy,
8464 .flags = GENL_ADMIN_PERM,
8465 .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
8466 NL80211_FLAG_NEED_RTNL,
8467 },
Antonio Quartullif4e583c2012-11-02 13:27:48 +01008468 {
8469 .cmd = NL80211_CMD_SET_MCAST_RATE,
8470 .doit = nl80211_set_mcast_rate,
8471 .policy = nl80211_policy,
8472 .flags = GENL_ADMIN_PERM,
8473 .internal_flags = NL80211_FLAG_NEED_NETDEV |
8474 NL80211_FLAG_NEED_RTNL,
8475 },
Vasanthakumar Thiagarajan77765ea2013-01-18 11:18:45 +05308476 {
8477 .cmd = NL80211_CMD_SET_MAC_ACL,
8478 .doit = nl80211_set_mac_acl,
8479 .policy = nl80211_policy,
8480 .flags = GENL_ADMIN_PERM,
8481 .internal_flags = NL80211_FLAG_NEED_NETDEV |
8482 NL80211_FLAG_NEED_RTNL,
8483 },
Simon Wunderlich04f39042013-02-08 18:16:19 +01008484 {
8485 .cmd = NL80211_CMD_RADAR_DETECT,
8486 .doit = nl80211_start_radar_detection,
8487 .policy = nl80211_policy,
8488 .flags = GENL_ADMIN_PERM,
8489 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
8490 NL80211_FLAG_NEED_RTNL,
8491 },
Johannes Berg55682962007-09-20 13:09:35 -04008492};
Jouni Malinen9588bbd2009-12-23 13:15:41 +01008493
Jouni Malinen6039f6d2009-03-19 13:39:21 +02008494static struct genl_multicast_group nl80211_mlme_mcgrp = {
8495 .name = "mlme",
8496};
Johannes Berg55682962007-09-20 13:09:35 -04008497
8498/* multicast groups */
8499static struct genl_multicast_group nl80211_config_mcgrp = {
8500 .name = "config",
8501};
Johannes Berg2a519312009-02-10 21:25:55 +01008502static struct genl_multicast_group nl80211_scan_mcgrp = {
8503 .name = "scan",
8504};
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04008505static struct genl_multicast_group nl80211_regulatory_mcgrp = {
8506 .name = "regulatory",
8507};
Johannes Berg55682962007-09-20 13:09:35 -04008508
8509/* notification functions */
8510
8511void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
8512{
8513 struct sk_buff *msg;
8514
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07008515 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04008516 if (!msg)
8517 return;
8518
8519 if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
8520 nlmsg_free(msg);
8521 return;
8522 }
8523
Johannes Berg463d0182009-07-14 00:33:35 +02008524 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8525 nl80211_config_mcgrp.id, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04008526}
8527
Johannes Berg362a4152009-05-24 16:43:15 +02008528static int nl80211_add_scan_req(struct sk_buff *msg,
8529 struct cfg80211_registered_device *rdev)
8530{
8531 struct cfg80211_scan_request *req = rdev->scan_req;
8532 struct nlattr *nest;
8533 int i;
8534
Johannes Berg667503dd2009-07-07 03:56:11 +02008535 ASSERT_RDEV_LOCK(rdev);
8536
Johannes Berg362a4152009-05-24 16:43:15 +02008537 if (WARN_ON(!req))
8538 return 0;
8539
8540 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
8541 if (!nest)
8542 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -04008543 for (i = 0; i < req->n_ssids; i++) {
8544 if (nla_put(msg, i, req->ssids[i].ssid_len, req->ssids[i].ssid))
8545 goto nla_put_failure;
8546 }
Johannes Berg362a4152009-05-24 16:43:15 +02008547 nla_nest_end(msg, nest);
8548
8549 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
8550 if (!nest)
8551 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -04008552 for (i = 0; i < req->n_channels; i++) {
8553 if (nla_put_u32(msg, i, req->channels[i]->center_freq))
8554 goto nla_put_failure;
8555 }
Johannes Berg362a4152009-05-24 16:43:15 +02008556 nla_nest_end(msg, nest);
8557
David S. Miller9360ffd2012-03-29 04:41:26 -04008558 if (req->ie &&
8559 nla_put(msg, NL80211_ATTR_IE, req->ie_len, req->ie))
8560 goto nla_put_failure;
Johannes Berg362a4152009-05-24 16:43:15 +02008561
Sam Lefflered4737712012-10-11 21:03:31 -07008562 if (req->flags)
8563 nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, req->flags);
8564
Johannes Berg362a4152009-05-24 16:43:15 +02008565 return 0;
8566 nla_put_failure:
8567 return -ENOBUFS;
8568}
8569
Johannes Berga538e2d2009-06-16 19:56:42 +02008570static int nl80211_send_scan_msg(struct sk_buff *msg,
8571 struct cfg80211_registered_device *rdev,
Johannes Bergfd014282012-06-18 19:17:03 +02008572 struct wireless_dev *wdev,
Eric W. Biederman15e47302012-09-07 20:12:54 +00008573 u32 portid, u32 seq, int flags,
Johannes Berga538e2d2009-06-16 19:56:42 +02008574 u32 cmd)
Johannes Berg2a519312009-02-10 21:25:55 +01008575{
8576 void *hdr;
8577
Eric W. Biederman15e47302012-09-07 20:12:54 +00008578 hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
Johannes Berg2a519312009-02-10 21:25:55 +01008579 if (!hdr)
8580 return -1;
8581
David S. Miller9360ffd2012-03-29 04:41:26 -04008582 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
Johannes Bergfd014282012-06-18 19:17:03 +02008583 (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
8584 wdev->netdev->ifindex)) ||
8585 nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)))
David S. Miller9360ffd2012-03-29 04:41:26 -04008586 goto nla_put_failure;
Johannes Berg2a519312009-02-10 21:25:55 +01008587
Johannes Berg362a4152009-05-24 16:43:15 +02008588 /* ignore errors and send incomplete event anyway */
8589 nl80211_add_scan_req(msg, rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01008590
8591 return genlmsg_end(msg, hdr);
8592
8593 nla_put_failure:
8594 genlmsg_cancel(msg, hdr);
8595 return -EMSGSIZE;
8596}
8597
Luciano Coelho807f8a82011-05-11 17:09:35 +03008598static int
8599nl80211_send_sched_scan_msg(struct sk_buff *msg,
8600 struct cfg80211_registered_device *rdev,
8601 struct net_device *netdev,
Eric W. Biederman15e47302012-09-07 20:12:54 +00008602 u32 portid, u32 seq, int flags, u32 cmd)
Luciano Coelho807f8a82011-05-11 17:09:35 +03008603{
8604 void *hdr;
8605
Eric W. Biederman15e47302012-09-07 20:12:54 +00008606 hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
Luciano Coelho807f8a82011-05-11 17:09:35 +03008607 if (!hdr)
8608 return -1;
8609
David S. Miller9360ffd2012-03-29 04:41:26 -04008610 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
8611 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
8612 goto nla_put_failure;
Luciano Coelho807f8a82011-05-11 17:09:35 +03008613
8614 return genlmsg_end(msg, hdr);
8615
8616 nla_put_failure:
8617 genlmsg_cancel(msg, hdr);
8618 return -EMSGSIZE;
8619}
8620
Johannes Berga538e2d2009-06-16 19:56:42 +02008621void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
Johannes Bergfd014282012-06-18 19:17:03 +02008622 struct wireless_dev *wdev)
Johannes Berga538e2d2009-06-16 19:56:42 +02008623{
8624 struct sk_buff *msg;
8625
Thomas Graf58050fc2012-06-28 03:57:45 +00008626 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berga538e2d2009-06-16 19:56:42 +02008627 if (!msg)
8628 return;
8629
Johannes Bergfd014282012-06-18 19:17:03 +02008630 if (nl80211_send_scan_msg(msg, rdev, wdev, 0, 0, 0,
Johannes Berga538e2d2009-06-16 19:56:42 +02008631 NL80211_CMD_TRIGGER_SCAN) < 0) {
8632 nlmsg_free(msg);
8633 return;
8634 }
8635
Johannes Berg463d0182009-07-14 00:33:35 +02008636 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8637 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berga538e2d2009-06-16 19:56:42 +02008638}
8639
Johannes Berg2a519312009-02-10 21:25:55 +01008640void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
Johannes Bergfd014282012-06-18 19:17:03 +02008641 struct wireless_dev *wdev)
Johannes Berg2a519312009-02-10 21:25:55 +01008642{
8643 struct sk_buff *msg;
8644
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07008645 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01008646 if (!msg)
8647 return;
8648
Johannes Bergfd014282012-06-18 19:17:03 +02008649 if (nl80211_send_scan_msg(msg, rdev, wdev, 0, 0, 0,
Johannes Berga538e2d2009-06-16 19:56:42 +02008650 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01008651 nlmsg_free(msg);
8652 return;
8653 }
8654
Johannes Berg463d0182009-07-14 00:33:35 +02008655 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8656 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01008657}
8658
8659void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
Johannes Bergfd014282012-06-18 19:17:03 +02008660 struct wireless_dev *wdev)
Johannes Berg2a519312009-02-10 21:25:55 +01008661{
8662 struct sk_buff *msg;
8663
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07008664 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01008665 if (!msg)
8666 return;
8667
Johannes Bergfd014282012-06-18 19:17:03 +02008668 if (nl80211_send_scan_msg(msg, rdev, wdev, 0, 0, 0,
Johannes Berga538e2d2009-06-16 19:56:42 +02008669 NL80211_CMD_SCAN_ABORTED) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01008670 nlmsg_free(msg);
8671 return;
8672 }
8673
Johannes Berg463d0182009-07-14 00:33:35 +02008674 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8675 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01008676}
8677
Luciano Coelho807f8a82011-05-11 17:09:35 +03008678void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
8679 struct net_device *netdev)
8680{
8681 struct sk_buff *msg;
8682
8683 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
8684 if (!msg)
8685 return;
8686
8687 if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0,
8688 NL80211_CMD_SCHED_SCAN_RESULTS) < 0) {
8689 nlmsg_free(msg);
8690 return;
8691 }
8692
8693 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8694 nl80211_scan_mcgrp.id, GFP_KERNEL);
8695}
8696
8697void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
8698 struct net_device *netdev, u32 cmd)
8699{
8700 struct sk_buff *msg;
8701
Thomas Graf58050fc2012-06-28 03:57:45 +00008702 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luciano Coelho807f8a82011-05-11 17:09:35 +03008703 if (!msg)
8704 return;
8705
8706 if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) {
8707 nlmsg_free(msg);
8708 return;
8709 }
8710
8711 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8712 nl80211_scan_mcgrp.id, GFP_KERNEL);
8713}
8714
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04008715/*
8716 * This can happen on global regulatory changes or device specific settings
8717 * based on custom world regulatory domains.
8718 */
8719void nl80211_send_reg_change_event(struct regulatory_request *request)
8720{
8721 struct sk_buff *msg;
8722 void *hdr;
8723
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07008724 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04008725 if (!msg)
8726 return;
8727
8728 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
8729 if (!hdr) {
8730 nlmsg_free(msg);
8731 return;
8732 }
8733
8734 /* Userspace can always count this one always being set */
David S. Miller9360ffd2012-03-29 04:41:26 -04008735 if (nla_put_u8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator))
8736 goto nla_put_failure;
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04008737
David S. Miller9360ffd2012-03-29 04:41:26 -04008738 if (request->alpha2[0] == '0' && request->alpha2[1] == '0') {
8739 if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE,
8740 NL80211_REGDOM_TYPE_WORLD))
8741 goto nla_put_failure;
8742 } else if (request->alpha2[0] == '9' && request->alpha2[1] == '9') {
8743 if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE,
8744 NL80211_REGDOM_TYPE_CUSTOM_WORLD))
8745 goto nla_put_failure;
8746 } else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
8747 request->intersect) {
8748 if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE,
8749 NL80211_REGDOM_TYPE_INTERSECTION))
8750 goto nla_put_failure;
8751 } else {
8752 if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE,
8753 NL80211_REGDOM_TYPE_COUNTRY) ||
8754 nla_put_string(msg, NL80211_ATTR_REG_ALPHA2,
8755 request->alpha2))
8756 goto nla_put_failure;
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04008757 }
8758
Johannes Bergf4173762012-12-03 18:23:37 +01008759 if (request->wiphy_idx != WIPHY_IDX_INVALID &&
David S. Miller9360ffd2012-03-29 04:41:26 -04008760 nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx))
8761 goto nla_put_failure;
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04008762
Johannes Berg3b7b72e2011-10-22 19:05:51 +02008763 genlmsg_end(msg, hdr);
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04008764
Johannes Bergbc43b282009-07-25 10:54:13 +02008765 rcu_read_lock();
Johannes Berg463d0182009-07-14 00:33:35 +02008766 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
Johannes Bergbc43b282009-07-25 10:54:13 +02008767 GFP_ATOMIC);
8768 rcu_read_unlock();
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04008769
8770 return;
8771
8772nla_put_failure:
8773 genlmsg_cancel(msg, hdr);
8774 nlmsg_free(msg);
8775}
8776
Jouni Malinen6039f6d2009-03-19 13:39:21 +02008777static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
8778 struct net_device *netdev,
8779 const u8 *buf, size_t len,
Johannes Berge6d6e342009-07-01 21:26:47 +02008780 enum nl80211_commands cmd, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02008781{
8782 struct sk_buff *msg;
8783 void *hdr;
8784
Johannes Berge6d6e342009-07-01 21:26:47 +02008785 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02008786 if (!msg)
8787 return;
8788
8789 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
8790 if (!hdr) {
8791 nlmsg_free(msg);
8792 return;
8793 }
8794
David S. Miller9360ffd2012-03-29 04:41:26 -04008795 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
8796 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
8797 nla_put(msg, NL80211_ATTR_FRAME, len, buf))
8798 goto nla_put_failure;
Jouni Malinen6039f6d2009-03-19 13:39:21 +02008799
Johannes Berg3b7b72e2011-10-22 19:05:51 +02008800 genlmsg_end(msg, hdr);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02008801
Johannes Berg463d0182009-07-14 00:33:35 +02008802 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8803 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02008804 return;
8805
8806 nla_put_failure:
8807 genlmsg_cancel(msg, hdr);
8808 nlmsg_free(msg);
8809}
8810
8811void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02008812 struct net_device *netdev, const u8 *buf,
8813 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02008814{
8815 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02008816 NL80211_CMD_AUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02008817}
8818
8819void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
8820 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02008821 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02008822{
Johannes Berge6d6e342009-07-01 21:26:47 +02008823 nl80211_send_mlme_event(rdev, netdev, buf, len,
8824 NL80211_CMD_ASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02008825}
8826
Jouni Malinen53b46b82009-03-27 20:53:56 +02008827void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02008828 struct net_device *netdev, const u8 *buf,
8829 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02008830{
8831 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02008832 NL80211_CMD_DEAUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02008833}
8834
Jouni Malinen53b46b82009-03-27 20:53:56 +02008835void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
8836 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02008837 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02008838{
8839 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02008840 NL80211_CMD_DISASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02008841}
8842
Jouni Malinencf4e5942010-12-16 00:52:40 +02008843void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev,
8844 struct net_device *netdev, const u8 *buf,
8845 size_t len, gfp_t gfp)
8846{
8847 nl80211_send_mlme_event(rdev, netdev, buf, len,
8848 NL80211_CMD_UNPROT_DEAUTHENTICATE, gfp);
8849}
8850
8851void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev,
8852 struct net_device *netdev, const u8 *buf,
8853 size_t len, gfp_t gfp)
8854{
8855 nl80211_send_mlme_event(rdev, netdev, buf, len,
8856 NL80211_CMD_UNPROT_DISASSOCIATE, gfp);
8857}
8858
Luis R. Rodriguez1b06bb42009-05-02 00:34:48 -04008859static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
8860 struct net_device *netdev, int cmd,
Johannes Berge6d6e342009-07-01 21:26:47 +02008861 const u8 *addr, gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03008862{
8863 struct sk_buff *msg;
8864 void *hdr;
8865
Johannes Berge6d6e342009-07-01 21:26:47 +02008866 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03008867 if (!msg)
8868 return;
8869
8870 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
8871 if (!hdr) {
8872 nlmsg_free(msg);
8873 return;
8874 }
8875
David S. Miller9360ffd2012-03-29 04:41:26 -04008876 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
8877 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
8878 nla_put_flag(msg, NL80211_ATTR_TIMED_OUT) ||
8879 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
8880 goto nla_put_failure;
Jouni Malinen1965c852009-04-22 21:38:25 +03008881
Johannes Berg3b7b72e2011-10-22 19:05:51 +02008882 genlmsg_end(msg, hdr);
Jouni Malinen1965c852009-04-22 21:38:25 +03008883
Johannes Berg463d0182009-07-14 00:33:35 +02008884 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8885 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03008886 return;
8887
8888 nla_put_failure:
8889 genlmsg_cancel(msg, hdr);
8890 nlmsg_free(msg);
8891}
8892
8893void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02008894 struct net_device *netdev, const u8 *addr,
8895 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03008896{
8897 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE,
Johannes Berge6d6e342009-07-01 21:26:47 +02008898 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03008899}
8900
8901void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02008902 struct net_device *netdev, const u8 *addr,
8903 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03008904{
Johannes Berge6d6e342009-07-01 21:26:47 +02008905 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE,
8906 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03008907}
8908
Samuel Ortizb23aa672009-07-01 21:26:54 +02008909void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
8910 struct net_device *netdev, const u8 *bssid,
8911 const u8 *req_ie, size_t req_ie_len,
8912 const u8 *resp_ie, size_t resp_ie_len,
8913 u16 status, gfp_t gfp)
8914{
8915 struct sk_buff *msg;
8916 void *hdr;
8917
Thomas Graf58050fc2012-06-28 03:57:45 +00008918 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02008919 if (!msg)
8920 return;
8921
8922 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT);
8923 if (!hdr) {
8924 nlmsg_free(msg);
8925 return;
8926 }
8927
David S. Miller9360ffd2012-03-29 04:41:26 -04008928 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
8929 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
8930 (bssid && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) ||
8931 nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, status) ||
8932 (req_ie &&
8933 nla_put(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie)) ||
8934 (resp_ie &&
8935 nla_put(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie)))
8936 goto nla_put_failure;
Samuel Ortizb23aa672009-07-01 21:26:54 +02008937
Johannes Berg3b7b72e2011-10-22 19:05:51 +02008938 genlmsg_end(msg, hdr);
Samuel Ortizb23aa672009-07-01 21:26:54 +02008939
Johannes Berg463d0182009-07-14 00:33:35 +02008940 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8941 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02008942 return;
8943
8944 nla_put_failure:
8945 genlmsg_cancel(msg, hdr);
8946 nlmsg_free(msg);
8947
8948}
8949
8950void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
8951 struct net_device *netdev, const u8 *bssid,
8952 const u8 *req_ie, size_t req_ie_len,
8953 const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
8954{
8955 struct sk_buff *msg;
8956 void *hdr;
8957
Thomas Graf58050fc2012-06-28 03:57:45 +00008958 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02008959 if (!msg)
8960 return;
8961
8962 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM);
8963 if (!hdr) {
8964 nlmsg_free(msg);
8965 return;
8966 }
8967
David S. Miller9360ffd2012-03-29 04:41:26 -04008968 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
8969 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
8970 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid) ||
8971 (req_ie &&
8972 nla_put(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie)) ||
8973 (resp_ie &&
8974 nla_put(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie)))
8975 goto nla_put_failure;
Samuel Ortizb23aa672009-07-01 21:26:54 +02008976
Johannes Berg3b7b72e2011-10-22 19:05:51 +02008977 genlmsg_end(msg, hdr);
Samuel Ortizb23aa672009-07-01 21:26:54 +02008978
Johannes Berg463d0182009-07-14 00:33:35 +02008979 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8980 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02008981 return;
8982
8983 nla_put_failure:
8984 genlmsg_cancel(msg, hdr);
8985 nlmsg_free(msg);
8986
8987}
8988
8989void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
8990 struct net_device *netdev, u16 reason,
Johannes Berg667503dd2009-07-07 03:56:11 +02008991 const u8 *ie, size_t ie_len, bool from_ap)
Samuel Ortizb23aa672009-07-01 21:26:54 +02008992{
8993 struct sk_buff *msg;
8994 void *hdr;
8995
Thomas Graf58050fc2012-06-28 03:57:45 +00008996 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02008997 if (!msg)
8998 return;
8999
9000 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT);
9001 if (!hdr) {
9002 nlmsg_free(msg);
9003 return;
9004 }
9005
David S. Miller9360ffd2012-03-29 04:41:26 -04009006 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
9007 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
9008 (from_ap && reason &&
9009 nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason)) ||
9010 (from_ap &&
9011 nla_put_flag(msg, NL80211_ATTR_DISCONNECTED_BY_AP)) ||
9012 (ie && nla_put(msg, NL80211_ATTR_IE, ie_len, ie)))
9013 goto nla_put_failure;
Samuel Ortizb23aa672009-07-01 21:26:54 +02009014
Johannes Berg3b7b72e2011-10-22 19:05:51 +02009015 genlmsg_end(msg, hdr);
Samuel Ortizb23aa672009-07-01 21:26:54 +02009016
Johannes Berg463d0182009-07-14 00:33:35 +02009017 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9018 nl80211_mlme_mcgrp.id, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02009019 return;
9020
9021 nla_put_failure:
9022 genlmsg_cancel(msg, hdr);
9023 nlmsg_free(msg);
9024
9025}
9026
Johannes Berg04a773a2009-04-19 21:24:32 +02009027void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
9028 struct net_device *netdev, const u8 *bssid,
9029 gfp_t gfp)
9030{
9031 struct sk_buff *msg;
9032 void *hdr;
9033
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07009034 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02009035 if (!msg)
9036 return;
9037
9038 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS);
9039 if (!hdr) {
9040 nlmsg_free(msg);
9041 return;
9042 }
9043
David S. Miller9360ffd2012-03-29 04:41:26 -04009044 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
9045 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
9046 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid))
9047 goto nla_put_failure;
Johannes Berg04a773a2009-04-19 21:24:32 +02009048
Johannes Berg3b7b72e2011-10-22 19:05:51 +02009049 genlmsg_end(msg, hdr);
Johannes Berg04a773a2009-04-19 21:24:32 +02009050
Johannes Berg463d0182009-07-14 00:33:35 +02009051 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9052 nl80211_mlme_mcgrp.id, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02009053 return;
9054
9055 nla_put_failure:
9056 genlmsg_cancel(msg, hdr);
9057 nlmsg_free(msg);
9058}
9059
Javier Cardonac93b5e72011-04-07 15:08:34 -07009060void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
9061 struct net_device *netdev,
9062 const u8 *macaddr, const u8* ie, u8 ie_len,
9063 gfp_t gfp)
9064{
9065 struct sk_buff *msg;
9066 void *hdr;
9067
9068 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
9069 if (!msg)
9070 return;
9071
9072 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NEW_PEER_CANDIDATE);
9073 if (!hdr) {
9074 nlmsg_free(msg);
9075 return;
9076 }
9077
David S. Miller9360ffd2012-03-29 04:41:26 -04009078 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
9079 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
9080 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, macaddr) ||
9081 (ie_len && ie &&
9082 nla_put(msg, NL80211_ATTR_IE, ie_len , ie)))
9083 goto nla_put_failure;
Javier Cardonac93b5e72011-04-07 15:08:34 -07009084
Johannes Berg3b7b72e2011-10-22 19:05:51 +02009085 genlmsg_end(msg, hdr);
Javier Cardonac93b5e72011-04-07 15:08:34 -07009086
9087 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9088 nl80211_mlme_mcgrp.id, gfp);
9089 return;
9090
9091 nla_put_failure:
9092 genlmsg_cancel(msg, hdr);
9093 nlmsg_free(msg);
9094}
9095
Jouni Malinena3b8b052009-03-27 21:59:49 +02009096void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
9097 struct net_device *netdev, const u8 *addr,
9098 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +02009099 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +02009100{
9101 struct sk_buff *msg;
9102 void *hdr;
9103
Johannes Berge6d6e342009-07-01 21:26:47 +02009104 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02009105 if (!msg)
9106 return;
9107
9108 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
9109 if (!hdr) {
9110 nlmsg_free(msg);
9111 return;
9112 }
9113
David S. Miller9360ffd2012-03-29 04:41:26 -04009114 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
9115 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
9116 (addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) ||
9117 nla_put_u32(msg, NL80211_ATTR_KEY_TYPE, key_type) ||
9118 (key_id != -1 &&
9119 nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_id)) ||
9120 (tsc && nla_put(msg, NL80211_ATTR_KEY_SEQ, 6, tsc)))
9121 goto nla_put_failure;
Jouni Malinena3b8b052009-03-27 21:59:49 +02009122
Johannes Berg3b7b72e2011-10-22 19:05:51 +02009123 genlmsg_end(msg, hdr);
Jouni Malinena3b8b052009-03-27 21:59:49 +02009124
Johannes Berg463d0182009-07-14 00:33:35 +02009125 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9126 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02009127 return;
9128
9129 nla_put_failure:
9130 genlmsg_cancel(msg, hdr);
9131 nlmsg_free(msg);
9132}
9133
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04009134void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
9135 struct ieee80211_channel *channel_before,
9136 struct ieee80211_channel *channel_after)
9137{
9138 struct sk_buff *msg;
9139 void *hdr;
9140 struct nlattr *nl_freq;
9141
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07009142 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04009143 if (!msg)
9144 return;
9145
9146 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
9147 if (!hdr) {
9148 nlmsg_free(msg);
9149 return;
9150 }
9151
9152 /*
9153 * Since we are applying the beacon hint to a wiphy we know its
9154 * wiphy_idx is valid
9155 */
David S. Miller9360ffd2012-03-29 04:41:26 -04009156 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy)))
9157 goto nla_put_failure;
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04009158
9159 /* Before */
9160 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
9161 if (!nl_freq)
9162 goto nla_put_failure;
9163 if (nl80211_msg_put_channel(msg, channel_before))
9164 goto nla_put_failure;
9165 nla_nest_end(msg, nl_freq);
9166
9167 /* After */
9168 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
9169 if (!nl_freq)
9170 goto nla_put_failure;
9171 if (nl80211_msg_put_channel(msg, channel_after))
9172 goto nla_put_failure;
9173 nla_nest_end(msg, nl_freq);
9174
Johannes Berg3b7b72e2011-10-22 19:05:51 +02009175 genlmsg_end(msg, hdr);
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04009176
Johannes Berg463d0182009-07-14 00:33:35 +02009177 rcu_read_lock();
9178 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
9179 GFP_ATOMIC);
9180 rcu_read_unlock();
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04009181
9182 return;
9183
9184nla_put_failure:
9185 genlmsg_cancel(msg, hdr);
9186 nlmsg_free(msg);
9187}
9188
Jouni Malinen9588bbd2009-12-23 13:15:41 +01009189static void nl80211_send_remain_on_chan_event(
9190 int cmd, struct cfg80211_registered_device *rdev,
Johannes Berg71bbc992012-06-15 15:30:18 +02009191 struct wireless_dev *wdev, u64 cookie,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01009192 struct ieee80211_channel *chan,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01009193 unsigned int duration, gfp_t gfp)
9194{
9195 struct sk_buff *msg;
9196 void *hdr;
9197
9198 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
9199 if (!msg)
9200 return;
9201
9202 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
9203 if (!hdr) {
9204 nlmsg_free(msg);
9205 return;
9206 }
9207
David S. Miller9360ffd2012-03-29 04:41:26 -04009208 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
Johannes Berg71bbc992012-06-15 15:30:18 +02009209 (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
9210 wdev->netdev->ifindex)) ||
Johannes Berg00f53352012-07-17 11:53:12 +02009211 nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) ||
David S. Miller9360ffd2012-03-29 04:41:26 -04009212 nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq) ||
Johannes Berg42d97a52012-11-08 18:31:02 +01009213 nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
9214 NL80211_CHAN_NO_HT) ||
David S. Miller9360ffd2012-03-29 04:41:26 -04009215 nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie))
9216 goto nla_put_failure;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01009217
David S. Miller9360ffd2012-03-29 04:41:26 -04009218 if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL &&
9219 nla_put_u32(msg, NL80211_ATTR_DURATION, duration))
9220 goto nla_put_failure;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01009221
Johannes Berg3b7b72e2011-10-22 19:05:51 +02009222 genlmsg_end(msg, hdr);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01009223
9224 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9225 nl80211_mlme_mcgrp.id, gfp);
9226 return;
9227
9228 nla_put_failure:
9229 genlmsg_cancel(msg, hdr);
9230 nlmsg_free(msg);
9231}
9232
9233void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
Johannes Berg71bbc992012-06-15 15:30:18 +02009234 struct wireless_dev *wdev, u64 cookie,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01009235 struct ieee80211_channel *chan,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01009236 unsigned int duration, gfp_t gfp)
9237{
9238 nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
Johannes Berg71bbc992012-06-15 15:30:18 +02009239 rdev, wdev, cookie, chan,
Johannes Berg42d97a52012-11-08 18:31:02 +01009240 duration, gfp);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01009241}
9242
9243void nl80211_send_remain_on_channel_cancel(
Johannes Berg71bbc992012-06-15 15:30:18 +02009244 struct cfg80211_registered_device *rdev,
9245 struct wireless_dev *wdev,
Johannes Berg42d97a52012-11-08 18:31:02 +01009246 u64 cookie, struct ieee80211_channel *chan, gfp_t gfp)
Jouni Malinen9588bbd2009-12-23 13:15:41 +01009247{
9248 nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
Johannes Berg42d97a52012-11-08 18:31:02 +01009249 rdev, wdev, cookie, chan, 0, gfp);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01009250}
9251
Johannes Berg98b62182009-12-23 13:15:44 +01009252void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
9253 struct net_device *dev, const u8 *mac_addr,
9254 struct station_info *sinfo, gfp_t gfp)
9255{
9256 struct sk_buff *msg;
9257
Thomas Graf58050fc2012-06-28 03:57:45 +00009258 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Johannes Berg98b62182009-12-23 13:15:44 +01009259 if (!msg)
9260 return;
9261
John W. Linville66266b32012-03-15 13:25:41 -04009262 if (nl80211_send_station(msg, 0, 0, 0,
9263 rdev, dev, mac_addr, sinfo) < 0) {
Johannes Berg98b62182009-12-23 13:15:44 +01009264 nlmsg_free(msg);
9265 return;
9266 }
9267
9268 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9269 nl80211_mlme_mcgrp.id, gfp);
9270}
9271
Jouni Malinenec15e682011-03-23 15:29:52 +02009272void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
9273 struct net_device *dev, const u8 *mac_addr,
9274 gfp_t gfp)
9275{
9276 struct sk_buff *msg;
9277 void *hdr;
9278
Thomas Graf58050fc2012-06-28 03:57:45 +00009279 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinenec15e682011-03-23 15:29:52 +02009280 if (!msg)
9281 return;
9282
9283 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DEL_STATION);
9284 if (!hdr) {
9285 nlmsg_free(msg);
9286 return;
9287 }
9288
David S. Miller9360ffd2012-03-29 04:41:26 -04009289 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
9290 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr))
9291 goto nla_put_failure;
Jouni Malinenec15e682011-03-23 15:29:52 +02009292
Johannes Berg3b7b72e2011-10-22 19:05:51 +02009293 genlmsg_end(msg, hdr);
Jouni Malinenec15e682011-03-23 15:29:52 +02009294
9295 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9296 nl80211_mlme_mcgrp.id, gfp);
9297 return;
9298
9299 nla_put_failure:
9300 genlmsg_cancel(msg, hdr);
9301 nlmsg_free(msg);
9302}
9303
Pandiyarajan Pitchaimuthued44a952012-09-18 16:50:49 +05309304void nl80211_send_conn_failed_event(struct cfg80211_registered_device *rdev,
9305 struct net_device *dev, const u8 *mac_addr,
9306 enum nl80211_connect_failed_reason reason,
9307 gfp_t gfp)
9308{
9309 struct sk_buff *msg;
9310 void *hdr;
9311
9312 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
9313 if (!msg)
9314 return;
9315
9316 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONN_FAILED);
9317 if (!hdr) {
9318 nlmsg_free(msg);
9319 return;
9320 }
9321
9322 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
9323 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr) ||
9324 nla_put_u32(msg, NL80211_ATTR_CONN_FAILED_REASON, reason))
9325 goto nla_put_failure;
9326
9327 genlmsg_end(msg, hdr);
9328
9329 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9330 nl80211_mlme_mcgrp.id, gfp);
9331 return;
9332
9333 nla_put_failure:
9334 genlmsg_cancel(msg, hdr);
9335 nlmsg_free(msg);
9336}
9337
Johannes Bergb92ab5d2011-11-04 11:18:19 +01009338static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
9339 const u8 *addr, gfp_t gfp)
Johannes Berg28946da2011-11-04 11:18:12 +01009340{
9341 struct wireless_dev *wdev = dev->ieee80211_ptr;
9342 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
9343 struct sk_buff *msg;
9344 void *hdr;
9345 int err;
Eric W. Biederman15e47302012-09-07 20:12:54 +00009346 u32 nlportid = ACCESS_ONCE(wdev->ap_unexpected_nlportid);
Johannes Berg28946da2011-11-04 11:18:12 +01009347
Eric W. Biederman15e47302012-09-07 20:12:54 +00009348 if (!nlportid)
Johannes Berg28946da2011-11-04 11:18:12 +01009349 return false;
9350
9351 msg = nlmsg_new(100, gfp);
9352 if (!msg)
9353 return true;
9354
Johannes Bergb92ab5d2011-11-04 11:18:19 +01009355 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
Johannes Berg28946da2011-11-04 11:18:12 +01009356 if (!hdr) {
9357 nlmsg_free(msg);
9358 return true;
9359 }
9360
David S. Miller9360ffd2012-03-29 04:41:26 -04009361 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
9362 nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
9363 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
9364 goto nla_put_failure;
Johannes Berg28946da2011-11-04 11:18:12 +01009365
9366 err = genlmsg_end(msg, hdr);
9367 if (err < 0) {
9368 nlmsg_free(msg);
9369 return true;
9370 }
9371
Eric W. Biederman15e47302012-09-07 20:12:54 +00009372 genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid);
Johannes Berg28946da2011-11-04 11:18:12 +01009373 return true;
9374
9375 nla_put_failure:
9376 genlmsg_cancel(msg, hdr);
9377 nlmsg_free(msg);
9378 return true;
9379}
9380
Johannes Bergb92ab5d2011-11-04 11:18:19 +01009381bool nl80211_unexpected_frame(struct net_device *dev, const u8 *addr, gfp_t gfp)
9382{
9383 return __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME,
9384 addr, gfp);
9385}
9386
9387bool nl80211_unexpected_4addr_frame(struct net_device *dev,
9388 const u8 *addr, gfp_t gfp)
9389{
9390 return __nl80211_unexpected_frame(dev,
9391 NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
9392 addr, gfp);
9393}
9394
Johannes Berg2e161f72010-08-12 15:38:38 +02009395int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
Eric W. Biederman15e47302012-09-07 20:12:54 +00009396 struct wireless_dev *wdev, u32 nlportid,
Johannes Berg804483e2012-03-05 22:18:41 +01009397 int freq, int sig_dbm,
9398 const u8 *buf, size_t len, gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +02009399{
Johannes Berg71bbc992012-06-15 15:30:18 +02009400 struct net_device *netdev = wdev->netdev;
Jouni Malinen026331c2010-02-15 12:53:10 +02009401 struct sk_buff *msg;
9402 void *hdr;
Jouni Malinen026331c2010-02-15 12:53:10 +02009403
9404 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
9405 if (!msg)
9406 return -ENOMEM;
9407
Johannes Berg2e161f72010-08-12 15:38:38 +02009408 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
Jouni Malinen026331c2010-02-15 12:53:10 +02009409 if (!hdr) {
9410 nlmsg_free(msg);
9411 return -ENOMEM;
9412 }
9413
David S. Miller9360ffd2012-03-29 04:41:26 -04009414 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
Johannes Berg71bbc992012-06-15 15:30:18 +02009415 (netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
9416 netdev->ifindex)) ||
David S. Miller9360ffd2012-03-29 04:41:26 -04009417 nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) ||
9418 (sig_dbm &&
9419 nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) ||
9420 nla_put(msg, NL80211_ATTR_FRAME, len, buf))
9421 goto nla_put_failure;
Jouni Malinen026331c2010-02-15 12:53:10 +02009422
Johannes Berg3b7b72e2011-10-22 19:05:51 +02009423 genlmsg_end(msg, hdr);
Jouni Malinen026331c2010-02-15 12:53:10 +02009424
Eric W. Biederman15e47302012-09-07 20:12:54 +00009425 return genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid);
Jouni Malinen026331c2010-02-15 12:53:10 +02009426
9427 nla_put_failure:
9428 genlmsg_cancel(msg, hdr);
9429 nlmsg_free(msg);
9430 return -ENOBUFS;
9431}
9432
Johannes Berg2e161f72010-08-12 15:38:38 +02009433void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
Johannes Berg71bbc992012-06-15 15:30:18 +02009434 struct wireless_dev *wdev, u64 cookie,
Johannes Berg2e161f72010-08-12 15:38:38 +02009435 const u8 *buf, size_t len, bool ack,
9436 gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +02009437{
Johannes Berg71bbc992012-06-15 15:30:18 +02009438 struct net_device *netdev = wdev->netdev;
Jouni Malinen026331c2010-02-15 12:53:10 +02009439 struct sk_buff *msg;
9440 void *hdr;
9441
9442 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
9443 if (!msg)
9444 return;
9445
Johannes Berg2e161f72010-08-12 15:38:38 +02009446 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME_TX_STATUS);
Jouni Malinen026331c2010-02-15 12:53:10 +02009447 if (!hdr) {
9448 nlmsg_free(msg);
9449 return;
9450 }
9451
David S. Miller9360ffd2012-03-29 04:41:26 -04009452 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
Johannes Berg71bbc992012-06-15 15:30:18 +02009453 (netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
9454 netdev->ifindex)) ||
David S. Miller9360ffd2012-03-29 04:41:26 -04009455 nla_put(msg, NL80211_ATTR_FRAME, len, buf) ||
9456 nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie) ||
9457 (ack && nla_put_flag(msg, NL80211_ATTR_ACK)))
9458 goto nla_put_failure;
Jouni Malinen026331c2010-02-15 12:53:10 +02009459
Johannes Berg3b7b72e2011-10-22 19:05:51 +02009460 genlmsg_end(msg, hdr);
Jouni Malinen026331c2010-02-15 12:53:10 +02009461
9462 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
9463 return;
9464
9465 nla_put_failure:
9466 genlmsg_cancel(msg, hdr);
9467 nlmsg_free(msg);
9468}
9469
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02009470void
9471nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
9472 struct net_device *netdev,
9473 enum nl80211_cqm_rssi_threshold_event rssi_event,
9474 gfp_t gfp)
9475{
9476 struct sk_buff *msg;
9477 struct nlattr *pinfoattr;
9478 void *hdr;
9479
Thomas Graf58050fc2012-06-28 03:57:45 +00009480 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02009481 if (!msg)
9482 return;
9483
9484 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
9485 if (!hdr) {
9486 nlmsg_free(msg);
9487 return;
9488 }
9489
David S. Miller9360ffd2012-03-29 04:41:26 -04009490 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
9491 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
9492 goto nla_put_failure;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02009493
9494 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
9495 if (!pinfoattr)
9496 goto nla_put_failure;
9497
David S. Miller9360ffd2012-03-29 04:41:26 -04009498 if (nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
9499 rssi_event))
9500 goto nla_put_failure;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02009501
9502 nla_nest_end(msg, pinfoattr);
9503
Johannes Berg3b7b72e2011-10-22 19:05:51 +02009504 genlmsg_end(msg, hdr);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02009505
9506 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9507 nl80211_mlme_mcgrp.id, gfp);
9508 return;
9509
9510 nla_put_failure:
9511 genlmsg_cancel(msg, hdr);
9512 nlmsg_free(msg);
9513}
9514
Johannes Berge5497d72011-07-05 16:35:40 +02009515void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
9516 struct net_device *netdev, const u8 *bssid,
9517 const u8 *replay_ctr, gfp_t gfp)
9518{
9519 struct sk_buff *msg;
9520 struct nlattr *rekey_attr;
9521 void *hdr;
9522
Thomas Graf58050fc2012-06-28 03:57:45 +00009523 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Johannes Berge5497d72011-07-05 16:35:40 +02009524 if (!msg)
9525 return;
9526
9527 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_REKEY_OFFLOAD);
9528 if (!hdr) {
9529 nlmsg_free(msg);
9530 return;
9531 }
9532
David S. Miller9360ffd2012-03-29 04:41:26 -04009533 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
9534 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
9535 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid))
9536 goto nla_put_failure;
Johannes Berge5497d72011-07-05 16:35:40 +02009537
9538 rekey_attr = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA);
9539 if (!rekey_attr)
9540 goto nla_put_failure;
9541
David S. Miller9360ffd2012-03-29 04:41:26 -04009542 if (nla_put(msg, NL80211_REKEY_DATA_REPLAY_CTR,
9543 NL80211_REPLAY_CTR_LEN, replay_ctr))
9544 goto nla_put_failure;
Johannes Berge5497d72011-07-05 16:35:40 +02009545
9546 nla_nest_end(msg, rekey_attr);
9547
Johannes Berg3b7b72e2011-10-22 19:05:51 +02009548 genlmsg_end(msg, hdr);
Johannes Berge5497d72011-07-05 16:35:40 +02009549
9550 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9551 nl80211_mlme_mcgrp.id, gfp);
9552 return;
9553
9554 nla_put_failure:
9555 genlmsg_cancel(msg, hdr);
9556 nlmsg_free(msg);
9557}
9558
Jouni Malinenc9df56b2011-09-16 18:56:23 +03009559void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
9560 struct net_device *netdev, int index,
9561 const u8 *bssid, bool preauth, gfp_t gfp)
9562{
9563 struct sk_buff *msg;
9564 struct nlattr *attr;
9565 void *hdr;
9566
Thomas Graf58050fc2012-06-28 03:57:45 +00009567 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinenc9df56b2011-09-16 18:56:23 +03009568 if (!msg)
9569 return;
9570
9571 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PMKSA_CANDIDATE);
9572 if (!hdr) {
9573 nlmsg_free(msg);
9574 return;
9575 }
9576
David S. Miller9360ffd2012-03-29 04:41:26 -04009577 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
9578 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
9579 goto nla_put_failure;
Jouni Malinenc9df56b2011-09-16 18:56:23 +03009580
9581 attr = nla_nest_start(msg, NL80211_ATTR_PMKSA_CANDIDATE);
9582 if (!attr)
9583 goto nla_put_failure;
9584
David S. Miller9360ffd2012-03-29 04:41:26 -04009585 if (nla_put_u32(msg, NL80211_PMKSA_CANDIDATE_INDEX, index) ||
9586 nla_put(msg, NL80211_PMKSA_CANDIDATE_BSSID, ETH_ALEN, bssid) ||
9587 (preauth &&
9588 nla_put_flag(msg, NL80211_PMKSA_CANDIDATE_PREAUTH)))
9589 goto nla_put_failure;
Jouni Malinenc9df56b2011-09-16 18:56:23 +03009590
9591 nla_nest_end(msg, attr);
9592
Johannes Berg3b7b72e2011-10-22 19:05:51 +02009593 genlmsg_end(msg, hdr);
Jouni Malinenc9df56b2011-09-16 18:56:23 +03009594
9595 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9596 nl80211_mlme_mcgrp.id, gfp);
9597 return;
9598
9599 nla_put_failure:
9600 genlmsg_cancel(msg, hdr);
9601 nlmsg_free(msg);
9602}
9603
Thomas Pedersen53145262012-04-06 13:35:47 -07009604void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
Johannes Berg683b6d32012-11-08 21:25:48 +01009605 struct net_device *netdev,
9606 struct cfg80211_chan_def *chandef, gfp_t gfp)
Thomas Pedersen53145262012-04-06 13:35:47 -07009607{
9608 struct sk_buff *msg;
9609 void *hdr;
9610
Thomas Graf58050fc2012-06-28 03:57:45 +00009611 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Thomas Pedersen53145262012-04-06 13:35:47 -07009612 if (!msg)
9613 return;
9614
9615 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CH_SWITCH_NOTIFY);
9616 if (!hdr) {
9617 nlmsg_free(msg);
9618 return;
9619 }
9620
Johannes Berg683b6d32012-11-08 21:25:48 +01009621 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
9622 goto nla_put_failure;
9623
9624 if (nl80211_send_chandef(msg, chandef))
John W. Linville7eab0f62012-04-12 14:25:14 -04009625 goto nla_put_failure;
Thomas Pedersen53145262012-04-06 13:35:47 -07009626
9627 genlmsg_end(msg, hdr);
9628
9629 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9630 nl80211_mlme_mcgrp.id, gfp);
9631 return;
9632
9633 nla_put_failure:
9634 genlmsg_cancel(msg, hdr);
9635 nlmsg_free(msg);
9636}
9637
Johannes Bergc063dbf2010-11-24 08:10:05 +01009638void
Thomas Pedersen84f10702012-07-12 16:17:33 -07009639nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev,
9640 struct net_device *netdev, const u8 *peer,
9641 u32 num_packets, u32 rate, u32 intvl, gfp_t gfp)
9642{
9643 struct sk_buff *msg;
9644 struct nlattr *pinfoattr;
9645 void *hdr;
9646
9647 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
9648 if (!msg)
9649 return;
9650
9651 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
9652 if (!hdr) {
9653 nlmsg_free(msg);
9654 return;
9655 }
9656
9657 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
9658 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
9659 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
9660 goto nla_put_failure;
9661
9662 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
9663 if (!pinfoattr)
9664 goto nla_put_failure;
9665
9666 if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_PKTS, num_packets))
9667 goto nla_put_failure;
9668
9669 if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_RATE, rate))
9670 goto nla_put_failure;
9671
9672 if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_INTVL, intvl))
9673 goto nla_put_failure;
9674
9675 nla_nest_end(msg, pinfoattr);
9676
9677 genlmsg_end(msg, hdr);
9678
9679 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9680 nl80211_mlme_mcgrp.id, gfp);
9681 return;
9682
9683 nla_put_failure:
9684 genlmsg_cancel(msg, hdr);
9685 nlmsg_free(msg);
9686}
9687
9688void
Simon Wunderlich04f39042013-02-08 18:16:19 +01009689nl80211_radar_notify(struct cfg80211_registered_device *rdev,
9690 struct cfg80211_chan_def *chandef,
9691 enum nl80211_radar_event event,
9692 struct net_device *netdev, gfp_t gfp)
9693{
9694 struct sk_buff *msg;
9695 void *hdr;
9696
9697 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
9698 if (!msg)
9699 return;
9700
9701 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_RADAR_DETECT);
9702 if (!hdr) {
9703 nlmsg_free(msg);
9704 return;
9705 }
9706
9707 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx))
9708 goto nla_put_failure;
9709
9710 /* NOP and radar events don't need a netdev parameter */
9711 if (netdev) {
9712 struct wireless_dev *wdev = netdev->ieee80211_ptr;
9713
9714 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
9715 nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)))
9716 goto nla_put_failure;
9717 }
9718
9719 if (nla_put_u32(msg, NL80211_ATTR_RADAR_EVENT, event))
9720 goto nla_put_failure;
9721
9722 if (nl80211_send_chandef(msg, chandef))
9723 goto nla_put_failure;
9724
9725 if (genlmsg_end(msg, hdr) < 0) {
9726 nlmsg_free(msg);
9727 return;
9728 }
9729
9730 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9731 nl80211_mlme_mcgrp.id, gfp);
9732 return;
9733
9734 nla_put_failure:
9735 genlmsg_cancel(msg, hdr);
9736 nlmsg_free(msg);
9737}
9738
9739void
Johannes Bergc063dbf2010-11-24 08:10:05 +01009740nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
9741 struct net_device *netdev, const u8 *peer,
9742 u32 num_packets, gfp_t gfp)
9743{
9744 struct sk_buff *msg;
9745 struct nlattr *pinfoattr;
9746 void *hdr;
9747
Thomas Graf58050fc2012-06-28 03:57:45 +00009748 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Johannes Bergc063dbf2010-11-24 08:10:05 +01009749 if (!msg)
9750 return;
9751
9752 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
9753 if (!hdr) {
9754 nlmsg_free(msg);
9755 return;
9756 }
9757
David S. Miller9360ffd2012-03-29 04:41:26 -04009758 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
9759 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
9760 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
9761 goto nla_put_failure;
Johannes Bergc063dbf2010-11-24 08:10:05 +01009762
9763 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
9764 if (!pinfoattr)
9765 goto nla_put_failure;
9766
David S. Miller9360ffd2012-03-29 04:41:26 -04009767 if (nla_put_u32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets))
9768 goto nla_put_failure;
Johannes Bergc063dbf2010-11-24 08:10:05 +01009769
9770 nla_nest_end(msg, pinfoattr);
9771
Johannes Berg3b7b72e2011-10-22 19:05:51 +02009772 genlmsg_end(msg, hdr);
Johannes Bergc063dbf2010-11-24 08:10:05 +01009773
9774 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9775 nl80211_mlme_mcgrp.id, gfp);
9776 return;
9777
9778 nla_put_failure:
9779 genlmsg_cancel(msg, hdr);
9780 nlmsg_free(msg);
9781}
9782
Johannes Berg7f6cf312011-11-04 11:18:15 +01009783void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
9784 u64 cookie, bool acked, gfp_t gfp)
9785{
9786 struct wireless_dev *wdev = dev->ieee80211_ptr;
9787 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
9788 struct sk_buff *msg;
9789 void *hdr;
9790 int err;
9791
Beni Lev4ee3e062012-08-27 12:49:39 +03009792 trace_cfg80211_probe_status(dev, addr, cookie, acked);
9793
Thomas Graf58050fc2012-06-28 03:57:45 +00009794 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Beni Lev4ee3e062012-08-27 12:49:39 +03009795
Johannes Berg7f6cf312011-11-04 11:18:15 +01009796 if (!msg)
9797 return;
9798
9799 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PROBE_CLIENT);
9800 if (!hdr) {
9801 nlmsg_free(msg);
9802 return;
9803 }
9804
David S. Miller9360ffd2012-03-29 04:41:26 -04009805 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
9806 nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
9807 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
9808 nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie) ||
9809 (acked && nla_put_flag(msg, NL80211_ATTR_ACK)))
9810 goto nla_put_failure;
Johannes Berg7f6cf312011-11-04 11:18:15 +01009811
9812 err = genlmsg_end(msg, hdr);
9813 if (err < 0) {
9814 nlmsg_free(msg);
9815 return;
9816 }
9817
9818 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9819 nl80211_mlme_mcgrp.id, gfp);
9820 return;
9821
9822 nla_put_failure:
9823 genlmsg_cancel(msg, hdr);
9824 nlmsg_free(msg);
9825}
9826EXPORT_SYMBOL(cfg80211_probe_status);
9827
Johannes Berg5e7602302011-11-04 11:18:17 +01009828void cfg80211_report_obss_beacon(struct wiphy *wiphy,
9829 const u8 *frame, size_t len,
Ben Greear37c73b52012-10-26 14:49:25 -07009830 int freq, int sig_dbm)
Johannes Berg5e7602302011-11-04 11:18:17 +01009831{
9832 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
9833 struct sk_buff *msg;
9834 void *hdr;
Ben Greear37c73b52012-10-26 14:49:25 -07009835 struct cfg80211_beacon_registration *reg;
Johannes Berg5e7602302011-11-04 11:18:17 +01009836
Beni Lev4ee3e062012-08-27 12:49:39 +03009837 trace_cfg80211_report_obss_beacon(wiphy, frame, len, freq, sig_dbm);
9838
Ben Greear37c73b52012-10-26 14:49:25 -07009839 spin_lock_bh(&rdev->beacon_registrations_lock);
9840 list_for_each_entry(reg, &rdev->beacon_registrations, list) {
9841 msg = nlmsg_new(len + 100, GFP_ATOMIC);
9842 if (!msg) {
9843 spin_unlock_bh(&rdev->beacon_registrations_lock);
9844 return;
9845 }
Johannes Berg5e7602302011-11-04 11:18:17 +01009846
Ben Greear37c73b52012-10-26 14:49:25 -07009847 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
9848 if (!hdr)
9849 goto nla_put_failure;
Johannes Berg5e7602302011-11-04 11:18:17 +01009850
Ben Greear37c73b52012-10-26 14:49:25 -07009851 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
9852 (freq &&
9853 nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) ||
9854 (sig_dbm &&
9855 nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) ||
9856 nla_put(msg, NL80211_ATTR_FRAME, len, frame))
9857 goto nla_put_failure;
9858
9859 genlmsg_end(msg, hdr);
9860
9861 genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, reg->nlportid);
Johannes Berg5e7602302011-11-04 11:18:17 +01009862 }
Ben Greear37c73b52012-10-26 14:49:25 -07009863 spin_unlock_bh(&rdev->beacon_registrations_lock);
Johannes Berg5e7602302011-11-04 11:18:17 +01009864 return;
9865
9866 nla_put_failure:
Ben Greear37c73b52012-10-26 14:49:25 -07009867 spin_unlock_bh(&rdev->beacon_registrations_lock);
9868 if (hdr)
9869 genlmsg_cancel(msg, hdr);
Johannes Berg5e7602302011-11-04 11:18:17 +01009870 nlmsg_free(msg);
9871}
9872EXPORT_SYMBOL(cfg80211_report_obss_beacon);
9873
Johannes Bergcd8f7cb2013-01-22 12:34:29 +01009874#ifdef CONFIG_PM
9875void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
9876 struct cfg80211_wowlan_wakeup *wakeup,
9877 gfp_t gfp)
9878{
9879 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
9880 struct sk_buff *msg;
9881 void *hdr;
9882 int err, size = 200;
9883
9884 trace_cfg80211_report_wowlan_wakeup(wdev->wiphy, wdev, wakeup);
9885
9886 if (wakeup)
9887 size += wakeup->packet_present_len;
9888
9889 msg = nlmsg_new(size, gfp);
9890 if (!msg)
9891 return;
9892
9893 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_WOWLAN);
9894 if (!hdr)
9895 goto free_msg;
9896
9897 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
9898 nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)))
9899 goto free_msg;
9900
9901 if (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
9902 wdev->netdev->ifindex))
9903 goto free_msg;
9904
9905 if (wakeup) {
9906 struct nlattr *reasons;
9907
9908 reasons = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
9909
9910 if (wakeup->disconnect &&
9911 nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT))
9912 goto free_msg;
9913 if (wakeup->magic_pkt &&
9914 nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT))
9915 goto free_msg;
9916 if (wakeup->gtk_rekey_failure &&
9917 nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE))
9918 goto free_msg;
9919 if (wakeup->eap_identity_req &&
9920 nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST))
9921 goto free_msg;
9922 if (wakeup->four_way_handshake &&
9923 nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE))
9924 goto free_msg;
9925 if (wakeup->rfkill_release &&
9926 nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE))
9927 goto free_msg;
9928
9929 if (wakeup->pattern_idx >= 0 &&
9930 nla_put_u32(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
9931 wakeup->pattern_idx))
9932 goto free_msg;
9933
Johannes Berg2a0e0472013-01-23 22:57:40 +01009934 if (wakeup->tcp_match)
9935 nla_put_flag(msg, NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH);
9936
9937 if (wakeup->tcp_connlost)
9938 nla_put_flag(msg,
9939 NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST);
9940
9941 if (wakeup->tcp_nomoretokens)
9942 nla_put_flag(msg,
9943 NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS);
9944
Johannes Bergcd8f7cb2013-01-22 12:34:29 +01009945 if (wakeup->packet) {
9946 u32 pkt_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211;
9947 u32 len_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN;
9948
9949 if (!wakeup->packet_80211) {
9950 pkt_attr =
9951 NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023;
9952 len_attr =
9953 NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN;
9954 }
9955
9956 if (wakeup->packet_len &&
9957 nla_put_u32(msg, len_attr, wakeup->packet_len))
9958 goto free_msg;
9959
9960 if (nla_put(msg, pkt_attr, wakeup->packet_present_len,
9961 wakeup->packet))
9962 goto free_msg;
9963 }
9964
9965 nla_nest_end(msg, reasons);
9966 }
9967
9968 err = genlmsg_end(msg, hdr);
9969 if (err < 0)
9970 goto free_msg;
9971
9972 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
9973 nl80211_mlme_mcgrp.id, gfp);
9974 return;
9975
9976 free_msg:
9977 nlmsg_free(msg);
9978}
9979EXPORT_SYMBOL(cfg80211_report_wowlan_wakeup);
9980#endif
9981
Jouni Malinen3475b092012-11-16 22:49:57 +02009982void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer,
9983 enum nl80211_tdls_operation oper,
9984 u16 reason_code, gfp_t gfp)
9985{
9986 struct wireless_dev *wdev = dev->ieee80211_ptr;
9987 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
9988 struct sk_buff *msg;
9989 void *hdr;
9990 int err;
9991
9992 trace_cfg80211_tdls_oper_request(wdev->wiphy, dev, peer, oper,
9993 reason_code);
9994
9995 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
9996 if (!msg)
9997 return;
9998
9999 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_TDLS_OPER);
10000 if (!hdr) {
10001 nlmsg_free(msg);
10002 return;
10003 }
10004
10005 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
10006 nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
10007 nla_put_u8(msg, NL80211_ATTR_TDLS_OPERATION, oper) ||
10008 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer) ||
10009 (reason_code > 0 &&
10010 nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code)))
10011 goto nla_put_failure;
10012
10013 err = genlmsg_end(msg, hdr);
10014 if (err < 0) {
10015 nlmsg_free(msg);
10016 return;
10017 }
10018
10019 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
10020 nl80211_mlme_mcgrp.id, gfp);
10021 return;
10022
10023 nla_put_failure:
10024 genlmsg_cancel(msg, hdr);
10025 nlmsg_free(msg);
10026}
10027EXPORT_SYMBOL(cfg80211_tdls_oper_request);
10028
Jouni Malinen026331c2010-02-15 12:53:10 +020010029static int nl80211_netlink_notify(struct notifier_block * nb,
10030 unsigned long state,
10031 void *_notify)
10032{
10033 struct netlink_notify *notify = _notify;
10034 struct cfg80211_registered_device *rdev;
10035 struct wireless_dev *wdev;
Ben Greear37c73b52012-10-26 14:49:25 -070010036 struct cfg80211_beacon_registration *reg, *tmp;
Jouni Malinen026331c2010-02-15 12:53:10 +020010037
10038 if (state != NETLINK_URELEASE)
10039 return NOTIFY_DONE;
10040
10041 rcu_read_lock();
10042
Johannes Berg5e7602302011-11-04 11:18:17 +010010043 list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
Johannes Berg89a54e42012-06-15 14:33:17 +020010044 list_for_each_entry_rcu(wdev, &rdev->wdev_list, list)
Eric W. Biederman15e47302012-09-07 20:12:54 +000010045 cfg80211_mlme_unregister_socket(wdev, notify->portid);
Ben Greear37c73b52012-10-26 14:49:25 -070010046
10047 spin_lock_bh(&rdev->beacon_registrations_lock);
10048 list_for_each_entry_safe(reg, tmp, &rdev->beacon_registrations,
10049 list) {
10050 if (reg->nlportid == notify->portid) {
10051 list_del(&reg->list);
10052 kfree(reg);
10053 break;
10054 }
10055 }
10056 spin_unlock_bh(&rdev->beacon_registrations_lock);
Johannes Berg5e7602302011-11-04 11:18:17 +010010057 }
Jouni Malinen026331c2010-02-15 12:53:10 +020010058
10059 rcu_read_unlock();
10060
10061 return NOTIFY_DONE;
10062}
10063
10064static struct notifier_block nl80211_netlink_notifier = {
10065 .notifier_call = nl80211_netlink_notify,
10066};
10067
Johannes Berg55682962007-09-20 13:09:35 -040010068/* initialisation/exit functions */
10069
10070int nl80211_init(void)
10071{
Michał Mirosław0d63cbb2009-05-21 10:34:06 +000010072 int err;
Johannes Berg55682962007-09-20 13:09:35 -040010073
Michał Mirosław0d63cbb2009-05-21 10:34:06 +000010074 err = genl_register_family_with_ops(&nl80211_fam,
10075 nl80211_ops, ARRAY_SIZE(nl80211_ops));
Johannes Berg55682962007-09-20 13:09:35 -040010076 if (err)
10077 return err;
10078
Johannes Berg55682962007-09-20 13:09:35 -040010079 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
10080 if (err)
10081 goto err_out;
10082
Johannes Berg2a519312009-02-10 21:25:55 +010010083 err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
10084 if (err)
10085 goto err_out;
10086
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -040010087 err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
10088 if (err)
10089 goto err_out;
10090
Jouni Malinen6039f6d2009-03-19 13:39:21 +020010091 err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
10092 if (err)
10093 goto err_out;
10094
Johannes Bergaff89a92009-07-01 21:26:51 +020010095#ifdef CONFIG_NL80211_TESTMODE
10096 err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp);
10097 if (err)
10098 goto err_out;
10099#endif
10100
Jouni Malinen026331c2010-02-15 12:53:10 +020010101 err = netlink_register_notifier(&nl80211_netlink_notifier);
10102 if (err)
10103 goto err_out;
10104
Johannes Berg55682962007-09-20 13:09:35 -040010105 return 0;
10106 err_out:
10107 genl_unregister_family(&nl80211_fam);
10108 return err;
10109}
10110
10111void nl80211_exit(void)
10112{
Jouni Malinen026331c2010-02-15 12:53:10 +020010113 netlink_unregister_notifier(&nl80211_netlink_notifier);
Johannes Berg55682962007-09-20 13:09:35 -040010114 genl_unregister_family(&nl80211_fam);
10115}