blob: a001ea32cb7d076de5be21fbb9c7ffcfdea448c8 [file] [log] [blame]
Johannes Berg55682962007-09-20 13:09:35 -04001/*
2 * This is the new netlink-based wireless configuration interface.
3 *
Johannes Berg08645122009-05-11 13:54:58 +02004 * Copyright 2006-2009 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>
Johannes Berg55682962007-09-20 13:09:35 -040010#include <linux/list.h>
11#include <linux/if_ether.h>
12#include <linux/ieee80211.h>
13#include <linux/nl80211.h>
14#include <linux/rtnetlink.h>
15#include <linux/netlink.h>
Johannes Berg2a519312009-02-10 21:25:55 +010016#include <linux/etherdevice.h>
Johannes Berg463d0182009-07-14 00:33:35 +020017#include <net/net_namespace.h>
Johannes Berg55682962007-09-20 13:09:35 -040018#include <net/genetlink.h>
19#include <net/cfg80211.h>
Johannes Berg463d0182009-07-14 00:33:35 +020020#include <net/sock.h>
Johannes Berg55682962007-09-20 13:09:35 -040021#include "core.h"
22#include "nl80211.h"
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -070023#include "reg.h"
Johannes Berg55682962007-09-20 13:09:35 -040024
25/* the netlink family */
26static struct genl_family nl80211_fam = {
27 .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
28 .name = "nl80211", /* have users key off the name instead */
29 .hdrsize = 0, /* no private header */
30 .version = 1, /* no particular meaning now */
31 .maxattr = NL80211_ATTR_MAX,
Johannes Berg463d0182009-07-14 00:33:35 +020032 .netnsok = true,
Johannes Berg55682962007-09-20 13:09:35 -040033};
34
Johannes Berg79c97e92009-07-07 03:56:12 +020035/* internal helper: get rdev and dev */
Johannes Berg463d0182009-07-14 00:33:35 +020036static int get_rdev_dev_by_info_ifindex(struct genl_info *info,
Johannes Berg79c97e92009-07-07 03:56:12 +020037 struct cfg80211_registered_device **rdev,
Johannes Berg55682962007-09-20 13:09:35 -040038 struct net_device **dev)
39{
Johannes Berg463d0182009-07-14 00:33:35 +020040 struct nlattr **attrs = info->attrs;
Johannes Berg55682962007-09-20 13:09:35 -040041 int ifindex;
42
Johannes Bergbba95fe2008-07-29 13:22:51 +020043 if (!attrs[NL80211_ATTR_IFINDEX])
Johannes Berg55682962007-09-20 13:09:35 -040044 return -EINVAL;
45
Johannes Bergbba95fe2008-07-29 13:22:51 +020046 ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
Johannes Berg463d0182009-07-14 00:33:35 +020047 *dev = dev_get_by_index(genl_info_net(info), ifindex);
Johannes Berg55682962007-09-20 13:09:35 -040048 if (!*dev)
49 return -ENODEV;
50
Johannes Berg463d0182009-07-14 00:33:35 +020051 *rdev = cfg80211_get_dev_from_ifindex(genl_info_net(info), ifindex);
Johannes Berg79c97e92009-07-07 03:56:12 +020052 if (IS_ERR(*rdev)) {
Johannes Berg55682962007-09-20 13:09:35 -040053 dev_put(*dev);
Johannes Berg79c97e92009-07-07 03:56:12 +020054 return PTR_ERR(*rdev);
Johannes Berg55682962007-09-20 13:09:35 -040055 }
56
57 return 0;
58}
59
60/* policy for the attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +000061static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
Johannes Berg55682962007-09-20 13:09:35 -040062 [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
63 [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
David S. Miller079e24e2009-05-26 21:15:00 -070064 .len = 20-1 },
Jouni Malinen31888482008-10-30 16:59:24 +020065 [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
Jouni Malinen72bdcf32008-11-26 16:15:24 +020066 [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
Sujith094d05d2008-12-12 11:57:43 +053067 [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +020068 [NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 },
69 [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
70 [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
71 [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 },
Lukáš Turek81077e82009-12-21 22:50:47 +010072 [NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 },
Johannes Berg55682962007-09-20 13:09:35 -040073
74 [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
75 [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
76 [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
Johannes Berg41ade002007-12-19 02:03:29 +010077
78 [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN },
Johannes Berg3e5d7642009-07-07 14:37:26 +020079 [NL80211_ATTR_PREV_BSSID] = { .type = NLA_BINARY, .len = ETH_ALEN },
Johannes Berg41ade002007-12-19 02:03:29 +010080
Johannes Bergb9454e82009-07-08 13:29:08 +020081 [NL80211_ATTR_KEY] = { .type = NLA_NESTED, },
Johannes Berg41ade002007-12-19 02:03:29 +010082 [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
83 .len = WLAN_MAX_KEY_LEN },
84 [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
85 [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
86 [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
Jouni Malinen9f26a952009-05-15 12:38:32 +030087 [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
Johannes Berged1b6cc2007-12-19 02:03:32 +010088
89 [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
90 [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
91 [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY,
92 .len = IEEE80211_MAX_DATA_LEN },
93 [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY,
94 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg5727ef12007-12-19 02:03:34 +010095 [NL80211_ATTR_STA_AID] = { .type = NLA_U16 },
96 [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED },
97 [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 },
98 [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY,
99 .len = NL80211_MAX_SUPP_RATES },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100100 [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
Johannes Berg5727ef12007-12-19 02:03:34 +0100101 [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
Johannes Berg0a9542e2008-10-15 11:54:04 +0200102 [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100103 [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
104 .len = IEEE80211_MAX_MESH_ID_LEN },
105 [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300106
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -0700107 [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 },
108 [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED },
109
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300110 [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
111 [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
112 [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
Jouni Malinen90c97a02008-10-30 16:59:22 +0200113 [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
114 .len = NL80211_MAX_SUPP_RATES },
Jouni Malinen36aedc92008-08-25 11:58:58 +0300115
colin@cozybit.com93da9cc2008-10-21 12:03:48 -0700116 [NL80211_ATTR_MESH_PARAMS] = { .type = NLA_NESTED },
117
Jouni Malinen36aedc92008-08-25 11:58:58 +0300118 [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY,
119 .len = NL80211_HT_CAPABILITY_LEN },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +0200120
121 [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
122 [NL80211_ATTR_IE] = { .type = NLA_BINARY,
123 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg2a519312009-02-10 21:25:55 +0100124 [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
125 [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
Jouni Malinen636a5d32009-03-19 13:39:22 +0200126
127 [NL80211_ATTR_SSID] = { .type = NLA_BINARY,
128 .len = IEEE80211_MAX_SSID_LEN },
129 [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
130 [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
Johannes Berg04a773a2009-04-19 21:24:32 +0200131 [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
Jouni Malinen1965c852009-04-22 21:38:25 +0300132 [NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG },
Jouni Malinendc6382ce2009-05-06 22:09:37 +0300133 [NL80211_ATTR_USE_MFP] = { .type = NLA_U32 },
Johannes Bergeccb8e82009-05-11 21:57:56 +0300134 [NL80211_ATTR_STA_FLAGS2] = {
135 .len = sizeof(struct nl80211_sta_flag_update),
136 },
Jouni Malinen3f77316c2009-05-11 21:57:57 +0300137 [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG },
Samuel Ortizb23aa672009-07-01 21:26:54 +0200138 [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG },
139 [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 },
140 [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
Johannes Berg463d0182009-07-14 00:33:35 +0200141 [NL80211_ATTR_PID] = { .type = NLA_U32 },
Felix Fietkau8b787642009-11-10 18:53:10 +0100142 [NL80211_ATTR_4ADDR] = { .type = NLA_U8 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100143 [NL80211_ATTR_PMKID] = { .type = NLA_BINARY,
144 .len = WLAN_PMKID_LEN },
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100145 [NL80211_ATTR_DURATION] = { .type = NLA_U32 },
146 [NL80211_ATTR_COOKIE] = { .type = NLA_U64 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +0200147 [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED },
Johannes Berg55682962007-09-20 13:09:35 -0400148};
149
Johannes Bergb9454e82009-07-08 13:29:08 +0200150/* policy for the attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +0000151static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = {
Johannes Bergfffd0932009-07-08 14:22:54 +0200152 [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN },
Johannes Bergb9454e82009-07-08 13:29:08 +0200153 [NL80211_KEY_IDX] = { .type = NLA_U8 },
154 [NL80211_KEY_CIPHER] = { .type = NLA_U32 },
155 [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
156 [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG },
157 [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
158};
159
Holger Schuriga0438972009-11-11 11:30:02 +0100160/* ifidx get helper */
161static int nl80211_get_ifidx(struct netlink_callback *cb)
162{
163 int res;
164
165 res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
166 nl80211_fam.attrbuf, nl80211_fam.maxattr,
167 nl80211_policy);
168 if (res)
169 return res;
170
171 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
172 return -EINVAL;
173
174 res = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
175 if (!res)
176 return -EINVAL;
177 return res;
178}
179
Johannes Bergf4a11bb2009-03-27 12:40:28 +0100180/* IE validation */
181static bool is_valid_ie_attr(const struct nlattr *attr)
182{
183 const u8 *pos;
184 int len;
185
186 if (!attr)
187 return true;
188
189 pos = nla_data(attr);
190 len = nla_len(attr);
191
192 while (len) {
193 u8 elemlen;
194
195 if (len < 2)
196 return false;
197 len -= 2;
198
199 elemlen = pos[1];
200 if (elemlen > len)
201 return false;
202
203 len -= elemlen;
204 pos += 2 + elemlen;
205 }
206
207 return true;
208}
209
Johannes Berg55682962007-09-20 13:09:35 -0400210/* message building helper */
211static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
212 int flags, u8 cmd)
213{
214 /* since there is no private header just add the generic one */
215 return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
216}
217
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400218static int nl80211_msg_put_channel(struct sk_buff *msg,
219 struct ieee80211_channel *chan)
220{
221 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
222 chan->center_freq);
223
224 if (chan->flags & IEEE80211_CHAN_DISABLED)
225 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
226 if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
227 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
228 if (chan->flags & IEEE80211_CHAN_NO_IBSS)
229 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
230 if (chan->flags & IEEE80211_CHAN_RADAR)
231 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
232
233 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
234 DBM_TO_MBM(chan->max_power));
235
236 return 0;
237
238 nla_put_failure:
239 return -ENOBUFS;
240}
241
Johannes Berg55682962007-09-20 13:09:35 -0400242/* netlink command implementations */
243
Johannes Bergb9454e82009-07-08 13:29:08 +0200244struct key_parse {
245 struct key_params p;
246 int idx;
247 bool def, defmgmt;
248};
249
250static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k)
251{
252 struct nlattr *tb[NL80211_KEY_MAX + 1];
253 int err = nla_parse_nested(tb, NL80211_KEY_MAX, key,
254 nl80211_key_policy);
255 if (err)
256 return err;
257
258 k->def = !!tb[NL80211_KEY_DEFAULT];
259 k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT];
260
261 if (tb[NL80211_KEY_IDX])
262 k->idx = nla_get_u8(tb[NL80211_KEY_IDX]);
263
264 if (tb[NL80211_KEY_DATA]) {
265 k->p.key = nla_data(tb[NL80211_KEY_DATA]);
266 k->p.key_len = nla_len(tb[NL80211_KEY_DATA]);
267 }
268
269 if (tb[NL80211_KEY_SEQ]) {
270 k->p.seq = nla_data(tb[NL80211_KEY_SEQ]);
271 k->p.seq_len = nla_len(tb[NL80211_KEY_SEQ]);
272 }
273
274 if (tb[NL80211_KEY_CIPHER])
275 k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]);
276
277 return 0;
278}
279
280static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k)
281{
282 if (info->attrs[NL80211_ATTR_KEY_DATA]) {
283 k->p.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
284 k->p.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
285 }
286
287 if (info->attrs[NL80211_ATTR_KEY_SEQ]) {
288 k->p.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]);
289 k->p.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]);
290 }
291
292 if (info->attrs[NL80211_ATTR_KEY_IDX])
293 k->idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
294
295 if (info->attrs[NL80211_ATTR_KEY_CIPHER])
296 k->p.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
297
298 k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT];
299 k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT];
300
301 return 0;
302}
303
304static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
305{
306 int err;
307
308 memset(k, 0, sizeof(*k));
309 k->idx = -1;
310
311 if (info->attrs[NL80211_ATTR_KEY])
312 err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k);
313 else
314 err = nl80211_parse_key_old(info, k);
315
316 if (err)
317 return err;
318
319 if (k->def && k->defmgmt)
320 return -EINVAL;
321
322 if (k->idx != -1) {
323 if (k->defmgmt) {
324 if (k->idx < 4 || k->idx > 5)
325 return -EINVAL;
326 } else if (k->def) {
327 if (k->idx < 0 || k->idx > 3)
328 return -EINVAL;
329 } else {
330 if (k->idx < 0 || k->idx > 5)
331 return -EINVAL;
332 }
333 }
334
335 return 0;
336}
337
Johannes Bergfffd0932009-07-08 14:22:54 +0200338static struct cfg80211_cached_keys *
339nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
340 struct nlattr *keys)
341{
342 struct key_parse parse;
343 struct nlattr *key;
344 struct cfg80211_cached_keys *result;
345 int rem, err, def = 0;
346
347 result = kzalloc(sizeof(*result), GFP_KERNEL);
348 if (!result)
349 return ERR_PTR(-ENOMEM);
350
351 result->def = -1;
352 result->defmgmt = -1;
353
354 nla_for_each_nested(key, keys, rem) {
355 memset(&parse, 0, sizeof(parse));
356 parse.idx = -1;
357
358 err = nl80211_parse_key_new(key, &parse);
359 if (err)
360 goto error;
361 err = -EINVAL;
362 if (!parse.p.key)
363 goto error;
364 if (parse.idx < 0 || parse.idx > 4)
365 goto error;
366 if (parse.def) {
367 if (def)
368 goto error;
369 def = 1;
370 result->def = parse.idx;
371 } else if (parse.defmgmt)
372 goto error;
373 err = cfg80211_validate_key_settings(rdev, &parse.p,
374 parse.idx, NULL);
375 if (err)
376 goto error;
377 result->params[parse.idx].cipher = parse.p.cipher;
378 result->params[parse.idx].key_len = parse.p.key_len;
379 result->params[parse.idx].key = result->data[parse.idx];
380 memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len);
381 }
382
383 return result;
384 error:
385 kfree(result);
386 return ERR_PTR(err);
387}
388
389static int nl80211_key_allowed(struct wireless_dev *wdev)
390{
391 ASSERT_WDEV_LOCK(wdev);
392
393 if (!netif_running(wdev->netdev))
394 return -ENETDOWN;
395
396 switch (wdev->iftype) {
397 case NL80211_IFTYPE_AP:
398 case NL80211_IFTYPE_AP_VLAN:
399 break;
400 case NL80211_IFTYPE_ADHOC:
401 if (!wdev->current_bss)
402 return -ENOLINK;
403 break;
404 case NL80211_IFTYPE_STATION:
405 if (wdev->sme_state != CFG80211_SME_CONNECTED)
406 return -ENOLINK;
407 break;
408 default:
409 return -EINVAL;
410 }
411
412 return 0;
413}
414
Johannes Berg55682962007-09-20 13:09:35 -0400415static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
416 struct cfg80211_registered_device *dev)
417{
418 void *hdr;
Johannes Bergee688b002008-01-24 19:38:39 +0100419 struct nlattr *nl_bands, *nl_band;
420 struct nlattr *nl_freqs, *nl_freq;
421 struct nlattr *nl_rates, *nl_rate;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700422 struct nlattr *nl_modes;
Johannes Berg8fdc6212009-03-14 09:34:01 +0100423 struct nlattr *nl_cmds;
Johannes Bergee688b002008-01-24 19:38:39 +0100424 enum ieee80211_band band;
425 struct ieee80211_channel *chan;
426 struct ieee80211_rate *rate;
427 int i;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700428 u16 ifmodes = dev->wiphy.interface_modes;
Johannes Berg55682962007-09-20 13:09:35 -0400429
430 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
431 if (!hdr)
432 return -1;
433
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -0500434 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400435 NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200436
Johannes Bergf5ea9122009-08-07 16:17:38 +0200437 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION,
438 cfg80211_rdev_list_generation);
439
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200440 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
441 dev->wiphy.retry_short);
442 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
443 dev->wiphy.retry_long);
444 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
445 dev->wiphy.frag_threshold);
446 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
447 dev->wiphy.rts_threshold);
Lukáš Turek81077e82009-12-21 22:50:47 +0100448 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
449 dev->wiphy.coverage_class);
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200450
Johannes Berg2a519312009-02-10 21:25:55 +0100451 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
452 dev->wiphy.max_scan_ssids);
Johannes Berg18a83652009-03-31 12:12:05 +0200453 NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
454 dev->wiphy.max_scan_ie_len);
Johannes Bergee688b002008-01-24 19:38:39 +0100455
Johannes Berg25e47c182009-04-02 20:14:06 +0200456 NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
457 sizeof(u32) * dev->wiphy.n_cipher_suites,
458 dev->wiphy.cipher_suites);
459
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100460 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
461 dev->wiphy.max_num_pmkids);
462
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700463 nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES);
464 if (!nl_modes)
465 goto nla_put_failure;
466
467 i = 0;
468 while (ifmodes) {
469 if (ifmodes & 1)
470 NLA_PUT_FLAG(msg, i);
471 ifmodes >>= 1;
472 i++;
473 }
474
475 nla_nest_end(msg, nl_modes);
476
Johannes Bergee688b002008-01-24 19:38:39 +0100477 nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
478 if (!nl_bands)
479 goto nla_put_failure;
480
481 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
482 if (!dev->wiphy.bands[band])
483 continue;
484
485 nl_band = nla_nest_start(msg, band);
486 if (!nl_band)
487 goto nla_put_failure;
488
Johannes Bergd51626d2008-10-09 12:20:13 +0200489 /* add HT info */
490 if (dev->wiphy.bands[band]->ht_cap.ht_supported) {
491 NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET,
492 sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
493 &dev->wiphy.bands[band]->ht_cap.mcs);
494 NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA,
495 dev->wiphy.bands[band]->ht_cap.cap);
496 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
497 dev->wiphy.bands[band]->ht_cap.ampdu_factor);
498 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
499 dev->wiphy.bands[band]->ht_cap.ampdu_density);
500 }
501
Johannes Bergee688b002008-01-24 19:38:39 +0100502 /* add frequencies */
503 nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
504 if (!nl_freqs)
505 goto nla_put_failure;
506
507 for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
508 nl_freq = nla_nest_start(msg, i);
509 if (!nl_freq)
510 goto nla_put_failure;
511
512 chan = &dev->wiphy.bands[band]->channels[i];
Johannes Bergee688b002008-01-24 19:38:39 +0100513
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400514 if (nl80211_msg_put_channel(msg, chan))
515 goto nla_put_failure;
Jouni Malinene2f367f262008-11-21 19:01:30 +0200516
Johannes Bergee688b002008-01-24 19:38:39 +0100517 nla_nest_end(msg, nl_freq);
518 }
519
520 nla_nest_end(msg, nl_freqs);
521
522 /* add bitrates */
523 nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
524 if (!nl_rates)
525 goto nla_put_failure;
526
527 for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
528 nl_rate = nla_nest_start(msg, i);
529 if (!nl_rate)
530 goto nla_put_failure;
531
532 rate = &dev->wiphy.bands[band]->bitrates[i];
533 NLA_PUT_U32(msg, NL80211_BITRATE_ATTR_RATE,
534 rate->bitrate);
535 if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
536 NLA_PUT_FLAG(msg,
537 NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE);
538
539 nla_nest_end(msg, nl_rate);
540 }
541
542 nla_nest_end(msg, nl_rates);
543
544 nla_nest_end(msg, nl_band);
545 }
546 nla_nest_end(msg, nl_bands);
547
Johannes Berg8fdc6212009-03-14 09:34:01 +0100548 nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
549 if (!nl_cmds)
550 goto nla_put_failure;
551
552 i = 0;
553#define CMD(op, n) \
554 do { \
555 if (dev->ops->op) { \
556 i++; \
557 NLA_PUT_U32(msg, i, NL80211_CMD_ ## n); \
558 } \
559 } while (0)
560
561 CMD(add_virtual_intf, NEW_INTERFACE);
562 CMD(change_virtual_intf, SET_INTERFACE);
563 CMD(add_key, NEW_KEY);
564 CMD(add_beacon, NEW_BEACON);
565 CMD(add_station, NEW_STATION);
566 CMD(add_mpath, NEW_MPATH);
567 CMD(set_mesh_params, SET_MESH_PARAMS);
568 CMD(change_bss, SET_BSS);
Jouni Malinen636a5d32009-03-19 13:39:22 +0200569 CMD(auth, AUTHENTICATE);
570 CMD(assoc, ASSOCIATE);
571 CMD(deauth, DEAUTHENTICATE);
572 CMD(disassoc, DISASSOCIATE);
Johannes Berg04a773a2009-04-19 21:24:32 +0200573 CMD(join_ibss, JOIN_IBSS);
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100574 CMD(set_pmksa, SET_PMKSA);
575 CMD(del_pmksa, DEL_PMKSA);
576 CMD(flush_pmksa, FLUSH_PMKSA);
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100577 CMD(remain_on_channel, REMAIN_ON_CHANNEL);
Jouni Malinen13ae75b2009-12-29 12:59:45 +0200578 CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
Johannes Berg5be83de2009-11-19 00:56:28 +0100579 if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
Johannes Berg463d0182009-07-14 00:33:35 +0200580 i++;
581 NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS);
582 }
Johannes Berg8fdc6212009-03-14 09:34:01 +0100583
584#undef CMD
Samuel Ortizb23aa672009-07-01 21:26:54 +0200585
Johannes Berg6829c872009-07-02 09:13:27 +0200586 if (dev->ops->connect || dev->ops->auth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +0200587 i++;
588 NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT);
589 }
590
Johannes Berg6829c872009-07-02 09:13:27 +0200591 if (dev->ops->disconnect || dev->ops->deauth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +0200592 i++;
593 NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT);
594 }
595
Johannes Berg8fdc6212009-03-14 09:34:01 +0100596 nla_nest_end(msg, nl_cmds);
597
Johannes Berg55682962007-09-20 13:09:35 -0400598 return genlmsg_end(msg, hdr);
599
600 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700601 genlmsg_cancel(msg, hdr);
602 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400603}
604
605static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
606{
607 int idx = 0;
608 int start = cb->args[0];
609 struct cfg80211_registered_device *dev;
610
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500611 mutex_lock(&cfg80211_mutex);
Johannes Berg79c97e92009-07-07 03:56:12 +0200612 list_for_each_entry(dev, &cfg80211_rdev_list, list) {
Johannes Berg463d0182009-07-14 00:33:35 +0200613 if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
614 continue;
Julius Volzb4637272008-07-08 14:02:19 +0200615 if (++idx <= start)
Johannes Berg55682962007-09-20 13:09:35 -0400616 continue;
617 if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
618 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Julius Volzb4637272008-07-08 14:02:19 +0200619 dev) < 0) {
620 idx--;
Johannes Berg55682962007-09-20 13:09:35 -0400621 break;
Julius Volzb4637272008-07-08 14:02:19 +0200622 }
Johannes Berg55682962007-09-20 13:09:35 -0400623 }
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500624 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400625
626 cb->args[0] = idx;
627
628 return skb->len;
629}
630
631static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
632{
633 struct sk_buff *msg;
634 struct cfg80211_registered_device *dev;
635
636 dev = cfg80211_get_dev_from_info(info);
637 if (IS_ERR(dev))
638 return PTR_ERR(dev);
639
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -0700640 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -0400641 if (!msg)
642 goto out_err;
643
644 if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0)
645 goto out_free;
646
Johannes Berg4d0c8ae2009-07-07 03:56:09 +0200647 cfg80211_unlock_rdev(dev);
Johannes Berg55682962007-09-20 13:09:35 -0400648
Johannes Berg134e6372009-07-10 09:51:34 +0000649 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -0400650
651 out_free:
652 nlmsg_free(msg);
653 out_err:
Johannes Berg4d0c8ae2009-07-07 03:56:09 +0200654 cfg80211_unlock_rdev(dev);
Johannes Berg55682962007-09-20 13:09:35 -0400655 return -ENOBUFS;
656}
657
Jouni Malinen31888482008-10-30 16:59:24 +0200658static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
659 [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
660 [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
661 [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
662 [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
663 [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
664};
665
666static int parse_txq_params(struct nlattr *tb[],
667 struct ieee80211_txq_params *txq_params)
668{
669 if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] ||
670 !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
671 !tb[NL80211_TXQ_ATTR_AIFS])
672 return -EINVAL;
673
674 txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]);
675 txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
676 txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
677 txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
678 txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
679
680 return 0;
681}
682
Johannes Berg55682962007-09-20 13:09:35 -0400683static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
684{
685 struct cfg80211_registered_device *rdev;
Jouni Malinen31888482008-10-30 16:59:24 +0200686 int result = 0, rem_txq_params = 0;
687 struct nlattr *nl_txq_params;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200688 u32 changed;
689 u8 retry_short = 0, retry_long = 0;
690 u32 frag_threshold = 0, rts_threshold = 0;
Lukáš Turek81077e82009-12-21 22:50:47 +0100691 u8 coverage_class = 0;
Johannes Berg55682962007-09-20 13:09:35 -0400692
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100693 rtnl_lock();
Johannes Berg55682962007-09-20 13:09:35 -0400694
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100695 mutex_lock(&cfg80211_mutex);
696
Johannes Berg79c97e92009-07-07 03:56:12 +0200697 rdev = __cfg80211_rdev_from_info(info);
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100698 if (IS_ERR(rdev)) {
Jiri Slaby1f5fc702009-06-20 12:31:11 +0200699 mutex_unlock(&cfg80211_mutex);
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100700 result = PTR_ERR(rdev);
701 goto unlock;
702 }
703
704 mutex_lock(&rdev->mtx);
705
706 if (info->attrs[NL80211_ATTR_WIPHY_NAME])
Jouni Malinen31888482008-10-30 16:59:24 +0200707 result = cfg80211_dev_rename(
708 rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100709
710 mutex_unlock(&cfg80211_mutex);
711
712 if (result)
713 goto bad_res;
Johannes Berg55682962007-09-20 13:09:35 -0400714
Jouni Malinen31888482008-10-30 16:59:24 +0200715 if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
716 struct ieee80211_txq_params txq_params;
717 struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
718
719 if (!rdev->ops->set_txq_params) {
720 result = -EOPNOTSUPP;
721 goto bad_res;
722 }
723
724 nla_for_each_nested(nl_txq_params,
725 info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
726 rem_txq_params) {
727 nla_parse(tb, NL80211_TXQ_ATTR_MAX,
728 nla_data(nl_txq_params),
729 nla_len(nl_txq_params),
730 txq_params_policy);
731 result = parse_txq_params(tb, &txq_params);
732 if (result)
733 goto bad_res;
734
735 result = rdev->ops->set_txq_params(&rdev->wiphy,
736 &txq_params);
737 if (result)
738 goto bad_res;
739 }
740 }
741
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200742 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Sujith094d05d2008-12-12 11:57:43 +0530743 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
Luis R. Rodriguez294196a2009-05-02 00:37:20 -0400744 u32 freq;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200745
Johannes Berg306d6112008-12-08 12:39:04 +0100746 result = -EINVAL;
747
Sujith094d05d2008-12-12 11:57:43 +0530748 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
749 channel_type = nla_get_u32(info->attrs[
750 NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
751 if (channel_type != NL80211_CHAN_NO_HT &&
752 channel_type != NL80211_CHAN_HT20 &&
753 channel_type != NL80211_CHAN_HT40PLUS &&
754 channel_type != NL80211_CHAN_HT40MINUS)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200755 goto bad_res;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200756 }
757
758 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
Johannes Berg306d6112008-12-08 12:39:04 +0100759
Johannes Berg59bbb6f2009-08-07 17:22:35 +0200760 mutex_lock(&rdev->devlist_mtx);
Johannes Berg4b181142009-08-08 11:03:58 +0200761 result = rdev_set_freq(rdev, NULL, freq, channel_type);
Johannes Berg59bbb6f2009-08-07 17:22:35 +0200762 mutex_unlock(&rdev->devlist_mtx);
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200763 if (result)
764 goto bad_res;
765 }
766
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200767 changed = 0;
768
769 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {
770 retry_short = nla_get_u8(
771 info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]);
772 if (retry_short == 0) {
773 result = -EINVAL;
774 goto bad_res;
775 }
776 changed |= WIPHY_PARAM_RETRY_SHORT;
777 }
778
779 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {
780 retry_long = nla_get_u8(
781 info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]);
782 if (retry_long == 0) {
783 result = -EINVAL;
784 goto bad_res;
785 }
786 changed |= WIPHY_PARAM_RETRY_LONG;
787 }
788
789 if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
790 frag_threshold = nla_get_u32(
791 info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
792 if (frag_threshold < 256) {
793 result = -EINVAL;
794 goto bad_res;
795 }
796 if (frag_threshold != (u32) -1) {
797 /*
798 * Fragments (apart from the last one) are required to
799 * have even length. Make the fragmentation code
800 * simpler by stripping LSB should someone try to use
801 * odd threshold value.
802 */
803 frag_threshold &= ~0x1;
804 }
805 changed |= WIPHY_PARAM_FRAG_THRESHOLD;
806 }
807
808 if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
809 rts_threshold = nla_get_u32(
810 info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
811 changed |= WIPHY_PARAM_RTS_THRESHOLD;
812 }
813
Lukáš Turek81077e82009-12-21 22:50:47 +0100814 if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) {
815 coverage_class = nla_get_u8(
816 info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]);
817 changed |= WIPHY_PARAM_COVERAGE_CLASS;
818 }
819
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200820 if (changed) {
821 u8 old_retry_short, old_retry_long;
822 u32 old_frag_threshold, old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +0100823 u8 old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200824
825 if (!rdev->ops->set_wiphy_params) {
826 result = -EOPNOTSUPP;
827 goto bad_res;
828 }
829
830 old_retry_short = rdev->wiphy.retry_short;
831 old_retry_long = rdev->wiphy.retry_long;
832 old_frag_threshold = rdev->wiphy.frag_threshold;
833 old_rts_threshold = rdev->wiphy.rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +0100834 old_coverage_class = rdev->wiphy.coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200835
836 if (changed & WIPHY_PARAM_RETRY_SHORT)
837 rdev->wiphy.retry_short = retry_short;
838 if (changed & WIPHY_PARAM_RETRY_LONG)
839 rdev->wiphy.retry_long = retry_long;
840 if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
841 rdev->wiphy.frag_threshold = frag_threshold;
842 if (changed & WIPHY_PARAM_RTS_THRESHOLD)
843 rdev->wiphy.rts_threshold = rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +0100844 if (changed & WIPHY_PARAM_COVERAGE_CLASS)
845 rdev->wiphy.coverage_class = coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200846
847 result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed);
848 if (result) {
849 rdev->wiphy.retry_short = old_retry_short;
850 rdev->wiphy.retry_long = old_retry_long;
851 rdev->wiphy.frag_threshold = old_frag_threshold;
852 rdev->wiphy.rts_threshold = old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +0100853 rdev->wiphy.coverage_class = old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200854 }
855 }
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200856
Johannes Berg306d6112008-12-08 12:39:04 +0100857 bad_res:
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100858 mutex_unlock(&rdev->mtx);
859 unlock:
860 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400861 return result;
862}
863
864
865static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
Johannes Bergd7264052009-04-19 16:23:20 +0200866 struct cfg80211_registered_device *rdev,
Johannes Berg55682962007-09-20 13:09:35 -0400867 struct net_device *dev)
868{
869 void *hdr;
870
871 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
872 if (!hdr)
873 return -1;
874
875 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
Johannes Bergd7264052009-04-19 16:23:20 +0200876 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400877 NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
Johannes Berg60719ff2008-09-16 14:55:09 +0200878 NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype);
Johannes Bergf5ea9122009-08-07 16:17:38 +0200879
880 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION,
881 rdev->devlist_generation ^
882 (cfg80211_rdev_list_generation << 2));
883
Johannes Berg55682962007-09-20 13:09:35 -0400884 return genlmsg_end(msg, hdr);
885
886 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700887 genlmsg_cancel(msg, hdr);
888 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400889}
890
891static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
892{
893 int wp_idx = 0;
894 int if_idx = 0;
895 int wp_start = cb->args[0];
896 int if_start = cb->args[1];
Johannes Bergf5ea9122009-08-07 16:17:38 +0200897 struct cfg80211_registered_device *rdev;
Johannes Berg55682962007-09-20 13:09:35 -0400898 struct wireless_dev *wdev;
899
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500900 mutex_lock(&cfg80211_mutex);
Johannes Bergf5ea9122009-08-07 16:17:38 +0200901 list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
902 if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
Johannes Berg463d0182009-07-14 00:33:35 +0200903 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200904 if (wp_idx < wp_start) {
905 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400906 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200907 }
Johannes Berg55682962007-09-20 13:09:35 -0400908 if_idx = 0;
909
Johannes Bergf5ea9122009-08-07 16:17:38 +0200910 mutex_lock(&rdev->devlist_mtx);
911 list_for_each_entry(wdev, &rdev->netdev_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +0200912 if (if_idx < if_start) {
913 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400914 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200915 }
Johannes Berg55682962007-09-20 13:09:35 -0400916 if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
917 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Bergf5ea9122009-08-07 16:17:38 +0200918 rdev, wdev->netdev) < 0) {
919 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +0200920 goto out;
921 }
922 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400923 }
Johannes Bergf5ea9122009-08-07 16:17:38 +0200924 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +0200925
926 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400927 }
Johannes Bergbba95fe2008-07-29 13:22:51 +0200928 out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500929 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400930
931 cb->args[0] = wp_idx;
932 cb->args[1] = if_idx;
933
934 return skb->len;
935}
936
937static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
938{
939 struct sk_buff *msg;
940 struct cfg80211_registered_device *dev;
941 struct net_device *netdev;
942 int err;
943
Johannes Berg463d0182009-07-14 00:33:35 +0200944 err = get_rdev_dev_by_info_ifindex(info, &dev, &netdev);
Johannes Berg55682962007-09-20 13:09:35 -0400945 if (err)
946 return err;
947
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -0700948 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -0400949 if (!msg)
950 goto out_err;
951
Johannes Bergd7264052009-04-19 16:23:20 +0200952 if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0,
953 dev, netdev) < 0)
Johannes Berg55682962007-09-20 13:09:35 -0400954 goto out_free;
955
956 dev_put(netdev);
Johannes Berg4d0c8ae2009-07-07 03:56:09 +0200957 cfg80211_unlock_rdev(dev);
Johannes Berg55682962007-09-20 13:09:35 -0400958
Johannes Berg134e6372009-07-10 09:51:34 +0000959 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -0400960
961 out_free:
962 nlmsg_free(msg);
963 out_err:
964 dev_put(netdev);
Johannes Berg4d0c8ae2009-07-07 03:56:09 +0200965 cfg80211_unlock_rdev(dev);
Johannes Berg55682962007-09-20 13:09:35 -0400966 return -ENOBUFS;
967}
968
Michael Wu66f7ac52008-01-31 19:48:22 +0100969static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
970 [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
971 [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
972 [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
973 [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
974 [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
975};
976
977static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
978{
979 struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
980 int flag;
981
982 *mntrflags = 0;
983
984 if (!nla)
985 return -EINVAL;
986
987 if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX,
988 nla, mntr_flags_policy))
989 return -EINVAL;
990
991 for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
992 if (flags[flag])
993 *mntrflags |= (1<<flag);
994
995 return 0;
996}
997
Johannes Berg9bc383d2009-11-19 11:55:19 +0100998static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev,
Johannes Bergad4bb6f2009-11-19 00:56:30 +0100999 struct net_device *netdev, u8 use_4addr,
1000 enum nl80211_iftype iftype)
Johannes Berg9bc383d2009-11-19 11:55:19 +01001001{
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001002 if (!use_4addr) {
1003 if (netdev && netdev->br_port)
1004 return -EBUSY;
Johannes Berg9bc383d2009-11-19 11:55:19 +01001005 return 0;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001006 }
Johannes Berg9bc383d2009-11-19 11:55:19 +01001007
1008 switch (iftype) {
1009 case NL80211_IFTYPE_AP_VLAN:
1010 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_AP)
1011 return 0;
1012 break;
1013 case NL80211_IFTYPE_STATION:
1014 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION)
1015 return 0;
1016 break;
1017 default:
1018 break;
1019 }
1020
1021 return -EOPNOTSUPP;
1022}
1023
Johannes Berg55682962007-09-20 13:09:35 -04001024static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
1025{
Johannes Berg79c97e92009-07-07 03:56:12 +02001026 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001027 struct vif_params params;
Johannes Berge36d56b2009-06-09 21:04:43 +02001028 int err;
Johannes Berg04a773a2009-04-19 21:24:32 +02001029 enum nl80211_iftype otype, ntype;
Johannes Berg55682962007-09-20 13:09:35 -04001030 struct net_device *dev;
Johannes Berg92ffe052008-09-16 20:39:36 +02001031 u32 _flags, *flags = NULL;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001032 bool change = false;
Johannes Berg55682962007-09-20 13:09:35 -04001033
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001034 memset(&params, 0, sizeof(params));
1035
Johannes Berg3b858752009-03-12 09:55:09 +01001036 rtnl_lock();
1037
Johannes Berg463d0182009-07-14 00:33:35 +02001038 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg55682962007-09-20 13:09:35 -04001039 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001040 goto unlock_rtnl;
1041
Johannes Berg04a773a2009-04-19 21:24:32 +02001042 otype = ntype = dev->ieee80211_ptr->iftype;
Johannes Berg55682962007-09-20 13:09:35 -04001043
Johannes Berg723b0382008-09-16 20:22:09 +02001044 if (info->attrs[NL80211_ATTR_IFTYPE]) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001045 ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
Johannes Berg04a773a2009-04-19 21:24:32 +02001046 if (otype != ntype)
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001047 change = true;
Johannes Berg04a773a2009-04-19 21:24:32 +02001048 if (ntype > NL80211_IFTYPE_MAX) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001049 err = -EINVAL;
Johannes Berg723b0382008-09-16 20:22:09 +02001050 goto unlock;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001051 }
Johannes Berg723b0382008-09-16 20:22:09 +02001052 }
1053
Johannes Berg92ffe052008-09-16 20:39:36 +02001054 if (info->attrs[NL80211_ATTR_MESH_ID]) {
Johannes Berg04a773a2009-04-19 21:24:32 +02001055 if (ntype != NL80211_IFTYPE_MESH_POINT) {
Johannes Berg92ffe052008-09-16 20:39:36 +02001056 err = -EINVAL;
1057 goto unlock;
1058 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001059 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
1060 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001061 change = true;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001062 }
1063
Felix Fietkau8b787642009-11-10 18:53:10 +01001064 if (info->attrs[NL80211_ATTR_4ADDR]) {
1065 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
1066 change = true;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001067 err = nl80211_valid_4addr(rdev, dev, params.use_4addr, ntype);
Johannes Berg9bc383d2009-11-19 11:55:19 +01001068 if (err)
1069 goto unlock;
Felix Fietkau8b787642009-11-10 18:53:10 +01001070 } else {
1071 params.use_4addr = -1;
1072 }
1073
Johannes Berg92ffe052008-09-16 20:39:36 +02001074 if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
Johannes Berg04a773a2009-04-19 21:24:32 +02001075 if (ntype != NL80211_IFTYPE_MONITOR) {
Johannes Berg92ffe052008-09-16 20:39:36 +02001076 err = -EINVAL;
1077 goto unlock;
1078 }
1079 err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
1080 &_flags);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001081 if (err)
1082 goto unlock;
1083
1084 flags = &_flags;
1085 change = true;
Johannes Berg92ffe052008-09-16 20:39:36 +02001086 }
Johannes Berg3b858752009-03-12 09:55:09 +01001087
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001088 if (change)
Johannes Berg3d54d252009-08-21 14:51:05 +02001089 err = cfg80211_change_iface(rdev, dev, ntype, flags, &params);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001090 else
1091 err = 0;
Johannes Berg60719ff2008-09-16 14:55:09 +02001092
Johannes Berg9bc383d2009-11-19 11:55:19 +01001093 if (!err && params.use_4addr != -1)
1094 dev->ieee80211_ptr->use_4addr = params.use_4addr;
1095
Johannes Berg55682962007-09-20 13:09:35 -04001096 unlock:
Johannes Berge36d56b2009-06-09 21:04:43 +02001097 dev_put(dev);
Johannes Berg79c97e92009-07-07 03:56:12 +02001098 cfg80211_unlock_rdev(rdev);
Johannes Berg3b858752009-03-12 09:55:09 +01001099 unlock_rtnl:
1100 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -04001101 return err;
1102}
1103
1104static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
1105{
Johannes Berg79c97e92009-07-07 03:56:12 +02001106 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001107 struct vif_params params;
Johannes Berg55682962007-09-20 13:09:35 -04001108 int err;
1109 enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
Michael Wu66f7ac52008-01-31 19:48:22 +01001110 u32 flags;
Johannes Berg55682962007-09-20 13:09:35 -04001111
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001112 memset(&params, 0, sizeof(params));
1113
Johannes Berg55682962007-09-20 13:09:35 -04001114 if (!info->attrs[NL80211_ATTR_IFNAME])
1115 return -EINVAL;
1116
1117 if (info->attrs[NL80211_ATTR_IFTYPE]) {
1118 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
1119 if (type > NL80211_IFTYPE_MAX)
1120 return -EINVAL;
1121 }
1122
Johannes Berg3b858752009-03-12 09:55:09 +01001123 rtnl_lock();
1124
Johannes Berg79c97e92009-07-07 03:56:12 +02001125 rdev = cfg80211_get_dev_from_info(info);
1126 if (IS_ERR(rdev)) {
1127 err = PTR_ERR(rdev);
Johannes Berg3b858752009-03-12 09:55:09 +01001128 goto unlock_rtnl;
1129 }
Johannes Berg55682962007-09-20 13:09:35 -04001130
Johannes Berg79c97e92009-07-07 03:56:12 +02001131 if (!rdev->ops->add_virtual_intf ||
1132 !(rdev->wiphy.interface_modes & (1 << type))) {
Johannes Berg55682962007-09-20 13:09:35 -04001133 err = -EOPNOTSUPP;
1134 goto unlock;
1135 }
1136
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001137 if (type == NL80211_IFTYPE_MESH_POINT &&
1138 info->attrs[NL80211_ATTR_MESH_ID]) {
1139 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
1140 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
1141 }
1142
Johannes Berg9bc383d2009-11-19 11:55:19 +01001143 if (info->attrs[NL80211_ATTR_4ADDR]) {
Felix Fietkau8b787642009-11-10 18:53:10 +01001144 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001145 err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type);
Johannes Berg9bc383d2009-11-19 11:55:19 +01001146 if (err)
1147 goto unlock;
1148 }
Felix Fietkau8b787642009-11-10 18:53:10 +01001149
Michael Wu66f7ac52008-01-31 19:48:22 +01001150 err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
1151 info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
1152 &flags);
Johannes Berg79c97e92009-07-07 03:56:12 +02001153 err = rdev->ops->add_virtual_intf(&rdev->wiphy,
Michael Wu66f7ac52008-01-31 19:48:22 +01001154 nla_data(info->attrs[NL80211_ATTR_IFNAME]),
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001155 type, err ? NULL : &flags, &params);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001156
Johannes Berg55682962007-09-20 13:09:35 -04001157 unlock:
Johannes Berg79c97e92009-07-07 03:56:12 +02001158 cfg80211_unlock_rdev(rdev);
Johannes Berg3b858752009-03-12 09:55:09 +01001159 unlock_rtnl:
1160 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -04001161 return err;
1162}
1163
1164static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
1165{
Johannes Berg79c97e92009-07-07 03:56:12 +02001166 struct cfg80211_registered_device *rdev;
Johannes Berg463d0182009-07-14 00:33:35 +02001167 int err;
Johannes Berg55682962007-09-20 13:09:35 -04001168 struct net_device *dev;
1169
Johannes Berg3b858752009-03-12 09:55:09 +01001170 rtnl_lock();
1171
Johannes Berg463d0182009-07-14 00:33:35 +02001172 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg55682962007-09-20 13:09:35 -04001173 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001174 goto unlock_rtnl;
Johannes Berg55682962007-09-20 13:09:35 -04001175
Johannes Berg79c97e92009-07-07 03:56:12 +02001176 if (!rdev->ops->del_virtual_intf) {
Johannes Berg55682962007-09-20 13:09:35 -04001177 err = -EOPNOTSUPP;
1178 goto out;
1179 }
1180
Johannes Berg463d0182009-07-14 00:33:35 +02001181 err = rdev->ops->del_virtual_intf(&rdev->wiphy, dev);
Johannes Berg55682962007-09-20 13:09:35 -04001182
1183 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001184 cfg80211_unlock_rdev(rdev);
Johannes Berg463d0182009-07-14 00:33:35 +02001185 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001186 unlock_rtnl:
1187 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -04001188 return err;
1189}
1190
Johannes Berg41ade002007-12-19 02:03:29 +01001191struct get_key_cookie {
1192 struct sk_buff *msg;
1193 int error;
Johannes Bergb9454e82009-07-08 13:29:08 +02001194 int idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001195};
1196
1197static void get_key_callback(void *c, struct key_params *params)
1198{
Johannes Bergb9454e82009-07-08 13:29:08 +02001199 struct nlattr *key;
Johannes Berg41ade002007-12-19 02:03:29 +01001200 struct get_key_cookie *cookie = c;
1201
1202 if (params->key)
1203 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA,
1204 params->key_len, params->key);
1205
1206 if (params->seq)
1207 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ,
1208 params->seq_len, params->seq);
1209
1210 if (params->cipher)
1211 NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
1212 params->cipher);
1213
Johannes Bergb9454e82009-07-08 13:29:08 +02001214 key = nla_nest_start(cookie->msg, NL80211_ATTR_KEY);
1215 if (!key)
1216 goto nla_put_failure;
1217
1218 if (params->key)
1219 NLA_PUT(cookie->msg, NL80211_KEY_DATA,
1220 params->key_len, params->key);
1221
1222 if (params->seq)
1223 NLA_PUT(cookie->msg, NL80211_KEY_SEQ,
1224 params->seq_len, params->seq);
1225
1226 if (params->cipher)
1227 NLA_PUT_U32(cookie->msg, NL80211_KEY_CIPHER,
1228 params->cipher);
1229
1230 NLA_PUT_U8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx);
1231
1232 nla_nest_end(cookie->msg, key);
1233
Johannes Berg41ade002007-12-19 02:03:29 +01001234 return;
1235 nla_put_failure:
1236 cookie->error = 1;
1237}
1238
1239static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
1240{
Johannes Berg79c97e92009-07-07 03:56:12 +02001241 struct cfg80211_registered_device *rdev;
Johannes Berg41ade002007-12-19 02:03:29 +01001242 int err;
1243 struct net_device *dev;
1244 u8 key_idx = 0;
1245 u8 *mac_addr = NULL;
1246 struct get_key_cookie cookie = {
1247 .error = 0,
1248 };
1249 void *hdr;
1250 struct sk_buff *msg;
1251
1252 if (info->attrs[NL80211_ATTR_KEY_IDX])
1253 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1254
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001255 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +01001256 return -EINVAL;
1257
1258 if (info->attrs[NL80211_ATTR_MAC])
1259 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1260
Johannes Berg3b858752009-03-12 09:55:09 +01001261 rtnl_lock();
1262
Johannes Berg463d0182009-07-14 00:33:35 +02001263 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001264 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001265 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001266
Johannes Berg79c97e92009-07-07 03:56:12 +02001267 if (!rdev->ops->get_key) {
Johannes Berg41ade002007-12-19 02:03:29 +01001268 err = -EOPNOTSUPP;
1269 goto out;
1270 }
1271
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001272 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg41ade002007-12-19 02:03:29 +01001273 if (!msg) {
1274 err = -ENOMEM;
1275 goto out;
1276 }
1277
1278 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
1279 NL80211_CMD_NEW_KEY);
1280
1281 if (IS_ERR(hdr)) {
1282 err = PTR_ERR(hdr);
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001283 goto free_msg;
Johannes Berg41ade002007-12-19 02:03:29 +01001284 }
1285
1286 cookie.msg = msg;
Johannes Bergb9454e82009-07-08 13:29:08 +02001287 cookie.idx = key_idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001288
1289 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1290 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
1291 if (mac_addr)
1292 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1293
Johannes Berg79c97e92009-07-07 03:56:12 +02001294 err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, mac_addr,
Johannes Berg41ade002007-12-19 02:03:29 +01001295 &cookie, get_key_callback);
Johannes Berg41ade002007-12-19 02:03:29 +01001296
1297 if (err)
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001298 goto free_msg;
Johannes Berg41ade002007-12-19 02:03:29 +01001299
1300 if (cookie.error)
1301 goto nla_put_failure;
1302
1303 genlmsg_end(msg, hdr);
Johannes Berg134e6372009-07-10 09:51:34 +00001304 err = genlmsg_reply(msg, info);
Johannes Berg41ade002007-12-19 02:03:29 +01001305 goto out;
1306
1307 nla_put_failure:
1308 err = -ENOBUFS;
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001309 free_msg:
Johannes Berg41ade002007-12-19 02:03:29 +01001310 nlmsg_free(msg);
1311 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001312 cfg80211_unlock_rdev(rdev);
Johannes Berg41ade002007-12-19 02:03:29 +01001313 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001314 unlock_rtnl:
1315 rtnl_unlock();
1316
Johannes Berg41ade002007-12-19 02:03:29 +01001317 return err;
1318}
1319
1320static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
1321{
Johannes Berg79c97e92009-07-07 03:56:12 +02001322 struct cfg80211_registered_device *rdev;
Johannes Bergb9454e82009-07-08 13:29:08 +02001323 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001324 int err;
1325 struct net_device *dev;
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001326 int (*func)(struct wiphy *wiphy, struct net_device *netdev,
1327 u8 key_index);
Johannes Berg41ade002007-12-19 02:03:29 +01001328
Johannes Bergb9454e82009-07-08 13:29:08 +02001329 err = nl80211_parse_key(info, &key);
1330 if (err)
1331 return err;
1332
1333 if (key.idx < 0)
Johannes Berg41ade002007-12-19 02:03:29 +01001334 return -EINVAL;
1335
Johannes Bergb9454e82009-07-08 13:29:08 +02001336 /* only support setting default key */
1337 if (!key.def && !key.defmgmt)
Johannes Berg41ade002007-12-19 02:03:29 +01001338 return -EINVAL;
1339
Johannes Berg3b858752009-03-12 09:55:09 +01001340 rtnl_lock();
1341
Johannes Berg463d0182009-07-14 00:33:35 +02001342 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001343 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001344 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001345
Johannes Bergb9454e82009-07-08 13:29:08 +02001346 if (key.def)
Johannes Berg79c97e92009-07-07 03:56:12 +02001347 func = rdev->ops->set_default_key;
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001348 else
Johannes Berg79c97e92009-07-07 03:56:12 +02001349 func = rdev->ops->set_default_mgmt_key;
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001350
1351 if (!func) {
Johannes Berg41ade002007-12-19 02:03:29 +01001352 err = -EOPNOTSUPP;
1353 goto out;
1354 }
1355
Johannes Bergfffd0932009-07-08 14:22:54 +02001356 wdev_lock(dev->ieee80211_ptr);
1357 err = nl80211_key_allowed(dev->ieee80211_ptr);
1358 if (!err)
1359 err = func(&rdev->wiphy, dev, key.idx);
1360
Johannes Berg3d23e342009-09-29 23:27:28 +02001361#ifdef CONFIG_CFG80211_WEXT
Johannes Berg08645122009-05-11 13:54:58 +02001362 if (!err) {
Johannes Berg79c97e92009-07-07 03:56:12 +02001363 if (func == rdev->ops->set_default_key)
Johannes Bergb9454e82009-07-08 13:29:08 +02001364 dev->ieee80211_ptr->wext.default_key = key.idx;
Johannes Berg08645122009-05-11 13:54:58 +02001365 else
Johannes Bergb9454e82009-07-08 13:29:08 +02001366 dev->ieee80211_ptr->wext.default_mgmt_key = key.idx;
Johannes Berg08645122009-05-11 13:54:58 +02001367 }
1368#endif
Johannes Bergfffd0932009-07-08 14:22:54 +02001369 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01001370
1371 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001372 cfg80211_unlock_rdev(rdev);
Johannes Berg41ade002007-12-19 02:03:29 +01001373 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001374
1375 unlock_rtnl:
1376 rtnl_unlock();
1377
Johannes Berg41ade002007-12-19 02:03:29 +01001378 return err;
1379}
1380
1381static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
1382{
Johannes Berg79c97e92009-07-07 03:56:12 +02001383 struct cfg80211_registered_device *rdev;
Johannes Bergfffd0932009-07-08 14:22:54 +02001384 int err;
Johannes Berg41ade002007-12-19 02:03:29 +01001385 struct net_device *dev;
Johannes Bergb9454e82009-07-08 13:29:08 +02001386 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001387 u8 *mac_addr = NULL;
1388
Johannes Bergb9454e82009-07-08 13:29:08 +02001389 err = nl80211_parse_key(info, &key);
1390 if (err)
1391 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01001392
Johannes Bergb9454e82009-07-08 13:29:08 +02001393 if (!key.p.key)
Johannes Berg41ade002007-12-19 02:03:29 +01001394 return -EINVAL;
1395
Johannes Berg41ade002007-12-19 02:03:29 +01001396 if (info->attrs[NL80211_ATTR_MAC])
1397 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1398
Johannes Berg3b858752009-03-12 09:55:09 +01001399 rtnl_lock();
1400
Johannes Berg463d0182009-07-14 00:33:35 +02001401 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001402 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001403 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001404
Johannes Berg79c97e92009-07-07 03:56:12 +02001405 if (!rdev->ops->add_key) {
Johannes Berg41ade002007-12-19 02:03:29 +01001406 err = -EOPNOTSUPP;
1407 goto out;
1408 }
1409
Johannes Bergfffd0932009-07-08 14:22:54 +02001410 if (cfg80211_validate_key_settings(rdev, &key.p, key.idx, mac_addr)) {
1411 err = -EINVAL;
1412 goto out;
1413 }
1414
1415 wdev_lock(dev->ieee80211_ptr);
1416 err = nl80211_key_allowed(dev->ieee80211_ptr);
1417 if (!err)
1418 err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx,
1419 mac_addr, &key.p);
1420 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01001421
1422 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001423 cfg80211_unlock_rdev(rdev);
Johannes Berg41ade002007-12-19 02:03:29 +01001424 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001425 unlock_rtnl:
1426 rtnl_unlock();
1427
Johannes Berg41ade002007-12-19 02:03:29 +01001428 return err;
1429}
1430
1431static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
1432{
Johannes Berg79c97e92009-07-07 03:56:12 +02001433 struct cfg80211_registered_device *rdev;
Johannes Berg41ade002007-12-19 02:03:29 +01001434 int err;
1435 struct net_device *dev;
Johannes Berg41ade002007-12-19 02:03:29 +01001436 u8 *mac_addr = NULL;
Johannes Bergb9454e82009-07-08 13:29:08 +02001437 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001438
Johannes Bergb9454e82009-07-08 13:29:08 +02001439 err = nl80211_parse_key(info, &key);
1440 if (err)
1441 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01001442
1443 if (info->attrs[NL80211_ATTR_MAC])
1444 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1445
Johannes Berg3b858752009-03-12 09:55:09 +01001446 rtnl_lock();
1447
Johannes Berg463d0182009-07-14 00:33:35 +02001448 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001449 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001450 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001451
Johannes Berg79c97e92009-07-07 03:56:12 +02001452 if (!rdev->ops->del_key) {
Johannes Berg41ade002007-12-19 02:03:29 +01001453 err = -EOPNOTSUPP;
1454 goto out;
1455 }
1456
Johannes Bergfffd0932009-07-08 14:22:54 +02001457 wdev_lock(dev->ieee80211_ptr);
1458 err = nl80211_key_allowed(dev->ieee80211_ptr);
1459 if (!err)
1460 err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr);
Johannes Berg41ade002007-12-19 02:03:29 +01001461
Johannes Berg3d23e342009-09-29 23:27:28 +02001462#ifdef CONFIG_CFG80211_WEXT
Johannes Berg08645122009-05-11 13:54:58 +02001463 if (!err) {
Johannes Bergb9454e82009-07-08 13:29:08 +02001464 if (key.idx == dev->ieee80211_ptr->wext.default_key)
Johannes Berg08645122009-05-11 13:54:58 +02001465 dev->ieee80211_ptr->wext.default_key = -1;
Johannes Bergb9454e82009-07-08 13:29:08 +02001466 else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key)
Johannes Berg08645122009-05-11 13:54:58 +02001467 dev->ieee80211_ptr->wext.default_mgmt_key = -1;
1468 }
1469#endif
Johannes Bergfffd0932009-07-08 14:22:54 +02001470 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg08645122009-05-11 13:54:58 +02001471
Johannes Berg41ade002007-12-19 02:03:29 +01001472 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001473 cfg80211_unlock_rdev(rdev);
Johannes Berg41ade002007-12-19 02:03:29 +01001474 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001475
1476 unlock_rtnl:
1477 rtnl_unlock();
1478
Johannes Berg41ade002007-12-19 02:03:29 +01001479 return err;
1480}
1481
Johannes Berged1b6cc2007-12-19 02:03:32 +01001482static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
1483{
1484 int (*call)(struct wiphy *wiphy, struct net_device *dev,
1485 struct beacon_parameters *info);
Johannes Berg79c97e92009-07-07 03:56:12 +02001486 struct cfg80211_registered_device *rdev;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001487 int err;
1488 struct net_device *dev;
1489 struct beacon_parameters params;
1490 int haveinfo = 0;
1491
Johannes Bergf4a11bb2009-03-27 12:40:28 +01001492 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]))
1493 return -EINVAL;
1494
Johannes Berg3b858752009-03-12 09:55:09 +01001495 rtnl_lock();
1496
Johannes Berg463d0182009-07-14 00:33:35 +02001497 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001498 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001499 goto unlock_rtnl;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001500
Jouni Malineneec60b02009-03-20 21:21:19 +02001501 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
1502 err = -EOPNOTSUPP;
1503 goto out;
1504 }
1505
Johannes Berged1b6cc2007-12-19 02:03:32 +01001506 switch (info->genlhdr->cmd) {
1507 case NL80211_CMD_NEW_BEACON:
1508 /* these are required for NEW_BEACON */
1509 if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
1510 !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
1511 !info->attrs[NL80211_ATTR_BEACON_HEAD]) {
1512 err = -EINVAL;
1513 goto out;
1514 }
1515
Johannes Berg79c97e92009-07-07 03:56:12 +02001516 call = rdev->ops->add_beacon;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001517 break;
1518 case NL80211_CMD_SET_BEACON:
Johannes Berg79c97e92009-07-07 03:56:12 +02001519 call = rdev->ops->set_beacon;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001520 break;
1521 default:
1522 WARN_ON(1);
1523 err = -EOPNOTSUPP;
1524 goto out;
1525 }
1526
1527 if (!call) {
1528 err = -EOPNOTSUPP;
1529 goto out;
1530 }
1531
1532 memset(&params, 0, sizeof(params));
1533
1534 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
1535 params.interval =
1536 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
1537 haveinfo = 1;
1538 }
1539
1540 if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) {
1541 params.dtim_period =
1542 nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
1543 haveinfo = 1;
1544 }
1545
1546 if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
1547 params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
1548 params.head_len =
1549 nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
1550 haveinfo = 1;
1551 }
1552
1553 if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
1554 params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
1555 params.tail_len =
1556 nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
1557 haveinfo = 1;
1558 }
1559
1560 if (!haveinfo) {
1561 err = -EINVAL;
1562 goto out;
1563 }
1564
Johannes Berg79c97e92009-07-07 03:56:12 +02001565 err = call(&rdev->wiphy, dev, &params);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001566
1567 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001568 cfg80211_unlock_rdev(rdev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001569 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001570 unlock_rtnl:
1571 rtnl_unlock();
1572
Johannes Berged1b6cc2007-12-19 02:03:32 +01001573 return err;
1574}
1575
1576static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
1577{
Johannes Berg79c97e92009-07-07 03:56:12 +02001578 struct cfg80211_registered_device *rdev;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001579 int err;
1580 struct net_device *dev;
1581
Johannes Berg3b858752009-03-12 09:55:09 +01001582 rtnl_lock();
1583
Johannes Berg463d0182009-07-14 00:33:35 +02001584 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001585 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001586 goto unlock_rtnl;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001587
Johannes Berg79c97e92009-07-07 03:56:12 +02001588 if (!rdev->ops->del_beacon) {
Johannes Berged1b6cc2007-12-19 02:03:32 +01001589 err = -EOPNOTSUPP;
1590 goto out;
1591 }
1592
Jouni Malineneec60b02009-03-20 21:21:19 +02001593 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
1594 err = -EOPNOTSUPP;
1595 goto out;
1596 }
Johannes Berg79c97e92009-07-07 03:56:12 +02001597 err = rdev->ops->del_beacon(&rdev->wiphy, dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001598
1599 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001600 cfg80211_unlock_rdev(rdev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001601 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001602 unlock_rtnl:
1603 rtnl_unlock();
1604
Johannes Berged1b6cc2007-12-19 02:03:32 +01001605 return err;
1606}
1607
Johannes Berg5727ef12007-12-19 02:03:34 +01001608static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
1609 [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
1610 [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
1611 [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
Jouni Malinen0e467242009-05-11 21:57:55 +03001612 [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG },
Johannes Berg5727ef12007-12-19 02:03:34 +01001613};
1614
Johannes Bergeccb8e82009-05-11 21:57:56 +03001615static int parse_station_flags(struct genl_info *info,
1616 struct station_parameters *params)
Johannes Berg5727ef12007-12-19 02:03:34 +01001617{
1618 struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
Johannes Bergeccb8e82009-05-11 21:57:56 +03001619 struct nlattr *nla;
Johannes Berg5727ef12007-12-19 02:03:34 +01001620 int flag;
1621
Johannes Bergeccb8e82009-05-11 21:57:56 +03001622 /*
1623 * Try parsing the new attribute first so userspace
1624 * can specify both for older kernels.
1625 */
1626 nla = info->attrs[NL80211_ATTR_STA_FLAGS2];
1627 if (nla) {
1628 struct nl80211_sta_flag_update *sta_flags;
Johannes Berg5727ef12007-12-19 02:03:34 +01001629
Johannes Bergeccb8e82009-05-11 21:57:56 +03001630 sta_flags = nla_data(nla);
1631 params->sta_flags_mask = sta_flags->mask;
1632 params->sta_flags_set = sta_flags->set;
1633 if ((params->sta_flags_mask |
1634 params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID))
1635 return -EINVAL;
1636 return 0;
1637 }
1638
1639 /* if present, parse the old attribute */
1640
1641 nla = info->attrs[NL80211_ATTR_STA_FLAGS];
Johannes Berg5727ef12007-12-19 02:03:34 +01001642 if (!nla)
1643 return 0;
1644
1645 if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX,
1646 nla, sta_flags_policy))
1647 return -EINVAL;
1648
Johannes Bergeccb8e82009-05-11 21:57:56 +03001649 params->sta_flags_mask = (1 << __NL80211_STA_FLAG_AFTER_LAST) - 1;
1650 params->sta_flags_mask &= ~1;
Johannes Berg5727ef12007-12-19 02:03:34 +01001651
1652 for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++)
1653 if (flags[flag])
Johannes Bergeccb8e82009-05-11 21:57:56 +03001654 params->sta_flags_set |= (1<<flag);
Johannes Berg5727ef12007-12-19 02:03:34 +01001655
1656 return 0;
1657}
1658
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001659static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
1660 int flags, struct net_device *dev,
Johannes Berg98b62182009-12-23 13:15:44 +01001661 const u8 *mac_addr, struct station_info *sinfo)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001662{
1663 void *hdr;
Henning Rogge420e7fa2008-12-11 22:04:19 +01001664 struct nlattr *sinfoattr, *txrate;
1665 u16 bitrate;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001666
1667 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
1668 if (!hdr)
1669 return -1;
1670
1671 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1672 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1673
Johannes Bergf5ea9122009-08-07 16:17:38 +02001674 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, sinfo->generation);
1675
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001676 sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
1677 if (!sinfoattr)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001678 goto nla_put_failure;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001679 if (sinfo->filled & STATION_INFO_INACTIVE_TIME)
1680 NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME,
1681 sinfo->inactive_time);
1682 if (sinfo->filled & STATION_INFO_RX_BYTES)
1683 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES,
1684 sinfo->rx_bytes);
1685 if (sinfo->filled & STATION_INFO_TX_BYTES)
1686 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES,
1687 sinfo->tx_bytes);
1688 if (sinfo->filled & STATION_INFO_LLID)
1689 NLA_PUT_U16(msg, NL80211_STA_INFO_LLID,
1690 sinfo->llid);
1691 if (sinfo->filled & STATION_INFO_PLID)
1692 NLA_PUT_U16(msg, NL80211_STA_INFO_PLID,
1693 sinfo->plid);
1694 if (sinfo->filled & STATION_INFO_PLINK_STATE)
1695 NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
1696 sinfo->plink_state);
Henning Rogge420e7fa2008-12-11 22:04:19 +01001697 if (sinfo->filled & STATION_INFO_SIGNAL)
1698 NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL,
1699 sinfo->signal);
1700 if (sinfo->filled & STATION_INFO_TX_BITRATE) {
1701 txrate = nla_nest_start(msg, NL80211_STA_INFO_TX_BITRATE);
1702 if (!txrate)
1703 goto nla_put_failure;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001704
John W. Linville254416a2009-12-09 16:43:52 -05001705 /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */
1706 bitrate = cfg80211_calculate_bitrate(&sinfo->txrate);
Henning Rogge420e7fa2008-12-11 22:04:19 +01001707 if (bitrate > 0)
1708 NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
1709
1710 if (sinfo->txrate.flags & RATE_INFO_FLAGS_MCS)
1711 NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS,
1712 sinfo->txrate.mcs);
1713 if (sinfo->txrate.flags & RATE_INFO_FLAGS_40_MHZ_WIDTH)
1714 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH);
1715 if (sinfo->txrate.flags & RATE_INFO_FLAGS_SHORT_GI)
1716 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI);
1717
1718 nla_nest_end(msg, txrate);
1719 }
Jouni Malinen98c8a60a2009-02-17 13:24:57 +02001720 if (sinfo->filled & STATION_INFO_RX_PACKETS)
1721 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS,
1722 sinfo->rx_packets);
1723 if (sinfo->filled & STATION_INFO_TX_PACKETS)
1724 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS,
1725 sinfo->tx_packets);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001726 nla_nest_end(msg, sinfoattr);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001727
1728 return genlmsg_end(msg, hdr);
1729
1730 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001731 genlmsg_cancel(msg, hdr);
1732 return -EMSGSIZE;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001733}
1734
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001735static int nl80211_dump_station(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02001736 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001737{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001738 struct station_info sinfo;
1739 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001740 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001741 u8 mac_addr[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02001742 int ifidx = cb->args[0];
1743 int sta_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001744 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001745
Holger Schuriga0438972009-11-11 11:30:02 +01001746 if (!ifidx)
1747 ifidx = nl80211_get_ifidx(cb);
1748 if (ifidx < 0)
1749 return ifidx;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001750
Johannes Berg3b858752009-03-12 09:55:09 +01001751 rtnl_lock();
1752
Johannes Berg463d0182009-07-14 00:33:35 +02001753 netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
Johannes Berg3b858752009-03-12 09:55:09 +01001754 if (!netdev) {
1755 err = -ENODEV;
1756 goto out_rtnl;
1757 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001758
Johannes Berg463d0182009-07-14 00:33:35 +02001759 dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001760 if (IS_ERR(dev)) {
1761 err = PTR_ERR(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001762 goto out_rtnl;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001763 }
1764
1765 if (!dev->ops->dump_station) {
Jouni Malineneec60b02009-03-20 21:21:19 +02001766 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001767 goto out_err;
1768 }
1769
Johannes Bergbba95fe2008-07-29 13:22:51 +02001770 while (1) {
1771 err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
1772 mac_addr, &sinfo);
1773 if (err == -ENOENT)
1774 break;
1775 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001776 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001777
1778 if (nl80211_send_station(skb,
1779 NETLINK_CB(cb->skb).pid,
1780 cb->nlh->nlmsg_seq, NLM_F_MULTI,
1781 netdev, mac_addr,
1782 &sinfo) < 0)
1783 goto out;
1784
1785 sta_idx++;
1786 }
1787
1788
1789 out:
1790 cb->args[1] = sta_idx;
1791 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001792 out_err:
Johannes Berg4d0c8ae2009-07-07 03:56:09 +02001793 cfg80211_unlock_rdev(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001794 out_rtnl:
1795 rtnl_unlock();
Johannes Bergbba95fe2008-07-29 13:22:51 +02001796
1797 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001798}
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001799
Johannes Berg5727ef12007-12-19 02:03:34 +01001800static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
1801{
Johannes Berg79c97e92009-07-07 03:56:12 +02001802 struct cfg80211_registered_device *rdev;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001803 int err;
1804 struct net_device *dev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001805 struct station_info sinfo;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001806 struct sk_buff *msg;
1807 u8 *mac_addr = NULL;
1808
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001809 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001810
1811 if (!info->attrs[NL80211_ATTR_MAC])
1812 return -EINVAL;
1813
1814 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1815
Johannes Berg3b858752009-03-12 09:55:09 +01001816 rtnl_lock();
1817
Johannes Berg463d0182009-07-14 00:33:35 +02001818 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001819 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001820 goto out_rtnl;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001821
Johannes Berg79c97e92009-07-07 03:56:12 +02001822 if (!rdev->ops->get_station) {
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001823 err = -EOPNOTSUPP;
1824 goto out;
1825 }
1826
Johannes Berg79c97e92009-07-07 03:56:12 +02001827 err = rdev->ops->get_station(&rdev->wiphy, dev, mac_addr, &sinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001828 if (err)
1829 goto out;
1830
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001831 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001832 if (!msg)
1833 goto out;
1834
1835 if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001836 dev, mac_addr, &sinfo) < 0)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001837 goto out_free;
1838
Johannes Berg134e6372009-07-10 09:51:34 +00001839 err = genlmsg_reply(msg, info);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001840 goto out;
1841
1842 out_free:
1843 nlmsg_free(msg);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001844 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001845 cfg80211_unlock_rdev(rdev);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001846 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001847 out_rtnl:
1848 rtnl_unlock();
1849
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001850 return err;
Johannes Berg5727ef12007-12-19 02:03:34 +01001851}
1852
1853/*
Felix Fietkauc258d2d2009-11-11 17:23:31 +01001854 * Get vlan interface making sure it is running and on the right wiphy.
Johannes Berg5727ef12007-12-19 02:03:34 +01001855 */
Johannes Berg463d0182009-07-14 00:33:35 +02001856static int get_vlan(struct genl_info *info,
Johannes Berg5727ef12007-12-19 02:03:34 +01001857 struct cfg80211_registered_device *rdev,
1858 struct net_device **vlan)
1859{
Johannes Berg463d0182009-07-14 00:33:35 +02001860 struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN];
Johannes Berg5727ef12007-12-19 02:03:34 +01001861 *vlan = NULL;
1862
1863 if (vlanattr) {
Johannes Berg463d0182009-07-14 00:33:35 +02001864 *vlan = dev_get_by_index(genl_info_net(info),
1865 nla_get_u32(vlanattr));
Johannes Berg5727ef12007-12-19 02:03:34 +01001866 if (!*vlan)
1867 return -ENODEV;
1868 if (!(*vlan)->ieee80211_ptr)
1869 return -EINVAL;
1870 if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy)
1871 return -EINVAL;
Felix Fietkauc258d2d2009-11-11 17:23:31 +01001872 if (!netif_running(*vlan))
1873 return -ENETDOWN;
Johannes Berg5727ef12007-12-19 02:03:34 +01001874 }
1875 return 0;
1876}
1877
1878static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
1879{
Johannes Berg79c97e92009-07-07 03:56:12 +02001880 struct cfg80211_registered_device *rdev;
Johannes Berg5727ef12007-12-19 02:03:34 +01001881 int err;
1882 struct net_device *dev;
1883 struct station_parameters params;
1884 u8 *mac_addr = NULL;
1885
1886 memset(&params, 0, sizeof(params));
1887
1888 params.listen_interval = -1;
1889
1890 if (info->attrs[NL80211_ATTR_STA_AID])
1891 return -EINVAL;
1892
1893 if (!info->attrs[NL80211_ATTR_MAC])
1894 return -EINVAL;
1895
1896 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1897
1898 if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
1899 params.supported_rates =
1900 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1901 params.supported_rates_len =
1902 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1903 }
1904
1905 if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
1906 params.listen_interval =
1907 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
1908
Jouni Malinen36aedc92008-08-25 11:58:58 +03001909 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
1910 params.ht_capa =
1911 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
1912
Johannes Bergeccb8e82009-05-11 21:57:56 +03001913 if (parse_station_flags(info, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01001914 return -EINVAL;
1915
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001916 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
1917 params.plink_action =
1918 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
1919
Johannes Berg3b858752009-03-12 09:55:09 +01001920 rtnl_lock();
1921
Johannes Berg463d0182009-07-14 00:33:35 +02001922 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001923 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001924 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01001925
Johannes Berg463d0182009-07-14 00:33:35 +02001926 err = get_vlan(info, rdev, &params.vlan);
Johannes Berga97f4422009-06-18 17:23:43 +02001927 if (err)
Johannes Berg034d6552009-05-27 10:35:29 +02001928 goto out;
Johannes Berga97f4422009-06-18 17:23:43 +02001929
1930 /* validate settings */
1931 err = 0;
1932
1933 switch (dev->ieee80211_ptr->iftype) {
1934 case NL80211_IFTYPE_AP:
1935 case NL80211_IFTYPE_AP_VLAN:
1936 /* disallow mesh-specific things */
1937 if (params.plink_action)
1938 err = -EINVAL;
1939 break;
1940 case NL80211_IFTYPE_STATION:
1941 /* disallow everything but AUTHORIZED flag */
1942 if (params.plink_action)
1943 err = -EINVAL;
1944 if (params.vlan)
1945 err = -EINVAL;
1946 if (params.supported_rates)
1947 err = -EINVAL;
1948 if (params.ht_capa)
1949 err = -EINVAL;
1950 if (params.listen_interval >= 0)
1951 err = -EINVAL;
1952 if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
1953 err = -EINVAL;
1954 break;
1955 case NL80211_IFTYPE_MESH_POINT:
1956 /* disallow things mesh doesn't support */
1957 if (params.vlan)
1958 err = -EINVAL;
1959 if (params.ht_capa)
1960 err = -EINVAL;
1961 if (params.listen_interval >= 0)
1962 err = -EINVAL;
1963 if (params.supported_rates)
1964 err = -EINVAL;
1965 if (params.sta_flags_mask)
1966 err = -EINVAL;
1967 break;
1968 default:
1969 err = -EINVAL;
Johannes Berg034d6552009-05-27 10:35:29 +02001970 }
1971
Johannes Berg5727ef12007-12-19 02:03:34 +01001972 if (err)
1973 goto out;
1974
Johannes Berg79c97e92009-07-07 03:56:12 +02001975 if (!rdev->ops->change_station) {
Johannes Berg5727ef12007-12-19 02:03:34 +01001976 err = -EOPNOTSUPP;
1977 goto out;
1978 }
1979
Johannes Berg79c97e92009-07-07 03:56:12 +02001980 err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01001981
1982 out:
1983 if (params.vlan)
1984 dev_put(params.vlan);
Johannes Berg79c97e92009-07-07 03:56:12 +02001985 cfg80211_unlock_rdev(rdev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001986 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001987 out_rtnl:
1988 rtnl_unlock();
1989
Johannes Berg5727ef12007-12-19 02:03:34 +01001990 return err;
1991}
1992
1993static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
1994{
Johannes Berg79c97e92009-07-07 03:56:12 +02001995 struct cfg80211_registered_device *rdev;
Johannes Berg5727ef12007-12-19 02:03:34 +01001996 int err;
1997 struct net_device *dev;
1998 struct station_parameters params;
1999 u8 *mac_addr = NULL;
2000
2001 memset(&params, 0, sizeof(params));
2002
2003 if (!info->attrs[NL80211_ATTR_MAC])
2004 return -EINVAL;
2005
Johannes Berg5727ef12007-12-19 02:03:34 +01002006 if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
2007 return -EINVAL;
2008
2009 if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
2010 return -EINVAL;
2011
2012 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2013 params.supported_rates =
2014 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2015 params.supported_rates_len =
2016 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2017 params.listen_interval =
2018 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
Johannes Berg51b50fb2009-05-24 16:42:30 +02002019
Johannes Berga97f4422009-06-18 17:23:43 +02002020 if (info->attrs[NL80211_ATTR_STA_AID]) {
2021 params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
2022 if (!params.aid || params.aid > IEEE80211_MAX_AID)
2023 return -EINVAL;
2024 }
Johannes Berg51b50fb2009-05-24 16:42:30 +02002025
Jouni Malinen36aedc92008-08-25 11:58:58 +03002026 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
2027 params.ht_capa =
2028 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
Johannes Berg5727ef12007-12-19 02:03:34 +01002029
Johannes Bergeccb8e82009-05-11 21:57:56 +03002030 if (parse_station_flags(info, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01002031 return -EINVAL;
2032
Johannes Berg3b858752009-03-12 09:55:09 +01002033 rtnl_lock();
2034
Johannes Berg463d0182009-07-14 00:33:35 +02002035 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01002036 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002037 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01002038
Johannes Berg463d0182009-07-14 00:33:35 +02002039 err = get_vlan(info, rdev, &params.vlan);
Johannes Berga97f4422009-06-18 17:23:43 +02002040 if (err)
Johannes Berge80cf852009-05-11 14:43:13 +02002041 goto out;
Johannes Berga97f4422009-06-18 17:23:43 +02002042
2043 /* validate settings */
2044 err = 0;
2045
2046 switch (dev->ieee80211_ptr->iftype) {
2047 case NL80211_IFTYPE_AP:
2048 case NL80211_IFTYPE_AP_VLAN:
2049 /* all ok but must have AID */
2050 if (!params.aid)
2051 err = -EINVAL;
2052 break;
2053 case NL80211_IFTYPE_MESH_POINT:
2054 /* disallow things mesh doesn't support */
2055 if (params.vlan)
2056 err = -EINVAL;
2057 if (params.aid)
2058 err = -EINVAL;
2059 if (params.ht_capa)
2060 err = -EINVAL;
2061 if (params.listen_interval >= 0)
2062 err = -EINVAL;
2063 if (params.supported_rates)
2064 err = -EINVAL;
2065 if (params.sta_flags_mask)
2066 err = -EINVAL;
2067 break;
2068 default:
2069 err = -EINVAL;
Johannes Berge80cf852009-05-11 14:43:13 +02002070 }
2071
Johannes Berg5727ef12007-12-19 02:03:34 +01002072 if (err)
2073 goto out;
2074
Johannes Berg79c97e92009-07-07 03:56:12 +02002075 if (!rdev->ops->add_station) {
Johannes Berg5727ef12007-12-19 02:03:34 +01002076 err = -EOPNOTSUPP;
2077 goto out;
2078 }
2079
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002080 if (!netif_running(dev)) {
2081 err = -ENETDOWN;
2082 goto out;
2083 }
2084
Johannes Berg79c97e92009-07-07 03:56:12 +02002085 err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01002086
2087 out:
2088 if (params.vlan)
2089 dev_put(params.vlan);
Johannes Berg79c97e92009-07-07 03:56:12 +02002090 cfg80211_unlock_rdev(rdev);
Johannes Berg5727ef12007-12-19 02:03:34 +01002091 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002092 out_rtnl:
2093 rtnl_unlock();
2094
Johannes Berg5727ef12007-12-19 02:03:34 +01002095 return err;
2096}
2097
2098static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
2099{
Johannes Berg79c97e92009-07-07 03:56:12 +02002100 struct cfg80211_registered_device *rdev;
Johannes Berg5727ef12007-12-19 02:03:34 +01002101 int err;
2102 struct net_device *dev;
2103 u8 *mac_addr = NULL;
2104
2105 if (info->attrs[NL80211_ATTR_MAC])
2106 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2107
Johannes Berg3b858752009-03-12 09:55:09 +01002108 rtnl_lock();
2109
Johannes Berg463d0182009-07-14 00:33:35 +02002110 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01002111 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002112 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01002113
Johannes Berge80cf852009-05-11 14:43:13 +02002114 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Andrey Yurovsky155cc9e2009-06-16 11:31:04 -07002115 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
2116 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
Johannes Berge80cf852009-05-11 14:43:13 +02002117 err = -EINVAL;
2118 goto out;
2119 }
2120
Johannes Berg79c97e92009-07-07 03:56:12 +02002121 if (!rdev->ops->del_station) {
Johannes Berg5727ef12007-12-19 02:03:34 +01002122 err = -EOPNOTSUPP;
2123 goto out;
2124 }
2125
Johannes Berg79c97e92009-07-07 03:56:12 +02002126 err = rdev->ops->del_station(&rdev->wiphy, dev, mac_addr);
Johannes Berg5727ef12007-12-19 02:03:34 +01002127
2128 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002129 cfg80211_unlock_rdev(rdev);
Johannes Berg5727ef12007-12-19 02:03:34 +01002130 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002131 out_rtnl:
2132 rtnl_unlock();
2133
Johannes Berg5727ef12007-12-19 02:03:34 +01002134 return err;
2135}
2136
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002137static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq,
2138 int flags, struct net_device *dev,
2139 u8 *dst, u8 *next_hop,
2140 struct mpath_info *pinfo)
2141{
2142 void *hdr;
2143 struct nlattr *pinfoattr;
2144
2145 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
2146 if (!hdr)
2147 return -1;
2148
2149 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2150 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
2151 NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop);
2152
Johannes Bergf5ea9122009-08-07 16:17:38 +02002153 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, pinfo->generation);
2154
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002155 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
2156 if (!pinfoattr)
2157 goto nla_put_failure;
2158 if (pinfo->filled & MPATH_INFO_FRAME_QLEN)
2159 NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
2160 pinfo->frame_qlen);
Rui Paulod19b3bf2009-11-09 23:46:55 +00002161 if (pinfo->filled & MPATH_INFO_SN)
2162 NLA_PUT_U32(msg, NL80211_MPATH_INFO_SN,
2163 pinfo->sn);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002164 if (pinfo->filled & MPATH_INFO_METRIC)
2165 NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC,
2166 pinfo->metric);
2167 if (pinfo->filled & MPATH_INFO_EXPTIME)
2168 NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME,
2169 pinfo->exptime);
2170 if (pinfo->filled & MPATH_INFO_FLAGS)
2171 NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS,
2172 pinfo->flags);
2173 if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT)
2174 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
2175 pinfo->discovery_timeout);
2176 if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES)
2177 NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
2178 pinfo->discovery_retries);
2179
2180 nla_nest_end(msg, pinfoattr);
2181
2182 return genlmsg_end(msg, hdr);
2183
2184 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07002185 genlmsg_cancel(msg, hdr);
2186 return -EMSGSIZE;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002187}
2188
2189static int nl80211_dump_mpath(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02002190 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002191{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002192 struct mpath_info pinfo;
2193 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002194 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002195 u8 dst[ETH_ALEN];
2196 u8 next_hop[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02002197 int ifidx = cb->args[0];
2198 int path_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002199 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002200
Holger Schuriga0438972009-11-11 11:30:02 +01002201 if (!ifidx)
2202 ifidx = nl80211_get_ifidx(cb);
2203 if (ifidx < 0)
2204 return ifidx;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002205
Johannes Berg3b858752009-03-12 09:55:09 +01002206 rtnl_lock();
2207
Johannes Berg463d0182009-07-14 00:33:35 +02002208 netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
Johannes Berg3b858752009-03-12 09:55:09 +01002209 if (!netdev) {
2210 err = -ENODEV;
2211 goto out_rtnl;
2212 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002213
Johannes Berg463d0182009-07-14 00:33:35 +02002214 dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02002215 if (IS_ERR(dev)) {
2216 err = PTR_ERR(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002217 goto out_rtnl;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002218 }
2219
2220 if (!dev->ops->dump_mpath) {
Jouni Malineneec60b02009-03-20 21:21:19 +02002221 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002222 goto out_err;
2223 }
2224
Jouni Malineneec60b02009-03-20 21:21:19 +02002225 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2226 err = -EOPNOTSUPP;
Roel Kluin0448b5f2009-08-22 21:15:49 +02002227 goto out_err;
Jouni Malineneec60b02009-03-20 21:21:19 +02002228 }
2229
Johannes Bergbba95fe2008-07-29 13:22:51 +02002230 while (1) {
2231 err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
2232 dst, next_hop, &pinfo);
2233 if (err == -ENOENT)
2234 break;
2235 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002236 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002237
2238 if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid,
2239 cb->nlh->nlmsg_seq, NLM_F_MULTI,
2240 netdev, dst, next_hop,
2241 &pinfo) < 0)
2242 goto out;
2243
2244 path_idx++;
2245 }
2246
2247
2248 out:
2249 cb->args[1] = path_idx;
2250 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002251 out_err:
Johannes Berg4d0c8ae2009-07-07 03:56:09 +02002252 cfg80211_unlock_rdev(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002253 out_rtnl:
2254 rtnl_unlock();
Johannes Bergbba95fe2008-07-29 13:22:51 +02002255
2256 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002257}
2258
2259static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
2260{
Johannes Berg79c97e92009-07-07 03:56:12 +02002261 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002262 int err;
2263 struct net_device *dev;
2264 struct mpath_info pinfo;
2265 struct sk_buff *msg;
2266 u8 *dst = NULL;
2267 u8 next_hop[ETH_ALEN];
2268
2269 memset(&pinfo, 0, sizeof(pinfo));
2270
2271 if (!info->attrs[NL80211_ATTR_MAC])
2272 return -EINVAL;
2273
2274 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2275
Johannes Berg3b858752009-03-12 09:55:09 +01002276 rtnl_lock();
2277
Johannes Berg463d0182009-07-14 00:33:35 +02002278 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002279 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002280 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002281
Johannes Berg79c97e92009-07-07 03:56:12 +02002282 if (!rdev->ops->get_mpath) {
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002283 err = -EOPNOTSUPP;
2284 goto out;
2285 }
2286
Jouni Malineneec60b02009-03-20 21:21:19 +02002287 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2288 err = -EOPNOTSUPP;
2289 goto out;
2290 }
2291
Johannes Berg79c97e92009-07-07 03:56:12 +02002292 err = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, &pinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002293 if (err)
2294 goto out;
2295
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002296 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002297 if (!msg)
2298 goto out;
2299
2300 if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0,
2301 dev, dst, next_hop, &pinfo) < 0)
2302 goto out_free;
2303
Johannes Berg134e6372009-07-10 09:51:34 +00002304 err = genlmsg_reply(msg, info);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002305 goto out;
2306
2307 out_free:
2308 nlmsg_free(msg);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002309 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002310 cfg80211_unlock_rdev(rdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002311 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002312 out_rtnl:
2313 rtnl_unlock();
2314
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002315 return err;
2316}
2317
2318static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
2319{
Johannes Berg79c97e92009-07-07 03:56:12 +02002320 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002321 int err;
2322 struct net_device *dev;
2323 u8 *dst = NULL;
2324 u8 *next_hop = NULL;
2325
2326 if (!info->attrs[NL80211_ATTR_MAC])
2327 return -EINVAL;
2328
2329 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
2330 return -EINVAL;
2331
2332 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2333 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
2334
Johannes Berg3b858752009-03-12 09:55:09 +01002335 rtnl_lock();
2336
Johannes Berg463d0182009-07-14 00:33:35 +02002337 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002338 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002339 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002340
Johannes Berg79c97e92009-07-07 03:56:12 +02002341 if (!rdev->ops->change_mpath) {
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002342 err = -EOPNOTSUPP;
2343 goto out;
2344 }
2345
Jouni Malineneec60b02009-03-20 21:21:19 +02002346 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2347 err = -EOPNOTSUPP;
2348 goto out;
2349 }
2350
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002351 if (!netif_running(dev)) {
2352 err = -ENETDOWN;
2353 goto out;
2354 }
2355
Johannes Berg79c97e92009-07-07 03:56:12 +02002356 err = rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002357
2358 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002359 cfg80211_unlock_rdev(rdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002360 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002361 out_rtnl:
2362 rtnl_unlock();
2363
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002364 return err;
2365}
2366static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
2367{
Johannes Berg79c97e92009-07-07 03:56:12 +02002368 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002369 int err;
2370 struct net_device *dev;
2371 u8 *dst = NULL;
2372 u8 *next_hop = NULL;
2373
2374 if (!info->attrs[NL80211_ATTR_MAC])
2375 return -EINVAL;
2376
2377 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
2378 return -EINVAL;
2379
2380 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2381 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
2382
Johannes Berg3b858752009-03-12 09:55:09 +01002383 rtnl_lock();
2384
Johannes Berg463d0182009-07-14 00:33:35 +02002385 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002386 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002387 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002388
Johannes Berg79c97e92009-07-07 03:56:12 +02002389 if (!rdev->ops->add_mpath) {
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002390 err = -EOPNOTSUPP;
2391 goto out;
2392 }
2393
Jouni Malineneec60b02009-03-20 21:21:19 +02002394 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2395 err = -EOPNOTSUPP;
2396 goto out;
2397 }
2398
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002399 if (!netif_running(dev)) {
2400 err = -ENETDOWN;
2401 goto out;
2402 }
2403
Johannes Berg79c97e92009-07-07 03:56:12 +02002404 err = rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002405
2406 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002407 cfg80211_unlock_rdev(rdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002408 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002409 out_rtnl:
2410 rtnl_unlock();
2411
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002412 return err;
2413}
2414
2415static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
2416{
Johannes Berg79c97e92009-07-07 03:56:12 +02002417 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002418 int err;
2419 struct net_device *dev;
2420 u8 *dst = NULL;
2421
2422 if (info->attrs[NL80211_ATTR_MAC])
2423 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2424
Johannes Berg3b858752009-03-12 09:55:09 +01002425 rtnl_lock();
2426
Johannes Berg463d0182009-07-14 00:33:35 +02002427 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002428 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002429 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002430
Johannes Berg79c97e92009-07-07 03:56:12 +02002431 if (!rdev->ops->del_mpath) {
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002432 err = -EOPNOTSUPP;
2433 goto out;
2434 }
2435
Johannes Berg79c97e92009-07-07 03:56:12 +02002436 err = rdev->ops->del_mpath(&rdev->wiphy, dev, dst);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002437
2438 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002439 cfg80211_unlock_rdev(rdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002440 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002441 out_rtnl:
2442 rtnl_unlock();
2443
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002444 return err;
2445}
2446
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002447static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
2448{
Johannes Berg79c97e92009-07-07 03:56:12 +02002449 struct cfg80211_registered_device *rdev;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002450 int err;
2451 struct net_device *dev;
2452 struct bss_parameters params;
2453
2454 memset(&params, 0, sizeof(params));
2455 /* default to not changing parameters */
2456 params.use_cts_prot = -1;
2457 params.use_short_preamble = -1;
2458 params.use_short_slot_time = -1;
2459
2460 if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
2461 params.use_cts_prot =
2462 nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
2463 if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
2464 params.use_short_preamble =
2465 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
2466 if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
2467 params.use_short_slot_time =
2468 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
Jouni Malinen90c97a02008-10-30 16:59:22 +02002469 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
2470 params.basic_rates =
2471 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2472 params.basic_rates_len =
2473 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2474 }
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002475
Johannes Berg3b858752009-03-12 09:55:09 +01002476 rtnl_lock();
2477
Johannes Berg463d0182009-07-14 00:33:35 +02002478 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002479 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002480 goto out_rtnl;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002481
Johannes Berg79c97e92009-07-07 03:56:12 +02002482 if (!rdev->ops->change_bss) {
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002483 err = -EOPNOTSUPP;
2484 goto out;
2485 }
2486
Jouni Malineneec60b02009-03-20 21:21:19 +02002487 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
2488 err = -EOPNOTSUPP;
2489 goto out;
2490 }
2491
Johannes Berg79c97e92009-07-07 03:56:12 +02002492 err = rdev->ops->change_bss(&rdev->wiphy, dev, &params);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002493
2494 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002495 cfg80211_unlock_rdev(rdev);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002496 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002497 out_rtnl:
2498 rtnl_unlock();
2499
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002500 return err;
2501}
2502
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00002503static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002504 [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
2505 [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
2506 [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
2507 [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
2508 [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
2509 [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
2510};
2511
2512static int parse_reg_rule(struct nlattr *tb[],
2513 struct ieee80211_reg_rule *reg_rule)
2514{
2515 struct ieee80211_freq_range *freq_range = &reg_rule->freq_range;
2516 struct ieee80211_power_rule *power_rule = &reg_rule->power_rule;
2517
2518 if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
2519 return -EINVAL;
2520 if (!tb[NL80211_ATTR_FREQ_RANGE_START])
2521 return -EINVAL;
2522 if (!tb[NL80211_ATTR_FREQ_RANGE_END])
2523 return -EINVAL;
2524 if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
2525 return -EINVAL;
2526 if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
2527 return -EINVAL;
2528
2529 reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
2530
2531 freq_range->start_freq_khz =
2532 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
2533 freq_range->end_freq_khz =
2534 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
2535 freq_range->max_bandwidth_khz =
2536 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
2537
2538 power_rule->max_eirp =
2539 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
2540
2541 if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
2542 power_rule->max_antenna_gain =
2543 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
2544
2545 return 0;
2546}
2547
2548static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
2549{
2550 int r;
2551 char *data = NULL;
2552
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002553 /*
2554 * You should only get this when cfg80211 hasn't yet initialized
2555 * completely when built-in to the kernel right between the time
2556 * window between nl80211_init() and regulatory_init(), if that is
2557 * even possible.
2558 */
2559 mutex_lock(&cfg80211_mutex);
2560 if (unlikely(!cfg80211_regdomain)) {
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002561 mutex_unlock(&cfg80211_mutex);
2562 return -EINPROGRESS;
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002563 }
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002564 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002565
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002566 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
2567 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002568
2569 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
2570
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002571 r = regulatory_hint_user(data);
2572
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002573 return r;
2574}
2575
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002576static int nl80211_get_mesh_params(struct sk_buff *skb,
2577 struct genl_info *info)
2578{
Johannes Berg79c97e92009-07-07 03:56:12 +02002579 struct cfg80211_registered_device *rdev;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002580 struct mesh_config cur_params;
2581 int err;
2582 struct net_device *dev;
2583 void *hdr;
2584 struct nlattr *pinfoattr;
2585 struct sk_buff *msg;
2586
Johannes Berg3b858752009-03-12 09:55:09 +01002587 rtnl_lock();
2588
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002589 /* Look up our device */
Johannes Berg463d0182009-07-14 00:33:35 +02002590 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002591 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002592 goto out_rtnl;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002593
Johannes Berg79c97e92009-07-07 03:56:12 +02002594 if (!rdev->ops->get_mesh_params) {
Jouni Malinenf3f92582009-03-20 17:57:36 +02002595 err = -EOPNOTSUPP;
2596 goto out;
2597 }
2598
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002599 /* Get the mesh params */
Johannes Berg79c97e92009-07-07 03:56:12 +02002600 err = rdev->ops->get_mesh_params(&rdev->wiphy, dev, &cur_params);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002601 if (err)
2602 goto out;
2603
2604 /* Draw up a netlink message to send back */
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002605 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002606 if (!msg) {
2607 err = -ENOBUFS;
2608 goto out;
2609 }
2610 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
2611 NL80211_CMD_GET_MESH_PARAMS);
2612 if (!hdr)
2613 goto nla_put_failure;
2614 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS);
2615 if (!pinfoattr)
2616 goto nla_put_failure;
2617 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2618 NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
2619 cur_params.dot11MeshRetryTimeout);
2620 NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
2621 cur_params.dot11MeshConfirmTimeout);
2622 NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
2623 cur_params.dot11MeshHoldingTimeout);
2624 NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
2625 cur_params.dot11MeshMaxPeerLinks);
2626 NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES,
2627 cur_params.dot11MeshMaxRetries);
2628 NLA_PUT_U8(msg, NL80211_MESHCONF_TTL,
2629 cur_params.dot11MeshTTL);
2630 NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
2631 cur_params.auto_open_plinks);
2632 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
2633 cur_params.dot11MeshHWMPmaxPREQretries);
2634 NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
2635 cur_params.path_refresh_time);
2636 NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
2637 cur_params.min_discovery_timeout);
2638 NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
2639 cur_params.dot11MeshHWMPactivePathTimeout);
2640 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
2641 cur_params.dot11MeshHWMPpreqMinInterval);
2642 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
2643 cur_params.dot11MeshHWMPnetDiameterTraversalTime);
Rui Paulo63c57232009-11-09 23:46:57 +00002644 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_ROOTMODE,
2645 cur_params.dot11MeshHWMPRootMode);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002646 nla_nest_end(msg, pinfoattr);
2647 genlmsg_end(msg, hdr);
Johannes Berg134e6372009-07-10 09:51:34 +00002648 err = genlmsg_reply(msg, info);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002649 goto out;
2650
Johannes Berg3b858752009-03-12 09:55:09 +01002651 nla_put_failure:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002652 genlmsg_cancel(msg, hdr);
2653 err = -EMSGSIZE;
Johannes Berg3b858752009-03-12 09:55:09 +01002654 out:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002655 /* Cleanup */
Johannes Berg79c97e92009-07-07 03:56:12 +02002656 cfg80211_unlock_rdev(rdev);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002657 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002658 out_rtnl:
2659 rtnl_unlock();
2660
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002661 return err;
2662}
2663
2664#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \
2665do {\
2666 if (table[attr_num]) {\
2667 cfg.param = nla_fn(table[attr_num]); \
2668 mask |= (1 << (attr_num - 1)); \
2669 } \
2670} while (0);\
2671
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00002672static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = {
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002673 [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
2674 [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
2675 [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
2676 [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
2677 [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
2678 [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
2679 [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
2680
2681 [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
2682 [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
2683 [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
2684 [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
2685 [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
2686 [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
2687};
2688
2689static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
2690{
2691 int err;
2692 u32 mask;
Johannes Berg79c97e92009-07-07 03:56:12 +02002693 struct cfg80211_registered_device *rdev;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002694 struct net_device *dev;
2695 struct mesh_config cfg;
2696 struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
2697 struct nlattr *parent_attr;
2698
2699 parent_attr = info->attrs[NL80211_ATTR_MESH_PARAMS];
2700 if (!parent_attr)
2701 return -EINVAL;
2702 if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
2703 parent_attr, nl80211_meshconf_params_policy))
2704 return -EINVAL;
2705
Johannes Berg3b858752009-03-12 09:55:09 +01002706 rtnl_lock();
2707
Johannes Berg463d0182009-07-14 00:33:35 +02002708 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002709 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002710 goto out_rtnl;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002711
Johannes Berg79c97e92009-07-07 03:56:12 +02002712 if (!rdev->ops->set_mesh_params) {
Jouni Malinenf3f92582009-03-20 17:57:36 +02002713 err = -EOPNOTSUPP;
2714 goto out;
2715 }
2716
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002717 /* This makes sure that there aren't more than 32 mesh config
2718 * parameters (otherwise our bitfield scheme would not work.) */
2719 BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
2720
2721 /* Fill in the params struct */
2722 mask = 0;
2723 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout,
2724 mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16);
2725 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout,
2726 mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16);
2727 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout,
2728 mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16);
2729 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks,
2730 mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16);
2731 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries,
2732 mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8);
2733 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL,
2734 mask, NL80211_MESHCONF_TTL, nla_get_u8);
2735 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
2736 mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8);
2737 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
2738 mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
2739 nla_get_u8);
2740 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time,
2741 mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32);
2742 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout,
2743 mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
2744 nla_get_u16);
2745 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
2746 mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
2747 nla_get_u32);
2748 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
2749 mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
2750 nla_get_u16);
2751 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
2752 dot11MeshHWMPnetDiameterTraversalTime,
2753 mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
2754 nla_get_u16);
Rui Paulo63c57232009-11-09 23:46:57 +00002755 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
2756 dot11MeshHWMPRootMode, mask,
2757 NL80211_MESHCONF_HWMP_ROOTMODE,
2758 nla_get_u8);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002759
2760 /* Apply changes */
Johannes Berg79c97e92009-07-07 03:56:12 +02002761 err = rdev->ops->set_mesh_params(&rdev->wiphy, dev, &cfg, mask);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002762
Jouni Malinenf3f92582009-03-20 17:57:36 +02002763 out:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002764 /* cleanup */
Johannes Berg79c97e92009-07-07 03:56:12 +02002765 cfg80211_unlock_rdev(rdev);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002766 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002767 out_rtnl:
2768 rtnl_unlock();
2769
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002770 return err;
2771}
2772
2773#undef FILL_IN_MESH_PARAM_IF_SET
2774
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002775static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
2776{
2777 struct sk_buff *msg;
2778 void *hdr = NULL;
2779 struct nlattr *nl_reg_rules;
2780 unsigned int i;
2781 int err = -EINVAL;
2782
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002783 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002784
2785 if (!cfg80211_regdomain)
2786 goto out;
2787
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002788 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002789 if (!msg) {
2790 err = -ENOBUFS;
2791 goto out;
2792 }
2793
2794 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
2795 NL80211_CMD_GET_REG);
2796 if (!hdr)
2797 goto nla_put_failure;
2798
2799 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2,
2800 cfg80211_regdomain->alpha2);
2801
2802 nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
2803 if (!nl_reg_rules)
2804 goto nla_put_failure;
2805
2806 for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) {
2807 struct nlattr *nl_reg_rule;
2808 const struct ieee80211_reg_rule *reg_rule;
2809 const struct ieee80211_freq_range *freq_range;
2810 const struct ieee80211_power_rule *power_rule;
2811
2812 reg_rule = &cfg80211_regdomain->reg_rules[i];
2813 freq_range = &reg_rule->freq_range;
2814 power_rule = &reg_rule->power_rule;
2815
2816 nl_reg_rule = nla_nest_start(msg, i);
2817 if (!nl_reg_rule)
2818 goto nla_put_failure;
2819
2820 NLA_PUT_U32(msg, NL80211_ATTR_REG_RULE_FLAGS,
2821 reg_rule->flags);
2822 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_START,
2823 freq_range->start_freq_khz);
2824 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_END,
2825 freq_range->end_freq_khz);
2826 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
2827 freq_range->max_bandwidth_khz);
2828 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
2829 power_rule->max_antenna_gain);
2830 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
2831 power_rule->max_eirp);
2832
2833 nla_nest_end(msg, nl_reg_rule);
2834 }
2835
2836 nla_nest_end(msg, nl_reg_rules);
2837
2838 genlmsg_end(msg, hdr);
Johannes Berg134e6372009-07-10 09:51:34 +00002839 err = genlmsg_reply(msg, info);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002840 goto out;
2841
2842nla_put_failure:
2843 genlmsg_cancel(msg, hdr);
2844 err = -EMSGSIZE;
2845out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002846 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002847 return err;
2848}
2849
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002850static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
2851{
2852 struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
2853 struct nlattr *nl_reg_rule;
2854 char *alpha2 = NULL;
2855 int rem_reg_rules = 0, r = 0;
2856 u32 num_rules = 0, rule_idx = 0, size_of_regd;
2857 struct ieee80211_regdomain *rd = NULL;
2858
2859 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
2860 return -EINVAL;
2861
2862 if (!info->attrs[NL80211_ATTR_REG_RULES])
2863 return -EINVAL;
2864
2865 alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
2866
2867 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
2868 rem_reg_rules) {
2869 num_rules++;
2870 if (num_rules > NL80211_MAX_SUPP_REG_RULES)
Luis R. Rodriguez4776c6e2009-05-13 17:04:39 -04002871 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002872 }
2873
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04002874 mutex_lock(&cfg80211_mutex);
2875
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002876 if (!reg_is_valid_request(alpha2)) {
2877 r = -EINVAL;
2878 goto bad_reg;
2879 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002880
2881 size_of_regd = sizeof(struct ieee80211_regdomain) +
2882 (num_rules * sizeof(struct ieee80211_reg_rule));
2883
2884 rd = kzalloc(size_of_regd, GFP_KERNEL);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002885 if (!rd) {
2886 r = -ENOMEM;
2887 goto bad_reg;
2888 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002889
2890 rd->n_reg_rules = num_rules;
2891 rd->alpha2[0] = alpha2[0];
2892 rd->alpha2[1] = alpha2[1];
2893
2894 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
2895 rem_reg_rules) {
2896 nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
2897 nla_data(nl_reg_rule), nla_len(nl_reg_rule),
2898 reg_rule_policy);
2899 r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
2900 if (r)
2901 goto bad_reg;
2902
2903 rule_idx++;
2904
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002905 if (rule_idx > NL80211_MAX_SUPP_REG_RULES) {
2906 r = -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002907 goto bad_reg;
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002908 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002909 }
2910
2911 BUG_ON(rule_idx != num_rules);
2912
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002913 r = set_regdom(rd);
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04002914
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002915 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002916
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002917 return r;
2918
Johannes Bergd2372b32008-10-24 20:32:20 +02002919 bad_reg:
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04002920 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002921 kfree(rd);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002922 return r;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002923}
2924
Johannes Berg83f5e2c2009-06-17 17:41:49 +02002925static int validate_scan_freqs(struct nlattr *freqs)
2926{
2927 struct nlattr *attr1, *attr2;
2928 int n_channels = 0, tmp1, tmp2;
2929
2930 nla_for_each_nested(attr1, freqs, tmp1) {
2931 n_channels++;
2932 /*
2933 * Some hardware has a limited channel list for
2934 * scanning, and it is pretty much nonsensical
2935 * to scan for a channel twice, so disallow that
2936 * and don't require drivers to check that the
2937 * channel list they get isn't longer than what
2938 * they can scan, as long as they can scan all
2939 * the channels they registered at once.
2940 */
2941 nla_for_each_nested(attr2, freqs, tmp2)
2942 if (attr1 != attr2 &&
2943 nla_get_u32(attr1) == nla_get_u32(attr2))
2944 return 0;
2945 }
2946
2947 return n_channels;
2948}
2949
Johannes Berg2a519312009-02-10 21:25:55 +01002950static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
2951{
Johannes Berg79c97e92009-07-07 03:56:12 +02002952 struct cfg80211_registered_device *rdev;
Johannes Berg2a519312009-02-10 21:25:55 +01002953 struct net_device *dev;
2954 struct cfg80211_scan_request *request;
2955 struct cfg80211_ssid *ssid;
2956 struct ieee80211_channel *channel;
2957 struct nlattr *attr;
2958 struct wiphy *wiphy;
Johannes Berg83f5e2c2009-06-17 17:41:49 +02002959 int err, tmp, n_ssids = 0, n_channels, i;
Johannes Berg2a519312009-02-10 21:25:55 +01002960 enum ieee80211_band band;
Jouni Malinen70692ad2009-02-16 19:39:13 +02002961 size_t ie_len;
Johannes Berg2a519312009-02-10 21:25:55 +01002962
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002963 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
2964 return -EINVAL;
2965
Johannes Berg3b858752009-03-12 09:55:09 +01002966 rtnl_lock();
2967
Johannes Berg463d0182009-07-14 00:33:35 +02002968 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg2a519312009-02-10 21:25:55 +01002969 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002970 goto out_rtnl;
Johannes Berg2a519312009-02-10 21:25:55 +01002971
Johannes Berg79c97e92009-07-07 03:56:12 +02002972 wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01002973
Johannes Berg79c97e92009-07-07 03:56:12 +02002974 if (!rdev->ops->scan) {
Johannes Berg2a519312009-02-10 21:25:55 +01002975 err = -EOPNOTSUPP;
2976 goto out;
2977 }
2978
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002979 if (!netif_running(dev)) {
2980 err = -ENETDOWN;
2981 goto out;
2982 }
2983
Johannes Berg79c97e92009-07-07 03:56:12 +02002984 if (rdev->scan_req) {
Johannes Berg2a519312009-02-10 21:25:55 +01002985 err = -EBUSY;
Johannes Berg3b858752009-03-12 09:55:09 +01002986 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002987 }
2988
2989 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
Johannes Berg83f5e2c2009-06-17 17:41:49 +02002990 n_channels = validate_scan_freqs(
2991 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
Johannes Berg2a519312009-02-10 21:25:55 +01002992 if (!n_channels) {
2993 err = -EINVAL;
Johannes Berg3b858752009-03-12 09:55:09 +01002994 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002995 }
2996 } else {
Johannes Berg83f5e2c2009-06-17 17:41:49 +02002997 n_channels = 0;
2998
Johannes Berg2a519312009-02-10 21:25:55 +01002999 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
3000 if (wiphy->bands[band])
3001 n_channels += wiphy->bands[band]->n_channels;
3002 }
3003
3004 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
3005 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
3006 n_ssids++;
3007
3008 if (n_ssids > wiphy->max_scan_ssids) {
3009 err = -EINVAL;
Johannes Berg3b858752009-03-12 09:55:09 +01003010 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01003011 }
3012
Jouni Malinen70692ad2009-02-16 19:39:13 +02003013 if (info->attrs[NL80211_ATTR_IE])
3014 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3015 else
3016 ie_len = 0;
3017
Johannes Berg18a83652009-03-31 12:12:05 +02003018 if (ie_len > wiphy->max_scan_ie_len) {
3019 err = -EINVAL;
3020 goto out;
3021 }
3022
Johannes Berg2a519312009-02-10 21:25:55 +01003023 request = kzalloc(sizeof(*request)
3024 + sizeof(*ssid) * n_ssids
Jouni Malinen70692ad2009-02-16 19:39:13 +02003025 + sizeof(channel) * n_channels
3026 + ie_len, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01003027 if (!request) {
3028 err = -ENOMEM;
Johannes Berg3b858752009-03-12 09:55:09 +01003029 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01003030 }
3031
Johannes Berg2a519312009-02-10 21:25:55 +01003032 if (n_ssids)
Johannes Berg5ba63532009-08-07 17:54:07 +02003033 request->ssids = (void *)&request->channels[n_channels];
Johannes Berg2a519312009-02-10 21:25:55 +01003034 request->n_ssids = n_ssids;
Jouni Malinen70692ad2009-02-16 19:39:13 +02003035 if (ie_len) {
3036 if (request->ssids)
3037 request->ie = (void *)(request->ssids + n_ssids);
3038 else
3039 request->ie = (void *)(request->channels + n_channels);
3040 }
Johannes Berg2a519312009-02-10 21:25:55 +01003041
Johannes Berg584991d2009-11-02 13:32:03 +01003042 i = 0;
Johannes Berg2a519312009-02-10 21:25:55 +01003043 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
3044 /* user specified, bail out if channel not found */
Johannes Berg2a519312009-02-10 21:25:55 +01003045 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
Johannes Berg584991d2009-11-02 13:32:03 +01003046 struct ieee80211_channel *chan;
3047
3048 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
3049
3050 if (!chan) {
Johannes Berg2a519312009-02-10 21:25:55 +01003051 err = -EINVAL;
3052 goto out_free;
3053 }
Johannes Berg584991d2009-11-02 13:32:03 +01003054
3055 /* ignore disabled channels */
3056 if (chan->flags & IEEE80211_CHAN_DISABLED)
3057 continue;
3058
3059 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01003060 i++;
3061 }
3062 } else {
3063 /* all channels */
Johannes Berg2a519312009-02-10 21:25:55 +01003064 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
3065 int j;
3066 if (!wiphy->bands[band])
3067 continue;
3068 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
Johannes Berg584991d2009-11-02 13:32:03 +01003069 struct ieee80211_channel *chan;
3070
3071 chan = &wiphy->bands[band]->channels[j];
3072
3073 if (chan->flags & IEEE80211_CHAN_DISABLED)
3074 continue;
3075
3076 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01003077 i++;
3078 }
3079 }
3080 }
3081
Johannes Berg584991d2009-11-02 13:32:03 +01003082 if (!i) {
3083 err = -EINVAL;
3084 goto out_free;
3085 }
3086
3087 request->n_channels = i;
3088
Johannes Berg2a519312009-02-10 21:25:55 +01003089 i = 0;
3090 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
3091 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
3092 if (request->ssids[i].ssid_len > IEEE80211_MAX_SSID_LEN) {
3093 err = -EINVAL;
3094 goto out_free;
3095 }
3096 memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
3097 request->ssids[i].ssid_len = nla_len(attr);
3098 i++;
3099 }
3100 }
3101
Jouni Malinen70692ad2009-02-16 19:39:13 +02003102 if (info->attrs[NL80211_ATTR_IE]) {
3103 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Johannes Bergde95a542009-04-01 11:58:36 +02003104 memcpy((void *)request->ie,
3105 nla_data(info->attrs[NL80211_ATTR_IE]),
Jouni Malinen70692ad2009-02-16 19:39:13 +02003106 request->ie_len);
3107 }
3108
Johannes Berg463d0182009-07-14 00:33:35 +02003109 request->dev = dev;
Johannes Berg79c97e92009-07-07 03:56:12 +02003110 request->wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01003111
Johannes Berg79c97e92009-07-07 03:56:12 +02003112 rdev->scan_req = request;
3113 err = rdev->ops->scan(&rdev->wiphy, dev, request);
Johannes Berg2a519312009-02-10 21:25:55 +01003114
Johannes Berg463d0182009-07-14 00:33:35 +02003115 if (!err) {
Johannes Berg79c97e92009-07-07 03:56:12 +02003116 nl80211_send_scan_start(rdev, dev);
Johannes Berg463d0182009-07-14 00:33:35 +02003117 dev_hold(dev);
3118 }
Johannes Berga538e2d2009-06-16 19:56:42 +02003119
Johannes Berg2a519312009-02-10 21:25:55 +01003120 out_free:
3121 if (err) {
Johannes Berg79c97e92009-07-07 03:56:12 +02003122 rdev->scan_req = NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01003123 kfree(request);
3124 }
Johannes Berg2a519312009-02-10 21:25:55 +01003125 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003126 cfg80211_unlock_rdev(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003127 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01003128 out_rtnl:
3129 rtnl_unlock();
3130
Johannes Berg2a519312009-02-10 21:25:55 +01003131 return err;
3132}
3133
3134static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
3135 struct cfg80211_registered_device *rdev,
Johannes Berg48ab9052009-07-10 18:42:31 +02003136 struct wireless_dev *wdev,
3137 struct cfg80211_internal_bss *intbss)
Johannes Berg2a519312009-02-10 21:25:55 +01003138{
Johannes Berg48ab9052009-07-10 18:42:31 +02003139 struct cfg80211_bss *res = &intbss->pub;
Johannes Berg2a519312009-02-10 21:25:55 +01003140 void *hdr;
3141 struct nlattr *bss;
Johannes Berg48ab9052009-07-10 18:42:31 +02003142 int i;
3143
3144 ASSERT_WDEV_LOCK(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003145
3146 hdr = nl80211hdr_put(msg, pid, seq, flags,
3147 NL80211_CMD_NEW_SCAN_RESULTS);
3148 if (!hdr)
3149 return -1;
3150
Johannes Bergf5ea9122009-08-07 16:17:38 +02003151 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation);
Johannes Berg48ab9052009-07-10 18:42:31 +02003152 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex);
Johannes Berg2a519312009-02-10 21:25:55 +01003153
3154 bss = nla_nest_start(msg, NL80211_ATTR_BSS);
3155 if (!bss)
3156 goto nla_put_failure;
3157 if (!is_zero_ether_addr(res->bssid))
3158 NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid);
3159 if (res->information_elements && res->len_information_elements)
3160 NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS,
3161 res->len_information_elements,
3162 res->information_elements);
Jouni Malinen34a6edd2010-01-06 16:19:24 +02003163 if (res->beacon_ies && res->len_beacon_ies &&
3164 res->beacon_ies != res->information_elements)
3165 NLA_PUT(msg, NL80211_BSS_BEACON_IES,
3166 res->len_beacon_ies, res->beacon_ies);
Johannes Berg2a519312009-02-10 21:25:55 +01003167 if (res->tsf)
3168 NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf);
3169 if (res->beacon_interval)
3170 NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval);
3171 NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability);
3172 NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq);
Holger Schurig7c896062009-09-24 12:21:01 +02003173 NLA_PUT_U32(msg, NL80211_BSS_SEEN_MS_AGO,
3174 jiffies_to_msecs(jiffies - intbss->ts));
Johannes Berg2a519312009-02-10 21:25:55 +01003175
Johannes Berg77965c92009-02-18 18:45:06 +01003176 switch (rdev->wiphy.signal_type) {
Johannes Berg2a519312009-02-10 21:25:55 +01003177 case CFG80211_SIGNAL_TYPE_MBM:
3178 NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal);
3179 break;
3180 case CFG80211_SIGNAL_TYPE_UNSPEC:
3181 NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal);
3182 break;
3183 default:
3184 break;
3185 }
3186
Johannes Berg48ab9052009-07-10 18:42:31 +02003187 switch (wdev->iftype) {
3188 case NL80211_IFTYPE_STATION:
3189 if (intbss == wdev->current_bss)
3190 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
3191 NL80211_BSS_STATUS_ASSOCIATED);
3192 else for (i = 0; i < MAX_AUTH_BSSES; i++) {
3193 if (intbss != wdev->auth_bsses[i])
3194 continue;
3195 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
3196 NL80211_BSS_STATUS_AUTHENTICATED);
3197 break;
3198 }
3199 break;
3200 case NL80211_IFTYPE_ADHOC:
3201 if (intbss == wdev->current_bss)
3202 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
3203 NL80211_BSS_STATUS_IBSS_JOINED);
3204 break;
3205 default:
3206 break;
3207 }
3208
Johannes Berg2a519312009-02-10 21:25:55 +01003209 nla_nest_end(msg, bss);
3210
3211 return genlmsg_end(msg, hdr);
3212
3213 nla_put_failure:
3214 genlmsg_cancel(msg, hdr);
3215 return -EMSGSIZE;
3216}
3217
3218static int nl80211_dump_scan(struct sk_buff *skb,
3219 struct netlink_callback *cb)
3220{
Johannes Berg48ab9052009-07-10 18:42:31 +02003221 struct cfg80211_registered_device *rdev;
3222 struct net_device *dev;
Johannes Berg2a519312009-02-10 21:25:55 +01003223 struct cfg80211_internal_bss *scan;
Johannes Berg48ab9052009-07-10 18:42:31 +02003224 struct wireless_dev *wdev;
Johannes Berg2a519312009-02-10 21:25:55 +01003225 int ifidx = cb->args[0];
3226 int start = cb->args[1], idx = 0;
3227 int err;
3228
Holger Schuriga0438972009-11-11 11:30:02 +01003229 if (!ifidx)
3230 ifidx = nl80211_get_ifidx(cb);
3231 if (ifidx < 0)
3232 return ifidx;
3233 cb->args[0] = ifidx;
Johannes Berg2a519312009-02-10 21:25:55 +01003234
Johannes Berg463d0182009-07-14 00:33:35 +02003235 dev = dev_get_by_index(sock_net(skb->sk), ifidx);
Johannes Berg48ab9052009-07-10 18:42:31 +02003236 if (!dev)
Johannes Berg2a519312009-02-10 21:25:55 +01003237 return -ENODEV;
3238
Johannes Berg463d0182009-07-14 00:33:35 +02003239 rdev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Johannes Berg48ab9052009-07-10 18:42:31 +02003240 if (IS_ERR(rdev)) {
3241 err = PTR_ERR(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003242 goto out_put_netdev;
3243 }
3244
Johannes Berg48ab9052009-07-10 18:42:31 +02003245 wdev = dev->ieee80211_ptr;
Johannes Berg2a519312009-02-10 21:25:55 +01003246
Johannes Berg48ab9052009-07-10 18:42:31 +02003247 wdev_lock(wdev);
3248 spin_lock_bh(&rdev->bss_lock);
3249 cfg80211_bss_expire(rdev);
3250
3251 list_for_each_entry(scan, &rdev->bss_list, list) {
Johannes Berg2a519312009-02-10 21:25:55 +01003252 if (++idx <= start)
3253 continue;
3254 if (nl80211_send_bss(skb,
3255 NETLINK_CB(cb->skb).pid,
3256 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Berg48ab9052009-07-10 18:42:31 +02003257 rdev, wdev, scan) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01003258 idx--;
3259 goto out;
3260 }
3261 }
3262
3263 out:
Johannes Berg48ab9052009-07-10 18:42:31 +02003264 spin_unlock_bh(&rdev->bss_lock);
3265 wdev_unlock(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003266
3267 cb->args[1] = idx;
3268 err = skb->len;
Johannes Berg48ab9052009-07-10 18:42:31 +02003269 cfg80211_unlock_rdev(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003270 out_put_netdev:
Johannes Berg48ab9052009-07-10 18:42:31 +02003271 dev_put(dev);
Johannes Berg2a519312009-02-10 21:25:55 +01003272
3273 return err;
3274}
3275
Holger Schurig61fa7132009-11-11 12:25:40 +01003276static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq,
3277 int flags, struct net_device *dev,
3278 struct survey_info *survey)
3279{
3280 void *hdr;
3281 struct nlattr *infoattr;
3282
3283 /* Survey without a channel doesn't make sense */
3284 if (!survey->channel)
3285 return -EINVAL;
3286
3287 hdr = nl80211hdr_put(msg, pid, seq, flags,
3288 NL80211_CMD_NEW_SURVEY_RESULTS);
3289 if (!hdr)
3290 return -ENOMEM;
3291
3292 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
3293
3294 infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO);
3295 if (!infoattr)
3296 goto nla_put_failure;
3297
3298 NLA_PUT_U32(msg, NL80211_SURVEY_INFO_FREQUENCY,
3299 survey->channel->center_freq);
3300 if (survey->filled & SURVEY_INFO_NOISE_DBM)
3301 NLA_PUT_U8(msg, NL80211_SURVEY_INFO_NOISE,
3302 survey->noise);
3303
3304 nla_nest_end(msg, infoattr);
3305
3306 return genlmsg_end(msg, hdr);
3307
3308 nla_put_failure:
3309 genlmsg_cancel(msg, hdr);
3310 return -EMSGSIZE;
3311}
3312
3313static int nl80211_dump_survey(struct sk_buff *skb,
3314 struct netlink_callback *cb)
3315{
3316 struct survey_info survey;
3317 struct cfg80211_registered_device *dev;
3318 struct net_device *netdev;
3319 int ifidx = cb->args[0];
3320 int survey_idx = cb->args[1];
3321 int res;
3322
3323 if (!ifidx)
3324 ifidx = nl80211_get_ifidx(cb);
3325 if (ifidx < 0)
3326 return ifidx;
3327 cb->args[0] = ifidx;
3328
3329 rtnl_lock();
3330
3331 netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
3332 if (!netdev) {
3333 res = -ENODEV;
3334 goto out_rtnl;
3335 }
3336
3337 dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
3338 if (IS_ERR(dev)) {
3339 res = PTR_ERR(dev);
3340 goto out_rtnl;
3341 }
3342
3343 if (!dev->ops->dump_survey) {
3344 res = -EOPNOTSUPP;
3345 goto out_err;
3346 }
3347
3348 while (1) {
3349 res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx,
3350 &survey);
3351 if (res == -ENOENT)
3352 break;
3353 if (res)
3354 goto out_err;
3355
3356 if (nl80211_send_survey(skb,
3357 NETLINK_CB(cb->skb).pid,
3358 cb->nlh->nlmsg_seq, NLM_F_MULTI,
3359 netdev,
3360 &survey) < 0)
3361 goto out;
3362 survey_idx++;
3363 }
3364
3365 out:
3366 cb->args[1] = survey_idx;
3367 res = skb->len;
3368 out_err:
3369 cfg80211_unlock_rdev(dev);
3370 out_rtnl:
3371 rtnl_unlock();
3372
3373 return res;
3374}
3375
Jouni Malinen255e7372009-03-20 21:21:17 +02003376static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
3377{
Samuel Ortizb23aa672009-07-01 21:26:54 +02003378 return auth_type <= NL80211_AUTHTYPE_MAX;
Jouni Malinen255e7372009-03-20 21:21:17 +02003379}
3380
Samuel Ortizb23aa672009-07-01 21:26:54 +02003381static bool nl80211_valid_wpa_versions(u32 wpa_versions)
3382{
3383 return !(wpa_versions & ~(NL80211_WPA_VERSION_1 |
3384 NL80211_WPA_VERSION_2));
3385}
3386
3387static bool nl80211_valid_akm_suite(u32 akm)
3388{
3389 return akm == WLAN_AKM_SUITE_8021X ||
3390 akm == WLAN_AKM_SUITE_PSK;
3391}
3392
3393static bool nl80211_valid_cipher_suite(u32 cipher)
3394{
3395 return cipher == WLAN_CIPHER_SUITE_WEP40 ||
3396 cipher == WLAN_CIPHER_SUITE_WEP104 ||
3397 cipher == WLAN_CIPHER_SUITE_TKIP ||
3398 cipher == WLAN_CIPHER_SUITE_CCMP ||
3399 cipher == WLAN_CIPHER_SUITE_AES_CMAC;
3400}
3401
3402
Jouni Malinen636a5d32009-03-19 13:39:22 +02003403static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
3404{
Johannes Berg79c97e92009-07-07 03:56:12 +02003405 struct cfg80211_registered_device *rdev;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003406 struct net_device *dev;
Johannes Berg19957bb2009-07-02 17:20:43 +02003407 struct ieee80211_channel *chan;
3408 const u8 *bssid, *ssid, *ie = NULL;
3409 int err, ssid_len, ie_len = 0;
3410 enum nl80211_auth_type auth_type;
Johannes Bergfffd0932009-07-08 14:22:54 +02003411 struct key_parse key;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003412
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003413 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3414 return -EINVAL;
3415
3416 if (!info->attrs[NL80211_ATTR_MAC])
3417 return -EINVAL;
3418
Jouni Malinen17780922009-03-27 20:52:47 +02003419 if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
3420 return -EINVAL;
3421
Johannes Berg19957bb2009-07-02 17:20:43 +02003422 if (!info->attrs[NL80211_ATTR_SSID])
3423 return -EINVAL;
3424
3425 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
3426 return -EINVAL;
3427
Johannes Bergfffd0932009-07-08 14:22:54 +02003428 err = nl80211_parse_key(info, &key);
3429 if (err)
3430 return err;
3431
3432 if (key.idx >= 0) {
3433 if (!key.p.key || !key.p.key_len)
3434 return -EINVAL;
3435 if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 ||
3436 key.p.key_len != WLAN_KEY_LEN_WEP40) &&
3437 (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 ||
3438 key.p.key_len != WLAN_KEY_LEN_WEP104))
3439 return -EINVAL;
3440 if (key.idx > 4)
3441 return -EINVAL;
3442 } else {
3443 key.p.key_len = 0;
3444 key.p.key = NULL;
3445 }
3446
Jouni Malinen636a5d32009-03-19 13:39:22 +02003447 rtnl_lock();
3448
Johannes Berg463d0182009-07-14 00:33:35 +02003449 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003450 if (err)
3451 goto unlock_rtnl;
3452
Johannes Berg79c97e92009-07-07 03:56:12 +02003453 if (!rdev->ops->auth) {
Jouni Malinen636a5d32009-03-19 13:39:22 +02003454 err = -EOPNOTSUPP;
3455 goto out;
3456 }
3457
Jouni Malineneec60b02009-03-20 21:21:19 +02003458 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
3459 err = -EOPNOTSUPP;
3460 goto out;
3461 }
3462
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003463 if (!netif_running(dev)) {
3464 err = -ENETDOWN;
3465 goto out;
3466 }
3467
Johannes Berg19957bb2009-07-02 17:20:43 +02003468 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg79c97e92009-07-07 03:56:12 +02003469 chan = ieee80211_get_channel(&rdev->wiphy,
Johannes Berg19957bb2009-07-02 17:20:43 +02003470 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
3471 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) {
Jouni Malinen17780922009-03-27 20:52:47 +02003472 err = -EINVAL;
3473 goto out;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003474 }
3475
Johannes Berg19957bb2009-07-02 17:20:43 +02003476 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3477 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
3478
3479 if (info->attrs[NL80211_ATTR_IE]) {
3480 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3481 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3482 }
3483
3484 auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
3485 if (!nl80211_valid_auth_type(auth_type)) {
3486 err = -EINVAL;
3487 goto out;
3488 }
3489
Johannes Berg79c97e92009-07-07 03:56:12 +02003490 err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
Johannes Bergfffd0932009-07-08 14:22:54 +02003491 ssid, ssid_len, ie, ie_len,
3492 key.p.key, key.p.key_len, key.idx);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003493
3494out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003495 cfg80211_unlock_rdev(rdev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003496 dev_put(dev);
3497unlock_rtnl:
3498 rtnl_unlock();
3499 return err;
3500}
3501
Samuel Ortizb23aa672009-07-01 21:26:54 +02003502static int nl80211_crypto_settings(struct genl_info *info,
Johannes Berg3dc27d22009-07-02 21:36:37 +02003503 struct cfg80211_crypto_settings *settings,
3504 int cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02003505{
Johannes Bergc0b2bbd2009-07-25 16:54:36 +02003506 memset(settings, 0, sizeof(*settings));
3507
Samuel Ortizb23aa672009-07-01 21:26:54 +02003508 settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT];
3509
3510 if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) {
3511 void *data;
3512 int len, i;
3513
3514 data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
3515 len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
3516 settings->n_ciphers_pairwise = len / sizeof(u32);
3517
3518 if (len % sizeof(u32))
3519 return -EINVAL;
3520
Johannes Berg3dc27d22009-07-02 21:36:37 +02003521 if (settings->n_ciphers_pairwise > cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02003522 return -EINVAL;
3523
3524 memcpy(settings->ciphers_pairwise, data, len);
3525
3526 for (i = 0; i < settings->n_ciphers_pairwise; i++)
3527 if (!nl80211_valid_cipher_suite(
3528 settings->ciphers_pairwise[i]))
3529 return -EINVAL;
3530 }
3531
3532 if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) {
3533 settings->cipher_group =
3534 nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]);
3535 if (!nl80211_valid_cipher_suite(settings->cipher_group))
3536 return -EINVAL;
3537 }
3538
3539 if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) {
3540 settings->wpa_versions =
3541 nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]);
3542 if (!nl80211_valid_wpa_versions(settings->wpa_versions))
3543 return -EINVAL;
3544 }
3545
3546 if (info->attrs[NL80211_ATTR_AKM_SUITES]) {
3547 void *data;
3548 int len, i;
3549
3550 data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]);
3551 len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]);
3552 settings->n_akm_suites = len / sizeof(u32);
3553
3554 if (len % sizeof(u32))
3555 return -EINVAL;
3556
3557 memcpy(settings->akm_suites, data, len);
3558
3559 for (i = 0; i < settings->n_ciphers_pairwise; i++)
3560 if (!nl80211_valid_akm_suite(settings->akm_suites[i]))
3561 return -EINVAL;
3562 }
3563
3564 return 0;
3565}
3566
Jouni Malinen636a5d32009-03-19 13:39:22 +02003567static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
3568{
Johannes Berg19957bb2009-07-02 17:20:43 +02003569 struct cfg80211_registered_device *rdev;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003570 struct net_device *dev;
Jouni Malinen27e310c2010-01-19 10:53:30 -08003571 struct wireless_dev *wdev;
Johannes Berg19957bb2009-07-02 17:20:43 +02003572 struct cfg80211_crypto_settings crypto;
Johannes Berg59bbb6f2009-08-07 17:22:35 +02003573 struct ieee80211_channel *chan, *fixedchan;
Johannes Berg3e5d7642009-07-07 14:37:26 +02003574 const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +02003575 int err, ssid_len, ie_len = 0;
3576 bool use_mfp = false;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003577
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003578 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3579 return -EINVAL;
3580
3581 if (!info->attrs[NL80211_ATTR_MAC] ||
Johannes Berg19957bb2009-07-02 17:20:43 +02003582 !info->attrs[NL80211_ATTR_SSID] ||
3583 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003584 return -EINVAL;
3585
Jouni Malinen636a5d32009-03-19 13:39:22 +02003586 rtnl_lock();
3587
Johannes Berg463d0182009-07-14 00:33:35 +02003588 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003589 if (err)
3590 goto unlock_rtnl;
3591
Johannes Berg19957bb2009-07-02 17:20:43 +02003592 if (!rdev->ops->assoc) {
Jouni Malinen636a5d32009-03-19 13:39:22 +02003593 err = -EOPNOTSUPP;
3594 goto out;
3595 }
3596
Jouni Malineneec60b02009-03-20 21:21:19 +02003597 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
3598 err = -EOPNOTSUPP;
3599 goto out;
3600 }
3601
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003602 if (!netif_running(dev)) {
3603 err = -ENETDOWN;
3604 goto out;
3605 }
3606
Johannes Berg19957bb2009-07-02 17:20:43 +02003607 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003608
Johannes Berg19957bb2009-07-02 17:20:43 +02003609 chan = ieee80211_get_channel(&rdev->wiphy,
3610 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
3611 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) {
3612 err = -EINVAL;
3613 goto out;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003614 }
3615
Johannes Berg59bbb6f2009-08-07 17:22:35 +02003616 mutex_lock(&rdev->devlist_mtx);
Jouni Malinen27e310c2010-01-19 10:53:30 -08003617 wdev = dev->ieee80211_ptr;
3618 fixedchan = rdev_fixed_channel(rdev, wdev);
Johannes Berg59bbb6f2009-08-07 17:22:35 +02003619 if (fixedchan && chan != fixedchan) {
3620 err = -EBUSY;
3621 mutex_unlock(&rdev->devlist_mtx);
3622 goto out;
3623 }
3624 mutex_unlock(&rdev->devlist_mtx);
3625
Johannes Berg19957bb2009-07-02 17:20:43 +02003626 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3627 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003628
3629 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02003630 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3631 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003632 }
3633
Jouni Malinendc6382ce2009-05-06 22:09:37 +03003634 if (info->attrs[NL80211_ATTR_USE_MFP]) {
Johannes Berg4f5dadc2009-07-07 03:56:10 +02003635 enum nl80211_mfp mfp =
Jouni Malinendc6382ce2009-05-06 22:09:37 +03003636 nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
Johannes Berg4f5dadc2009-07-07 03:56:10 +02003637 if (mfp == NL80211_MFP_REQUIRED)
Johannes Berg19957bb2009-07-02 17:20:43 +02003638 use_mfp = true;
Johannes Berg4f5dadc2009-07-07 03:56:10 +02003639 else if (mfp != NL80211_MFP_NO) {
Jouni Malinendc6382ce2009-05-06 22:09:37 +03003640 err = -EINVAL;
3641 goto out;
3642 }
3643 }
3644
Johannes Berg3e5d7642009-07-07 14:37:26 +02003645 if (info->attrs[NL80211_ATTR_PREV_BSSID])
3646 prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
3647
Johannes Berg3dc27d22009-07-02 21:36:37 +02003648 err = nl80211_crypto_settings(info, &crypto, 1);
Samuel Ortizb23aa672009-07-01 21:26:54 +02003649 if (!err)
Johannes Berg3e5d7642009-07-07 14:37:26 +02003650 err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
3651 ssid, ssid_len, ie, ie_len, use_mfp,
Johannes Berg19957bb2009-07-02 17:20:43 +02003652 &crypto);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003653
3654out:
Johannes Berg4d0c8ae2009-07-07 03:56:09 +02003655 cfg80211_unlock_rdev(rdev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003656 dev_put(dev);
3657unlock_rtnl:
3658 rtnl_unlock();
3659 return err;
3660}
3661
3662static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
3663{
Johannes Berg79c97e92009-07-07 03:56:12 +02003664 struct cfg80211_registered_device *rdev;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003665 struct net_device *dev;
Johannes Berg19957bb2009-07-02 17:20:43 +02003666 const u8 *ie = NULL, *bssid;
3667 int err, ie_len = 0;
3668 u16 reason_code;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003669
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003670 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3671 return -EINVAL;
3672
3673 if (!info->attrs[NL80211_ATTR_MAC])
3674 return -EINVAL;
3675
3676 if (!info->attrs[NL80211_ATTR_REASON_CODE])
3677 return -EINVAL;
3678
Jouni Malinen636a5d32009-03-19 13:39:22 +02003679 rtnl_lock();
3680
Johannes Berg463d0182009-07-14 00:33:35 +02003681 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003682 if (err)
3683 goto unlock_rtnl;
3684
Johannes Berg79c97e92009-07-07 03:56:12 +02003685 if (!rdev->ops->deauth) {
Jouni Malinen636a5d32009-03-19 13:39:22 +02003686 err = -EOPNOTSUPP;
3687 goto out;
3688 }
3689
Jouni Malineneec60b02009-03-20 21:21:19 +02003690 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
3691 err = -EOPNOTSUPP;
3692 goto out;
3693 }
3694
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003695 if (!netif_running(dev)) {
3696 err = -ENETDOWN;
3697 goto out;
3698 }
3699
Johannes Berg19957bb2009-07-02 17:20:43 +02003700 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003701
Johannes Berg19957bb2009-07-02 17:20:43 +02003702 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
3703 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003704 /* Reason Code 0 is reserved */
3705 err = -EINVAL;
3706 goto out;
Jouni Malinen255e7372009-03-20 21:21:17 +02003707 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02003708
3709 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02003710 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3711 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003712 }
3713
Johannes Berg79c97e92009-07-07 03:56:12 +02003714 err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003715
3716out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003717 cfg80211_unlock_rdev(rdev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003718 dev_put(dev);
3719unlock_rtnl:
3720 rtnl_unlock();
3721 return err;
3722}
3723
3724static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
3725{
Johannes Berg79c97e92009-07-07 03:56:12 +02003726 struct cfg80211_registered_device *rdev;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003727 struct net_device *dev;
Johannes Berg19957bb2009-07-02 17:20:43 +02003728 const u8 *ie = NULL, *bssid;
3729 int err, ie_len = 0;
3730 u16 reason_code;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003731
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003732 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3733 return -EINVAL;
3734
3735 if (!info->attrs[NL80211_ATTR_MAC])
3736 return -EINVAL;
3737
3738 if (!info->attrs[NL80211_ATTR_REASON_CODE])
3739 return -EINVAL;
3740
Jouni Malinen636a5d32009-03-19 13:39:22 +02003741 rtnl_lock();
3742
Johannes Berg463d0182009-07-14 00:33:35 +02003743 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003744 if (err)
3745 goto unlock_rtnl;
3746
Johannes Berg79c97e92009-07-07 03:56:12 +02003747 if (!rdev->ops->disassoc) {
Jouni Malinen636a5d32009-03-19 13:39:22 +02003748 err = -EOPNOTSUPP;
3749 goto out;
3750 }
3751
Jouni Malineneec60b02009-03-20 21:21:19 +02003752 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
3753 err = -EOPNOTSUPP;
3754 goto out;
3755 }
3756
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003757 if (!netif_running(dev)) {
3758 err = -ENETDOWN;
3759 goto out;
3760 }
3761
Johannes Berg19957bb2009-07-02 17:20:43 +02003762 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003763
Johannes Berg19957bb2009-07-02 17:20:43 +02003764 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
3765 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003766 /* Reason Code 0 is reserved */
3767 err = -EINVAL;
3768 goto out;
Jouni Malinen255e7372009-03-20 21:21:17 +02003769 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02003770
3771 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02003772 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3773 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003774 }
3775
Johannes Berg79c97e92009-07-07 03:56:12 +02003776 err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003777
3778out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003779 cfg80211_unlock_rdev(rdev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003780 dev_put(dev);
3781unlock_rtnl:
3782 rtnl_unlock();
3783 return err;
3784}
3785
Johannes Berg04a773a2009-04-19 21:24:32 +02003786static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
3787{
Johannes Berg79c97e92009-07-07 03:56:12 +02003788 struct cfg80211_registered_device *rdev;
Johannes Berg04a773a2009-04-19 21:24:32 +02003789 struct net_device *dev;
3790 struct cfg80211_ibss_params ibss;
3791 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02003792 struct cfg80211_cached_keys *connkeys = NULL;
Johannes Berg04a773a2009-04-19 21:24:32 +02003793 int err;
3794
Johannes Berg8e30bc52009-04-22 17:45:38 +02003795 memset(&ibss, 0, sizeof(ibss));
3796
Johannes Berg04a773a2009-04-19 21:24:32 +02003797 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3798 return -EINVAL;
3799
3800 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
3801 !info->attrs[NL80211_ATTR_SSID] ||
3802 !nla_len(info->attrs[NL80211_ATTR_SSID]))
3803 return -EINVAL;
3804
Johannes Berg8e30bc52009-04-22 17:45:38 +02003805 ibss.beacon_interval = 100;
3806
3807 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
3808 ibss.beacon_interval =
3809 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
3810 if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000)
3811 return -EINVAL;
3812 }
3813
Johannes Berg04a773a2009-04-19 21:24:32 +02003814 rtnl_lock();
3815
Johannes Berg463d0182009-07-14 00:33:35 +02003816 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg04a773a2009-04-19 21:24:32 +02003817 if (err)
3818 goto unlock_rtnl;
3819
Johannes Berg79c97e92009-07-07 03:56:12 +02003820 if (!rdev->ops->join_ibss) {
Johannes Berg04a773a2009-04-19 21:24:32 +02003821 err = -EOPNOTSUPP;
3822 goto out;
3823 }
3824
3825 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
3826 err = -EOPNOTSUPP;
3827 goto out;
3828 }
3829
3830 if (!netif_running(dev)) {
3831 err = -ENETDOWN;
3832 goto out;
3833 }
3834
Johannes Berg79c97e92009-07-07 03:56:12 +02003835 wiphy = &rdev->wiphy;
Johannes Berg04a773a2009-04-19 21:24:32 +02003836
3837 if (info->attrs[NL80211_ATTR_MAC])
3838 ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
3839 ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3840 ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
3841
3842 if (info->attrs[NL80211_ATTR_IE]) {
3843 ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3844 ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3845 }
3846
3847 ibss.channel = ieee80211_get_channel(wiphy,
3848 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
3849 if (!ibss.channel ||
3850 ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
3851 ibss.channel->flags & IEEE80211_CHAN_DISABLED) {
3852 err = -EINVAL;
3853 goto out;
3854 }
3855
3856 ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
Johannes Bergfffd0932009-07-08 14:22:54 +02003857 ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
Johannes Berg04a773a2009-04-19 21:24:32 +02003858
Johannes Bergfffd0932009-07-08 14:22:54 +02003859 if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
3860 connkeys = nl80211_parse_connkeys(rdev,
3861 info->attrs[NL80211_ATTR_KEYS]);
3862 if (IS_ERR(connkeys)) {
3863 err = PTR_ERR(connkeys);
3864 connkeys = NULL;
3865 goto out;
3866 }
3867 }
3868
3869 err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
Johannes Berg04a773a2009-04-19 21:24:32 +02003870
3871out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003872 cfg80211_unlock_rdev(rdev);
Johannes Berg04a773a2009-04-19 21:24:32 +02003873 dev_put(dev);
3874unlock_rtnl:
Johannes Bergfffd0932009-07-08 14:22:54 +02003875 if (err)
3876 kfree(connkeys);
Johannes Berg04a773a2009-04-19 21:24:32 +02003877 rtnl_unlock();
3878 return err;
3879}
3880
3881static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
3882{
Johannes Berg79c97e92009-07-07 03:56:12 +02003883 struct cfg80211_registered_device *rdev;
Johannes Berg04a773a2009-04-19 21:24:32 +02003884 struct net_device *dev;
3885 int err;
3886
3887 rtnl_lock();
3888
Johannes Berg463d0182009-07-14 00:33:35 +02003889 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg04a773a2009-04-19 21:24:32 +02003890 if (err)
3891 goto unlock_rtnl;
3892
Johannes Berg79c97e92009-07-07 03:56:12 +02003893 if (!rdev->ops->leave_ibss) {
Johannes Berg04a773a2009-04-19 21:24:32 +02003894 err = -EOPNOTSUPP;
3895 goto out;
3896 }
3897
3898 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
3899 err = -EOPNOTSUPP;
3900 goto out;
3901 }
3902
3903 if (!netif_running(dev)) {
3904 err = -ENETDOWN;
3905 goto out;
3906 }
3907
Johannes Berg79c97e92009-07-07 03:56:12 +02003908 err = cfg80211_leave_ibss(rdev, dev, false);
Johannes Berg04a773a2009-04-19 21:24:32 +02003909
3910out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003911 cfg80211_unlock_rdev(rdev);
Johannes Berg04a773a2009-04-19 21:24:32 +02003912 dev_put(dev);
3913unlock_rtnl:
3914 rtnl_unlock();
3915 return err;
3916}
3917
Johannes Bergaff89a92009-07-01 21:26:51 +02003918#ifdef CONFIG_NL80211_TESTMODE
3919static struct genl_multicast_group nl80211_testmode_mcgrp = {
3920 .name = "testmode",
3921};
3922
3923static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
3924{
3925 struct cfg80211_registered_device *rdev;
3926 int err;
3927
3928 if (!info->attrs[NL80211_ATTR_TESTDATA])
3929 return -EINVAL;
3930
3931 rtnl_lock();
3932
3933 rdev = cfg80211_get_dev_from_info(info);
3934 if (IS_ERR(rdev)) {
3935 err = PTR_ERR(rdev);
3936 goto unlock_rtnl;
3937 }
3938
3939 err = -EOPNOTSUPP;
3940 if (rdev->ops->testmode_cmd) {
3941 rdev->testmode_info = info;
3942 err = rdev->ops->testmode_cmd(&rdev->wiphy,
3943 nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
3944 nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
3945 rdev->testmode_info = NULL;
3946 }
3947
Johannes Berg4d0c8ae2009-07-07 03:56:09 +02003948 cfg80211_unlock_rdev(rdev);
Johannes Bergaff89a92009-07-01 21:26:51 +02003949
3950 unlock_rtnl:
3951 rtnl_unlock();
3952 return err;
3953}
3954
3955static struct sk_buff *
3956__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
3957 int approxlen, u32 pid, u32 seq, gfp_t gfp)
3958{
3959 struct sk_buff *skb;
3960 void *hdr;
3961 struct nlattr *data;
3962
3963 skb = nlmsg_new(approxlen + 100, gfp);
3964 if (!skb)
3965 return NULL;
3966
3967 hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE);
3968 if (!hdr) {
3969 kfree_skb(skb);
3970 return NULL;
3971 }
3972
3973 NLA_PUT_U32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
3974 data = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
3975
3976 ((void **)skb->cb)[0] = rdev;
3977 ((void **)skb->cb)[1] = hdr;
3978 ((void **)skb->cb)[2] = data;
3979
3980 return skb;
3981
3982 nla_put_failure:
3983 kfree_skb(skb);
3984 return NULL;
3985}
3986
3987struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
3988 int approxlen)
3989{
3990 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
3991
3992 if (WARN_ON(!rdev->testmode_info))
3993 return NULL;
3994
3995 return __cfg80211_testmode_alloc_skb(rdev, approxlen,
3996 rdev->testmode_info->snd_pid,
3997 rdev->testmode_info->snd_seq,
3998 GFP_KERNEL);
3999}
4000EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb);
4001
4002int cfg80211_testmode_reply(struct sk_buff *skb)
4003{
4004 struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
4005 void *hdr = ((void **)skb->cb)[1];
4006 struct nlattr *data = ((void **)skb->cb)[2];
4007
4008 if (WARN_ON(!rdev->testmode_info)) {
4009 kfree_skb(skb);
4010 return -EINVAL;
4011 }
4012
4013 nla_nest_end(skb, data);
4014 genlmsg_end(skb, hdr);
4015 return genlmsg_reply(skb, rdev->testmode_info);
4016}
4017EXPORT_SYMBOL(cfg80211_testmode_reply);
4018
4019struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
4020 int approxlen, gfp_t gfp)
4021{
4022 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
4023
4024 return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp);
4025}
4026EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb);
4027
4028void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
4029{
4030 void *hdr = ((void **)skb->cb)[1];
4031 struct nlattr *data = ((void **)skb->cb)[2];
4032
4033 nla_nest_end(skb, data);
4034 genlmsg_end(skb, hdr);
4035 genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp);
4036}
4037EXPORT_SYMBOL(cfg80211_testmode_event);
4038#endif
4039
Samuel Ortizb23aa672009-07-01 21:26:54 +02004040static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
4041{
Johannes Berg79c97e92009-07-07 03:56:12 +02004042 struct cfg80211_registered_device *rdev;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004043 struct net_device *dev;
4044 struct cfg80211_connect_params connect;
4045 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02004046 struct cfg80211_cached_keys *connkeys = NULL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004047 int err;
4048
4049 memset(&connect, 0, sizeof(connect));
4050
4051 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4052 return -EINVAL;
4053
4054 if (!info->attrs[NL80211_ATTR_SSID] ||
4055 !nla_len(info->attrs[NL80211_ATTR_SSID]))
4056 return -EINVAL;
4057
4058 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
4059 connect.auth_type =
4060 nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
4061 if (!nl80211_valid_auth_type(connect.auth_type))
4062 return -EINVAL;
4063 } else
4064 connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
4065
4066 connect.privacy = info->attrs[NL80211_ATTR_PRIVACY];
4067
Johannes Berg3dc27d22009-07-02 21:36:37 +02004068 err = nl80211_crypto_settings(info, &connect.crypto,
4069 NL80211_MAX_NR_CIPHER_SUITES);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004070 if (err)
4071 return err;
4072 rtnl_lock();
4073
Johannes Berg463d0182009-07-14 00:33:35 +02004074 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004075 if (err)
4076 goto unlock_rtnl;
4077
4078 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
4079 err = -EOPNOTSUPP;
4080 goto out;
4081 }
4082
4083 if (!netif_running(dev)) {
4084 err = -ENETDOWN;
4085 goto out;
4086 }
4087
Johannes Berg79c97e92009-07-07 03:56:12 +02004088 wiphy = &rdev->wiphy;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004089
Samuel Ortizb23aa672009-07-01 21:26:54 +02004090 if (info->attrs[NL80211_ATTR_MAC])
4091 connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
4092 connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4093 connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4094
4095 if (info->attrs[NL80211_ATTR_IE]) {
4096 connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4097 connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4098 }
4099
4100 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
4101 connect.channel =
4102 ieee80211_get_channel(wiphy,
4103 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
4104 if (!connect.channel ||
4105 connect.channel->flags & IEEE80211_CHAN_DISABLED) {
4106 err = -EINVAL;
4107 goto out;
4108 }
4109 }
4110
Johannes Bergfffd0932009-07-08 14:22:54 +02004111 if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
4112 connkeys = nl80211_parse_connkeys(rdev,
4113 info->attrs[NL80211_ATTR_KEYS]);
4114 if (IS_ERR(connkeys)) {
4115 err = PTR_ERR(connkeys);
4116 connkeys = NULL;
4117 goto out;
4118 }
4119 }
4120
4121 err = cfg80211_connect(rdev, dev, &connect, connkeys);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004122
4123out:
Johannes Berg79c97e92009-07-07 03:56:12 +02004124 cfg80211_unlock_rdev(rdev);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004125 dev_put(dev);
4126unlock_rtnl:
Johannes Bergfffd0932009-07-08 14:22:54 +02004127 if (err)
4128 kfree(connkeys);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004129 rtnl_unlock();
4130 return err;
4131}
4132
4133static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
4134{
Johannes Berg79c97e92009-07-07 03:56:12 +02004135 struct cfg80211_registered_device *rdev;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004136 struct net_device *dev;
4137 int err;
4138 u16 reason;
4139
4140 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4141 reason = WLAN_REASON_DEAUTH_LEAVING;
4142 else
4143 reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4144
4145 if (reason == 0)
4146 return -EINVAL;
4147
4148 rtnl_lock();
4149
Johannes Berg463d0182009-07-14 00:33:35 +02004150 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004151 if (err)
4152 goto unlock_rtnl;
4153
4154 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
4155 err = -EOPNOTSUPP;
4156 goto out;
4157 }
4158
4159 if (!netif_running(dev)) {
4160 err = -ENETDOWN;
4161 goto out;
4162 }
4163
Johannes Berg79c97e92009-07-07 03:56:12 +02004164 err = cfg80211_disconnect(rdev, dev, reason, true);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004165
4166out:
Johannes Berg79c97e92009-07-07 03:56:12 +02004167 cfg80211_unlock_rdev(rdev);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004168 dev_put(dev);
4169unlock_rtnl:
4170 rtnl_unlock();
4171 return err;
4172}
4173
Johannes Berg463d0182009-07-14 00:33:35 +02004174static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
4175{
4176 struct cfg80211_registered_device *rdev;
4177 struct net *net;
4178 int err;
4179 u32 pid;
4180
4181 if (!info->attrs[NL80211_ATTR_PID])
4182 return -EINVAL;
4183
4184 pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
4185
4186 rtnl_lock();
4187
4188 rdev = cfg80211_get_dev_from_info(info);
4189 if (IS_ERR(rdev)) {
4190 err = PTR_ERR(rdev);
Johannes Berg8a8e05e2009-10-08 21:02:02 +02004191 goto out_rtnl;
Johannes Berg463d0182009-07-14 00:33:35 +02004192 }
4193
4194 net = get_net_ns_by_pid(pid);
4195 if (IS_ERR(net)) {
4196 err = PTR_ERR(net);
4197 goto out;
4198 }
4199
4200 err = 0;
4201
4202 /* check if anything to do */
4203 if (net_eq(wiphy_net(&rdev->wiphy), net))
4204 goto out_put_net;
4205
4206 err = cfg80211_switch_netns(rdev, net);
4207 out_put_net:
4208 put_net(net);
4209 out:
4210 cfg80211_unlock_rdev(rdev);
Johannes Berg8a8e05e2009-10-08 21:02:02 +02004211 out_rtnl:
Johannes Berg463d0182009-07-14 00:33:35 +02004212 rtnl_unlock();
4213 return err;
4214}
4215
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004216static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info)
4217{
4218 struct cfg80211_registered_device *rdev;
4219 int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev,
4220 struct cfg80211_pmksa *pmksa) = NULL;
4221 int err;
4222 struct net_device *dev;
4223 struct cfg80211_pmksa pmksa;
4224
4225 memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
4226
4227 if (!info->attrs[NL80211_ATTR_MAC])
4228 return -EINVAL;
4229
4230 if (!info->attrs[NL80211_ATTR_PMKID])
4231 return -EINVAL;
4232
4233 rtnl_lock();
4234
4235 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
4236 if (err)
4237 goto out_rtnl;
4238
4239 pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]);
4240 pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
4241
4242 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
4243 err = -EOPNOTSUPP;
4244 goto out;
4245 }
4246
4247 switch (info->genlhdr->cmd) {
4248 case NL80211_CMD_SET_PMKSA:
4249 rdev_ops = rdev->ops->set_pmksa;
4250 break;
4251 case NL80211_CMD_DEL_PMKSA:
4252 rdev_ops = rdev->ops->del_pmksa;
4253 break;
4254 default:
4255 WARN_ON(1);
4256 break;
4257 }
4258
4259 if (!rdev_ops) {
4260 err = -EOPNOTSUPP;
4261 goto out;
4262 }
4263
4264 err = rdev_ops(&rdev->wiphy, dev, &pmksa);
4265
4266 out:
4267 cfg80211_unlock_rdev(rdev);
4268 dev_put(dev);
4269 out_rtnl:
4270 rtnl_unlock();
4271
4272 return err;
4273}
4274
4275static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info)
4276{
4277 struct cfg80211_registered_device *rdev;
4278 int err;
4279 struct net_device *dev;
4280
4281 rtnl_lock();
4282
4283 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
4284 if (err)
4285 goto out_rtnl;
4286
4287 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
4288 err = -EOPNOTSUPP;
4289 goto out;
4290 }
4291
4292 if (!rdev->ops->flush_pmksa) {
4293 err = -EOPNOTSUPP;
4294 goto out;
4295 }
4296
4297 err = rdev->ops->flush_pmksa(&rdev->wiphy, dev);
4298
4299 out:
4300 cfg80211_unlock_rdev(rdev);
4301 dev_put(dev);
4302 out_rtnl:
4303 rtnl_unlock();
4304
4305 return err;
4306
4307}
4308
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004309static int nl80211_remain_on_channel(struct sk_buff *skb,
4310 struct genl_info *info)
4311{
4312 struct cfg80211_registered_device *rdev;
4313 struct net_device *dev;
4314 struct ieee80211_channel *chan;
4315 struct sk_buff *msg;
4316 void *hdr;
4317 u64 cookie;
4318 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
4319 u32 freq, duration;
4320 int err;
4321
4322 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
4323 !info->attrs[NL80211_ATTR_DURATION])
4324 return -EINVAL;
4325
4326 duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
4327
4328 /*
4329 * We should be on that channel for at least one jiffie,
4330 * and more than 5 seconds seems excessive.
4331 */
4332 if (!duration || !msecs_to_jiffies(duration) || duration > 5000)
4333 return -EINVAL;
4334
4335 rtnl_lock();
4336
4337 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
4338 if (err)
4339 goto unlock_rtnl;
4340
4341 if (!rdev->ops->remain_on_channel) {
4342 err = -EOPNOTSUPP;
4343 goto out;
4344 }
4345
4346 if (!netif_running(dev)) {
4347 err = -ENETDOWN;
4348 goto out;
4349 }
4350
4351 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
4352 channel_type = nla_get_u32(
4353 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
4354 if (channel_type != NL80211_CHAN_NO_HT &&
4355 channel_type != NL80211_CHAN_HT20 &&
4356 channel_type != NL80211_CHAN_HT40PLUS &&
4357 channel_type != NL80211_CHAN_HT40MINUS)
4358 err = -EINVAL;
4359 goto out;
4360 }
4361
4362 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
4363 chan = rdev_freq_to_chan(rdev, freq, channel_type);
4364 if (chan == NULL) {
4365 err = -EINVAL;
4366 goto out;
4367 }
4368
4369 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
4370 if (!msg) {
4371 err = -ENOMEM;
4372 goto out;
4373 }
4374
4375 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
4376 NL80211_CMD_REMAIN_ON_CHANNEL);
4377
4378 if (IS_ERR(hdr)) {
4379 err = PTR_ERR(hdr);
4380 goto free_msg;
4381 }
4382
4383 err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan,
4384 channel_type, duration, &cookie);
4385
4386 if (err)
4387 goto free_msg;
4388
4389 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
4390
4391 genlmsg_end(msg, hdr);
4392 err = genlmsg_reply(msg, info);
4393 goto out;
4394
4395 nla_put_failure:
4396 err = -ENOBUFS;
4397 free_msg:
4398 nlmsg_free(msg);
4399 out:
4400 cfg80211_unlock_rdev(rdev);
4401 dev_put(dev);
4402 unlock_rtnl:
4403 rtnl_unlock();
4404 return err;
4405}
4406
4407static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,
4408 struct genl_info *info)
4409{
4410 struct cfg80211_registered_device *rdev;
4411 struct net_device *dev;
4412 u64 cookie;
4413 int err;
4414
4415 if (!info->attrs[NL80211_ATTR_COOKIE])
4416 return -EINVAL;
4417
4418 rtnl_lock();
4419
4420 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
4421 if (err)
4422 goto unlock_rtnl;
4423
4424 if (!rdev->ops->cancel_remain_on_channel) {
4425 err = -EOPNOTSUPP;
4426 goto out;
4427 }
4428
4429 if (!netif_running(dev)) {
4430 err = -ENETDOWN;
4431 goto out;
4432 }
4433
4434 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
4435
4436 err = rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie);
4437
4438 out:
4439 cfg80211_unlock_rdev(rdev);
4440 dev_put(dev);
4441 unlock_rtnl:
4442 rtnl_unlock();
4443 return err;
4444}
4445
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004446static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
4447 u8 *rates, u8 rates_len)
4448{
4449 u8 i;
4450 u32 mask = 0;
4451
4452 for (i = 0; i < rates_len; i++) {
4453 int rate = (rates[i] & 0x7f) * 5;
4454 int ridx;
4455 for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
4456 struct ieee80211_rate *srate =
4457 &sband->bitrates[ridx];
4458 if (rate == srate->bitrate) {
4459 mask |= 1 << ridx;
4460 break;
4461 }
4462 }
4463 if (ridx == sband->n_bitrates)
4464 return 0; /* rate not found */
4465 }
4466
4467 return mask;
4468}
4469
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00004470static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004471 [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
4472 .len = NL80211_MAX_SUPP_RATES },
4473};
4474
4475static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
4476 struct genl_info *info)
4477{
4478 struct nlattr *tb[NL80211_TXRATE_MAX + 1];
4479 struct cfg80211_registered_device *rdev;
4480 struct cfg80211_bitrate_mask mask;
4481 int err, rem, i;
4482 struct net_device *dev;
4483 struct nlattr *tx_rates;
4484 struct ieee80211_supported_band *sband;
4485
4486 if (info->attrs[NL80211_ATTR_TX_RATES] == NULL)
4487 return -EINVAL;
4488
4489 rtnl_lock();
4490
4491 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
4492 if (err)
4493 goto unlock_rtnl;
4494
4495 if (!rdev->ops->set_bitrate_mask) {
4496 err = -EOPNOTSUPP;
4497 goto unlock;
4498 }
4499
4500 memset(&mask, 0, sizeof(mask));
4501 /* Default to all rates enabled */
4502 for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
4503 sband = rdev->wiphy.bands[i];
4504 mask.control[i].legacy =
4505 sband ? (1 << sband->n_bitrates) - 1 : 0;
4506 }
4507
4508 /*
4509 * The nested attribute uses enum nl80211_band as the index. This maps
4510 * directly to the enum ieee80211_band values used in cfg80211.
4511 */
4512 nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem)
4513 {
4514 enum ieee80211_band band = nla_type(tx_rates);
4515 if (band < 0 || band >= IEEE80211_NUM_BANDS) {
4516 err = -EINVAL;
4517 goto unlock;
4518 }
4519 sband = rdev->wiphy.bands[band];
4520 if (sband == NULL) {
4521 err = -EINVAL;
4522 goto unlock;
4523 }
4524 nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
4525 nla_len(tx_rates), nl80211_txattr_policy);
4526 if (tb[NL80211_TXRATE_LEGACY]) {
4527 mask.control[band].legacy = rateset_to_mask(
4528 sband,
4529 nla_data(tb[NL80211_TXRATE_LEGACY]),
4530 nla_len(tb[NL80211_TXRATE_LEGACY]));
4531 if (mask.control[band].legacy == 0) {
4532 err = -EINVAL;
4533 goto unlock;
4534 }
4535 }
4536 }
4537
4538 err = rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask);
4539
4540 unlock:
4541 dev_put(dev);
4542 cfg80211_unlock_rdev(rdev);
4543 unlock_rtnl:
4544 rtnl_unlock();
4545 return err;
4546}
4547
Johannes Berg55682962007-09-20 13:09:35 -04004548static struct genl_ops nl80211_ops[] = {
4549 {
4550 .cmd = NL80211_CMD_GET_WIPHY,
4551 .doit = nl80211_get_wiphy,
4552 .dumpit = nl80211_dump_wiphy,
4553 .policy = nl80211_policy,
4554 /* can be retrieved by unprivileged users */
4555 },
4556 {
4557 .cmd = NL80211_CMD_SET_WIPHY,
4558 .doit = nl80211_set_wiphy,
4559 .policy = nl80211_policy,
4560 .flags = GENL_ADMIN_PERM,
4561 },
4562 {
4563 .cmd = NL80211_CMD_GET_INTERFACE,
4564 .doit = nl80211_get_interface,
4565 .dumpit = nl80211_dump_interface,
4566 .policy = nl80211_policy,
4567 /* can be retrieved by unprivileged users */
4568 },
4569 {
4570 .cmd = NL80211_CMD_SET_INTERFACE,
4571 .doit = nl80211_set_interface,
4572 .policy = nl80211_policy,
4573 .flags = GENL_ADMIN_PERM,
4574 },
4575 {
4576 .cmd = NL80211_CMD_NEW_INTERFACE,
4577 .doit = nl80211_new_interface,
4578 .policy = nl80211_policy,
4579 .flags = GENL_ADMIN_PERM,
4580 },
4581 {
4582 .cmd = NL80211_CMD_DEL_INTERFACE,
4583 .doit = nl80211_del_interface,
4584 .policy = nl80211_policy,
4585 .flags = GENL_ADMIN_PERM,
4586 },
Johannes Berg41ade002007-12-19 02:03:29 +01004587 {
4588 .cmd = NL80211_CMD_GET_KEY,
4589 .doit = nl80211_get_key,
4590 .policy = nl80211_policy,
4591 .flags = GENL_ADMIN_PERM,
4592 },
4593 {
4594 .cmd = NL80211_CMD_SET_KEY,
4595 .doit = nl80211_set_key,
4596 .policy = nl80211_policy,
4597 .flags = GENL_ADMIN_PERM,
4598 },
4599 {
4600 .cmd = NL80211_CMD_NEW_KEY,
4601 .doit = nl80211_new_key,
4602 .policy = nl80211_policy,
4603 .flags = GENL_ADMIN_PERM,
4604 },
4605 {
4606 .cmd = NL80211_CMD_DEL_KEY,
4607 .doit = nl80211_del_key,
4608 .policy = nl80211_policy,
4609 .flags = GENL_ADMIN_PERM,
4610 },
Johannes Berged1b6cc2007-12-19 02:03:32 +01004611 {
4612 .cmd = NL80211_CMD_SET_BEACON,
4613 .policy = nl80211_policy,
4614 .flags = GENL_ADMIN_PERM,
4615 .doit = nl80211_addset_beacon,
4616 },
4617 {
4618 .cmd = NL80211_CMD_NEW_BEACON,
4619 .policy = nl80211_policy,
4620 .flags = GENL_ADMIN_PERM,
4621 .doit = nl80211_addset_beacon,
4622 },
4623 {
4624 .cmd = NL80211_CMD_DEL_BEACON,
4625 .policy = nl80211_policy,
4626 .flags = GENL_ADMIN_PERM,
4627 .doit = nl80211_del_beacon,
4628 },
Johannes Berg5727ef12007-12-19 02:03:34 +01004629 {
4630 .cmd = NL80211_CMD_GET_STATION,
4631 .doit = nl80211_get_station,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004632 .dumpit = nl80211_dump_station,
Johannes Berg5727ef12007-12-19 02:03:34 +01004633 .policy = nl80211_policy,
Johannes Berg5727ef12007-12-19 02:03:34 +01004634 },
4635 {
4636 .cmd = NL80211_CMD_SET_STATION,
4637 .doit = nl80211_set_station,
4638 .policy = nl80211_policy,
4639 .flags = GENL_ADMIN_PERM,
4640 },
4641 {
4642 .cmd = NL80211_CMD_NEW_STATION,
4643 .doit = nl80211_new_station,
4644 .policy = nl80211_policy,
4645 .flags = GENL_ADMIN_PERM,
4646 },
4647 {
4648 .cmd = NL80211_CMD_DEL_STATION,
4649 .doit = nl80211_del_station,
4650 .policy = nl80211_policy,
4651 .flags = GENL_ADMIN_PERM,
4652 },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004653 {
4654 .cmd = NL80211_CMD_GET_MPATH,
4655 .doit = nl80211_get_mpath,
4656 .dumpit = nl80211_dump_mpath,
4657 .policy = nl80211_policy,
4658 .flags = GENL_ADMIN_PERM,
4659 },
4660 {
4661 .cmd = NL80211_CMD_SET_MPATH,
4662 .doit = nl80211_set_mpath,
4663 .policy = nl80211_policy,
4664 .flags = GENL_ADMIN_PERM,
4665 },
4666 {
4667 .cmd = NL80211_CMD_NEW_MPATH,
4668 .doit = nl80211_new_mpath,
4669 .policy = nl80211_policy,
4670 .flags = GENL_ADMIN_PERM,
4671 },
4672 {
4673 .cmd = NL80211_CMD_DEL_MPATH,
4674 .doit = nl80211_del_mpath,
4675 .policy = nl80211_policy,
4676 .flags = GENL_ADMIN_PERM,
4677 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +03004678 {
4679 .cmd = NL80211_CMD_SET_BSS,
4680 .doit = nl80211_set_bss,
4681 .policy = nl80211_policy,
4682 .flags = GENL_ADMIN_PERM,
4683 },
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004684 {
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004685 .cmd = NL80211_CMD_GET_REG,
4686 .doit = nl80211_get_reg,
4687 .policy = nl80211_policy,
4688 /* can be retrieved by unprivileged users */
4689 },
4690 {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004691 .cmd = NL80211_CMD_SET_REG,
4692 .doit = nl80211_set_reg,
4693 .policy = nl80211_policy,
4694 .flags = GENL_ADMIN_PERM,
4695 },
4696 {
4697 .cmd = NL80211_CMD_REQ_SET_REG,
4698 .doit = nl80211_req_set_reg,
4699 .policy = nl80211_policy,
4700 .flags = GENL_ADMIN_PERM,
4701 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004702 {
4703 .cmd = NL80211_CMD_GET_MESH_PARAMS,
4704 .doit = nl80211_get_mesh_params,
4705 .policy = nl80211_policy,
4706 /* can be retrieved by unprivileged users */
4707 },
4708 {
4709 .cmd = NL80211_CMD_SET_MESH_PARAMS,
4710 .doit = nl80211_set_mesh_params,
4711 .policy = nl80211_policy,
4712 .flags = GENL_ADMIN_PERM,
4713 },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +02004714 {
Johannes Berg2a519312009-02-10 21:25:55 +01004715 .cmd = NL80211_CMD_TRIGGER_SCAN,
4716 .doit = nl80211_trigger_scan,
4717 .policy = nl80211_policy,
4718 .flags = GENL_ADMIN_PERM,
4719 },
4720 {
4721 .cmd = NL80211_CMD_GET_SCAN,
4722 .policy = nl80211_policy,
4723 .dumpit = nl80211_dump_scan,
4724 },
Jouni Malinen636a5d32009-03-19 13:39:22 +02004725 {
4726 .cmd = NL80211_CMD_AUTHENTICATE,
4727 .doit = nl80211_authenticate,
4728 .policy = nl80211_policy,
4729 .flags = GENL_ADMIN_PERM,
4730 },
4731 {
4732 .cmd = NL80211_CMD_ASSOCIATE,
4733 .doit = nl80211_associate,
4734 .policy = nl80211_policy,
4735 .flags = GENL_ADMIN_PERM,
4736 },
4737 {
4738 .cmd = NL80211_CMD_DEAUTHENTICATE,
4739 .doit = nl80211_deauthenticate,
4740 .policy = nl80211_policy,
4741 .flags = GENL_ADMIN_PERM,
4742 },
4743 {
4744 .cmd = NL80211_CMD_DISASSOCIATE,
4745 .doit = nl80211_disassociate,
4746 .policy = nl80211_policy,
4747 .flags = GENL_ADMIN_PERM,
4748 },
Johannes Berg04a773a2009-04-19 21:24:32 +02004749 {
4750 .cmd = NL80211_CMD_JOIN_IBSS,
4751 .doit = nl80211_join_ibss,
4752 .policy = nl80211_policy,
4753 .flags = GENL_ADMIN_PERM,
4754 },
4755 {
4756 .cmd = NL80211_CMD_LEAVE_IBSS,
4757 .doit = nl80211_leave_ibss,
4758 .policy = nl80211_policy,
4759 .flags = GENL_ADMIN_PERM,
4760 },
Johannes Bergaff89a92009-07-01 21:26:51 +02004761#ifdef CONFIG_NL80211_TESTMODE
4762 {
4763 .cmd = NL80211_CMD_TESTMODE,
4764 .doit = nl80211_testmode_do,
4765 .policy = nl80211_policy,
4766 .flags = GENL_ADMIN_PERM,
4767 },
4768#endif
Samuel Ortizb23aa672009-07-01 21:26:54 +02004769 {
4770 .cmd = NL80211_CMD_CONNECT,
4771 .doit = nl80211_connect,
4772 .policy = nl80211_policy,
4773 .flags = GENL_ADMIN_PERM,
4774 },
4775 {
4776 .cmd = NL80211_CMD_DISCONNECT,
4777 .doit = nl80211_disconnect,
4778 .policy = nl80211_policy,
4779 .flags = GENL_ADMIN_PERM,
4780 },
Johannes Berg463d0182009-07-14 00:33:35 +02004781 {
4782 .cmd = NL80211_CMD_SET_WIPHY_NETNS,
4783 .doit = nl80211_wiphy_netns,
4784 .policy = nl80211_policy,
4785 .flags = GENL_ADMIN_PERM,
4786 },
Holger Schurig61fa7132009-11-11 12:25:40 +01004787 {
4788 .cmd = NL80211_CMD_GET_SURVEY,
4789 .policy = nl80211_policy,
4790 .dumpit = nl80211_dump_survey,
4791 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004792 {
4793 .cmd = NL80211_CMD_SET_PMKSA,
4794 .doit = nl80211_setdel_pmksa,
4795 .policy = nl80211_policy,
4796 .flags = GENL_ADMIN_PERM,
4797 },
4798 {
4799 .cmd = NL80211_CMD_DEL_PMKSA,
4800 .doit = nl80211_setdel_pmksa,
4801 .policy = nl80211_policy,
4802 .flags = GENL_ADMIN_PERM,
4803 },
4804 {
4805 .cmd = NL80211_CMD_FLUSH_PMKSA,
4806 .doit = nl80211_flush_pmksa,
4807 .policy = nl80211_policy,
4808 .flags = GENL_ADMIN_PERM,
4809 },
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004810 {
4811 .cmd = NL80211_CMD_REMAIN_ON_CHANNEL,
4812 .doit = nl80211_remain_on_channel,
4813 .policy = nl80211_policy,
4814 .flags = GENL_ADMIN_PERM,
4815 },
4816 {
4817 .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
4818 .doit = nl80211_cancel_remain_on_channel,
4819 .policy = nl80211_policy,
4820 .flags = GENL_ADMIN_PERM,
4821 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004822 {
4823 .cmd = NL80211_CMD_SET_TX_BITRATE_MASK,
4824 .doit = nl80211_set_tx_bitrate_mask,
4825 .policy = nl80211_policy,
4826 .flags = GENL_ADMIN_PERM,
4827 },
Johannes Berg55682962007-09-20 13:09:35 -04004828};
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004829
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004830static struct genl_multicast_group nl80211_mlme_mcgrp = {
4831 .name = "mlme",
4832};
Johannes Berg55682962007-09-20 13:09:35 -04004833
4834/* multicast groups */
4835static struct genl_multicast_group nl80211_config_mcgrp = {
4836 .name = "config",
4837};
Johannes Berg2a519312009-02-10 21:25:55 +01004838static struct genl_multicast_group nl80211_scan_mcgrp = {
4839 .name = "scan",
4840};
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04004841static struct genl_multicast_group nl80211_regulatory_mcgrp = {
4842 .name = "regulatory",
4843};
Johannes Berg55682962007-09-20 13:09:35 -04004844
4845/* notification functions */
4846
4847void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
4848{
4849 struct sk_buff *msg;
4850
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07004851 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04004852 if (!msg)
4853 return;
4854
4855 if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
4856 nlmsg_free(msg);
4857 return;
4858 }
4859
Johannes Berg463d0182009-07-14 00:33:35 +02004860 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4861 nl80211_config_mcgrp.id, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04004862}
4863
Johannes Berg362a4152009-05-24 16:43:15 +02004864static int nl80211_add_scan_req(struct sk_buff *msg,
4865 struct cfg80211_registered_device *rdev)
4866{
4867 struct cfg80211_scan_request *req = rdev->scan_req;
4868 struct nlattr *nest;
4869 int i;
4870
Johannes Berg667503d2009-07-07 03:56:11 +02004871 ASSERT_RDEV_LOCK(rdev);
4872
Johannes Berg362a4152009-05-24 16:43:15 +02004873 if (WARN_ON(!req))
4874 return 0;
4875
4876 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
4877 if (!nest)
4878 goto nla_put_failure;
4879 for (i = 0; i < req->n_ssids; i++)
4880 NLA_PUT(msg, i, req->ssids[i].ssid_len, req->ssids[i].ssid);
4881 nla_nest_end(msg, nest);
4882
4883 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
4884 if (!nest)
4885 goto nla_put_failure;
4886 for (i = 0; i < req->n_channels; i++)
4887 NLA_PUT_U32(msg, i, req->channels[i]->center_freq);
4888 nla_nest_end(msg, nest);
4889
4890 if (req->ie)
4891 NLA_PUT(msg, NL80211_ATTR_IE, req->ie_len, req->ie);
4892
4893 return 0;
4894 nla_put_failure:
4895 return -ENOBUFS;
4896}
4897
Johannes Berga538e2d2009-06-16 19:56:42 +02004898static int nl80211_send_scan_msg(struct sk_buff *msg,
4899 struct cfg80211_registered_device *rdev,
4900 struct net_device *netdev,
4901 u32 pid, u32 seq, int flags,
4902 u32 cmd)
Johannes Berg2a519312009-02-10 21:25:55 +01004903{
4904 void *hdr;
4905
4906 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
4907 if (!hdr)
4908 return -1;
4909
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -05004910 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg2a519312009-02-10 21:25:55 +01004911 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
4912
Johannes Berg362a4152009-05-24 16:43:15 +02004913 /* ignore errors and send incomplete event anyway */
4914 nl80211_add_scan_req(msg, rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01004915
4916 return genlmsg_end(msg, hdr);
4917
4918 nla_put_failure:
4919 genlmsg_cancel(msg, hdr);
4920 return -EMSGSIZE;
4921}
4922
Johannes Berga538e2d2009-06-16 19:56:42 +02004923void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
4924 struct net_device *netdev)
4925{
4926 struct sk_buff *msg;
4927
4928 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
4929 if (!msg)
4930 return;
4931
4932 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
4933 NL80211_CMD_TRIGGER_SCAN) < 0) {
4934 nlmsg_free(msg);
4935 return;
4936 }
4937
Johannes Berg463d0182009-07-14 00:33:35 +02004938 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4939 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berga538e2d2009-06-16 19:56:42 +02004940}
4941
Johannes Berg2a519312009-02-10 21:25:55 +01004942void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
4943 struct net_device *netdev)
4944{
4945 struct sk_buff *msg;
4946
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07004947 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01004948 if (!msg)
4949 return;
4950
Johannes Berga538e2d2009-06-16 19:56:42 +02004951 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
4952 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01004953 nlmsg_free(msg);
4954 return;
4955 }
4956
Johannes Berg463d0182009-07-14 00:33:35 +02004957 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4958 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01004959}
4960
4961void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
4962 struct net_device *netdev)
4963{
4964 struct sk_buff *msg;
4965
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07004966 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01004967 if (!msg)
4968 return;
4969
Johannes Berga538e2d2009-06-16 19:56:42 +02004970 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
4971 NL80211_CMD_SCAN_ABORTED) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01004972 nlmsg_free(msg);
4973 return;
4974 }
4975
Johannes Berg463d0182009-07-14 00:33:35 +02004976 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4977 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01004978}
4979
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04004980/*
4981 * This can happen on global regulatory changes or device specific settings
4982 * based on custom world regulatory domains.
4983 */
4984void nl80211_send_reg_change_event(struct regulatory_request *request)
4985{
4986 struct sk_buff *msg;
4987 void *hdr;
4988
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07004989 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04004990 if (!msg)
4991 return;
4992
4993 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
4994 if (!hdr) {
4995 nlmsg_free(msg);
4996 return;
4997 }
4998
4999 /* Userspace can always count this one always being set */
5000 NLA_PUT_U8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator);
5001
5002 if (request->alpha2[0] == '0' && request->alpha2[1] == '0')
5003 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
5004 NL80211_REGDOM_TYPE_WORLD);
5005 else if (request->alpha2[0] == '9' && request->alpha2[1] == '9')
5006 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
5007 NL80211_REGDOM_TYPE_CUSTOM_WORLD);
5008 else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
5009 request->intersect)
5010 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
5011 NL80211_REGDOM_TYPE_INTERSECTION);
5012 else {
5013 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
5014 NL80211_REGDOM_TYPE_COUNTRY);
5015 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2);
5016 }
5017
5018 if (wiphy_idx_valid(request->wiphy_idx))
5019 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx);
5020
5021 if (genlmsg_end(msg, hdr) < 0) {
5022 nlmsg_free(msg);
5023 return;
5024 }
5025
Johannes Bergbc43b282009-07-25 10:54:13 +02005026 rcu_read_lock();
Johannes Berg463d0182009-07-14 00:33:35 +02005027 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
Johannes Bergbc43b282009-07-25 10:54:13 +02005028 GFP_ATOMIC);
5029 rcu_read_unlock();
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04005030
5031 return;
5032
5033nla_put_failure:
5034 genlmsg_cancel(msg, hdr);
5035 nlmsg_free(msg);
5036}
5037
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005038static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
5039 struct net_device *netdev,
5040 const u8 *buf, size_t len,
Johannes Berge6d6e342009-07-01 21:26:47 +02005041 enum nl80211_commands cmd, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005042{
5043 struct sk_buff *msg;
5044 void *hdr;
5045
Johannes Berge6d6e342009-07-01 21:26:47 +02005046 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005047 if (!msg)
5048 return;
5049
5050 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
5051 if (!hdr) {
5052 nlmsg_free(msg);
5053 return;
5054 }
5055
5056 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5057 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5058 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
5059
5060 if (genlmsg_end(msg, hdr) < 0) {
5061 nlmsg_free(msg);
5062 return;
5063 }
5064
Johannes Berg463d0182009-07-14 00:33:35 +02005065 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5066 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005067 return;
5068
5069 nla_put_failure:
5070 genlmsg_cancel(msg, hdr);
5071 nlmsg_free(msg);
5072}
5073
5074void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02005075 struct net_device *netdev, const u8 *buf,
5076 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005077{
5078 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02005079 NL80211_CMD_AUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005080}
5081
5082void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
5083 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02005084 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005085{
Johannes Berge6d6e342009-07-01 21:26:47 +02005086 nl80211_send_mlme_event(rdev, netdev, buf, len,
5087 NL80211_CMD_ASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005088}
5089
Jouni Malinen53b46b82009-03-27 20:53:56 +02005090void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02005091 struct net_device *netdev, const u8 *buf,
5092 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005093{
5094 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02005095 NL80211_CMD_DEAUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005096}
5097
Jouni Malinen53b46b82009-03-27 20:53:56 +02005098void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
5099 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02005100 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005101{
5102 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02005103 NL80211_CMD_DISASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005104}
5105
Luis R. Rodriguez1b06bb42009-05-02 00:34:48 -04005106static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
5107 struct net_device *netdev, int cmd,
Johannes Berge6d6e342009-07-01 21:26:47 +02005108 const u8 *addr, gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03005109{
5110 struct sk_buff *msg;
5111 void *hdr;
5112
Johannes Berge6d6e342009-07-01 21:26:47 +02005113 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03005114 if (!msg)
5115 return;
5116
5117 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
5118 if (!hdr) {
5119 nlmsg_free(msg);
5120 return;
5121 }
5122
5123 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5124 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5125 NLA_PUT_FLAG(msg, NL80211_ATTR_TIMED_OUT);
5126 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
5127
5128 if (genlmsg_end(msg, hdr) < 0) {
5129 nlmsg_free(msg);
5130 return;
5131 }
5132
Johannes Berg463d0182009-07-14 00:33:35 +02005133 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5134 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03005135 return;
5136
5137 nla_put_failure:
5138 genlmsg_cancel(msg, hdr);
5139 nlmsg_free(msg);
5140}
5141
5142void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02005143 struct net_device *netdev, const u8 *addr,
5144 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03005145{
5146 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE,
Johannes Berge6d6e342009-07-01 21:26:47 +02005147 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03005148}
5149
5150void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02005151 struct net_device *netdev, const u8 *addr,
5152 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03005153{
Johannes Berge6d6e342009-07-01 21:26:47 +02005154 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE,
5155 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03005156}
5157
Samuel Ortizb23aa672009-07-01 21:26:54 +02005158void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
5159 struct net_device *netdev, const u8 *bssid,
5160 const u8 *req_ie, size_t req_ie_len,
5161 const u8 *resp_ie, size_t resp_ie_len,
5162 u16 status, gfp_t gfp)
5163{
5164 struct sk_buff *msg;
5165 void *hdr;
5166
5167 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
5168 if (!msg)
5169 return;
5170
5171 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT);
5172 if (!hdr) {
5173 nlmsg_free(msg);
5174 return;
5175 }
5176
5177 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5178 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5179 if (bssid)
5180 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
5181 NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status);
5182 if (req_ie)
5183 NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
5184 if (resp_ie)
5185 NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
5186
5187 if (genlmsg_end(msg, hdr) < 0) {
5188 nlmsg_free(msg);
5189 return;
5190 }
5191
Johannes Berg463d0182009-07-14 00:33:35 +02005192 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5193 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005194 return;
5195
5196 nla_put_failure:
5197 genlmsg_cancel(msg, hdr);
5198 nlmsg_free(msg);
5199
5200}
5201
5202void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
5203 struct net_device *netdev, const u8 *bssid,
5204 const u8 *req_ie, size_t req_ie_len,
5205 const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
5206{
5207 struct sk_buff *msg;
5208 void *hdr;
5209
5210 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
5211 if (!msg)
5212 return;
5213
5214 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM);
5215 if (!hdr) {
5216 nlmsg_free(msg);
5217 return;
5218 }
5219
5220 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5221 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5222 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
5223 if (req_ie)
5224 NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
5225 if (resp_ie)
5226 NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
5227
5228 if (genlmsg_end(msg, hdr) < 0) {
5229 nlmsg_free(msg);
5230 return;
5231 }
5232
Johannes Berg463d0182009-07-14 00:33:35 +02005233 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5234 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005235 return;
5236
5237 nla_put_failure:
5238 genlmsg_cancel(msg, hdr);
5239 nlmsg_free(msg);
5240
5241}
5242
5243void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
5244 struct net_device *netdev, u16 reason,
Johannes Berg667503d2009-07-07 03:56:11 +02005245 const u8 *ie, size_t ie_len, bool from_ap)
Samuel Ortizb23aa672009-07-01 21:26:54 +02005246{
5247 struct sk_buff *msg;
5248 void *hdr;
5249
Johannes Berg667503d2009-07-07 03:56:11 +02005250 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005251 if (!msg)
5252 return;
5253
5254 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT);
5255 if (!hdr) {
5256 nlmsg_free(msg);
5257 return;
5258 }
5259
5260 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5261 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5262 if (from_ap && reason)
5263 NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason);
5264 if (from_ap)
5265 NLA_PUT_FLAG(msg, NL80211_ATTR_DISCONNECTED_BY_AP);
5266 if (ie)
5267 NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie);
5268
5269 if (genlmsg_end(msg, hdr) < 0) {
5270 nlmsg_free(msg);
5271 return;
5272 }
5273
Johannes Berg463d0182009-07-14 00:33:35 +02005274 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5275 nl80211_mlme_mcgrp.id, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005276 return;
5277
5278 nla_put_failure:
5279 genlmsg_cancel(msg, hdr);
5280 nlmsg_free(msg);
5281
5282}
5283
Johannes Berg04a773a2009-04-19 21:24:32 +02005284void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
5285 struct net_device *netdev, const u8 *bssid,
5286 gfp_t gfp)
5287{
5288 struct sk_buff *msg;
5289 void *hdr;
5290
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07005291 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02005292 if (!msg)
5293 return;
5294
5295 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS);
5296 if (!hdr) {
5297 nlmsg_free(msg);
5298 return;
5299 }
5300
5301 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5302 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5303 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
5304
5305 if (genlmsg_end(msg, hdr) < 0) {
5306 nlmsg_free(msg);
5307 return;
5308 }
5309
Johannes Berg463d0182009-07-14 00:33:35 +02005310 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5311 nl80211_mlme_mcgrp.id, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02005312 return;
5313
5314 nla_put_failure:
5315 genlmsg_cancel(msg, hdr);
5316 nlmsg_free(msg);
5317}
5318
Jouni Malinena3b8b052009-03-27 21:59:49 +02005319void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
5320 struct net_device *netdev, const u8 *addr,
5321 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +02005322 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +02005323{
5324 struct sk_buff *msg;
5325 void *hdr;
5326
Johannes Berge6d6e342009-07-01 21:26:47 +02005327 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02005328 if (!msg)
5329 return;
5330
5331 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
5332 if (!hdr) {
5333 nlmsg_free(msg);
5334 return;
5335 }
5336
5337 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5338 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5339 if (addr)
5340 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
5341 NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, key_type);
5342 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id);
5343 if (tsc)
5344 NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc);
5345
5346 if (genlmsg_end(msg, hdr) < 0) {
5347 nlmsg_free(msg);
5348 return;
5349 }
5350
Johannes Berg463d0182009-07-14 00:33:35 +02005351 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5352 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02005353 return;
5354
5355 nla_put_failure:
5356 genlmsg_cancel(msg, hdr);
5357 nlmsg_free(msg);
5358}
5359
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04005360void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
5361 struct ieee80211_channel *channel_before,
5362 struct ieee80211_channel *channel_after)
5363{
5364 struct sk_buff *msg;
5365 void *hdr;
5366 struct nlattr *nl_freq;
5367
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07005368 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04005369 if (!msg)
5370 return;
5371
5372 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
5373 if (!hdr) {
5374 nlmsg_free(msg);
5375 return;
5376 }
5377
5378 /*
5379 * Since we are applying the beacon hint to a wiphy we know its
5380 * wiphy_idx is valid
5381 */
5382 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy));
5383
5384 /* Before */
5385 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
5386 if (!nl_freq)
5387 goto nla_put_failure;
5388 if (nl80211_msg_put_channel(msg, channel_before))
5389 goto nla_put_failure;
5390 nla_nest_end(msg, nl_freq);
5391
5392 /* After */
5393 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
5394 if (!nl_freq)
5395 goto nla_put_failure;
5396 if (nl80211_msg_put_channel(msg, channel_after))
5397 goto nla_put_failure;
5398 nla_nest_end(msg, nl_freq);
5399
5400 if (genlmsg_end(msg, hdr) < 0) {
5401 nlmsg_free(msg);
5402 return;
5403 }
5404
Johannes Berg463d0182009-07-14 00:33:35 +02005405 rcu_read_lock();
5406 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
5407 GFP_ATOMIC);
5408 rcu_read_unlock();
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04005409
5410 return;
5411
5412nla_put_failure:
5413 genlmsg_cancel(msg, hdr);
5414 nlmsg_free(msg);
5415}
5416
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005417static void nl80211_send_remain_on_chan_event(
5418 int cmd, struct cfg80211_registered_device *rdev,
5419 struct net_device *netdev, u64 cookie,
5420 struct ieee80211_channel *chan,
5421 enum nl80211_channel_type channel_type,
5422 unsigned int duration, gfp_t gfp)
5423{
5424 struct sk_buff *msg;
5425 void *hdr;
5426
5427 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
5428 if (!msg)
5429 return;
5430
5431 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
5432 if (!hdr) {
5433 nlmsg_free(msg);
5434 return;
5435 }
5436
5437 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5438 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5439 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq);
5440 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type);
5441 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
5442
5443 if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL)
5444 NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration);
5445
5446 if (genlmsg_end(msg, hdr) < 0) {
5447 nlmsg_free(msg);
5448 return;
5449 }
5450
5451 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5452 nl80211_mlme_mcgrp.id, gfp);
5453 return;
5454
5455 nla_put_failure:
5456 genlmsg_cancel(msg, hdr);
5457 nlmsg_free(msg);
5458}
5459
5460void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
5461 struct net_device *netdev, u64 cookie,
5462 struct ieee80211_channel *chan,
5463 enum nl80211_channel_type channel_type,
5464 unsigned int duration, gfp_t gfp)
5465{
5466 nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
5467 rdev, netdev, cookie, chan,
5468 channel_type, duration, gfp);
5469}
5470
5471void nl80211_send_remain_on_channel_cancel(
5472 struct cfg80211_registered_device *rdev, struct net_device *netdev,
5473 u64 cookie, struct ieee80211_channel *chan,
5474 enum nl80211_channel_type channel_type, gfp_t gfp)
5475{
5476 nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
5477 rdev, netdev, cookie, chan,
5478 channel_type, 0, gfp);
5479}
5480
Johannes Berg98b62182009-12-23 13:15:44 +01005481void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
5482 struct net_device *dev, const u8 *mac_addr,
5483 struct station_info *sinfo, gfp_t gfp)
5484{
5485 struct sk_buff *msg;
5486
5487 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
5488 if (!msg)
5489 return;
5490
5491 if (nl80211_send_station(msg, 0, 0, 0, dev, mac_addr, sinfo) < 0) {
5492 nlmsg_free(msg);
5493 return;
5494 }
5495
5496 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5497 nl80211_mlme_mcgrp.id, gfp);
5498}
5499
Johannes Berg55682962007-09-20 13:09:35 -04005500/* initialisation/exit functions */
5501
5502int nl80211_init(void)
5503{
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00005504 int err;
Johannes Berg55682962007-09-20 13:09:35 -04005505
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00005506 err = genl_register_family_with_ops(&nl80211_fam,
5507 nl80211_ops, ARRAY_SIZE(nl80211_ops));
Johannes Berg55682962007-09-20 13:09:35 -04005508 if (err)
5509 return err;
5510
Johannes Berg55682962007-09-20 13:09:35 -04005511 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
5512 if (err)
5513 goto err_out;
5514
Johannes Berg2a519312009-02-10 21:25:55 +01005515 err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
5516 if (err)
5517 goto err_out;
5518
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04005519 err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
5520 if (err)
5521 goto err_out;
5522
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005523 err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
5524 if (err)
5525 goto err_out;
5526
Johannes Bergaff89a92009-07-01 21:26:51 +02005527#ifdef CONFIG_NL80211_TESTMODE
5528 err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp);
5529 if (err)
5530 goto err_out;
5531#endif
5532
Johannes Berg55682962007-09-20 13:09:35 -04005533 return 0;
5534 err_out:
5535 genl_unregister_family(&nl80211_fam);
5536 return err;
5537}
5538
5539void nl80211_exit(void)
5540{
5541 genl_unregister_family(&nl80211_fam);
5542}