blob: 37264d56bace7fe79f7e7eb605c2a4b36ae6af72 [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 */
61static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
62 [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 },
Johannes Berg55682962007-09-20 13:09:35 -040072
73 [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
74 [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
75 [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
Johannes Berg41ade002007-12-19 02:03:29 +010076
77 [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN },
Johannes Berg3e5d7642009-07-07 14:37:26 +020078 [NL80211_ATTR_PREV_BSSID] = { .type = NLA_BINARY, .len = ETH_ALEN },
Johannes Berg41ade002007-12-19 02:03:29 +010079
Johannes Bergb9454e82009-07-08 13:29:08 +020080 [NL80211_ATTR_KEY] = { .type = NLA_NESTED, },
Johannes Berg41ade002007-12-19 02:03:29 +010081 [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
82 .len = WLAN_MAX_KEY_LEN },
83 [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
84 [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
85 [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
Jouni Malinen9f26a952009-05-15 12:38:32 +030086 [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
Johannes Berged1b6cc2007-12-19 02:03:32 +010087
88 [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
89 [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
90 [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY,
91 .len = IEEE80211_MAX_DATA_LEN },
92 [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY,
93 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg5727ef12007-12-19 02:03:34 +010094 [NL80211_ATTR_STA_AID] = { .type = NLA_U16 },
95 [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED },
96 [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 },
97 [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY,
98 .len = NL80211_MAX_SUPP_RATES },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +010099 [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
Johannes Berg5727ef12007-12-19 02:03:34 +0100100 [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
Johannes Berg0a9542e2008-10-15 11:54:04 +0200101 [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100102 [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
103 .len = IEEE80211_MAX_MESH_ID_LEN },
104 [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300105
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -0700106 [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 },
107 [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED },
108
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300109 [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
110 [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
111 [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
Jouni Malinen90c97a02008-10-30 16:59:22 +0200112 [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
113 .len = NL80211_MAX_SUPP_RATES },
Jouni Malinen36aedc902008-08-25 11:58:58 +0300114
colin@cozybit.com93da9cc2008-10-21 12:03:48 -0700115 [NL80211_ATTR_MESH_PARAMS] = { .type = NLA_NESTED },
116
Jouni Malinen36aedc902008-08-25 11:58:58 +0300117 [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY,
118 .len = NL80211_HT_CAPABILITY_LEN },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +0200119
120 [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
121 [NL80211_ATTR_IE] = { .type = NLA_BINARY,
122 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg2a519312009-02-10 21:25:55 +0100123 [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
124 [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
Jouni Malinen636a5d32009-03-19 13:39:22 +0200125
126 [NL80211_ATTR_SSID] = { .type = NLA_BINARY,
127 .len = IEEE80211_MAX_SSID_LEN },
128 [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
129 [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
Johannes Berg04a773a2009-04-19 21:24:32 +0200130 [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
Jouni Malinen1965c852009-04-22 21:38:25 +0300131 [NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG },
Jouni Malinendc6382c2009-05-06 22:09:37 +0300132 [NL80211_ATTR_USE_MFP] = { .type = NLA_U32 },
Johannes Bergeccb8e82009-05-11 21:57:56 +0300133 [NL80211_ATTR_STA_FLAGS2] = {
134 .len = sizeof(struct nl80211_sta_flag_update),
135 },
Jouni Malinen3f77316c2009-05-11 21:57:57 +0300136 [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG },
Samuel Ortizb23aa672009-07-01 21:26:54 +0200137 [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG },
138 [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 },
139 [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
Johannes Berg463d0182009-07-14 00:33:35 +0200140 [NL80211_ATTR_PID] = { .type = NLA_U32 },
Felix Fietkau8b787642009-11-10 18:53:10 +0100141 [NL80211_ATTR_4ADDR] = { .type = NLA_U8 },
Johannes Berg55682962007-09-20 13:09:35 -0400142};
143
Johannes Bergb9454e82009-07-08 13:29:08 +0200144/* policy for the attributes */
145static struct nla_policy
146nl80211_key_policy[NL80211_KEY_MAX + 1] __read_mostly = {
Johannes Bergfffd0932009-07-08 14:22:54 +0200147 [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN },
Johannes Bergb9454e82009-07-08 13:29:08 +0200148 [NL80211_KEY_IDX] = { .type = NLA_U8 },
149 [NL80211_KEY_CIPHER] = { .type = NLA_U32 },
150 [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
151 [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG },
152 [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
153};
154
Holger Schuriga0438972009-11-11 11:30:02 +0100155/* ifidx get helper */
156static int nl80211_get_ifidx(struct netlink_callback *cb)
157{
158 int res;
159
160 res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
161 nl80211_fam.attrbuf, nl80211_fam.maxattr,
162 nl80211_policy);
163 if (res)
164 return res;
165
166 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
167 return -EINVAL;
168
169 res = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
170 if (!res)
171 return -EINVAL;
172 return res;
173}
174
Johannes Bergf4a11bb2009-03-27 12:40:28 +0100175/* IE validation */
176static bool is_valid_ie_attr(const struct nlattr *attr)
177{
178 const u8 *pos;
179 int len;
180
181 if (!attr)
182 return true;
183
184 pos = nla_data(attr);
185 len = nla_len(attr);
186
187 while (len) {
188 u8 elemlen;
189
190 if (len < 2)
191 return false;
192 len -= 2;
193
194 elemlen = pos[1];
195 if (elemlen > len)
196 return false;
197
198 len -= elemlen;
199 pos += 2 + elemlen;
200 }
201
202 return true;
203}
204
Johannes Berg55682962007-09-20 13:09:35 -0400205/* message building helper */
206static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
207 int flags, u8 cmd)
208{
209 /* since there is no private header just add the generic one */
210 return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
211}
212
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400213static int nl80211_msg_put_channel(struct sk_buff *msg,
214 struct ieee80211_channel *chan)
215{
216 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
217 chan->center_freq);
218
219 if (chan->flags & IEEE80211_CHAN_DISABLED)
220 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
221 if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
222 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
223 if (chan->flags & IEEE80211_CHAN_NO_IBSS)
224 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
225 if (chan->flags & IEEE80211_CHAN_RADAR)
226 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
227
228 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
229 DBM_TO_MBM(chan->max_power));
230
231 return 0;
232
233 nla_put_failure:
234 return -ENOBUFS;
235}
236
Johannes Berg55682962007-09-20 13:09:35 -0400237/* netlink command implementations */
238
Johannes Bergb9454e82009-07-08 13:29:08 +0200239struct key_parse {
240 struct key_params p;
241 int idx;
242 bool def, defmgmt;
243};
244
245static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k)
246{
247 struct nlattr *tb[NL80211_KEY_MAX + 1];
248 int err = nla_parse_nested(tb, NL80211_KEY_MAX, key,
249 nl80211_key_policy);
250 if (err)
251 return err;
252
253 k->def = !!tb[NL80211_KEY_DEFAULT];
254 k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT];
255
256 if (tb[NL80211_KEY_IDX])
257 k->idx = nla_get_u8(tb[NL80211_KEY_IDX]);
258
259 if (tb[NL80211_KEY_DATA]) {
260 k->p.key = nla_data(tb[NL80211_KEY_DATA]);
261 k->p.key_len = nla_len(tb[NL80211_KEY_DATA]);
262 }
263
264 if (tb[NL80211_KEY_SEQ]) {
265 k->p.seq = nla_data(tb[NL80211_KEY_SEQ]);
266 k->p.seq_len = nla_len(tb[NL80211_KEY_SEQ]);
267 }
268
269 if (tb[NL80211_KEY_CIPHER])
270 k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]);
271
272 return 0;
273}
274
275static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k)
276{
277 if (info->attrs[NL80211_ATTR_KEY_DATA]) {
278 k->p.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
279 k->p.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
280 }
281
282 if (info->attrs[NL80211_ATTR_KEY_SEQ]) {
283 k->p.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]);
284 k->p.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]);
285 }
286
287 if (info->attrs[NL80211_ATTR_KEY_IDX])
288 k->idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
289
290 if (info->attrs[NL80211_ATTR_KEY_CIPHER])
291 k->p.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
292
293 k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT];
294 k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT];
295
296 return 0;
297}
298
299static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
300{
301 int err;
302
303 memset(k, 0, sizeof(*k));
304 k->idx = -1;
305
306 if (info->attrs[NL80211_ATTR_KEY])
307 err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k);
308 else
309 err = nl80211_parse_key_old(info, k);
310
311 if (err)
312 return err;
313
314 if (k->def && k->defmgmt)
315 return -EINVAL;
316
317 if (k->idx != -1) {
318 if (k->defmgmt) {
319 if (k->idx < 4 || k->idx > 5)
320 return -EINVAL;
321 } else if (k->def) {
322 if (k->idx < 0 || k->idx > 3)
323 return -EINVAL;
324 } else {
325 if (k->idx < 0 || k->idx > 5)
326 return -EINVAL;
327 }
328 }
329
330 return 0;
331}
332
Johannes Bergfffd0932009-07-08 14:22:54 +0200333static struct cfg80211_cached_keys *
334nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
335 struct nlattr *keys)
336{
337 struct key_parse parse;
338 struct nlattr *key;
339 struct cfg80211_cached_keys *result;
340 int rem, err, def = 0;
341
342 result = kzalloc(sizeof(*result), GFP_KERNEL);
343 if (!result)
344 return ERR_PTR(-ENOMEM);
345
346 result->def = -1;
347 result->defmgmt = -1;
348
349 nla_for_each_nested(key, keys, rem) {
350 memset(&parse, 0, sizeof(parse));
351 parse.idx = -1;
352
353 err = nl80211_parse_key_new(key, &parse);
354 if (err)
355 goto error;
356 err = -EINVAL;
357 if (!parse.p.key)
358 goto error;
359 if (parse.idx < 0 || parse.idx > 4)
360 goto error;
361 if (parse.def) {
362 if (def)
363 goto error;
364 def = 1;
365 result->def = parse.idx;
366 } else if (parse.defmgmt)
367 goto error;
368 err = cfg80211_validate_key_settings(rdev, &parse.p,
369 parse.idx, NULL);
370 if (err)
371 goto error;
372 result->params[parse.idx].cipher = parse.p.cipher;
373 result->params[parse.idx].key_len = parse.p.key_len;
374 result->params[parse.idx].key = result->data[parse.idx];
375 memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len);
376 }
377
378 return result;
379 error:
380 kfree(result);
381 return ERR_PTR(err);
382}
383
384static int nl80211_key_allowed(struct wireless_dev *wdev)
385{
386 ASSERT_WDEV_LOCK(wdev);
387
388 if (!netif_running(wdev->netdev))
389 return -ENETDOWN;
390
391 switch (wdev->iftype) {
392 case NL80211_IFTYPE_AP:
393 case NL80211_IFTYPE_AP_VLAN:
394 break;
395 case NL80211_IFTYPE_ADHOC:
396 if (!wdev->current_bss)
397 return -ENOLINK;
398 break;
399 case NL80211_IFTYPE_STATION:
400 if (wdev->sme_state != CFG80211_SME_CONNECTED)
401 return -ENOLINK;
402 break;
403 default:
404 return -EINVAL;
405 }
406
407 return 0;
408}
409
Johannes Berg55682962007-09-20 13:09:35 -0400410static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
411 struct cfg80211_registered_device *dev)
412{
413 void *hdr;
Johannes Bergee688b002008-01-24 19:38:39 +0100414 struct nlattr *nl_bands, *nl_band;
415 struct nlattr *nl_freqs, *nl_freq;
416 struct nlattr *nl_rates, *nl_rate;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700417 struct nlattr *nl_modes;
Johannes Berg8fdc6212009-03-14 09:34:01 +0100418 struct nlattr *nl_cmds;
Johannes Bergee688b002008-01-24 19:38:39 +0100419 enum ieee80211_band band;
420 struct ieee80211_channel *chan;
421 struct ieee80211_rate *rate;
422 int i;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700423 u16 ifmodes = dev->wiphy.interface_modes;
Johannes Berg55682962007-09-20 13:09:35 -0400424
425 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
426 if (!hdr)
427 return -1;
428
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -0500429 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400430 NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200431
Johannes Bergf5ea9122009-08-07 16:17:38 +0200432 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION,
433 cfg80211_rdev_list_generation);
434
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200435 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
436 dev->wiphy.retry_short);
437 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
438 dev->wiphy.retry_long);
439 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
440 dev->wiphy.frag_threshold);
441 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
442 dev->wiphy.rts_threshold);
443
Johannes Berg2a519312009-02-10 21:25:55 +0100444 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
445 dev->wiphy.max_scan_ssids);
Johannes Berg18a83652009-03-31 12:12:05 +0200446 NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
447 dev->wiphy.max_scan_ie_len);
Johannes Bergee688b002008-01-24 19:38:39 +0100448
Johannes Berg25e47c12009-04-02 20:14:06 +0200449 NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
450 sizeof(u32) * dev->wiphy.n_cipher_suites,
451 dev->wiphy.cipher_suites);
452
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700453 nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES);
454 if (!nl_modes)
455 goto nla_put_failure;
456
457 i = 0;
458 while (ifmodes) {
459 if (ifmodes & 1)
460 NLA_PUT_FLAG(msg, i);
461 ifmodes >>= 1;
462 i++;
463 }
464
465 nla_nest_end(msg, nl_modes);
466
Johannes Bergee688b002008-01-24 19:38:39 +0100467 nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
468 if (!nl_bands)
469 goto nla_put_failure;
470
471 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
472 if (!dev->wiphy.bands[band])
473 continue;
474
475 nl_band = nla_nest_start(msg, band);
476 if (!nl_band)
477 goto nla_put_failure;
478
Johannes Bergd51626d2008-10-09 12:20:13 +0200479 /* add HT info */
480 if (dev->wiphy.bands[band]->ht_cap.ht_supported) {
481 NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET,
482 sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
483 &dev->wiphy.bands[band]->ht_cap.mcs);
484 NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA,
485 dev->wiphy.bands[band]->ht_cap.cap);
486 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
487 dev->wiphy.bands[band]->ht_cap.ampdu_factor);
488 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
489 dev->wiphy.bands[band]->ht_cap.ampdu_density);
490 }
491
Johannes Bergee688b002008-01-24 19:38:39 +0100492 /* add frequencies */
493 nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
494 if (!nl_freqs)
495 goto nla_put_failure;
496
497 for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
498 nl_freq = nla_nest_start(msg, i);
499 if (!nl_freq)
500 goto nla_put_failure;
501
502 chan = &dev->wiphy.bands[band]->channels[i];
Johannes Bergee688b002008-01-24 19:38:39 +0100503
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400504 if (nl80211_msg_put_channel(msg, chan))
505 goto nla_put_failure;
Jouni Malinene2f367f262008-11-21 19:01:30 +0200506
Johannes Bergee688b002008-01-24 19:38:39 +0100507 nla_nest_end(msg, nl_freq);
508 }
509
510 nla_nest_end(msg, nl_freqs);
511
512 /* add bitrates */
513 nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
514 if (!nl_rates)
515 goto nla_put_failure;
516
517 for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
518 nl_rate = nla_nest_start(msg, i);
519 if (!nl_rate)
520 goto nla_put_failure;
521
522 rate = &dev->wiphy.bands[band]->bitrates[i];
523 NLA_PUT_U32(msg, NL80211_BITRATE_ATTR_RATE,
524 rate->bitrate);
525 if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
526 NLA_PUT_FLAG(msg,
527 NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE);
528
529 nla_nest_end(msg, nl_rate);
530 }
531
532 nla_nest_end(msg, nl_rates);
533
534 nla_nest_end(msg, nl_band);
535 }
536 nla_nest_end(msg, nl_bands);
537
Johannes Berg8fdc6212009-03-14 09:34:01 +0100538 nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
539 if (!nl_cmds)
540 goto nla_put_failure;
541
542 i = 0;
543#define CMD(op, n) \
544 do { \
545 if (dev->ops->op) { \
546 i++; \
547 NLA_PUT_U32(msg, i, NL80211_CMD_ ## n); \
548 } \
549 } while (0)
550
551 CMD(add_virtual_intf, NEW_INTERFACE);
552 CMD(change_virtual_intf, SET_INTERFACE);
553 CMD(add_key, NEW_KEY);
554 CMD(add_beacon, NEW_BEACON);
555 CMD(add_station, NEW_STATION);
556 CMD(add_mpath, NEW_MPATH);
557 CMD(set_mesh_params, SET_MESH_PARAMS);
558 CMD(change_bss, SET_BSS);
Jouni Malinen636a5d32009-03-19 13:39:22 +0200559 CMD(auth, AUTHENTICATE);
560 CMD(assoc, ASSOCIATE);
561 CMD(deauth, DEAUTHENTICATE);
562 CMD(disassoc, DISASSOCIATE);
Johannes Berg04a773a2009-04-19 21:24:32 +0200563 CMD(join_ibss, JOIN_IBSS);
Johannes Berg463d0182009-07-14 00:33:35 +0200564 if (dev->wiphy.netnsok) {
565 i++;
566 NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS);
567 }
Johannes Berg8fdc6212009-03-14 09:34:01 +0100568
569#undef CMD
Samuel Ortizb23aa672009-07-01 21:26:54 +0200570
Johannes Berg6829c872009-07-02 09:13:27 +0200571 if (dev->ops->connect || dev->ops->auth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +0200572 i++;
573 NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT);
574 }
575
Johannes Berg6829c872009-07-02 09:13:27 +0200576 if (dev->ops->disconnect || dev->ops->deauth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +0200577 i++;
578 NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT);
579 }
580
Johannes Berg8fdc6212009-03-14 09:34:01 +0100581 nla_nest_end(msg, nl_cmds);
582
Johannes Berg55682962007-09-20 13:09:35 -0400583 return genlmsg_end(msg, hdr);
584
585 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700586 genlmsg_cancel(msg, hdr);
587 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400588}
589
590static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
591{
592 int idx = 0;
593 int start = cb->args[0];
594 struct cfg80211_registered_device *dev;
595
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500596 mutex_lock(&cfg80211_mutex);
Johannes Berg79c97e92009-07-07 03:56:12 +0200597 list_for_each_entry(dev, &cfg80211_rdev_list, list) {
Johannes Berg463d0182009-07-14 00:33:35 +0200598 if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
599 continue;
Julius Volzb4637272008-07-08 14:02:19 +0200600 if (++idx <= start)
Johannes Berg55682962007-09-20 13:09:35 -0400601 continue;
602 if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
603 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Julius Volzb4637272008-07-08 14:02:19 +0200604 dev) < 0) {
605 idx--;
Johannes Berg55682962007-09-20 13:09:35 -0400606 break;
Julius Volzb4637272008-07-08 14:02:19 +0200607 }
Johannes Berg55682962007-09-20 13:09:35 -0400608 }
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500609 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400610
611 cb->args[0] = idx;
612
613 return skb->len;
614}
615
616static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
617{
618 struct sk_buff *msg;
619 struct cfg80211_registered_device *dev;
620
621 dev = cfg80211_get_dev_from_info(info);
622 if (IS_ERR(dev))
623 return PTR_ERR(dev);
624
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -0700625 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -0400626 if (!msg)
627 goto out_err;
628
629 if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0)
630 goto out_free;
631
Johannes Berg4d0c8ae2009-07-07 03:56:09 +0200632 cfg80211_unlock_rdev(dev);
Johannes Berg55682962007-09-20 13:09:35 -0400633
Johannes Berg134e6372009-07-10 09:51:34 +0000634 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -0400635
636 out_free:
637 nlmsg_free(msg);
638 out_err:
Johannes Berg4d0c8ae2009-07-07 03:56:09 +0200639 cfg80211_unlock_rdev(dev);
Johannes Berg55682962007-09-20 13:09:35 -0400640 return -ENOBUFS;
641}
642
Jouni Malinen31888482008-10-30 16:59:24 +0200643static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
644 [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
645 [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
646 [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
647 [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
648 [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
649};
650
651static int parse_txq_params(struct nlattr *tb[],
652 struct ieee80211_txq_params *txq_params)
653{
654 if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] ||
655 !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
656 !tb[NL80211_TXQ_ATTR_AIFS])
657 return -EINVAL;
658
659 txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]);
660 txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
661 txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
662 txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
663 txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
664
665 return 0;
666}
667
Johannes Berg55682962007-09-20 13:09:35 -0400668static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
669{
670 struct cfg80211_registered_device *rdev;
Jouni Malinen31888482008-10-30 16:59:24 +0200671 int result = 0, rem_txq_params = 0;
672 struct nlattr *nl_txq_params;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200673 u32 changed;
674 u8 retry_short = 0, retry_long = 0;
675 u32 frag_threshold = 0, rts_threshold = 0;
Johannes Berg55682962007-09-20 13:09:35 -0400676
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100677 rtnl_lock();
Johannes Berg55682962007-09-20 13:09:35 -0400678
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100679 mutex_lock(&cfg80211_mutex);
680
Johannes Berg79c97e92009-07-07 03:56:12 +0200681 rdev = __cfg80211_rdev_from_info(info);
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100682 if (IS_ERR(rdev)) {
Jiri Slaby1f5fc702009-06-20 12:31:11 +0200683 mutex_unlock(&cfg80211_mutex);
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100684 result = PTR_ERR(rdev);
685 goto unlock;
686 }
687
688 mutex_lock(&rdev->mtx);
689
690 if (info->attrs[NL80211_ATTR_WIPHY_NAME])
Jouni Malinen31888482008-10-30 16:59:24 +0200691 result = cfg80211_dev_rename(
692 rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100693
694 mutex_unlock(&cfg80211_mutex);
695
696 if (result)
697 goto bad_res;
Johannes Berg55682962007-09-20 13:09:35 -0400698
Jouni Malinen31888482008-10-30 16:59:24 +0200699 if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
700 struct ieee80211_txq_params txq_params;
701 struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
702
703 if (!rdev->ops->set_txq_params) {
704 result = -EOPNOTSUPP;
705 goto bad_res;
706 }
707
708 nla_for_each_nested(nl_txq_params,
709 info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
710 rem_txq_params) {
711 nla_parse(tb, NL80211_TXQ_ATTR_MAX,
712 nla_data(nl_txq_params),
713 nla_len(nl_txq_params),
714 txq_params_policy);
715 result = parse_txq_params(tb, &txq_params);
716 if (result)
717 goto bad_res;
718
719 result = rdev->ops->set_txq_params(&rdev->wiphy,
720 &txq_params);
721 if (result)
722 goto bad_res;
723 }
724 }
725
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200726 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Sujith094d05d2008-12-12 11:57:43 +0530727 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
Luis R. Rodriguez294196a2009-05-02 00:37:20 -0400728 u32 freq;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200729
Johannes Berg306d6112008-12-08 12:39:04 +0100730 result = -EINVAL;
731
Sujith094d05d2008-12-12 11:57:43 +0530732 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
733 channel_type = nla_get_u32(info->attrs[
734 NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
735 if (channel_type != NL80211_CHAN_NO_HT &&
736 channel_type != NL80211_CHAN_HT20 &&
737 channel_type != NL80211_CHAN_HT40PLUS &&
738 channel_type != NL80211_CHAN_HT40MINUS)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200739 goto bad_res;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200740 }
741
742 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
Johannes Berg306d6112008-12-08 12:39:04 +0100743
Johannes Berg59bbb6f2009-08-07 17:22:35 +0200744 mutex_lock(&rdev->devlist_mtx);
Johannes Berg4b181142009-08-08 11:03:58 +0200745 result = rdev_set_freq(rdev, NULL, freq, channel_type);
Johannes Berg59bbb6f2009-08-07 17:22:35 +0200746 mutex_unlock(&rdev->devlist_mtx);
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200747 if (result)
748 goto bad_res;
749 }
750
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200751 changed = 0;
752
753 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {
754 retry_short = nla_get_u8(
755 info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]);
756 if (retry_short == 0) {
757 result = -EINVAL;
758 goto bad_res;
759 }
760 changed |= WIPHY_PARAM_RETRY_SHORT;
761 }
762
763 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {
764 retry_long = nla_get_u8(
765 info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]);
766 if (retry_long == 0) {
767 result = -EINVAL;
768 goto bad_res;
769 }
770 changed |= WIPHY_PARAM_RETRY_LONG;
771 }
772
773 if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
774 frag_threshold = nla_get_u32(
775 info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
776 if (frag_threshold < 256) {
777 result = -EINVAL;
778 goto bad_res;
779 }
780 if (frag_threshold != (u32) -1) {
781 /*
782 * Fragments (apart from the last one) are required to
783 * have even length. Make the fragmentation code
784 * simpler by stripping LSB should someone try to use
785 * odd threshold value.
786 */
787 frag_threshold &= ~0x1;
788 }
789 changed |= WIPHY_PARAM_FRAG_THRESHOLD;
790 }
791
792 if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
793 rts_threshold = nla_get_u32(
794 info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
795 changed |= WIPHY_PARAM_RTS_THRESHOLD;
796 }
797
798 if (changed) {
799 u8 old_retry_short, old_retry_long;
800 u32 old_frag_threshold, old_rts_threshold;
801
802 if (!rdev->ops->set_wiphy_params) {
803 result = -EOPNOTSUPP;
804 goto bad_res;
805 }
806
807 old_retry_short = rdev->wiphy.retry_short;
808 old_retry_long = rdev->wiphy.retry_long;
809 old_frag_threshold = rdev->wiphy.frag_threshold;
810 old_rts_threshold = rdev->wiphy.rts_threshold;
811
812 if (changed & WIPHY_PARAM_RETRY_SHORT)
813 rdev->wiphy.retry_short = retry_short;
814 if (changed & WIPHY_PARAM_RETRY_LONG)
815 rdev->wiphy.retry_long = retry_long;
816 if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
817 rdev->wiphy.frag_threshold = frag_threshold;
818 if (changed & WIPHY_PARAM_RTS_THRESHOLD)
819 rdev->wiphy.rts_threshold = rts_threshold;
820
821 result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed);
822 if (result) {
823 rdev->wiphy.retry_short = old_retry_short;
824 rdev->wiphy.retry_long = old_retry_long;
825 rdev->wiphy.frag_threshold = old_frag_threshold;
826 rdev->wiphy.rts_threshold = old_rts_threshold;
827 }
828 }
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200829
Johannes Berg306d6112008-12-08 12:39:04 +0100830 bad_res:
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100831 mutex_unlock(&rdev->mtx);
832 unlock:
833 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400834 return result;
835}
836
837
838static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
Johannes Bergd7264052009-04-19 16:23:20 +0200839 struct cfg80211_registered_device *rdev,
Johannes Berg55682962007-09-20 13:09:35 -0400840 struct net_device *dev)
841{
842 void *hdr;
843
844 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
845 if (!hdr)
846 return -1;
847
848 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
Johannes Bergd7264052009-04-19 16:23:20 +0200849 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400850 NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
Johannes Berg60719ff2008-09-16 14:55:09 +0200851 NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype);
Johannes Bergf5ea9122009-08-07 16:17:38 +0200852
853 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION,
854 rdev->devlist_generation ^
855 (cfg80211_rdev_list_generation << 2));
856
Johannes Berg55682962007-09-20 13:09:35 -0400857 return genlmsg_end(msg, hdr);
858
859 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700860 genlmsg_cancel(msg, hdr);
861 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400862}
863
864static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
865{
866 int wp_idx = 0;
867 int if_idx = 0;
868 int wp_start = cb->args[0];
869 int if_start = cb->args[1];
Johannes Bergf5ea9122009-08-07 16:17:38 +0200870 struct cfg80211_registered_device *rdev;
Johannes Berg55682962007-09-20 13:09:35 -0400871 struct wireless_dev *wdev;
872
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500873 mutex_lock(&cfg80211_mutex);
Johannes Bergf5ea9122009-08-07 16:17:38 +0200874 list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
875 if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
Johannes Berg463d0182009-07-14 00:33:35 +0200876 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200877 if (wp_idx < wp_start) {
878 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400879 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200880 }
Johannes Berg55682962007-09-20 13:09:35 -0400881 if_idx = 0;
882
Johannes Bergf5ea9122009-08-07 16:17:38 +0200883 mutex_lock(&rdev->devlist_mtx);
884 list_for_each_entry(wdev, &rdev->netdev_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +0200885 if (if_idx < if_start) {
886 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400887 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200888 }
Johannes Berg55682962007-09-20 13:09:35 -0400889 if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
890 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Bergf5ea9122009-08-07 16:17:38 +0200891 rdev, wdev->netdev) < 0) {
892 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +0200893 goto out;
894 }
895 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400896 }
Johannes Bergf5ea9122009-08-07 16:17:38 +0200897 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +0200898
899 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400900 }
Johannes Bergbba95fe2008-07-29 13:22:51 +0200901 out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500902 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400903
904 cb->args[0] = wp_idx;
905 cb->args[1] = if_idx;
906
907 return skb->len;
908}
909
910static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
911{
912 struct sk_buff *msg;
913 struct cfg80211_registered_device *dev;
914 struct net_device *netdev;
915 int err;
916
Johannes Berg463d0182009-07-14 00:33:35 +0200917 err = get_rdev_dev_by_info_ifindex(info, &dev, &netdev);
Johannes Berg55682962007-09-20 13:09:35 -0400918 if (err)
919 return err;
920
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -0700921 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -0400922 if (!msg)
923 goto out_err;
924
Johannes Bergd7264052009-04-19 16:23:20 +0200925 if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0,
926 dev, netdev) < 0)
Johannes Berg55682962007-09-20 13:09:35 -0400927 goto out_free;
928
929 dev_put(netdev);
Johannes Berg4d0c8ae2009-07-07 03:56:09 +0200930 cfg80211_unlock_rdev(dev);
Johannes Berg55682962007-09-20 13:09:35 -0400931
Johannes Berg134e6372009-07-10 09:51:34 +0000932 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -0400933
934 out_free:
935 nlmsg_free(msg);
936 out_err:
937 dev_put(netdev);
Johannes Berg4d0c8ae2009-07-07 03:56:09 +0200938 cfg80211_unlock_rdev(dev);
Johannes Berg55682962007-09-20 13:09:35 -0400939 return -ENOBUFS;
940}
941
Michael Wu66f7ac52008-01-31 19:48:22 +0100942static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
943 [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
944 [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
945 [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
946 [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
947 [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
948};
949
950static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
951{
952 struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
953 int flag;
954
955 *mntrflags = 0;
956
957 if (!nla)
958 return -EINVAL;
959
960 if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX,
961 nla, mntr_flags_policy))
962 return -EINVAL;
963
964 for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
965 if (flags[flag])
966 *mntrflags |= (1<<flag);
967
968 return 0;
969}
970
Johannes Berg55682962007-09-20 13:09:35 -0400971static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
972{
Johannes Berg79c97e92009-07-07 03:56:12 +0200973 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100974 struct vif_params params;
Johannes Berge36d56b2009-06-09 21:04:43 +0200975 int err;
Johannes Berg04a773a2009-04-19 21:24:32 +0200976 enum nl80211_iftype otype, ntype;
Johannes Berg55682962007-09-20 13:09:35 -0400977 struct net_device *dev;
Johannes Berg92ffe052008-09-16 20:39:36 +0200978 u32 _flags, *flags = NULL;
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100979 bool change = false;
Johannes Berg55682962007-09-20 13:09:35 -0400980
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100981 memset(&params, 0, sizeof(params));
982
Johannes Berg3b858752009-03-12 09:55:09 +0100983 rtnl_lock();
984
Johannes Berg463d0182009-07-14 00:33:35 +0200985 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg55682962007-09-20 13:09:35 -0400986 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +0100987 goto unlock_rtnl;
988
Johannes Berg04a773a2009-04-19 21:24:32 +0200989 otype = ntype = dev->ieee80211_ptr->iftype;
Johannes Berg55682962007-09-20 13:09:35 -0400990
Johannes Berg723b0382008-09-16 20:22:09 +0200991 if (info->attrs[NL80211_ATTR_IFTYPE]) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100992 ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
Johannes Berg04a773a2009-04-19 21:24:32 +0200993 if (otype != ntype)
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100994 change = true;
Johannes Berg04a773a2009-04-19 21:24:32 +0200995 if (ntype > NL80211_IFTYPE_MAX) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100996 err = -EINVAL;
Johannes Berg723b0382008-09-16 20:22:09 +0200997 goto unlock;
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100998 }
Johannes Berg723b0382008-09-16 20:22:09 +0200999 }
1000
Johannes Berg92ffe052008-09-16 20:39:36 +02001001 if (info->attrs[NL80211_ATTR_MESH_ID]) {
Johannes Berg04a773a2009-04-19 21:24:32 +02001002 if (ntype != NL80211_IFTYPE_MESH_POINT) {
Johannes Berg92ffe052008-09-16 20:39:36 +02001003 err = -EINVAL;
1004 goto unlock;
1005 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001006 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
1007 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001008 change = true;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001009 }
1010
Felix Fietkau8b787642009-11-10 18:53:10 +01001011 if (info->attrs[NL80211_ATTR_4ADDR]) {
1012 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
1013 change = true;
1014 } else {
1015 params.use_4addr = -1;
1016 }
1017
Johannes Berg92ffe052008-09-16 20:39:36 +02001018 if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
Johannes Berg04a773a2009-04-19 21:24:32 +02001019 if (ntype != NL80211_IFTYPE_MONITOR) {
Johannes Berg92ffe052008-09-16 20:39:36 +02001020 err = -EINVAL;
1021 goto unlock;
1022 }
1023 err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
1024 &_flags);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001025 if (err)
1026 goto unlock;
1027
1028 flags = &_flags;
1029 change = true;
Johannes Berg92ffe052008-09-16 20:39:36 +02001030 }
Johannes Berg3b858752009-03-12 09:55:09 +01001031
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001032 if (change)
Johannes Berg3d54d252009-08-21 14:51:05 +02001033 err = cfg80211_change_iface(rdev, dev, ntype, flags, &params);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001034 else
1035 err = 0;
Johannes Berg60719ff2008-09-16 14:55:09 +02001036
Johannes Berg55682962007-09-20 13:09:35 -04001037 unlock:
Johannes Berge36d56b2009-06-09 21:04:43 +02001038 dev_put(dev);
Johannes Berg79c97e92009-07-07 03:56:12 +02001039 cfg80211_unlock_rdev(rdev);
Johannes Berg3b858752009-03-12 09:55:09 +01001040 unlock_rtnl:
1041 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -04001042 return err;
1043}
1044
1045static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
1046{
Johannes Berg79c97e92009-07-07 03:56:12 +02001047 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001048 struct vif_params params;
Johannes Berg55682962007-09-20 13:09:35 -04001049 int err;
1050 enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
Michael Wu66f7ac52008-01-31 19:48:22 +01001051 u32 flags;
Johannes Berg55682962007-09-20 13:09:35 -04001052
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001053 memset(&params, 0, sizeof(params));
1054
Johannes Berg55682962007-09-20 13:09:35 -04001055 if (!info->attrs[NL80211_ATTR_IFNAME])
1056 return -EINVAL;
1057
1058 if (info->attrs[NL80211_ATTR_IFTYPE]) {
1059 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
1060 if (type > NL80211_IFTYPE_MAX)
1061 return -EINVAL;
1062 }
1063
Johannes Berg3b858752009-03-12 09:55:09 +01001064 rtnl_lock();
1065
Johannes Berg79c97e92009-07-07 03:56:12 +02001066 rdev = cfg80211_get_dev_from_info(info);
1067 if (IS_ERR(rdev)) {
1068 err = PTR_ERR(rdev);
Johannes Berg3b858752009-03-12 09:55:09 +01001069 goto unlock_rtnl;
1070 }
Johannes Berg55682962007-09-20 13:09:35 -04001071
Johannes Berg79c97e92009-07-07 03:56:12 +02001072 if (!rdev->ops->add_virtual_intf ||
1073 !(rdev->wiphy.interface_modes & (1 << type))) {
Johannes Berg55682962007-09-20 13:09:35 -04001074 err = -EOPNOTSUPP;
1075 goto unlock;
1076 }
1077
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001078 if (type == NL80211_IFTYPE_MESH_POINT &&
1079 info->attrs[NL80211_ATTR_MESH_ID]) {
1080 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
1081 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
1082 }
1083
Felix Fietkau8b787642009-11-10 18:53:10 +01001084 if (info->attrs[NL80211_ATTR_4ADDR])
1085 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
1086
Michael Wu66f7ac52008-01-31 19:48:22 +01001087 err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
1088 info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
1089 &flags);
Johannes Berg79c97e92009-07-07 03:56:12 +02001090 err = rdev->ops->add_virtual_intf(&rdev->wiphy,
Michael Wu66f7ac52008-01-31 19:48:22 +01001091 nla_data(info->attrs[NL80211_ATTR_IFNAME]),
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001092 type, err ? NULL : &flags, &params);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001093
Johannes Berg55682962007-09-20 13:09:35 -04001094 unlock:
Johannes Berg79c97e92009-07-07 03:56:12 +02001095 cfg80211_unlock_rdev(rdev);
Johannes Berg3b858752009-03-12 09:55:09 +01001096 unlock_rtnl:
1097 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -04001098 return err;
1099}
1100
1101static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
1102{
Johannes Berg79c97e92009-07-07 03:56:12 +02001103 struct cfg80211_registered_device *rdev;
Johannes Berg463d0182009-07-14 00:33:35 +02001104 int err;
Johannes Berg55682962007-09-20 13:09:35 -04001105 struct net_device *dev;
1106
Johannes Berg3b858752009-03-12 09:55:09 +01001107 rtnl_lock();
1108
Johannes Berg463d0182009-07-14 00:33:35 +02001109 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg55682962007-09-20 13:09:35 -04001110 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001111 goto unlock_rtnl;
Johannes Berg55682962007-09-20 13:09:35 -04001112
Johannes Berg79c97e92009-07-07 03:56:12 +02001113 if (!rdev->ops->del_virtual_intf) {
Johannes Berg55682962007-09-20 13:09:35 -04001114 err = -EOPNOTSUPP;
1115 goto out;
1116 }
1117
Johannes Berg463d0182009-07-14 00:33:35 +02001118 err = rdev->ops->del_virtual_intf(&rdev->wiphy, dev);
Johannes Berg55682962007-09-20 13:09:35 -04001119
1120 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001121 cfg80211_unlock_rdev(rdev);
Johannes Berg463d0182009-07-14 00:33:35 +02001122 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001123 unlock_rtnl:
1124 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -04001125 return err;
1126}
1127
Johannes Berg41ade002007-12-19 02:03:29 +01001128struct get_key_cookie {
1129 struct sk_buff *msg;
1130 int error;
Johannes Bergb9454e82009-07-08 13:29:08 +02001131 int idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001132};
1133
1134static void get_key_callback(void *c, struct key_params *params)
1135{
Johannes Bergb9454e82009-07-08 13:29:08 +02001136 struct nlattr *key;
Johannes Berg41ade002007-12-19 02:03:29 +01001137 struct get_key_cookie *cookie = c;
1138
1139 if (params->key)
1140 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA,
1141 params->key_len, params->key);
1142
1143 if (params->seq)
1144 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ,
1145 params->seq_len, params->seq);
1146
1147 if (params->cipher)
1148 NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
1149 params->cipher);
1150
Johannes Bergb9454e82009-07-08 13:29:08 +02001151 key = nla_nest_start(cookie->msg, NL80211_ATTR_KEY);
1152 if (!key)
1153 goto nla_put_failure;
1154
1155 if (params->key)
1156 NLA_PUT(cookie->msg, NL80211_KEY_DATA,
1157 params->key_len, params->key);
1158
1159 if (params->seq)
1160 NLA_PUT(cookie->msg, NL80211_KEY_SEQ,
1161 params->seq_len, params->seq);
1162
1163 if (params->cipher)
1164 NLA_PUT_U32(cookie->msg, NL80211_KEY_CIPHER,
1165 params->cipher);
1166
1167 NLA_PUT_U8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx);
1168
1169 nla_nest_end(cookie->msg, key);
1170
Johannes Berg41ade002007-12-19 02:03:29 +01001171 return;
1172 nla_put_failure:
1173 cookie->error = 1;
1174}
1175
1176static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
1177{
Johannes Berg79c97e92009-07-07 03:56:12 +02001178 struct cfg80211_registered_device *rdev;
Johannes Berg41ade002007-12-19 02:03:29 +01001179 int err;
1180 struct net_device *dev;
1181 u8 key_idx = 0;
1182 u8 *mac_addr = NULL;
1183 struct get_key_cookie cookie = {
1184 .error = 0,
1185 };
1186 void *hdr;
1187 struct sk_buff *msg;
1188
1189 if (info->attrs[NL80211_ATTR_KEY_IDX])
1190 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1191
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001192 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +01001193 return -EINVAL;
1194
1195 if (info->attrs[NL80211_ATTR_MAC])
1196 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1197
Johannes Berg3b858752009-03-12 09:55:09 +01001198 rtnl_lock();
1199
Johannes Berg463d0182009-07-14 00:33:35 +02001200 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001201 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001202 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001203
Johannes Berg79c97e92009-07-07 03:56:12 +02001204 if (!rdev->ops->get_key) {
Johannes Berg41ade002007-12-19 02:03:29 +01001205 err = -EOPNOTSUPP;
1206 goto out;
1207 }
1208
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001209 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg41ade002007-12-19 02:03:29 +01001210 if (!msg) {
1211 err = -ENOMEM;
1212 goto out;
1213 }
1214
1215 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
1216 NL80211_CMD_NEW_KEY);
1217
1218 if (IS_ERR(hdr)) {
1219 err = PTR_ERR(hdr);
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001220 goto free_msg;
Johannes Berg41ade002007-12-19 02:03:29 +01001221 }
1222
1223 cookie.msg = msg;
Johannes Bergb9454e82009-07-08 13:29:08 +02001224 cookie.idx = key_idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001225
1226 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1227 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
1228 if (mac_addr)
1229 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1230
Johannes Berg79c97e92009-07-07 03:56:12 +02001231 err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, mac_addr,
Johannes Berg41ade002007-12-19 02:03:29 +01001232 &cookie, get_key_callback);
Johannes Berg41ade002007-12-19 02:03:29 +01001233
1234 if (err)
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001235 goto free_msg;
Johannes Berg41ade002007-12-19 02:03:29 +01001236
1237 if (cookie.error)
1238 goto nla_put_failure;
1239
1240 genlmsg_end(msg, hdr);
Johannes Berg134e6372009-07-10 09:51:34 +00001241 err = genlmsg_reply(msg, info);
Johannes Berg41ade002007-12-19 02:03:29 +01001242 goto out;
1243
1244 nla_put_failure:
1245 err = -ENOBUFS;
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001246 free_msg:
Johannes Berg41ade002007-12-19 02:03:29 +01001247 nlmsg_free(msg);
1248 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001249 cfg80211_unlock_rdev(rdev);
Johannes Berg41ade002007-12-19 02:03:29 +01001250 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001251 unlock_rtnl:
1252 rtnl_unlock();
1253
Johannes Berg41ade002007-12-19 02:03:29 +01001254 return err;
1255}
1256
1257static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
1258{
Johannes Berg79c97e92009-07-07 03:56:12 +02001259 struct cfg80211_registered_device *rdev;
Johannes Bergb9454e82009-07-08 13:29:08 +02001260 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001261 int err;
1262 struct net_device *dev;
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001263 int (*func)(struct wiphy *wiphy, struct net_device *netdev,
1264 u8 key_index);
Johannes Berg41ade002007-12-19 02:03:29 +01001265
Johannes Bergb9454e82009-07-08 13:29:08 +02001266 err = nl80211_parse_key(info, &key);
1267 if (err)
1268 return err;
1269
1270 if (key.idx < 0)
Johannes Berg41ade002007-12-19 02:03:29 +01001271 return -EINVAL;
1272
Johannes Bergb9454e82009-07-08 13:29:08 +02001273 /* only support setting default key */
1274 if (!key.def && !key.defmgmt)
Johannes Berg41ade002007-12-19 02:03:29 +01001275 return -EINVAL;
1276
Johannes Berg3b858752009-03-12 09:55:09 +01001277 rtnl_lock();
1278
Johannes Berg463d0182009-07-14 00:33:35 +02001279 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001280 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001281 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001282
Johannes Bergb9454e82009-07-08 13:29:08 +02001283 if (key.def)
Johannes Berg79c97e92009-07-07 03:56:12 +02001284 func = rdev->ops->set_default_key;
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001285 else
Johannes Berg79c97e92009-07-07 03:56:12 +02001286 func = rdev->ops->set_default_mgmt_key;
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001287
1288 if (!func) {
Johannes Berg41ade002007-12-19 02:03:29 +01001289 err = -EOPNOTSUPP;
1290 goto out;
1291 }
1292
Johannes Bergfffd0932009-07-08 14:22:54 +02001293 wdev_lock(dev->ieee80211_ptr);
1294 err = nl80211_key_allowed(dev->ieee80211_ptr);
1295 if (!err)
1296 err = func(&rdev->wiphy, dev, key.idx);
1297
Johannes Berg3d23e342009-09-29 23:27:28 +02001298#ifdef CONFIG_CFG80211_WEXT
Johannes Berg08645122009-05-11 13:54:58 +02001299 if (!err) {
Johannes Berg79c97e92009-07-07 03:56:12 +02001300 if (func == rdev->ops->set_default_key)
Johannes Bergb9454e82009-07-08 13:29:08 +02001301 dev->ieee80211_ptr->wext.default_key = key.idx;
Johannes Berg08645122009-05-11 13:54:58 +02001302 else
Johannes Bergb9454e82009-07-08 13:29:08 +02001303 dev->ieee80211_ptr->wext.default_mgmt_key = key.idx;
Johannes Berg08645122009-05-11 13:54:58 +02001304 }
1305#endif
Johannes Bergfffd0932009-07-08 14:22:54 +02001306 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01001307
1308 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001309 cfg80211_unlock_rdev(rdev);
Johannes Berg41ade002007-12-19 02:03:29 +01001310 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001311
1312 unlock_rtnl:
1313 rtnl_unlock();
1314
Johannes Berg41ade002007-12-19 02:03:29 +01001315 return err;
1316}
1317
1318static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
1319{
Johannes Berg79c97e92009-07-07 03:56:12 +02001320 struct cfg80211_registered_device *rdev;
Johannes Bergfffd0932009-07-08 14:22:54 +02001321 int err;
Johannes Berg41ade002007-12-19 02:03:29 +01001322 struct net_device *dev;
Johannes Bergb9454e82009-07-08 13:29:08 +02001323 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001324 u8 *mac_addr = NULL;
1325
Johannes Bergb9454e82009-07-08 13:29:08 +02001326 err = nl80211_parse_key(info, &key);
1327 if (err)
1328 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01001329
Johannes Bergb9454e82009-07-08 13:29:08 +02001330 if (!key.p.key)
Johannes Berg41ade002007-12-19 02:03:29 +01001331 return -EINVAL;
1332
Johannes Berg41ade002007-12-19 02:03:29 +01001333 if (info->attrs[NL80211_ATTR_MAC])
1334 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1335
Johannes Berg3b858752009-03-12 09:55:09 +01001336 rtnl_lock();
1337
Johannes Berg463d0182009-07-14 00:33:35 +02001338 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001339 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001340 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001341
Johannes Berg79c97e92009-07-07 03:56:12 +02001342 if (!rdev->ops->add_key) {
Johannes Berg41ade002007-12-19 02:03:29 +01001343 err = -EOPNOTSUPP;
1344 goto out;
1345 }
1346
Johannes Bergfffd0932009-07-08 14:22:54 +02001347 if (cfg80211_validate_key_settings(rdev, &key.p, key.idx, mac_addr)) {
1348 err = -EINVAL;
1349 goto out;
1350 }
1351
1352 wdev_lock(dev->ieee80211_ptr);
1353 err = nl80211_key_allowed(dev->ieee80211_ptr);
1354 if (!err)
1355 err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx,
1356 mac_addr, &key.p);
1357 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01001358
1359 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001360 cfg80211_unlock_rdev(rdev);
Johannes Berg41ade002007-12-19 02:03:29 +01001361 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001362 unlock_rtnl:
1363 rtnl_unlock();
1364
Johannes Berg41ade002007-12-19 02:03:29 +01001365 return err;
1366}
1367
1368static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
1369{
Johannes Berg79c97e92009-07-07 03:56:12 +02001370 struct cfg80211_registered_device *rdev;
Johannes Berg41ade002007-12-19 02:03:29 +01001371 int err;
1372 struct net_device *dev;
Johannes Berg41ade002007-12-19 02:03:29 +01001373 u8 *mac_addr = NULL;
Johannes Bergb9454e82009-07-08 13:29:08 +02001374 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001375
Johannes Bergb9454e82009-07-08 13:29:08 +02001376 err = nl80211_parse_key(info, &key);
1377 if (err)
1378 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01001379
1380 if (info->attrs[NL80211_ATTR_MAC])
1381 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1382
Johannes Berg3b858752009-03-12 09:55:09 +01001383 rtnl_lock();
1384
Johannes Berg463d0182009-07-14 00:33:35 +02001385 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001386 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001387 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001388
Johannes Berg79c97e92009-07-07 03:56:12 +02001389 if (!rdev->ops->del_key) {
Johannes Berg41ade002007-12-19 02:03:29 +01001390 err = -EOPNOTSUPP;
1391 goto out;
1392 }
1393
Johannes Bergfffd0932009-07-08 14:22:54 +02001394 wdev_lock(dev->ieee80211_ptr);
1395 err = nl80211_key_allowed(dev->ieee80211_ptr);
1396 if (!err)
1397 err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr);
Johannes Berg41ade002007-12-19 02:03:29 +01001398
Johannes Berg3d23e342009-09-29 23:27:28 +02001399#ifdef CONFIG_CFG80211_WEXT
Johannes Berg08645122009-05-11 13:54:58 +02001400 if (!err) {
Johannes Bergb9454e82009-07-08 13:29:08 +02001401 if (key.idx == dev->ieee80211_ptr->wext.default_key)
Johannes Berg08645122009-05-11 13:54:58 +02001402 dev->ieee80211_ptr->wext.default_key = -1;
Johannes Bergb9454e82009-07-08 13:29:08 +02001403 else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key)
Johannes Berg08645122009-05-11 13:54:58 +02001404 dev->ieee80211_ptr->wext.default_mgmt_key = -1;
1405 }
1406#endif
Johannes Bergfffd0932009-07-08 14:22:54 +02001407 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg08645122009-05-11 13:54:58 +02001408
Johannes Berg41ade002007-12-19 02:03:29 +01001409 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001410 cfg80211_unlock_rdev(rdev);
Johannes Berg41ade002007-12-19 02:03:29 +01001411 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001412
1413 unlock_rtnl:
1414 rtnl_unlock();
1415
Johannes Berg41ade002007-12-19 02:03:29 +01001416 return err;
1417}
1418
Johannes Berged1b6cc2007-12-19 02:03:32 +01001419static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
1420{
1421 int (*call)(struct wiphy *wiphy, struct net_device *dev,
1422 struct beacon_parameters *info);
Johannes Berg79c97e92009-07-07 03:56:12 +02001423 struct cfg80211_registered_device *rdev;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001424 int err;
1425 struct net_device *dev;
1426 struct beacon_parameters params;
1427 int haveinfo = 0;
1428
Johannes Bergf4a11bb2009-03-27 12:40:28 +01001429 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]))
1430 return -EINVAL;
1431
Johannes Berg3b858752009-03-12 09:55:09 +01001432 rtnl_lock();
1433
Johannes Berg463d0182009-07-14 00:33:35 +02001434 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001435 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001436 goto unlock_rtnl;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001437
Jouni Malineneec60b02009-03-20 21:21:19 +02001438 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
1439 err = -EOPNOTSUPP;
1440 goto out;
1441 }
1442
Johannes Berged1b6cc2007-12-19 02:03:32 +01001443 switch (info->genlhdr->cmd) {
1444 case NL80211_CMD_NEW_BEACON:
1445 /* these are required for NEW_BEACON */
1446 if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
1447 !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
1448 !info->attrs[NL80211_ATTR_BEACON_HEAD]) {
1449 err = -EINVAL;
1450 goto out;
1451 }
1452
Johannes Berg79c97e92009-07-07 03:56:12 +02001453 call = rdev->ops->add_beacon;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001454 break;
1455 case NL80211_CMD_SET_BEACON:
Johannes Berg79c97e92009-07-07 03:56:12 +02001456 call = rdev->ops->set_beacon;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001457 break;
1458 default:
1459 WARN_ON(1);
1460 err = -EOPNOTSUPP;
1461 goto out;
1462 }
1463
1464 if (!call) {
1465 err = -EOPNOTSUPP;
1466 goto out;
1467 }
1468
1469 memset(&params, 0, sizeof(params));
1470
1471 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
1472 params.interval =
1473 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
1474 haveinfo = 1;
1475 }
1476
1477 if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) {
1478 params.dtim_period =
1479 nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
1480 haveinfo = 1;
1481 }
1482
1483 if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
1484 params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
1485 params.head_len =
1486 nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
1487 haveinfo = 1;
1488 }
1489
1490 if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
1491 params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
1492 params.tail_len =
1493 nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
1494 haveinfo = 1;
1495 }
1496
1497 if (!haveinfo) {
1498 err = -EINVAL;
1499 goto out;
1500 }
1501
Johannes Berg79c97e92009-07-07 03:56:12 +02001502 err = call(&rdev->wiphy, dev, &params);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001503
1504 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001505 cfg80211_unlock_rdev(rdev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001506 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001507 unlock_rtnl:
1508 rtnl_unlock();
1509
Johannes Berged1b6cc2007-12-19 02:03:32 +01001510 return err;
1511}
1512
1513static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
1514{
Johannes Berg79c97e92009-07-07 03:56:12 +02001515 struct cfg80211_registered_device *rdev;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001516 int err;
1517 struct net_device *dev;
1518
Johannes Berg3b858752009-03-12 09:55:09 +01001519 rtnl_lock();
1520
Johannes Berg463d0182009-07-14 00:33:35 +02001521 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001522 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001523 goto unlock_rtnl;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001524
Johannes Berg79c97e92009-07-07 03:56:12 +02001525 if (!rdev->ops->del_beacon) {
Johannes Berged1b6cc2007-12-19 02:03:32 +01001526 err = -EOPNOTSUPP;
1527 goto out;
1528 }
1529
Jouni Malineneec60b02009-03-20 21:21:19 +02001530 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
1531 err = -EOPNOTSUPP;
1532 goto out;
1533 }
Johannes Berg79c97e92009-07-07 03:56:12 +02001534 err = rdev->ops->del_beacon(&rdev->wiphy, dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001535
1536 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001537 cfg80211_unlock_rdev(rdev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001538 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001539 unlock_rtnl:
1540 rtnl_unlock();
1541
Johannes Berged1b6cc2007-12-19 02:03:32 +01001542 return err;
1543}
1544
Johannes Berg5727ef12007-12-19 02:03:34 +01001545static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
1546 [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
1547 [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
1548 [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
Jouni Malinen0e467242009-05-11 21:57:55 +03001549 [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG },
Johannes Berg5727ef12007-12-19 02:03:34 +01001550};
1551
Johannes Bergeccb8e82009-05-11 21:57:56 +03001552static int parse_station_flags(struct genl_info *info,
1553 struct station_parameters *params)
Johannes Berg5727ef12007-12-19 02:03:34 +01001554{
1555 struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
Johannes Bergeccb8e82009-05-11 21:57:56 +03001556 struct nlattr *nla;
Johannes Berg5727ef12007-12-19 02:03:34 +01001557 int flag;
1558
Johannes Bergeccb8e82009-05-11 21:57:56 +03001559 /*
1560 * Try parsing the new attribute first so userspace
1561 * can specify both for older kernels.
1562 */
1563 nla = info->attrs[NL80211_ATTR_STA_FLAGS2];
1564 if (nla) {
1565 struct nl80211_sta_flag_update *sta_flags;
Johannes Berg5727ef12007-12-19 02:03:34 +01001566
Johannes Bergeccb8e82009-05-11 21:57:56 +03001567 sta_flags = nla_data(nla);
1568 params->sta_flags_mask = sta_flags->mask;
1569 params->sta_flags_set = sta_flags->set;
1570 if ((params->sta_flags_mask |
1571 params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID))
1572 return -EINVAL;
1573 return 0;
1574 }
1575
1576 /* if present, parse the old attribute */
1577
1578 nla = info->attrs[NL80211_ATTR_STA_FLAGS];
Johannes Berg5727ef12007-12-19 02:03:34 +01001579 if (!nla)
1580 return 0;
1581
1582 if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX,
1583 nla, sta_flags_policy))
1584 return -EINVAL;
1585
Johannes Bergeccb8e82009-05-11 21:57:56 +03001586 params->sta_flags_mask = (1 << __NL80211_STA_FLAG_AFTER_LAST) - 1;
1587 params->sta_flags_mask &= ~1;
Johannes Berg5727ef12007-12-19 02:03:34 +01001588
1589 for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++)
1590 if (flags[flag])
Johannes Bergeccb8e82009-05-11 21:57:56 +03001591 params->sta_flags_set |= (1<<flag);
Johannes Berg5727ef12007-12-19 02:03:34 +01001592
1593 return 0;
1594}
1595
Henning Rogge420e7fa2008-12-11 22:04:19 +01001596static u16 nl80211_calculate_bitrate(struct rate_info *rate)
1597{
1598 int modulation, streams, bitrate;
1599
1600 if (!(rate->flags & RATE_INFO_FLAGS_MCS))
1601 return rate->legacy;
1602
1603 /* the formula below does only work for MCS values smaller than 32 */
1604 if (rate->mcs >= 32)
1605 return 0;
1606
1607 modulation = rate->mcs & 7;
1608 streams = (rate->mcs >> 3) + 1;
1609
1610 bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ?
1611 13500000 : 6500000;
1612
1613 if (modulation < 4)
1614 bitrate *= (modulation + 1);
1615 else if (modulation == 4)
1616 bitrate *= (modulation + 2);
1617 else
1618 bitrate *= (modulation + 3);
1619
1620 bitrate *= streams;
1621
1622 if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
1623 bitrate = (bitrate / 9) * 10;
1624
1625 /* do NOT round down here */
1626 return (bitrate + 50000) / 100000;
1627}
1628
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001629static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
1630 int flags, struct net_device *dev,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001631 u8 *mac_addr, struct station_info *sinfo)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001632{
1633 void *hdr;
Henning Rogge420e7fa2008-12-11 22:04:19 +01001634 struct nlattr *sinfoattr, *txrate;
1635 u16 bitrate;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001636
1637 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
1638 if (!hdr)
1639 return -1;
1640
1641 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1642 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1643
Johannes Bergf5ea9122009-08-07 16:17:38 +02001644 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, sinfo->generation);
1645
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001646 sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
1647 if (!sinfoattr)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001648 goto nla_put_failure;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001649 if (sinfo->filled & STATION_INFO_INACTIVE_TIME)
1650 NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME,
1651 sinfo->inactive_time);
1652 if (sinfo->filled & STATION_INFO_RX_BYTES)
1653 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES,
1654 sinfo->rx_bytes);
1655 if (sinfo->filled & STATION_INFO_TX_BYTES)
1656 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES,
1657 sinfo->tx_bytes);
1658 if (sinfo->filled & STATION_INFO_LLID)
1659 NLA_PUT_U16(msg, NL80211_STA_INFO_LLID,
1660 sinfo->llid);
1661 if (sinfo->filled & STATION_INFO_PLID)
1662 NLA_PUT_U16(msg, NL80211_STA_INFO_PLID,
1663 sinfo->plid);
1664 if (sinfo->filled & STATION_INFO_PLINK_STATE)
1665 NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
1666 sinfo->plink_state);
Henning Rogge420e7fa2008-12-11 22:04:19 +01001667 if (sinfo->filled & STATION_INFO_SIGNAL)
1668 NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL,
1669 sinfo->signal);
1670 if (sinfo->filled & STATION_INFO_TX_BITRATE) {
1671 txrate = nla_nest_start(msg, NL80211_STA_INFO_TX_BITRATE);
1672 if (!txrate)
1673 goto nla_put_failure;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001674
Henning Rogge420e7fa2008-12-11 22:04:19 +01001675 /* nl80211_calculate_bitrate will return 0 for mcs >= 32 */
1676 bitrate = nl80211_calculate_bitrate(&sinfo->txrate);
1677 if (bitrate > 0)
1678 NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
1679
1680 if (sinfo->txrate.flags & RATE_INFO_FLAGS_MCS)
1681 NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS,
1682 sinfo->txrate.mcs);
1683 if (sinfo->txrate.flags & RATE_INFO_FLAGS_40_MHZ_WIDTH)
1684 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH);
1685 if (sinfo->txrate.flags & RATE_INFO_FLAGS_SHORT_GI)
1686 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI);
1687
1688 nla_nest_end(msg, txrate);
1689 }
Jouni Malinen98c8a60a2009-02-17 13:24:57 +02001690 if (sinfo->filled & STATION_INFO_RX_PACKETS)
1691 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS,
1692 sinfo->rx_packets);
1693 if (sinfo->filled & STATION_INFO_TX_PACKETS)
1694 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS,
1695 sinfo->tx_packets);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001696 nla_nest_end(msg, sinfoattr);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001697
1698 return genlmsg_end(msg, hdr);
1699
1700 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001701 genlmsg_cancel(msg, hdr);
1702 return -EMSGSIZE;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001703}
1704
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001705static int nl80211_dump_station(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02001706 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001707{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001708 struct station_info sinfo;
1709 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001710 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001711 u8 mac_addr[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02001712 int ifidx = cb->args[0];
1713 int sta_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001714 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001715
Holger Schuriga0438972009-11-11 11:30:02 +01001716 if (!ifidx)
1717 ifidx = nl80211_get_ifidx(cb);
1718 if (ifidx < 0)
1719 return ifidx;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001720
Johannes Berg3b858752009-03-12 09:55:09 +01001721 rtnl_lock();
1722
Johannes Berg463d0182009-07-14 00:33:35 +02001723 netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
Johannes Berg3b858752009-03-12 09:55:09 +01001724 if (!netdev) {
1725 err = -ENODEV;
1726 goto out_rtnl;
1727 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001728
Johannes Berg463d0182009-07-14 00:33:35 +02001729 dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001730 if (IS_ERR(dev)) {
1731 err = PTR_ERR(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001732 goto out_rtnl;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001733 }
1734
1735 if (!dev->ops->dump_station) {
Jouni Malineneec60b02009-03-20 21:21:19 +02001736 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001737 goto out_err;
1738 }
1739
Johannes Bergbba95fe2008-07-29 13:22:51 +02001740 while (1) {
1741 err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
1742 mac_addr, &sinfo);
1743 if (err == -ENOENT)
1744 break;
1745 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001746 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001747
1748 if (nl80211_send_station(skb,
1749 NETLINK_CB(cb->skb).pid,
1750 cb->nlh->nlmsg_seq, NLM_F_MULTI,
1751 netdev, mac_addr,
1752 &sinfo) < 0)
1753 goto out;
1754
1755 sta_idx++;
1756 }
1757
1758
1759 out:
1760 cb->args[1] = sta_idx;
1761 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001762 out_err:
Johannes Berg4d0c8ae2009-07-07 03:56:09 +02001763 cfg80211_unlock_rdev(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001764 out_rtnl:
1765 rtnl_unlock();
Johannes Bergbba95fe2008-07-29 13:22:51 +02001766
1767 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001768}
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001769
Johannes Berg5727ef12007-12-19 02:03:34 +01001770static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
1771{
Johannes Berg79c97e92009-07-07 03:56:12 +02001772 struct cfg80211_registered_device *rdev;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001773 int err;
1774 struct net_device *dev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001775 struct station_info sinfo;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001776 struct sk_buff *msg;
1777 u8 *mac_addr = NULL;
1778
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001779 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001780
1781 if (!info->attrs[NL80211_ATTR_MAC])
1782 return -EINVAL;
1783
1784 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1785
Johannes Berg3b858752009-03-12 09:55:09 +01001786 rtnl_lock();
1787
Johannes Berg463d0182009-07-14 00:33:35 +02001788 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001789 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001790 goto out_rtnl;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001791
Johannes Berg79c97e92009-07-07 03:56:12 +02001792 if (!rdev->ops->get_station) {
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001793 err = -EOPNOTSUPP;
1794 goto out;
1795 }
1796
Johannes Berg79c97e92009-07-07 03:56:12 +02001797 err = rdev->ops->get_station(&rdev->wiphy, dev, mac_addr, &sinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001798 if (err)
1799 goto out;
1800
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001801 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001802 if (!msg)
1803 goto out;
1804
1805 if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001806 dev, mac_addr, &sinfo) < 0)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001807 goto out_free;
1808
Johannes Berg134e6372009-07-10 09:51:34 +00001809 err = genlmsg_reply(msg, info);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001810 goto out;
1811
1812 out_free:
1813 nlmsg_free(msg);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001814 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001815 cfg80211_unlock_rdev(rdev);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001816 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001817 out_rtnl:
1818 rtnl_unlock();
1819
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001820 return err;
Johannes Berg5727ef12007-12-19 02:03:34 +01001821}
1822
1823/*
Felix Fietkauc258d2d2009-11-11 17:23:31 +01001824 * Get vlan interface making sure it is running and on the right wiphy.
Johannes Berg5727ef12007-12-19 02:03:34 +01001825 */
Johannes Berg463d0182009-07-14 00:33:35 +02001826static int get_vlan(struct genl_info *info,
Johannes Berg5727ef12007-12-19 02:03:34 +01001827 struct cfg80211_registered_device *rdev,
1828 struct net_device **vlan)
1829{
Johannes Berg463d0182009-07-14 00:33:35 +02001830 struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN];
Johannes Berg5727ef12007-12-19 02:03:34 +01001831 *vlan = NULL;
1832
1833 if (vlanattr) {
Johannes Berg463d0182009-07-14 00:33:35 +02001834 *vlan = dev_get_by_index(genl_info_net(info),
1835 nla_get_u32(vlanattr));
Johannes Berg5727ef12007-12-19 02:03:34 +01001836 if (!*vlan)
1837 return -ENODEV;
1838 if (!(*vlan)->ieee80211_ptr)
1839 return -EINVAL;
1840 if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy)
1841 return -EINVAL;
Felix Fietkauc258d2d2009-11-11 17:23:31 +01001842 if (!netif_running(*vlan))
1843 return -ENETDOWN;
Johannes Berg5727ef12007-12-19 02:03:34 +01001844 }
1845 return 0;
1846}
1847
1848static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
1849{
Johannes Berg79c97e92009-07-07 03:56:12 +02001850 struct cfg80211_registered_device *rdev;
Johannes Berg5727ef12007-12-19 02:03:34 +01001851 int err;
1852 struct net_device *dev;
1853 struct station_parameters params;
1854 u8 *mac_addr = NULL;
1855
1856 memset(&params, 0, sizeof(params));
1857
1858 params.listen_interval = -1;
1859
1860 if (info->attrs[NL80211_ATTR_STA_AID])
1861 return -EINVAL;
1862
1863 if (!info->attrs[NL80211_ATTR_MAC])
1864 return -EINVAL;
1865
1866 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1867
1868 if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
1869 params.supported_rates =
1870 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1871 params.supported_rates_len =
1872 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1873 }
1874
1875 if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
1876 params.listen_interval =
1877 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
1878
Jouni Malinen36aedc902008-08-25 11:58:58 +03001879 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
1880 params.ht_capa =
1881 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
1882
Johannes Bergeccb8e82009-05-11 21:57:56 +03001883 if (parse_station_flags(info, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01001884 return -EINVAL;
1885
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001886 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
1887 params.plink_action =
1888 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
1889
Johannes Berg3b858752009-03-12 09:55:09 +01001890 rtnl_lock();
1891
Johannes Berg463d0182009-07-14 00:33:35 +02001892 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001893 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001894 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01001895
Johannes Berg463d0182009-07-14 00:33:35 +02001896 err = get_vlan(info, rdev, &params.vlan);
Johannes Berga97f4422009-06-18 17:23:43 +02001897 if (err)
Johannes Berg034d6552009-05-27 10:35:29 +02001898 goto out;
Johannes Berga97f4422009-06-18 17:23:43 +02001899
1900 /* validate settings */
1901 err = 0;
1902
1903 switch (dev->ieee80211_ptr->iftype) {
1904 case NL80211_IFTYPE_AP:
1905 case NL80211_IFTYPE_AP_VLAN:
1906 /* disallow mesh-specific things */
1907 if (params.plink_action)
1908 err = -EINVAL;
1909 break;
1910 case NL80211_IFTYPE_STATION:
1911 /* disallow everything but AUTHORIZED flag */
1912 if (params.plink_action)
1913 err = -EINVAL;
1914 if (params.vlan)
1915 err = -EINVAL;
1916 if (params.supported_rates)
1917 err = -EINVAL;
1918 if (params.ht_capa)
1919 err = -EINVAL;
1920 if (params.listen_interval >= 0)
1921 err = -EINVAL;
1922 if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
1923 err = -EINVAL;
1924 break;
1925 case NL80211_IFTYPE_MESH_POINT:
1926 /* disallow things mesh doesn't support */
1927 if (params.vlan)
1928 err = -EINVAL;
1929 if (params.ht_capa)
1930 err = -EINVAL;
1931 if (params.listen_interval >= 0)
1932 err = -EINVAL;
1933 if (params.supported_rates)
1934 err = -EINVAL;
1935 if (params.sta_flags_mask)
1936 err = -EINVAL;
1937 break;
1938 default:
1939 err = -EINVAL;
Johannes Berg034d6552009-05-27 10:35:29 +02001940 }
1941
Johannes Berg5727ef12007-12-19 02:03:34 +01001942 if (err)
1943 goto out;
1944
Johannes Berg79c97e92009-07-07 03:56:12 +02001945 if (!rdev->ops->change_station) {
Johannes Berg5727ef12007-12-19 02:03:34 +01001946 err = -EOPNOTSUPP;
1947 goto out;
1948 }
1949
Johannes Berg79c97e92009-07-07 03:56:12 +02001950 err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01001951
1952 out:
1953 if (params.vlan)
1954 dev_put(params.vlan);
Johannes Berg79c97e92009-07-07 03:56:12 +02001955 cfg80211_unlock_rdev(rdev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001956 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001957 out_rtnl:
1958 rtnl_unlock();
1959
Johannes Berg5727ef12007-12-19 02:03:34 +01001960 return err;
1961}
1962
1963static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
1964{
Johannes Berg79c97e92009-07-07 03:56:12 +02001965 struct cfg80211_registered_device *rdev;
Johannes Berg5727ef12007-12-19 02:03:34 +01001966 int err;
1967 struct net_device *dev;
1968 struct station_parameters params;
1969 u8 *mac_addr = NULL;
1970
1971 memset(&params, 0, sizeof(params));
1972
1973 if (!info->attrs[NL80211_ATTR_MAC])
1974 return -EINVAL;
1975
Johannes Berg5727ef12007-12-19 02:03:34 +01001976 if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
1977 return -EINVAL;
1978
1979 if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
1980 return -EINVAL;
1981
1982 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1983 params.supported_rates =
1984 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1985 params.supported_rates_len =
1986 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1987 params.listen_interval =
1988 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
Johannes Berg51b50fb2009-05-24 16:42:30 +02001989
Johannes Berga97f4422009-06-18 17:23:43 +02001990 if (info->attrs[NL80211_ATTR_STA_AID]) {
1991 params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
1992 if (!params.aid || params.aid > IEEE80211_MAX_AID)
1993 return -EINVAL;
1994 }
Johannes Berg51b50fb2009-05-24 16:42:30 +02001995
Jouni Malinen36aedc902008-08-25 11:58:58 +03001996 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
1997 params.ht_capa =
1998 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
Johannes Berg5727ef12007-12-19 02:03:34 +01001999
Johannes Bergeccb8e82009-05-11 21:57:56 +03002000 if (parse_station_flags(info, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01002001 return -EINVAL;
2002
Johannes Berg3b858752009-03-12 09:55:09 +01002003 rtnl_lock();
2004
Johannes Berg463d0182009-07-14 00:33:35 +02002005 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01002006 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002007 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01002008
Johannes Berg463d0182009-07-14 00:33:35 +02002009 err = get_vlan(info, rdev, &params.vlan);
Johannes Berga97f4422009-06-18 17:23:43 +02002010 if (err)
Johannes Berge80cf852009-05-11 14:43:13 +02002011 goto out;
Johannes Berga97f4422009-06-18 17:23:43 +02002012
2013 /* validate settings */
2014 err = 0;
2015
2016 switch (dev->ieee80211_ptr->iftype) {
2017 case NL80211_IFTYPE_AP:
2018 case NL80211_IFTYPE_AP_VLAN:
2019 /* all ok but must have AID */
2020 if (!params.aid)
2021 err = -EINVAL;
2022 break;
2023 case NL80211_IFTYPE_MESH_POINT:
2024 /* disallow things mesh doesn't support */
2025 if (params.vlan)
2026 err = -EINVAL;
2027 if (params.aid)
2028 err = -EINVAL;
2029 if (params.ht_capa)
2030 err = -EINVAL;
2031 if (params.listen_interval >= 0)
2032 err = -EINVAL;
2033 if (params.supported_rates)
2034 err = -EINVAL;
2035 if (params.sta_flags_mask)
2036 err = -EINVAL;
2037 break;
2038 default:
2039 err = -EINVAL;
Johannes Berge80cf852009-05-11 14:43:13 +02002040 }
2041
Johannes Berg5727ef12007-12-19 02:03:34 +01002042 if (err)
2043 goto out;
2044
Johannes Berg79c97e92009-07-07 03:56:12 +02002045 if (!rdev->ops->add_station) {
Johannes Berg5727ef12007-12-19 02:03:34 +01002046 err = -EOPNOTSUPP;
2047 goto out;
2048 }
2049
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002050 if (!netif_running(dev)) {
2051 err = -ENETDOWN;
2052 goto out;
2053 }
2054
Johannes Berg79c97e92009-07-07 03:56:12 +02002055 err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01002056
2057 out:
2058 if (params.vlan)
2059 dev_put(params.vlan);
Johannes Berg79c97e92009-07-07 03:56:12 +02002060 cfg80211_unlock_rdev(rdev);
Johannes Berg5727ef12007-12-19 02:03:34 +01002061 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002062 out_rtnl:
2063 rtnl_unlock();
2064
Johannes Berg5727ef12007-12-19 02:03:34 +01002065 return err;
2066}
2067
2068static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
2069{
Johannes Berg79c97e92009-07-07 03:56:12 +02002070 struct cfg80211_registered_device *rdev;
Johannes Berg5727ef12007-12-19 02:03:34 +01002071 int err;
2072 struct net_device *dev;
2073 u8 *mac_addr = NULL;
2074
2075 if (info->attrs[NL80211_ATTR_MAC])
2076 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2077
Johannes Berg3b858752009-03-12 09:55:09 +01002078 rtnl_lock();
2079
Johannes Berg463d0182009-07-14 00:33:35 +02002080 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01002081 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002082 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01002083
Johannes Berge80cf852009-05-11 14:43:13 +02002084 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Andrey Yurovsky155cc9e2009-06-16 11:31:04 -07002085 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
2086 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
Johannes Berge80cf852009-05-11 14:43:13 +02002087 err = -EINVAL;
2088 goto out;
2089 }
2090
Johannes Berg79c97e92009-07-07 03:56:12 +02002091 if (!rdev->ops->del_station) {
Johannes Berg5727ef12007-12-19 02:03:34 +01002092 err = -EOPNOTSUPP;
2093 goto out;
2094 }
2095
Johannes Berg79c97e92009-07-07 03:56:12 +02002096 err = rdev->ops->del_station(&rdev->wiphy, dev, mac_addr);
Johannes Berg5727ef12007-12-19 02:03:34 +01002097
2098 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002099 cfg80211_unlock_rdev(rdev);
Johannes Berg5727ef12007-12-19 02:03:34 +01002100 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002101 out_rtnl:
2102 rtnl_unlock();
2103
Johannes Berg5727ef12007-12-19 02:03:34 +01002104 return err;
2105}
2106
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002107static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq,
2108 int flags, struct net_device *dev,
2109 u8 *dst, u8 *next_hop,
2110 struct mpath_info *pinfo)
2111{
2112 void *hdr;
2113 struct nlattr *pinfoattr;
2114
2115 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
2116 if (!hdr)
2117 return -1;
2118
2119 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2120 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
2121 NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop);
2122
Johannes Bergf5ea9122009-08-07 16:17:38 +02002123 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, pinfo->generation);
2124
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002125 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
2126 if (!pinfoattr)
2127 goto nla_put_failure;
2128 if (pinfo->filled & MPATH_INFO_FRAME_QLEN)
2129 NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
2130 pinfo->frame_qlen);
Rui Paulod19b3bf2009-11-09 23:46:55 +00002131 if (pinfo->filled & MPATH_INFO_SN)
2132 NLA_PUT_U32(msg, NL80211_MPATH_INFO_SN,
2133 pinfo->sn);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002134 if (pinfo->filled & MPATH_INFO_METRIC)
2135 NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC,
2136 pinfo->metric);
2137 if (pinfo->filled & MPATH_INFO_EXPTIME)
2138 NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME,
2139 pinfo->exptime);
2140 if (pinfo->filled & MPATH_INFO_FLAGS)
2141 NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS,
2142 pinfo->flags);
2143 if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT)
2144 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
2145 pinfo->discovery_timeout);
2146 if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES)
2147 NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
2148 pinfo->discovery_retries);
2149
2150 nla_nest_end(msg, pinfoattr);
2151
2152 return genlmsg_end(msg, hdr);
2153
2154 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07002155 genlmsg_cancel(msg, hdr);
2156 return -EMSGSIZE;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002157}
2158
2159static int nl80211_dump_mpath(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02002160 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002161{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002162 struct mpath_info pinfo;
2163 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002164 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002165 u8 dst[ETH_ALEN];
2166 u8 next_hop[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02002167 int ifidx = cb->args[0];
2168 int path_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002169 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002170
Holger Schuriga0438972009-11-11 11:30:02 +01002171 if (!ifidx)
2172 ifidx = nl80211_get_ifidx(cb);
2173 if (ifidx < 0)
2174 return ifidx;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002175
Johannes Berg3b858752009-03-12 09:55:09 +01002176 rtnl_lock();
2177
Johannes Berg463d0182009-07-14 00:33:35 +02002178 netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
Johannes Berg3b858752009-03-12 09:55:09 +01002179 if (!netdev) {
2180 err = -ENODEV;
2181 goto out_rtnl;
2182 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002183
Johannes Berg463d0182009-07-14 00:33:35 +02002184 dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02002185 if (IS_ERR(dev)) {
2186 err = PTR_ERR(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002187 goto out_rtnl;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002188 }
2189
2190 if (!dev->ops->dump_mpath) {
Jouni Malineneec60b02009-03-20 21:21:19 +02002191 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002192 goto out_err;
2193 }
2194
Jouni Malineneec60b02009-03-20 21:21:19 +02002195 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2196 err = -EOPNOTSUPP;
Roel Kluin0448b5f2009-08-22 21:15:49 +02002197 goto out_err;
Jouni Malineneec60b02009-03-20 21:21:19 +02002198 }
2199
Johannes Bergbba95fe2008-07-29 13:22:51 +02002200 while (1) {
2201 err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
2202 dst, next_hop, &pinfo);
2203 if (err == -ENOENT)
2204 break;
2205 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002206 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002207
2208 if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid,
2209 cb->nlh->nlmsg_seq, NLM_F_MULTI,
2210 netdev, dst, next_hop,
2211 &pinfo) < 0)
2212 goto out;
2213
2214 path_idx++;
2215 }
2216
2217
2218 out:
2219 cb->args[1] = path_idx;
2220 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002221 out_err:
Johannes Berg4d0c8ae2009-07-07 03:56:09 +02002222 cfg80211_unlock_rdev(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002223 out_rtnl:
2224 rtnl_unlock();
Johannes Bergbba95fe2008-07-29 13:22:51 +02002225
2226 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002227}
2228
2229static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
2230{
Johannes Berg79c97e92009-07-07 03:56:12 +02002231 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002232 int err;
2233 struct net_device *dev;
2234 struct mpath_info pinfo;
2235 struct sk_buff *msg;
2236 u8 *dst = NULL;
2237 u8 next_hop[ETH_ALEN];
2238
2239 memset(&pinfo, 0, sizeof(pinfo));
2240
2241 if (!info->attrs[NL80211_ATTR_MAC])
2242 return -EINVAL;
2243
2244 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2245
Johannes Berg3b858752009-03-12 09:55:09 +01002246 rtnl_lock();
2247
Johannes Berg463d0182009-07-14 00:33:35 +02002248 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002249 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002250 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002251
Johannes Berg79c97e92009-07-07 03:56:12 +02002252 if (!rdev->ops->get_mpath) {
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002253 err = -EOPNOTSUPP;
2254 goto out;
2255 }
2256
Jouni Malineneec60b02009-03-20 21:21:19 +02002257 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2258 err = -EOPNOTSUPP;
2259 goto out;
2260 }
2261
Johannes Berg79c97e92009-07-07 03:56:12 +02002262 err = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, &pinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002263 if (err)
2264 goto out;
2265
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002266 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002267 if (!msg)
2268 goto out;
2269
2270 if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0,
2271 dev, dst, next_hop, &pinfo) < 0)
2272 goto out_free;
2273
Johannes Berg134e6372009-07-10 09:51:34 +00002274 err = genlmsg_reply(msg, info);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002275 goto out;
2276
2277 out_free:
2278 nlmsg_free(msg);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002279 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002280 cfg80211_unlock_rdev(rdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002281 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002282 out_rtnl:
2283 rtnl_unlock();
2284
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002285 return err;
2286}
2287
2288static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
2289{
Johannes Berg79c97e92009-07-07 03:56:12 +02002290 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002291 int err;
2292 struct net_device *dev;
2293 u8 *dst = NULL;
2294 u8 *next_hop = NULL;
2295
2296 if (!info->attrs[NL80211_ATTR_MAC])
2297 return -EINVAL;
2298
2299 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
2300 return -EINVAL;
2301
2302 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2303 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
2304
Johannes Berg3b858752009-03-12 09:55:09 +01002305 rtnl_lock();
2306
Johannes Berg463d0182009-07-14 00:33:35 +02002307 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002308 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002309 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002310
Johannes Berg79c97e92009-07-07 03:56:12 +02002311 if (!rdev->ops->change_mpath) {
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002312 err = -EOPNOTSUPP;
2313 goto out;
2314 }
2315
Jouni Malineneec60b02009-03-20 21:21:19 +02002316 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2317 err = -EOPNOTSUPP;
2318 goto out;
2319 }
2320
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002321 if (!netif_running(dev)) {
2322 err = -ENETDOWN;
2323 goto out;
2324 }
2325
Johannes Berg79c97e92009-07-07 03:56:12 +02002326 err = rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002327
2328 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002329 cfg80211_unlock_rdev(rdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002330 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002331 out_rtnl:
2332 rtnl_unlock();
2333
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002334 return err;
2335}
2336static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
2337{
Johannes Berg79c97e92009-07-07 03:56:12 +02002338 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002339 int err;
2340 struct net_device *dev;
2341 u8 *dst = NULL;
2342 u8 *next_hop = NULL;
2343
2344 if (!info->attrs[NL80211_ATTR_MAC])
2345 return -EINVAL;
2346
2347 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
2348 return -EINVAL;
2349
2350 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2351 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
2352
Johannes Berg3b858752009-03-12 09:55:09 +01002353 rtnl_lock();
2354
Johannes Berg463d0182009-07-14 00:33:35 +02002355 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002356 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002357 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002358
Johannes Berg79c97e92009-07-07 03:56:12 +02002359 if (!rdev->ops->add_mpath) {
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002360 err = -EOPNOTSUPP;
2361 goto out;
2362 }
2363
Jouni Malineneec60b02009-03-20 21:21:19 +02002364 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2365 err = -EOPNOTSUPP;
2366 goto out;
2367 }
2368
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002369 if (!netif_running(dev)) {
2370 err = -ENETDOWN;
2371 goto out;
2372 }
2373
Johannes Berg79c97e92009-07-07 03:56:12 +02002374 err = rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002375
2376 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002377 cfg80211_unlock_rdev(rdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002378 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002379 out_rtnl:
2380 rtnl_unlock();
2381
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002382 return err;
2383}
2384
2385static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
2386{
Johannes Berg79c97e92009-07-07 03:56:12 +02002387 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002388 int err;
2389 struct net_device *dev;
2390 u8 *dst = NULL;
2391
2392 if (info->attrs[NL80211_ATTR_MAC])
2393 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2394
Johannes Berg3b858752009-03-12 09:55:09 +01002395 rtnl_lock();
2396
Johannes Berg463d0182009-07-14 00:33:35 +02002397 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002398 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002399 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002400
Johannes Berg79c97e92009-07-07 03:56:12 +02002401 if (!rdev->ops->del_mpath) {
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002402 err = -EOPNOTSUPP;
2403 goto out;
2404 }
2405
Johannes Berg79c97e92009-07-07 03:56:12 +02002406 err = rdev->ops->del_mpath(&rdev->wiphy, dev, dst);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002407
2408 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002409 cfg80211_unlock_rdev(rdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002410 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002411 out_rtnl:
2412 rtnl_unlock();
2413
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002414 return err;
2415}
2416
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002417static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
2418{
Johannes Berg79c97e92009-07-07 03:56:12 +02002419 struct cfg80211_registered_device *rdev;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002420 int err;
2421 struct net_device *dev;
2422 struct bss_parameters params;
2423
2424 memset(&params, 0, sizeof(params));
2425 /* default to not changing parameters */
2426 params.use_cts_prot = -1;
2427 params.use_short_preamble = -1;
2428 params.use_short_slot_time = -1;
2429
2430 if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
2431 params.use_cts_prot =
2432 nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
2433 if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
2434 params.use_short_preamble =
2435 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
2436 if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
2437 params.use_short_slot_time =
2438 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
Jouni Malinen90c97a02008-10-30 16:59:22 +02002439 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
2440 params.basic_rates =
2441 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2442 params.basic_rates_len =
2443 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2444 }
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002445
Johannes Berg3b858752009-03-12 09:55:09 +01002446 rtnl_lock();
2447
Johannes Berg463d0182009-07-14 00:33:35 +02002448 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002449 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002450 goto out_rtnl;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002451
Johannes Berg79c97e92009-07-07 03:56:12 +02002452 if (!rdev->ops->change_bss) {
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002453 err = -EOPNOTSUPP;
2454 goto out;
2455 }
2456
Jouni Malineneec60b02009-03-20 21:21:19 +02002457 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
2458 err = -EOPNOTSUPP;
2459 goto out;
2460 }
2461
Johannes Berg79c97e92009-07-07 03:56:12 +02002462 err = rdev->ops->change_bss(&rdev->wiphy, dev, &params);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002463
2464 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002465 cfg80211_unlock_rdev(rdev);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002466 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002467 out_rtnl:
2468 rtnl_unlock();
2469
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002470 return err;
2471}
2472
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002473static const struct nla_policy
2474 reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
2475 [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
2476 [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
2477 [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
2478 [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
2479 [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
2480 [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
2481};
2482
2483static int parse_reg_rule(struct nlattr *tb[],
2484 struct ieee80211_reg_rule *reg_rule)
2485{
2486 struct ieee80211_freq_range *freq_range = &reg_rule->freq_range;
2487 struct ieee80211_power_rule *power_rule = &reg_rule->power_rule;
2488
2489 if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
2490 return -EINVAL;
2491 if (!tb[NL80211_ATTR_FREQ_RANGE_START])
2492 return -EINVAL;
2493 if (!tb[NL80211_ATTR_FREQ_RANGE_END])
2494 return -EINVAL;
2495 if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
2496 return -EINVAL;
2497 if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
2498 return -EINVAL;
2499
2500 reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
2501
2502 freq_range->start_freq_khz =
2503 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
2504 freq_range->end_freq_khz =
2505 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
2506 freq_range->max_bandwidth_khz =
2507 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
2508
2509 power_rule->max_eirp =
2510 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
2511
2512 if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
2513 power_rule->max_antenna_gain =
2514 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
2515
2516 return 0;
2517}
2518
2519static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
2520{
2521 int r;
2522 char *data = NULL;
2523
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002524 /*
2525 * You should only get this when cfg80211 hasn't yet initialized
2526 * completely when built-in to the kernel right between the time
2527 * window between nl80211_init() and regulatory_init(), if that is
2528 * even possible.
2529 */
2530 mutex_lock(&cfg80211_mutex);
2531 if (unlikely(!cfg80211_regdomain)) {
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002532 mutex_unlock(&cfg80211_mutex);
2533 return -EINPROGRESS;
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002534 }
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002535 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002536
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002537 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
2538 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002539
2540 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
2541
2542#ifdef CONFIG_WIRELESS_OLD_REGULATORY
2543 /* We ignore world regdom requests with the old regdom setup */
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002544 if (is_world_regdom(data))
2545 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002546#endif
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002547
2548 r = regulatory_hint_user(data);
2549
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002550 return r;
2551}
2552
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002553static int nl80211_get_mesh_params(struct sk_buff *skb,
2554 struct genl_info *info)
2555{
Johannes Berg79c97e92009-07-07 03:56:12 +02002556 struct cfg80211_registered_device *rdev;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002557 struct mesh_config cur_params;
2558 int err;
2559 struct net_device *dev;
2560 void *hdr;
2561 struct nlattr *pinfoattr;
2562 struct sk_buff *msg;
2563
Johannes Berg3b858752009-03-12 09:55:09 +01002564 rtnl_lock();
2565
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002566 /* Look up our device */
Johannes Berg463d0182009-07-14 00:33:35 +02002567 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002568 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002569 goto out_rtnl;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002570
Johannes Berg79c97e92009-07-07 03:56:12 +02002571 if (!rdev->ops->get_mesh_params) {
Jouni Malinenf3f92582009-03-20 17:57:36 +02002572 err = -EOPNOTSUPP;
2573 goto out;
2574 }
2575
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002576 /* Get the mesh params */
Johannes Berg79c97e92009-07-07 03:56:12 +02002577 err = rdev->ops->get_mesh_params(&rdev->wiphy, dev, &cur_params);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002578 if (err)
2579 goto out;
2580
2581 /* Draw up a netlink message to send back */
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002582 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002583 if (!msg) {
2584 err = -ENOBUFS;
2585 goto out;
2586 }
2587 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
2588 NL80211_CMD_GET_MESH_PARAMS);
2589 if (!hdr)
2590 goto nla_put_failure;
2591 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS);
2592 if (!pinfoattr)
2593 goto nla_put_failure;
2594 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2595 NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
2596 cur_params.dot11MeshRetryTimeout);
2597 NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
2598 cur_params.dot11MeshConfirmTimeout);
2599 NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
2600 cur_params.dot11MeshHoldingTimeout);
2601 NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
2602 cur_params.dot11MeshMaxPeerLinks);
2603 NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES,
2604 cur_params.dot11MeshMaxRetries);
2605 NLA_PUT_U8(msg, NL80211_MESHCONF_TTL,
2606 cur_params.dot11MeshTTL);
2607 NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
2608 cur_params.auto_open_plinks);
2609 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
2610 cur_params.dot11MeshHWMPmaxPREQretries);
2611 NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
2612 cur_params.path_refresh_time);
2613 NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
2614 cur_params.min_discovery_timeout);
2615 NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
2616 cur_params.dot11MeshHWMPactivePathTimeout);
2617 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
2618 cur_params.dot11MeshHWMPpreqMinInterval);
2619 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
2620 cur_params.dot11MeshHWMPnetDiameterTraversalTime);
Rui Paulo63c57232009-11-09 23:46:57 +00002621 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_ROOTMODE,
2622 cur_params.dot11MeshHWMPRootMode);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002623 nla_nest_end(msg, pinfoattr);
2624 genlmsg_end(msg, hdr);
Johannes Berg134e6372009-07-10 09:51:34 +00002625 err = genlmsg_reply(msg, info);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002626 goto out;
2627
Johannes Berg3b858752009-03-12 09:55:09 +01002628 nla_put_failure:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002629 genlmsg_cancel(msg, hdr);
2630 err = -EMSGSIZE;
Johannes Berg3b858752009-03-12 09:55:09 +01002631 out:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002632 /* Cleanup */
Johannes Berg79c97e92009-07-07 03:56:12 +02002633 cfg80211_unlock_rdev(rdev);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002634 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002635 out_rtnl:
2636 rtnl_unlock();
2637
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002638 return err;
2639}
2640
2641#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \
2642do {\
2643 if (table[attr_num]) {\
2644 cfg.param = nla_fn(table[attr_num]); \
2645 mask |= (1 << (attr_num - 1)); \
2646 } \
2647} while (0);\
2648
2649static struct nla_policy
2650nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] __read_mostly = {
2651 [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
2652 [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
2653 [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
2654 [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
2655 [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
2656 [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
2657 [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
2658
2659 [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
2660 [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
2661 [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
2662 [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
2663 [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
2664 [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
2665};
2666
2667static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
2668{
2669 int err;
2670 u32 mask;
Johannes Berg79c97e92009-07-07 03:56:12 +02002671 struct cfg80211_registered_device *rdev;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002672 struct net_device *dev;
2673 struct mesh_config cfg;
2674 struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
2675 struct nlattr *parent_attr;
2676
2677 parent_attr = info->attrs[NL80211_ATTR_MESH_PARAMS];
2678 if (!parent_attr)
2679 return -EINVAL;
2680 if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
2681 parent_attr, nl80211_meshconf_params_policy))
2682 return -EINVAL;
2683
Johannes Berg3b858752009-03-12 09:55:09 +01002684 rtnl_lock();
2685
Johannes Berg463d0182009-07-14 00:33:35 +02002686 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002687 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002688 goto out_rtnl;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002689
Johannes Berg79c97e92009-07-07 03:56:12 +02002690 if (!rdev->ops->set_mesh_params) {
Jouni Malinenf3f92582009-03-20 17:57:36 +02002691 err = -EOPNOTSUPP;
2692 goto out;
2693 }
2694
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002695 /* This makes sure that there aren't more than 32 mesh config
2696 * parameters (otherwise our bitfield scheme would not work.) */
2697 BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
2698
2699 /* Fill in the params struct */
2700 mask = 0;
2701 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout,
2702 mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16);
2703 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout,
2704 mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16);
2705 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout,
2706 mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16);
2707 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks,
2708 mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16);
2709 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries,
2710 mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8);
2711 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL,
2712 mask, NL80211_MESHCONF_TTL, nla_get_u8);
2713 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
2714 mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8);
2715 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
2716 mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
2717 nla_get_u8);
2718 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time,
2719 mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32);
2720 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout,
2721 mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
2722 nla_get_u16);
2723 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
2724 mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
2725 nla_get_u32);
2726 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
2727 mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
2728 nla_get_u16);
2729 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
2730 dot11MeshHWMPnetDiameterTraversalTime,
2731 mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
2732 nla_get_u16);
Rui Paulo63c57232009-11-09 23:46:57 +00002733 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
2734 dot11MeshHWMPRootMode, mask,
2735 NL80211_MESHCONF_HWMP_ROOTMODE,
2736 nla_get_u8);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002737
2738 /* Apply changes */
Johannes Berg79c97e92009-07-07 03:56:12 +02002739 err = rdev->ops->set_mesh_params(&rdev->wiphy, dev, &cfg, mask);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002740
Jouni Malinenf3f92582009-03-20 17:57:36 +02002741 out:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002742 /* cleanup */
Johannes Berg79c97e92009-07-07 03:56:12 +02002743 cfg80211_unlock_rdev(rdev);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002744 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002745 out_rtnl:
2746 rtnl_unlock();
2747
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002748 return err;
2749}
2750
2751#undef FILL_IN_MESH_PARAM_IF_SET
2752
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002753static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
2754{
2755 struct sk_buff *msg;
2756 void *hdr = NULL;
2757 struct nlattr *nl_reg_rules;
2758 unsigned int i;
2759 int err = -EINVAL;
2760
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002761 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002762
2763 if (!cfg80211_regdomain)
2764 goto out;
2765
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002766 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002767 if (!msg) {
2768 err = -ENOBUFS;
2769 goto out;
2770 }
2771
2772 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
2773 NL80211_CMD_GET_REG);
2774 if (!hdr)
2775 goto nla_put_failure;
2776
2777 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2,
2778 cfg80211_regdomain->alpha2);
2779
2780 nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
2781 if (!nl_reg_rules)
2782 goto nla_put_failure;
2783
2784 for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) {
2785 struct nlattr *nl_reg_rule;
2786 const struct ieee80211_reg_rule *reg_rule;
2787 const struct ieee80211_freq_range *freq_range;
2788 const struct ieee80211_power_rule *power_rule;
2789
2790 reg_rule = &cfg80211_regdomain->reg_rules[i];
2791 freq_range = &reg_rule->freq_range;
2792 power_rule = &reg_rule->power_rule;
2793
2794 nl_reg_rule = nla_nest_start(msg, i);
2795 if (!nl_reg_rule)
2796 goto nla_put_failure;
2797
2798 NLA_PUT_U32(msg, NL80211_ATTR_REG_RULE_FLAGS,
2799 reg_rule->flags);
2800 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_START,
2801 freq_range->start_freq_khz);
2802 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_END,
2803 freq_range->end_freq_khz);
2804 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
2805 freq_range->max_bandwidth_khz);
2806 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
2807 power_rule->max_antenna_gain);
2808 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
2809 power_rule->max_eirp);
2810
2811 nla_nest_end(msg, nl_reg_rule);
2812 }
2813
2814 nla_nest_end(msg, nl_reg_rules);
2815
2816 genlmsg_end(msg, hdr);
Johannes Berg134e6372009-07-10 09:51:34 +00002817 err = genlmsg_reply(msg, info);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002818 goto out;
2819
2820nla_put_failure:
2821 genlmsg_cancel(msg, hdr);
2822 err = -EMSGSIZE;
2823out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002824 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002825 return err;
2826}
2827
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002828static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
2829{
2830 struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
2831 struct nlattr *nl_reg_rule;
2832 char *alpha2 = NULL;
2833 int rem_reg_rules = 0, r = 0;
2834 u32 num_rules = 0, rule_idx = 0, size_of_regd;
2835 struct ieee80211_regdomain *rd = NULL;
2836
2837 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
2838 return -EINVAL;
2839
2840 if (!info->attrs[NL80211_ATTR_REG_RULES])
2841 return -EINVAL;
2842
2843 alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
2844
2845 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
2846 rem_reg_rules) {
2847 num_rules++;
2848 if (num_rules > NL80211_MAX_SUPP_REG_RULES)
Luis R. Rodriguez4776c6e2009-05-13 17:04:39 -04002849 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002850 }
2851
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04002852 mutex_lock(&cfg80211_mutex);
2853
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002854 if (!reg_is_valid_request(alpha2)) {
2855 r = -EINVAL;
2856 goto bad_reg;
2857 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002858
2859 size_of_regd = sizeof(struct ieee80211_regdomain) +
2860 (num_rules * sizeof(struct ieee80211_reg_rule));
2861
2862 rd = kzalloc(size_of_regd, GFP_KERNEL);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002863 if (!rd) {
2864 r = -ENOMEM;
2865 goto bad_reg;
2866 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002867
2868 rd->n_reg_rules = num_rules;
2869 rd->alpha2[0] = alpha2[0];
2870 rd->alpha2[1] = alpha2[1];
2871
2872 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
2873 rem_reg_rules) {
2874 nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
2875 nla_data(nl_reg_rule), nla_len(nl_reg_rule),
2876 reg_rule_policy);
2877 r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
2878 if (r)
2879 goto bad_reg;
2880
2881 rule_idx++;
2882
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002883 if (rule_idx > NL80211_MAX_SUPP_REG_RULES) {
2884 r = -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002885 goto bad_reg;
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002886 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002887 }
2888
2889 BUG_ON(rule_idx != num_rules);
2890
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002891 r = set_regdom(rd);
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04002892
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002893 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002894
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002895 return r;
2896
Johannes Bergd2372b32008-10-24 20:32:20 +02002897 bad_reg:
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04002898 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002899 kfree(rd);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002900 return r;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002901}
2902
Johannes Berg83f5e2c2009-06-17 17:41:49 +02002903static int validate_scan_freqs(struct nlattr *freqs)
2904{
2905 struct nlattr *attr1, *attr2;
2906 int n_channels = 0, tmp1, tmp2;
2907
2908 nla_for_each_nested(attr1, freqs, tmp1) {
2909 n_channels++;
2910 /*
2911 * Some hardware has a limited channel list for
2912 * scanning, and it is pretty much nonsensical
2913 * to scan for a channel twice, so disallow that
2914 * and don't require drivers to check that the
2915 * channel list they get isn't longer than what
2916 * they can scan, as long as they can scan all
2917 * the channels they registered at once.
2918 */
2919 nla_for_each_nested(attr2, freqs, tmp2)
2920 if (attr1 != attr2 &&
2921 nla_get_u32(attr1) == nla_get_u32(attr2))
2922 return 0;
2923 }
2924
2925 return n_channels;
2926}
2927
Johannes Berg2a519312009-02-10 21:25:55 +01002928static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
2929{
Johannes Berg79c97e92009-07-07 03:56:12 +02002930 struct cfg80211_registered_device *rdev;
Johannes Berg2a519312009-02-10 21:25:55 +01002931 struct net_device *dev;
2932 struct cfg80211_scan_request *request;
2933 struct cfg80211_ssid *ssid;
2934 struct ieee80211_channel *channel;
2935 struct nlattr *attr;
2936 struct wiphy *wiphy;
Johannes Berg83f5e2c2009-06-17 17:41:49 +02002937 int err, tmp, n_ssids = 0, n_channels, i;
Johannes Berg2a519312009-02-10 21:25:55 +01002938 enum ieee80211_band band;
Jouni Malinen70692ad2009-02-16 19:39:13 +02002939 size_t ie_len;
Johannes Berg2a519312009-02-10 21:25:55 +01002940
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002941 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
2942 return -EINVAL;
2943
Johannes Berg3b858752009-03-12 09:55:09 +01002944 rtnl_lock();
2945
Johannes Berg463d0182009-07-14 00:33:35 +02002946 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg2a519312009-02-10 21:25:55 +01002947 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002948 goto out_rtnl;
Johannes Berg2a519312009-02-10 21:25:55 +01002949
Johannes Berg79c97e92009-07-07 03:56:12 +02002950 wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01002951
Johannes Berg79c97e92009-07-07 03:56:12 +02002952 if (!rdev->ops->scan) {
Johannes Berg2a519312009-02-10 21:25:55 +01002953 err = -EOPNOTSUPP;
2954 goto out;
2955 }
2956
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002957 if (!netif_running(dev)) {
2958 err = -ENETDOWN;
2959 goto out;
2960 }
2961
Johannes Berg79c97e92009-07-07 03:56:12 +02002962 if (rdev->scan_req) {
Johannes Berg2a519312009-02-10 21:25:55 +01002963 err = -EBUSY;
Johannes Berg3b858752009-03-12 09:55:09 +01002964 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002965 }
2966
2967 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
Johannes Berg83f5e2c2009-06-17 17:41:49 +02002968 n_channels = validate_scan_freqs(
2969 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
Johannes Berg2a519312009-02-10 21:25:55 +01002970 if (!n_channels) {
2971 err = -EINVAL;
Johannes Berg3b858752009-03-12 09:55:09 +01002972 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002973 }
2974 } else {
Johannes Berg83f5e2c2009-06-17 17:41:49 +02002975 n_channels = 0;
2976
Johannes Berg2a519312009-02-10 21:25:55 +01002977 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
2978 if (wiphy->bands[band])
2979 n_channels += wiphy->bands[band]->n_channels;
2980 }
2981
2982 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
2983 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
2984 n_ssids++;
2985
2986 if (n_ssids > wiphy->max_scan_ssids) {
2987 err = -EINVAL;
Johannes Berg3b858752009-03-12 09:55:09 +01002988 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002989 }
2990
Jouni Malinen70692ad2009-02-16 19:39:13 +02002991 if (info->attrs[NL80211_ATTR_IE])
2992 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
2993 else
2994 ie_len = 0;
2995
Johannes Berg18a83652009-03-31 12:12:05 +02002996 if (ie_len > wiphy->max_scan_ie_len) {
2997 err = -EINVAL;
2998 goto out;
2999 }
3000
Johannes Berg2a519312009-02-10 21:25:55 +01003001 request = kzalloc(sizeof(*request)
3002 + sizeof(*ssid) * n_ssids
Jouni Malinen70692ad2009-02-16 19:39:13 +02003003 + sizeof(channel) * n_channels
3004 + ie_len, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01003005 if (!request) {
3006 err = -ENOMEM;
Johannes Berg3b858752009-03-12 09:55:09 +01003007 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01003008 }
3009
Johannes Berg2a519312009-02-10 21:25:55 +01003010 if (n_ssids)
Johannes Berg5ba63532009-08-07 17:54:07 +02003011 request->ssids = (void *)&request->channels[n_channels];
Johannes Berg2a519312009-02-10 21:25:55 +01003012 request->n_ssids = n_ssids;
Jouni Malinen70692ad2009-02-16 19:39:13 +02003013 if (ie_len) {
3014 if (request->ssids)
3015 request->ie = (void *)(request->ssids + n_ssids);
3016 else
3017 request->ie = (void *)(request->channels + n_channels);
3018 }
Johannes Berg2a519312009-02-10 21:25:55 +01003019
Johannes Berg584991d2009-11-02 13:32:03 +01003020 i = 0;
Johannes Berg2a519312009-02-10 21:25:55 +01003021 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
3022 /* user specified, bail out if channel not found */
Johannes Berg2a519312009-02-10 21:25:55 +01003023 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
Johannes Berg584991d2009-11-02 13:32:03 +01003024 struct ieee80211_channel *chan;
3025
3026 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
3027
3028 if (!chan) {
Johannes Berg2a519312009-02-10 21:25:55 +01003029 err = -EINVAL;
3030 goto out_free;
3031 }
Johannes Berg584991d2009-11-02 13:32:03 +01003032
3033 /* ignore disabled channels */
3034 if (chan->flags & IEEE80211_CHAN_DISABLED)
3035 continue;
3036
3037 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01003038 i++;
3039 }
3040 } else {
3041 /* all channels */
Johannes Berg2a519312009-02-10 21:25:55 +01003042 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
3043 int j;
3044 if (!wiphy->bands[band])
3045 continue;
3046 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
Johannes Berg584991d2009-11-02 13:32:03 +01003047 struct ieee80211_channel *chan;
3048
3049 chan = &wiphy->bands[band]->channels[j];
3050
3051 if (chan->flags & IEEE80211_CHAN_DISABLED)
3052 continue;
3053
3054 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01003055 i++;
3056 }
3057 }
3058 }
3059
Johannes Berg584991d2009-11-02 13:32:03 +01003060 if (!i) {
3061 err = -EINVAL;
3062 goto out_free;
3063 }
3064
3065 request->n_channels = i;
3066
Johannes Berg2a519312009-02-10 21:25:55 +01003067 i = 0;
3068 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
3069 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
3070 if (request->ssids[i].ssid_len > IEEE80211_MAX_SSID_LEN) {
3071 err = -EINVAL;
3072 goto out_free;
3073 }
3074 memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
3075 request->ssids[i].ssid_len = nla_len(attr);
3076 i++;
3077 }
3078 }
3079
Jouni Malinen70692ad2009-02-16 19:39:13 +02003080 if (info->attrs[NL80211_ATTR_IE]) {
3081 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Johannes Bergde95a542009-04-01 11:58:36 +02003082 memcpy((void *)request->ie,
3083 nla_data(info->attrs[NL80211_ATTR_IE]),
Jouni Malinen70692ad2009-02-16 19:39:13 +02003084 request->ie_len);
3085 }
3086
Johannes Berg463d0182009-07-14 00:33:35 +02003087 request->dev = dev;
Johannes Berg79c97e92009-07-07 03:56:12 +02003088 request->wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01003089
Johannes Berg79c97e92009-07-07 03:56:12 +02003090 rdev->scan_req = request;
3091 err = rdev->ops->scan(&rdev->wiphy, dev, request);
Johannes Berg2a519312009-02-10 21:25:55 +01003092
Johannes Berg463d0182009-07-14 00:33:35 +02003093 if (!err) {
Johannes Berg79c97e92009-07-07 03:56:12 +02003094 nl80211_send_scan_start(rdev, dev);
Johannes Berg463d0182009-07-14 00:33:35 +02003095 dev_hold(dev);
3096 }
Johannes Berga538e2d2009-06-16 19:56:42 +02003097
Johannes Berg2a519312009-02-10 21:25:55 +01003098 out_free:
3099 if (err) {
Johannes Berg79c97e92009-07-07 03:56:12 +02003100 rdev->scan_req = NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01003101 kfree(request);
3102 }
Johannes Berg2a519312009-02-10 21:25:55 +01003103 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003104 cfg80211_unlock_rdev(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003105 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01003106 out_rtnl:
3107 rtnl_unlock();
3108
Johannes Berg2a519312009-02-10 21:25:55 +01003109 return err;
3110}
3111
3112static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
3113 struct cfg80211_registered_device *rdev,
Johannes Berg48ab9052009-07-10 18:42:31 +02003114 struct wireless_dev *wdev,
3115 struct cfg80211_internal_bss *intbss)
Johannes Berg2a519312009-02-10 21:25:55 +01003116{
Johannes Berg48ab9052009-07-10 18:42:31 +02003117 struct cfg80211_bss *res = &intbss->pub;
Johannes Berg2a519312009-02-10 21:25:55 +01003118 void *hdr;
3119 struct nlattr *bss;
Johannes Berg48ab9052009-07-10 18:42:31 +02003120 int i;
3121
3122 ASSERT_WDEV_LOCK(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003123
3124 hdr = nl80211hdr_put(msg, pid, seq, flags,
3125 NL80211_CMD_NEW_SCAN_RESULTS);
3126 if (!hdr)
3127 return -1;
3128
Johannes Bergf5ea9122009-08-07 16:17:38 +02003129 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation);
Johannes Berg48ab9052009-07-10 18:42:31 +02003130 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex);
Johannes Berg2a519312009-02-10 21:25:55 +01003131
3132 bss = nla_nest_start(msg, NL80211_ATTR_BSS);
3133 if (!bss)
3134 goto nla_put_failure;
3135 if (!is_zero_ether_addr(res->bssid))
3136 NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid);
3137 if (res->information_elements && res->len_information_elements)
3138 NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS,
3139 res->len_information_elements,
3140 res->information_elements);
3141 if (res->tsf)
3142 NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf);
3143 if (res->beacon_interval)
3144 NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval);
3145 NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability);
3146 NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq);
Holger Schurig7c896062009-09-24 12:21:01 +02003147 NLA_PUT_U32(msg, NL80211_BSS_SEEN_MS_AGO,
3148 jiffies_to_msecs(jiffies - intbss->ts));
Johannes Berg2a519312009-02-10 21:25:55 +01003149
Johannes Berg77965c92009-02-18 18:45:06 +01003150 switch (rdev->wiphy.signal_type) {
Johannes Berg2a519312009-02-10 21:25:55 +01003151 case CFG80211_SIGNAL_TYPE_MBM:
3152 NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal);
3153 break;
3154 case CFG80211_SIGNAL_TYPE_UNSPEC:
3155 NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal);
3156 break;
3157 default:
3158 break;
3159 }
3160
Johannes Berg48ab9052009-07-10 18:42:31 +02003161 switch (wdev->iftype) {
3162 case NL80211_IFTYPE_STATION:
3163 if (intbss == wdev->current_bss)
3164 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
3165 NL80211_BSS_STATUS_ASSOCIATED);
3166 else for (i = 0; i < MAX_AUTH_BSSES; i++) {
3167 if (intbss != wdev->auth_bsses[i])
3168 continue;
3169 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
3170 NL80211_BSS_STATUS_AUTHENTICATED);
3171 break;
3172 }
3173 break;
3174 case NL80211_IFTYPE_ADHOC:
3175 if (intbss == wdev->current_bss)
3176 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
3177 NL80211_BSS_STATUS_IBSS_JOINED);
3178 break;
3179 default:
3180 break;
3181 }
3182
Johannes Berg2a519312009-02-10 21:25:55 +01003183 nla_nest_end(msg, bss);
3184
3185 return genlmsg_end(msg, hdr);
3186
3187 nla_put_failure:
3188 genlmsg_cancel(msg, hdr);
3189 return -EMSGSIZE;
3190}
3191
3192static int nl80211_dump_scan(struct sk_buff *skb,
3193 struct netlink_callback *cb)
3194{
Johannes Berg48ab9052009-07-10 18:42:31 +02003195 struct cfg80211_registered_device *rdev;
3196 struct net_device *dev;
Johannes Berg2a519312009-02-10 21:25:55 +01003197 struct cfg80211_internal_bss *scan;
Johannes Berg48ab9052009-07-10 18:42:31 +02003198 struct wireless_dev *wdev;
Johannes Berg2a519312009-02-10 21:25:55 +01003199 int ifidx = cb->args[0];
3200 int start = cb->args[1], idx = 0;
3201 int err;
3202
Holger Schuriga0438972009-11-11 11:30:02 +01003203 if (!ifidx)
3204 ifidx = nl80211_get_ifidx(cb);
3205 if (ifidx < 0)
3206 return ifidx;
3207 cb->args[0] = ifidx;
Johannes Berg2a519312009-02-10 21:25:55 +01003208
Johannes Berg463d0182009-07-14 00:33:35 +02003209 dev = dev_get_by_index(sock_net(skb->sk), ifidx);
Johannes Berg48ab9052009-07-10 18:42:31 +02003210 if (!dev)
Johannes Berg2a519312009-02-10 21:25:55 +01003211 return -ENODEV;
3212
Johannes Berg463d0182009-07-14 00:33:35 +02003213 rdev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Johannes Berg48ab9052009-07-10 18:42:31 +02003214 if (IS_ERR(rdev)) {
3215 err = PTR_ERR(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003216 goto out_put_netdev;
3217 }
3218
Johannes Berg48ab9052009-07-10 18:42:31 +02003219 wdev = dev->ieee80211_ptr;
Johannes Berg2a519312009-02-10 21:25:55 +01003220
Johannes Berg48ab9052009-07-10 18:42:31 +02003221 wdev_lock(wdev);
3222 spin_lock_bh(&rdev->bss_lock);
3223 cfg80211_bss_expire(rdev);
3224
3225 list_for_each_entry(scan, &rdev->bss_list, list) {
Johannes Berg2a519312009-02-10 21:25:55 +01003226 if (++idx <= start)
3227 continue;
3228 if (nl80211_send_bss(skb,
3229 NETLINK_CB(cb->skb).pid,
3230 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Berg48ab9052009-07-10 18:42:31 +02003231 rdev, wdev, scan) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01003232 idx--;
3233 goto out;
3234 }
3235 }
3236
3237 out:
Johannes Berg48ab9052009-07-10 18:42:31 +02003238 spin_unlock_bh(&rdev->bss_lock);
3239 wdev_unlock(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003240
3241 cb->args[1] = idx;
3242 err = skb->len;
Johannes Berg48ab9052009-07-10 18:42:31 +02003243 cfg80211_unlock_rdev(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003244 out_put_netdev:
Johannes Berg48ab9052009-07-10 18:42:31 +02003245 dev_put(dev);
Johannes Berg2a519312009-02-10 21:25:55 +01003246
3247 return err;
3248}
3249
Holger Schurig61fa7132009-11-11 12:25:40 +01003250static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq,
3251 int flags, struct net_device *dev,
3252 struct survey_info *survey)
3253{
3254 void *hdr;
3255 struct nlattr *infoattr;
3256
3257 /* Survey without a channel doesn't make sense */
3258 if (!survey->channel)
3259 return -EINVAL;
3260
3261 hdr = nl80211hdr_put(msg, pid, seq, flags,
3262 NL80211_CMD_NEW_SURVEY_RESULTS);
3263 if (!hdr)
3264 return -ENOMEM;
3265
3266 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
3267
3268 infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO);
3269 if (!infoattr)
3270 goto nla_put_failure;
3271
3272 NLA_PUT_U32(msg, NL80211_SURVEY_INFO_FREQUENCY,
3273 survey->channel->center_freq);
3274 if (survey->filled & SURVEY_INFO_NOISE_DBM)
3275 NLA_PUT_U8(msg, NL80211_SURVEY_INFO_NOISE,
3276 survey->noise);
3277
3278 nla_nest_end(msg, infoattr);
3279
3280 return genlmsg_end(msg, hdr);
3281
3282 nla_put_failure:
3283 genlmsg_cancel(msg, hdr);
3284 return -EMSGSIZE;
3285}
3286
3287static int nl80211_dump_survey(struct sk_buff *skb,
3288 struct netlink_callback *cb)
3289{
3290 struct survey_info survey;
3291 struct cfg80211_registered_device *dev;
3292 struct net_device *netdev;
3293 int ifidx = cb->args[0];
3294 int survey_idx = cb->args[1];
3295 int res;
3296
3297 if (!ifidx)
3298 ifidx = nl80211_get_ifidx(cb);
3299 if (ifidx < 0)
3300 return ifidx;
3301 cb->args[0] = ifidx;
3302
3303 rtnl_lock();
3304
3305 netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
3306 if (!netdev) {
3307 res = -ENODEV;
3308 goto out_rtnl;
3309 }
3310
3311 dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
3312 if (IS_ERR(dev)) {
3313 res = PTR_ERR(dev);
3314 goto out_rtnl;
3315 }
3316
3317 if (!dev->ops->dump_survey) {
3318 res = -EOPNOTSUPP;
3319 goto out_err;
3320 }
3321
3322 while (1) {
3323 res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx,
3324 &survey);
3325 if (res == -ENOENT)
3326 break;
3327 if (res)
3328 goto out_err;
3329
3330 if (nl80211_send_survey(skb,
3331 NETLINK_CB(cb->skb).pid,
3332 cb->nlh->nlmsg_seq, NLM_F_MULTI,
3333 netdev,
3334 &survey) < 0)
3335 goto out;
3336 survey_idx++;
3337 }
3338
3339 out:
3340 cb->args[1] = survey_idx;
3341 res = skb->len;
3342 out_err:
3343 cfg80211_unlock_rdev(dev);
3344 out_rtnl:
3345 rtnl_unlock();
3346
3347 return res;
3348}
3349
Jouni Malinen255e7372009-03-20 21:21:17 +02003350static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
3351{
Samuel Ortizb23aa672009-07-01 21:26:54 +02003352 return auth_type <= NL80211_AUTHTYPE_MAX;
Jouni Malinen255e7372009-03-20 21:21:17 +02003353}
3354
Samuel Ortizb23aa672009-07-01 21:26:54 +02003355static bool nl80211_valid_wpa_versions(u32 wpa_versions)
3356{
3357 return !(wpa_versions & ~(NL80211_WPA_VERSION_1 |
3358 NL80211_WPA_VERSION_2));
3359}
3360
3361static bool nl80211_valid_akm_suite(u32 akm)
3362{
3363 return akm == WLAN_AKM_SUITE_8021X ||
3364 akm == WLAN_AKM_SUITE_PSK;
3365}
3366
3367static bool nl80211_valid_cipher_suite(u32 cipher)
3368{
3369 return cipher == WLAN_CIPHER_SUITE_WEP40 ||
3370 cipher == WLAN_CIPHER_SUITE_WEP104 ||
3371 cipher == WLAN_CIPHER_SUITE_TKIP ||
3372 cipher == WLAN_CIPHER_SUITE_CCMP ||
3373 cipher == WLAN_CIPHER_SUITE_AES_CMAC;
3374}
3375
3376
Jouni Malinen636a5d32009-03-19 13:39:22 +02003377static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
3378{
Johannes Berg79c97e92009-07-07 03:56:12 +02003379 struct cfg80211_registered_device *rdev;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003380 struct net_device *dev;
Johannes Berg19957bb2009-07-02 17:20:43 +02003381 struct ieee80211_channel *chan;
3382 const u8 *bssid, *ssid, *ie = NULL;
3383 int err, ssid_len, ie_len = 0;
3384 enum nl80211_auth_type auth_type;
Johannes Bergfffd0932009-07-08 14:22:54 +02003385 struct key_parse key;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003386
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003387 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3388 return -EINVAL;
3389
3390 if (!info->attrs[NL80211_ATTR_MAC])
3391 return -EINVAL;
3392
Jouni Malinen17780922009-03-27 20:52:47 +02003393 if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
3394 return -EINVAL;
3395
Johannes Berg19957bb2009-07-02 17:20:43 +02003396 if (!info->attrs[NL80211_ATTR_SSID])
3397 return -EINVAL;
3398
3399 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
3400 return -EINVAL;
3401
Johannes Bergfffd0932009-07-08 14:22:54 +02003402 err = nl80211_parse_key(info, &key);
3403 if (err)
3404 return err;
3405
3406 if (key.idx >= 0) {
3407 if (!key.p.key || !key.p.key_len)
3408 return -EINVAL;
3409 if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 ||
3410 key.p.key_len != WLAN_KEY_LEN_WEP40) &&
3411 (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 ||
3412 key.p.key_len != WLAN_KEY_LEN_WEP104))
3413 return -EINVAL;
3414 if (key.idx > 4)
3415 return -EINVAL;
3416 } else {
3417 key.p.key_len = 0;
3418 key.p.key = NULL;
3419 }
3420
Jouni Malinen636a5d32009-03-19 13:39:22 +02003421 rtnl_lock();
3422
Johannes Berg463d0182009-07-14 00:33:35 +02003423 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003424 if (err)
3425 goto unlock_rtnl;
3426
Johannes Berg79c97e92009-07-07 03:56:12 +02003427 if (!rdev->ops->auth) {
Jouni Malinen636a5d32009-03-19 13:39:22 +02003428 err = -EOPNOTSUPP;
3429 goto out;
3430 }
3431
Jouni Malineneec60b02009-03-20 21:21:19 +02003432 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
3433 err = -EOPNOTSUPP;
3434 goto out;
3435 }
3436
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003437 if (!netif_running(dev)) {
3438 err = -ENETDOWN;
3439 goto out;
3440 }
3441
Johannes Berg19957bb2009-07-02 17:20:43 +02003442 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg79c97e92009-07-07 03:56:12 +02003443 chan = ieee80211_get_channel(&rdev->wiphy,
Johannes Berg19957bb2009-07-02 17:20:43 +02003444 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
3445 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) {
Jouni Malinen17780922009-03-27 20:52:47 +02003446 err = -EINVAL;
3447 goto out;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003448 }
3449
Johannes Berg19957bb2009-07-02 17:20:43 +02003450 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3451 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
3452
3453 if (info->attrs[NL80211_ATTR_IE]) {
3454 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3455 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3456 }
3457
3458 auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
3459 if (!nl80211_valid_auth_type(auth_type)) {
3460 err = -EINVAL;
3461 goto out;
3462 }
3463
Johannes Berg79c97e92009-07-07 03:56:12 +02003464 err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
Johannes Bergfffd0932009-07-08 14:22:54 +02003465 ssid, ssid_len, ie, ie_len,
3466 key.p.key, key.p.key_len, key.idx);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003467
3468out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003469 cfg80211_unlock_rdev(rdev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003470 dev_put(dev);
3471unlock_rtnl:
3472 rtnl_unlock();
3473 return err;
3474}
3475
Samuel Ortizb23aa672009-07-01 21:26:54 +02003476static int nl80211_crypto_settings(struct genl_info *info,
Johannes Berg3dc27d22009-07-02 21:36:37 +02003477 struct cfg80211_crypto_settings *settings,
3478 int cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02003479{
Johannes Bergc0b2bbd2009-07-25 16:54:36 +02003480 memset(settings, 0, sizeof(*settings));
3481
Samuel Ortizb23aa672009-07-01 21:26:54 +02003482 settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT];
3483
3484 if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) {
3485 void *data;
3486 int len, i;
3487
3488 data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
3489 len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
3490 settings->n_ciphers_pairwise = len / sizeof(u32);
3491
3492 if (len % sizeof(u32))
3493 return -EINVAL;
3494
Johannes Berg3dc27d22009-07-02 21:36:37 +02003495 if (settings->n_ciphers_pairwise > cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02003496 return -EINVAL;
3497
3498 memcpy(settings->ciphers_pairwise, data, len);
3499
3500 for (i = 0; i < settings->n_ciphers_pairwise; i++)
3501 if (!nl80211_valid_cipher_suite(
3502 settings->ciphers_pairwise[i]))
3503 return -EINVAL;
3504 }
3505
3506 if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) {
3507 settings->cipher_group =
3508 nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]);
3509 if (!nl80211_valid_cipher_suite(settings->cipher_group))
3510 return -EINVAL;
3511 }
3512
3513 if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) {
3514 settings->wpa_versions =
3515 nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]);
3516 if (!nl80211_valid_wpa_versions(settings->wpa_versions))
3517 return -EINVAL;
3518 }
3519
3520 if (info->attrs[NL80211_ATTR_AKM_SUITES]) {
3521 void *data;
3522 int len, i;
3523
3524 data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]);
3525 len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]);
3526 settings->n_akm_suites = len / sizeof(u32);
3527
3528 if (len % sizeof(u32))
3529 return -EINVAL;
3530
3531 memcpy(settings->akm_suites, data, len);
3532
3533 for (i = 0; i < settings->n_ciphers_pairwise; i++)
3534 if (!nl80211_valid_akm_suite(settings->akm_suites[i]))
3535 return -EINVAL;
3536 }
3537
3538 return 0;
3539}
3540
Jouni Malinen636a5d32009-03-19 13:39:22 +02003541static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
3542{
Johannes Berg19957bb2009-07-02 17:20:43 +02003543 struct cfg80211_registered_device *rdev;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003544 struct net_device *dev;
Johannes Berg19957bb2009-07-02 17:20:43 +02003545 struct cfg80211_crypto_settings crypto;
Johannes Berg59bbb6f2009-08-07 17:22:35 +02003546 struct ieee80211_channel *chan, *fixedchan;
Johannes Berg3e5d7642009-07-07 14:37:26 +02003547 const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +02003548 int err, ssid_len, ie_len = 0;
3549 bool use_mfp = false;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003550
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003551 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3552 return -EINVAL;
3553
3554 if (!info->attrs[NL80211_ATTR_MAC] ||
Johannes Berg19957bb2009-07-02 17:20:43 +02003555 !info->attrs[NL80211_ATTR_SSID] ||
3556 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003557 return -EINVAL;
3558
Jouni Malinen636a5d32009-03-19 13:39:22 +02003559 rtnl_lock();
3560
Johannes Berg463d0182009-07-14 00:33:35 +02003561 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003562 if (err)
3563 goto unlock_rtnl;
3564
Johannes Berg19957bb2009-07-02 17:20:43 +02003565 if (!rdev->ops->assoc) {
Jouni Malinen636a5d32009-03-19 13:39:22 +02003566 err = -EOPNOTSUPP;
3567 goto out;
3568 }
3569
Jouni Malineneec60b02009-03-20 21:21:19 +02003570 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
3571 err = -EOPNOTSUPP;
3572 goto out;
3573 }
3574
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003575 if (!netif_running(dev)) {
3576 err = -ENETDOWN;
3577 goto out;
3578 }
3579
Johannes Berg19957bb2009-07-02 17:20:43 +02003580 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003581
Johannes Berg19957bb2009-07-02 17:20:43 +02003582 chan = ieee80211_get_channel(&rdev->wiphy,
3583 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
3584 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) {
3585 err = -EINVAL;
3586 goto out;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003587 }
3588
Johannes Berg59bbb6f2009-08-07 17:22:35 +02003589 mutex_lock(&rdev->devlist_mtx);
3590 fixedchan = rdev_fixed_channel(rdev, NULL);
3591 if (fixedchan && chan != fixedchan) {
3592 err = -EBUSY;
3593 mutex_unlock(&rdev->devlist_mtx);
3594 goto out;
3595 }
3596 mutex_unlock(&rdev->devlist_mtx);
3597
Johannes Berg19957bb2009-07-02 17:20:43 +02003598 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3599 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003600
3601 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02003602 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3603 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003604 }
3605
Jouni Malinendc6382c2009-05-06 22:09:37 +03003606 if (info->attrs[NL80211_ATTR_USE_MFP]) {
Johannes Berg4f5dadc2009-07-07 03:56:10 +02003607 enum nl80211_mfp mfp =
Jouni Malinendc6382c2009-05-06 22:09:37 +03003608 nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
Johannes Berg4f5dadc2009-07-07 03:56:10 +02003609 if (mfp == NL80211_MFP_REQUIRED)
Johannes Berg19957bb2009-07-02 17:20:43 +02003610 use_mfp = true;
Johannes Berg4f5dadc2009-07-07 03:56:10 +02003611 else if (mfp != NL80211_MFP_NO) {
Jouni Malinendc6382c2009-05-06 22:09:37 +03003612 err = -EINVAL;
3613 goto out;
3614 }
3615 }
3616
Johannes Berg3e5d7642009-07-07 14:37:26 +02003617 if (info->attrs[NL80211_ATTR_PREV_BSSID])
3618 prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
3619
Johannes Berg3dc27d22009-07-02 21:36:37 +02003620 err = nl80211_crypto_settings(info, &crypto, 1);
Samuel Ortizb23aa672009-07-01 21:26:54 +02003621 if (!err)
Johannes Berg3e5d7642009-07-07 14:37:26 +02003622 err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
3623 ssid, ssid_len, ie, ie_len, use_mfp,
Johannes Berg19957bb2009-07-02 17:20:43 +02003624 &crypto);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003625
3626out:
Johannes Berg4d0c8ae2009-07-07 03:56:09 +02003627 cfg80211_unlock_rdev(rdev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003628 dev_put(dev);
3629unlock_rtnl:
3630 rtnl_unlock();
3631 return err;
3632}
3633
3634static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
3635{
Johannes Berg79c97e92009-07-07 03:56:12 +02003636 struct cfg80211_registered_device *rdev;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003637 struct net_device *dev;
Johannes Berg19957bb2009-07-02 17:20:43 +02003638 const u8 *ie = NULL, *bssid;
3639 int err, ie_len = 0;
3640 u16 reason_code;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003641
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003642 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3643 return -EINVAL;
3644
3645 if (!info->attrs[NL80211_ATTR_MAC])
3646 return -EINVAL;
3647
3648 if (!info->attrs[NL80211_ATTR_REASON_CODE])
3649 return -EINVAL;
3650
Jouni Malinen636a5d32009-03-19 13:39:22 +02003651 rtnl_lock();
3652
Johannes Berg463d0182009-07-14 00:33:35 +02003653 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003654 if (err)
3655 goto unlock_rtnl;
3656
Johannes Berg79c97e92009-07-07 03:56:12 +02003657 if (!rdev->ops->deauth) {
Jouni Malinen636a5d32009-03-19 13:39:22 +02003658 err = -EOPNOTSUPP;
3659 goto out;
3660 }
3661
Jouni Malineneec60b02009-03-20 21:21:19 +02003662 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
3663 err = -EOPNOTSUPP;
3664 goto out;
3665 }
3666
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003667 if (!netif_running(dev)) {
3668 err = -ENETDOWN;
3669 goto out;
3670 }
3671
Johannes Berg19957bb2009-07-02 17:20:43 +02003672 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003673
Johannes Berg19957bb2009-07-02 17:20:43 +02003674 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
3675 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003676 /* Reason Code 0 is reserved */
3677 err = -EINVAL;
3678 goto out;
Jouni Malinen255e7372009-03-20 21:21:17 +02003679 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02003680
3681 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02003682 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3683 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003684 }
3685
Johannes Berg79c97e92009-07-07 03:56:12 +02003686 err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003687
3688out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003689 cfg80211_unlock_rdev(rdev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003690 dev_put(dev);
3691unlock_rtnl:
3692 rtnl_unlock();
3693 return err;
3694}
3695
3696static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
3697{
Johannes Berg79c97e92009-07-07 03:56:12 +02003698 struct cfg80211_registered_device *rdev;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003699 struct net_device *dev;
Johannes Berg19957bb2009-07-02 17:20:43 +02003700 const u8 *ie = NULL, *bssid;
3701 int err, ie_len = 0;
3702 u16 reason_code;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003703
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003704 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3705 return -EINVAL;
3706
3707 if (!info->attrs[NL80211_ATTR_MAC])
3708 return -EINVAL;
3709
3710 if (!info->attrs[NL80211_ATTR_REASON_CODE])
3711 return -EINVAL;
3712
Jouni Malinen636a5d32009-03-19 13:39:22 +02003713 rtnl_lock();
3714
Johannes Berg463d0182009-07-14 00:33:35 +02003715 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003716 if (err)
3717 goto unlock_rtnl;
3718
Johannes Berg79c97e92009-07-07 03:56:12 +02003719 if (!rdev->ops->disassoc) {
Jouni Malinen636a5d32009-03-19 13:39:22 +02003720 err = -EOPNOTSUPP;
3721 goto out;
3722 }
3723
Jouni Malineneec60b02009-03-20 21:21:19 +02003724 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
3725 err = -EOPNOTSUPP;
3726 goto out;
3727 }
3728
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003729 if (!netif_running(dev)) {
3730 err = -ENETDOWN;
3731 goto out;
3732 }
3733
Johannes Berg19957bb2009-07-02 17:20:43 +02003734 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003735
Johannes Berg19957bb2009-07-02 17:20:43 +02003736 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
3737 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003738 /* Reason Code 0 is reserved */
3739 err = -EINVAL;
3740 goto out;
Jouni Malinen255e7372009-03-20 21:21:17 +02003741 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02003742
3743 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02003744 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3745 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003746 }
3747
Johannes Berg79c97e92009-07-07 03:56:12 +02003748 err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003749
3750out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003751 cfg80211_unlock_rdev(rdev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003752 dev_put(dev);
3753unlock_rtnl:
3754 rtnl_unlock();
3755 return err;
3756}
3757
Johannes Berg04a773a2009-04-19 21:24:32 +02003758static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
3759{
Johannes Berg79c97e92009-07-07 03:56:12 +02003760 struct cfg80211_registered_device *rdev;
Johannes Berg04a773a2009-04-19 21:24:32 +02003761 struct net_device *dev;
3762 struct cfg80211_ibss_params ibss;
3763 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02003764 struct cfg80211_cached_keys *connkeys = NULL;
Johannes Berg04a773a2009-04-19 21:24:32 +02003765 int err;
3766
Johannes Berg8e30bc52009-04-22 17:45:38 +02003767 memset(&ibss, 0, sizeof(ibss));
3768
Johannes Berg04a773a2009-04-19 21:24:32 +02003769 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3770 return -EINVAL;
3771
3772 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
3773 !info->attrs[NL80211_ATTR_SSID] ||
3774 !nla_len(info->attrs[NL80211_ATTR_SSID]))
3775 return -EINVAL;
3776
Johannes Berg8e30bc52009-04-22 17:45:38 +02003777 ibss.beacon_interval = 100;
3778
3779 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
3780 ibss.beacon_interval =
3781 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
3782 if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000)
3783 return -EINVAL;
3784 }
3785
Johannes Berg04a773a2009-04-19 21:24:32 +02003786 rtnl_lock();
3787
Johannes Berg463d0182009-07-14 00:33:35 +02003788 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg04a773a2009-04-19 21:24:32 +02003789 if (err)
3790 goto unlock_rtnl;
3791
Johannes Berg79c97e92009-07-07 03:56:12 +02003792 if (!rdev->ops->join_ibss) {
Johannes Berg04a773a2009-04-19 21:24:32 +02003793 err = -EOPNOTSUPP;
3794 goto out;
3795 }
3796
3797 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
3798 err = -EOPNOTSUPP;
3799 goto out;
3800 }
3801
3802 if (!netif_running(dev)) {
3803 err = -ENETDOWN;
3804 goto out;
3805 }
3806
Johannes Berg79c97e92009-07-07 03:56:12 +02003807 wiphy = &rdev->wiphy;
Johannes Berg04a773a2009-04-19 21:24:32 +02003808
3809 if (info->attrs[NL80211_ATTR_MAC])
3810 ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
3811 ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3812 ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
3813
3814 if (info->attrs[NL80211_ATTR_IE]) {
3815 ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3816 ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3817 }
3818
3819 ibss.channel = ieee80211_get_channel(wiphy,
3820 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
3821 if (!ibss.channel ||
3822 ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
3823 ibss.channel->flags & IEEE80211_CHAN_DISABLED) {
3824 err = -EINVAL;
3825 goto out;
3826 }
3827
3828 ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
Johannes Bergfffd0932009-07-08 14:22:54 +02003829 ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
Johannes Berg04a773a2009-04-19 21:24:32 +02003830
Johannes Bergfffd0932009-07-08 14:22:54 +02003831 if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
3832 connkeys = nl80211_parse_connkeys(rdev,
3833 info->attrs[NL80211_ATTR_KEYS]);
3834 if (IS_ERR(connkeys)) {
3835 err = PTR_ERR(connkeys);
3836 connkeys = NULL;
3837 goto out;
3838 }
3839 }
3840
3841 err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
Johannes Berg04a773a2009-04-19 21:24:32 +02003842
3843out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003844 cfg80211_unlock_rdev(rdev);
Johannes Berg04a773a2009-04-19 21:24:32 +02003845 dev_put(dev);
3846unlock_rtnl:
Johannes Bergfffd0932009-07-08 14:22:54 +02003847 if (err)
3848 kfree(connkeys);
Johannes Berg04a773a2009-04-19 21:24:32 +02003849 rtnl_unlock();
3850 return err;
3851}
3852
3853static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
3854{
Johannes Berg79c97e92009-07-07 03:56:12 +02003855 struct cfg80211_registered_device *rdev;
Johannes Berg04a773a2009-04-19 21:24:32 +02003856 struct net_device *dev;
3857 int err;
3858
3859 rtnl_lock();
3860
Johannes Berg463d0182009-07-14 00:33:35 +02003861 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg04a773a2009-04-19 21:24:32 +02003862 if (err)
3863 goto unlock_rtnl;
3864
Johannes Berg79c97e92009-07-07 03:56:12 +02003865 if (!rdev->ops->leave_ibss) {
Johannes Berg04a773a2009-04-19 21:24:32 +02003866 err = -EOPNOTSUPP;
3867 goto out;
3868 }
3869
3870 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
3871 err = -EOPNOTSUPP;
3872 goto out;
3873 }
3874
3875 if (!netif_running(dev)) {
3876 err = -ENETDOWN;
3877 goto out;
3878 }
3879
Johannes Berg79c97e92009-07-07 03:56:12 +02003880 err = cfg80211_leave_ibss(rdev, dev, false);
Johannes Berg04a773a2009-04-19 21:24:32 +02003881
3882out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003883 cfg80211_unlock_rdev(rdev);
Johannes Berg04a773a2009-04-19 21:24:32 +02003884 dev_put(dev);
3885unlock_rtnl:
3886 rtnl_unlock();
3887 return err;
3888}
3889
Johannes Bergaff89a92009-07-01 21:26:51 +02003890#ifdef CONFIG_NL80211_TESTMODE
3891static struct genl_multicast_group nl80211_testmode_mcgrp = {
3892 .name = "testmode",
3893};
3894
3895static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
3896{
3897 struct cfg80211_registered_device *rdev;
3898 int err;
3899
3900 if (!info->attrs[NL80211_ATTR_TESTDATA])
3901 return -EINVAL;
3902
3903 rtnl_lock();
3904
3905 rdev = cfg80211_get_dev_from_info(info);
3906 if (IS_ERR(rdev)) {
3907 err = PTR_ERR(rdev);
3908 goto unlock_rtnl;
3909 }
3910
3911 err = -EOPNOTSUPP;
3912 if (rdev->ops->testmode_cmd) {
3913 rdev->testmode_info = info;
3914 err = rdev->ops->testmode_cmd(&rdev->wiphy,
3915 nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
3916 nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
3917 rdev->testmode_info = NULL;
3918 }
3919
Johannes Berg4d0c8ae2009-07-07 03:56:09 +02003920 cfg80211_unlock_rdev(rdev);
Johannes Bergaff89a92009-07-01 21:26:51 +02003921
3922 unlock_rtnl:
3923 rtnl_unlock();
3924 return err;
3925}
3926
3927static struct sk_buff *
3928__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
3929 int approxlen, u32 pid, u32 seq, gfp_t gfp)
3930{
3931 struct sk_buff *skb;
3932 void *hdr;
3933 struct nlattr *data;
3934
3935 skb = nlmsg_new(approxlen + 100, gfp);
3936 if (!skb)
3937 return NULL;
3938
3939 hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE);
3940 if (!hdr) {
3941 kfree_skb(skb);
3942 return NULL;
3943 }
3944
3945 NLA_PUT_U32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
3946 data = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
3947
3948 ((void **)skb->cb)[0] = rdev;
3949 ((void **)skb->cb)[1] = hdr;
3950 ((void **)skb->cb)[2] = data;
3951
3952 return skb;
3953
3954 nla_put_failure:
3955 kfree_skb(skb);
3956 return NULL;
3957}
3958
3959struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
3960 int approxlen)
3961{
3962 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
3963
3964 if (WARN_ON(!rdev->testmode_info))
3965 return NULL;
3966
3967 return __cfg80211_testmode_alloc_skb(rdev, approxlen,
3968 rdev->testmode_info->snd_pid,
3969 rdev->testmode_info->snd_seq,
3970 GFP_KERNEL);
3971}
3972EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb);
3973
3974int cfg80211_testmode_reply(struct sk_buff *skb)
3975{
3976 struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
3977 void *hdr = ((void **)skb->cb)[1];
3978 struct nlattr *data = ((void **)skb->cb)[2];
3979
3980 if (WARN_ON(!rdev->testmode_info)) {
3981 kfree_skb(skb);
3982 return -EINVAL;
3983 }
3984
3985 nla_nest_end(skb, data);
3986 genlmsg_end(skb, hdr);
3987 return genlmsg_reply(skb, rdev->testmode_info);
3988}
3989EXPORT_SYMBOL(cfg80211_testmode_reply);
3990
3991struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
3992 int approxlen, gfp_t gfp)
3993{
3994 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
3995
3996 return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp);
3997}
3998EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb);
3999
4000void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
4001{
4002 void *hdr = ((void **)skb->cb)[1];
4003 struct nlattr *data = ((void **)skb->cb)[2];
4004
4005 nla_nest_end(skb, data);
4006 genlmsg_end(skb, hdr);
4007 genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp);
4008}
4009EXPORT_SYMBOL(cfg80211_testmode_event);
4010#endif
4011
Samuel Ortizb23aa672009-07-01 21:26:54 +02004012static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
4013{
Johannes Berg79c97e92009-07-07 03:56:12 +02004014 struct cfg80211_registered_device *rdev;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004015 struct net_device *dev;
4016 struct cfg80211_connect_params connect;
4017 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02004018 struct cfg80211_cached_keys *connkeys = NULL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004019 int err;
4020
4021 memset(&connect, 0, sizeof(connect));
4022
4023 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4024 return -EINVAL;
4025
4026 if (!info->attrs[NL80211_ATTR_SSID] ||
4027 !nla_len(info->attrs[NL80211_ATTR_SSID]))
4028 return -EINVAL;
4029
4030 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
4031 connect.auth_type =
4032 nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
4033 if (!nl80211_valid_auth_type(connect.auth_type))
4034 return -EINVAL;
4035 } else
4036 connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
4037
4038 connect.privacy = info->attrs[NL80211_ATTR_PRIVACY];
4039
Johannes Berg3dc27d22009-07-02 21:36:37 +02004040 err = nl80211_crypto_settings(info, &connect.crypto,
4041 NL80211_MAX_NR_CIPHER_SUITES);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004042 if (err)
4043 return err;
4044 rtnl_lock();
4045
Johannes Berg463d0182009-07-14 00:33:35 +02004046 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004047 if (err)
4048 goto unlock_rtnl;
4049
4050 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
4051 err = -EOPNOTSUPP;
4052 goto out;
4053 }
4054
4055 if (!netif_running(dev)) {
4056 err = -ENETDOWN;
4057 goto out;
4058 }
4059
Johannes Berg79c97e92009-07-07 03:56:12 +02004060 wiphy = &rdev->wiphy;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004061
Samuel Ortizb23aa672009-07-01 21:26:54 +02004062 if (info->attrs[NL80211_ATTR_MAC])
4063 connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
4064 connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4065 connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4066
4067 if (info->attrs[NL80211_ATTR_IE]) {
4068 connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4069 connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4070 }
4071
4072 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
4073 connect.channel =
4074 ieee80211_get_channel(wiphy,
4075 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
4076 if (!connect.channel ||
4077 connect.channel->flags & IEEE80211_CHAN_DISABLED) {
4078 err = -EINVAL;
4079 goto out;
4080 }
4081 }
4082
Johannes Bergfffd0932009-07-08 14:22:54 +02004083 if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
4084 connkeys = nl80211_parse_connkeys(rdev,
4085 info->attrs[NL80211_ATTR_KEYS]);
4086 if (IS_ERR(connkeys)) {
4087 err = PTR_ERR(connkeys);
4088 connkeys = NULL;
4089 goto out;
4090 }
4091 }
4092
4093 err = cfg80211_connect(rdev, dev, &connect, connkeys);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004094
4095out:
Johannes Berg79c97e92009-07-07 03:56:12 +02004096 cfg80211_unlock_rdev(rdev);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004097 dev_put(dev);
4098unlock_rtnl:
Johannes Bergfffd0932009-07-08 14:22:54 +02004099 if (err)
4100 kfree(connkeys);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004101 rtnl_unlock();
4102 return err;
4103}
4104
4105static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
4106{
Johannes Berg79c97e92009-07-07 03:56:12 +02004107 struct cfg80211_registered_device *rdev;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004108 struct net_device *dev;
4109 int err;
4110 u16 reason;
4111
4112 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4113 reason = WLAN_REASON_DEAUTH_LEAVING;
4114 else
4115 reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4116
4117 if (reason == 0)
4118 return -EINVAL;
4119
4120 rtnl_lock();
4121
Johannes Berg463d0182009-07-14 00:33:35 +02004122 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004123 if (err)
4124 goto unlock_rtnl;
4125
4126 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
4127 err = -EOPNOTSUPP;
4128 goto out;
4129 }
4130
4131 if (!netif_running(dev)) {
4132 err = -ENETDOWN;
4133 goto out;
4134 }
4135
Johannes Berg79c97e92009-07-07 03:56:12 +02004136 err = cfg80211_disconnect(rdev, dev, reason, true);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004137
4138out:
Johannes Berg79c97e92009-07-07 03:56:12 +02004139 cfg80211_unlock_rdev(rdev);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004140 dev_put(dev);
4141unlock_rtnl:
4142 rtnl_unlock();
4143 return err;
4144}
4145
Johannes Berg463d0182009-07-14 00:33:35 +02004146static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
4147{
4148 struct cfg80211_registered_device *rdev;
4149 struct net *net;
4150 int err;
4151 u32 pid;
4152
4153 if (!info->attrs[NL80211_ATTR_PID])
4154 return -EINVAL;
4155
4156 pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
4157
4158 rtnl_lock();
4159
4160 rdev = cfg80211_get_dev_from_info(info);
4161 if (IS_ERR(rdev)) {
4162 err = PTR_ERR(rdev);
Johannes Berg8a8e05e2009-10-08 21:02:02 +02004163 goto out_rtnl;
Johannes Berg463d0182009-07-14 00:33:35 +02004164 }
4165
4166 net = get_net_ns_by_pid(pid);
4167 if (IS_ERR(net)) {
4168 err = PTR_ERR(net);
4169 goto out;
4170 }
4171
4172 err = 0;
4173
4174 /* check if anything to do */
4175 if (net_eq(wiphy_net(&rdev->wiphy), net))
4176 goto out_put_net;
4177
4178 err = cfg80211_switch_netns(rdev, net);
4179 out_put_net:
4180 put_net(net);
4181 out:
4182 cfg80211_unlock_rdev(rdev);
Johannes Berg8a8e05e2009-10-08 21:02:02 +02004183 out_rtnl:
Johannes Berg463d0182009-07-14 00:33:35 +02004184 rtnl_unlock();
4185 return err;
4186}
4187
Johannes Berg55682962007-09-20 13:09:35 -04004188static struct genl_ops nl80211_ops[] = {
4189 {
4190 .cmd = NL80211_CMD_GET_WIPHY,
4191 .doit = nl80211_get_wiphy,
4192 .dumpit = nl80211_dump_wiphy,
4193 .policy = nl80211_policy,
4194 /* can be retrieved by unprivileged users */
4195 },
4196 {
4197 .cmd = NL80211_CMD_SET_WIPHY,
4198 .doit = nl80211_set_wiphy,
4199 .policy = nl80211_policy,
4200 .flags = GENL_ADMIN_PERM,
4201 },
4202 {
4203 .cmd = NL80211_CMD_GET_INTERFACE,
4204 .doit = nl80211_get_interface,
4205 .dumpit = nl80211_dump_interface,
4206 .policy = nl80211_policy,
4207 /* can be retrieved by unprivileged users */
4208 },
4209 {
4210 .cmd = NL80211_CMD_SET_INTERFACE,
4211 .doit = nl80211_set_interface,
4212 .policy = nl80211_policy,
4213 .flags = GENL_ADMIN_PERM,
4214 },
4215 {
4216 .cmd = NL80211_CMD_NEW_INTERFACE,
4217 .doit = nl80211_new_interface,
4218 .policy = nl80211_policy,
4219 .flags = GENL_ADMIN_PERM,
4220 },
4221 {
4222 .cmd = NL80211_CMD_DEL_INTERFACE,
4223 .doit = nl80211_del_interface,
4224 .policy = nl80211_policy,
4225 .flags = GENL_ADMIN_PERM,
4226 },
Johannes Berg41ade002007-12-19 02:03:29 +01004227 {
4228 .cmd = NL80211_CMD_GET_KEY,
4229 .doit = nl80211_get_key,
4230 .policy = nl80211_policy,
4231 .flags = GENL_ADMIN_PERM,
4232 },
4233 {
4234 .cmd = NL80211_CMD_SET_KEY,
4235 .doit = nl80211_set_key,
4236 .policy = nl80211_policy,
4237 .flags = GENL_ADMIN_PERM,
4238 },
4239 {
4240 .cmd = NL80211_CMD_NEW_KEY,
4241 .doit = nl80211_new_key,
4242 .policy = nl80211_policy,
4243 .flags = GENL_ADMIN_PERM,
4244 },
4245 {
4246 .cmd = NL80211_CMD_DEL_KEY,
4247 .doit = nl80211_del_key,
4248 .policy = nl80211_policy,
4249 .flags = GENL_ADMIN_PERM,
4250 },
Johannes Berged1b6cc2007-12-19 02:03:32 +01004251 {
4252 .cmd = NL80211_CMD_SET_BEACON,
4253 .policy = nl80211_policy,
4254 .flags = GENL_ADMIN_PERM,
4255 .doit = nl80211_addset_beacon,
4256 },
4257 {
4258 .cmd = NL80211_CMD_NEW_BEACON,
4259 .policy = nl80211_policy,
4260 .flags = GENL_ADMIN_PERM,
4261 .doit = nl80211_addset_beacon,
4262 },
4263 {
4264 .cmd = NL80211_CMD_DEL_BEACON,
4265 .policy = nl80211_policy,
4266 .flags = GENL_ADMIN_PERM,
4267 .doit = nl80211_del_beacon,
4268 },
Johannes Berg5727ef12007-12-19 02:03:34 +01004269 {
4270 .cmd = NL80211_CMD_GET_STATION,
4271 .doit = nl80211_get_station,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004272 .dumpit = nl80211_dump_station,
Johannes Berg5727ef12007-12-19 02:03:34 +01004273 .policy = nl80211_policy,
Johannes Berg5727ef12007-12-19 02:03:34 +01004274 },
4275 {
4276 .cmd = NL80211_CMD_SET_STATION,
4277 .doit = nl80211_set_station,
4278 .policy = nl80211_policy,
4279 .flags = GENL_ADMIN_PERM,
4280 },
4281 {
4282 .cmd = NL80211_CMD_NEW_STATION,
4283 .doit = nl80211_new_station,
4284 .policy = nl80211_policy,
4285 .flags = GENL_ADMIN_PERM,
4286 },
4287 {
4288 .cmd = NL80211_CMD_DEL_STATION,
4289 .doit = nl80211_del_station,
4290 .policy = nl80211_policy,
4291 .flags = GENL_ADMIN_PERM,
4292 },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004293 {
4294 .cmd = NL80211_CMD_GET_MPATH,
4295 .doit = nl80211_get_mpath,
4296 .dumpit = nl80211_dump_mpath,
4297 .policy = nl80211_policy,
4298 .flags = GENL_ADMIN_PERM,
4299 },
4300 {
4301 .cmd = NL80211_CMD_SET_MPATH,
4302 .doit = nl80211_set_mpath,
4303 .policy = nl80211_policy,
4304 .flags = GENL_ADMIN_PERM,
4305 },
4306 {
4307 .cmd = NL80211_CMD_NEW_MPATH,
4308 .doit = nl80211_new_mpath,
4309 .policy = nl80211_policy,
4310 .flags = GENL_ADMIN_PERM,
4311 },
4312 {
4313 .cmd = NL80211_CMD_DEL_MPATH,
4314 .doit = nl80211_del_mpath,
4315 .policy = nl80211_policy,
4316 .flags = GENL_ADMIN_PERM,
4317 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +03004318 {
4319 .cmd = NL80211_CMD_SET_BSS,
4320 .doit = nl80211_set_bss,
4321 .policy = nl80211_policy,
4322 .flags = GENL_ADMIN_PERM,
4323 },
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004324 {
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004325 .cmd = NL80211_CMD_GET_REG,
4326 .doit = nl80211_get_reg,
4327 .policy = nl80211_policy,
4328 /* can be retrieved by unprivileged users */
4329 },
4330 {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004331 .cmd = NL80211_CMD_SET_REG,
4332 .doit = nl80211_set_reg,
4333 .policy = nl80211_policy,
4334 .flags = GENL_ADMIN_PERM,
4335 },
4336 {
4337 .cmd = NL80211_CMD_REQ_SET_REG,
4338 .doit = nl80211_req_set_reg,
4339 .policy = nl80211_policy,
4340 .flags = GENL_ADMIN_PERM,
4341 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004342 {
4343 .cmd = NL80211_CMD_GET_MESH_PARAMS,
4344 .doit = nl80211_get_mesh_params,
4345 .policy = nl80211_policy,
4346 /* can be retrieved by unprivileged users */
4347 },
4348 {
4349 .cmd = NL80211_CMD_SET_MESH_PARAMS,
4350 .doit = nl80211_set_mesh_params,
4351 .policy = nl80211_policy,
4352 .flags = GENL_ADMIN_PERM,
4353 },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +02004354 {
Johannes Berg2a519312009-02-10 21:25:55 +01004355 .cmd = NL80211_CMD_TRIGGER_SCAN,
4356 .doit = nl80211_trigger_scan,
4357 .policy = nl80211_policy,
4358 .flags = GENL_ADMIN_PERM,
4359 },
4360 {
4361 .cmd = NL80211_CMD_GET_SCAN,
4362 .policy = nl80211_policy,
4363 .dumpit = nl80211_dump_scan,
4364 },
Jouni Malinen636a5d32009-03-19 13:39:22 +02004365 {
4366 .cmd = NL80211_CMD_AUTHENTICATE,
4367 .doit = nl80211_authenticate,
4368 .policy = nl80211_policy,
4369 .flags = GENL_ADMIN_PERM,
4370 },
4371 {
4372 .cmd = NL80211_CMD_ASSOCIATE,
4373 .doit = nl80211_associate,
4374 .policy = nl80211_policy,
4375 .flags = GENL_ADMIN_PERM,
4376 },
4377 {
4378 .cmd = NL80211_CMD_DEAUTHENTICATE,
4379 .doit = nl80211_deauthenticate,
4380 .policy = nl80211_policy,
4381 .flags = GENL_ADMIN_PERM,
4382 },
4383 {
4384 .cmd = NL80211_CMD_DISASSOCIATE,
4385 .doit = nl80211_disassociate,
4386 .policy = nl80211_policy,
4387 .flags = GENL_ADMIN_PERM,
4388 },
Johannes Berg04a773a2009-04-19 21:24:32 +02004389 {
4390 .cmd = NL80211_CMD_JOIN_IBSS,
4391 .doit = nl80211_join_ibss,
4392 .policy = nl80211_policy,
4393 .flags = GENL_ADMIN_PERM,
4394 },
4395 {
4396 .cmd = NL80211_CMD_LEAVE_IBSS,
4397 .doit = nl80211_leave_ibss,
4398 .policy = nl80211_policy,
4399 .flags = GENL_ADMIN_PERM,
4400 },
Johannes Bergaff89a92009-07-01 21:26:51 +02004401#ifdef CONFIG_NL80211_TESTMODE
4402 {
4403 .cmd = NL80211_CMD_TESTMODE,
4404 .doit = nl80211_testmode_do,
4405 .policy = nl80211_policy,
4406 .flags = GENL_ADMIN_PERM,
4407 },
4408#endif
Samuel Ortizb23aa672009-07-01 21:26:54 +02004409 {
4410 .cmd = NL80211_CMD_CONNECT,
4411 .doit = nl80211_connect,
4412 .policy = nl80211_policy,
4413 .flags = GENL_ADMIN_PERM,
4414 },
4415 {
4416 .cmd = NL80211_CMD_DISCONNECT,
4417 .doit = nl80211_disconnect,
4418 .policy = nl80211_policy,
4419 .flags = GENL_ADMIN_PERM,
4420 },
Johannes Berg463d0182009-07-14 00:33:35 +02004421 {
4422 .cmd = NL80211_CMD_SET_WIPHY_NETNS,
4423 .doit = nl80211_wiphy_netns,
4424 .policy = nl80211_policy,
4425 .flags = GENL_ADMIN_PERM,
4426 },
Holger Schurig61fa7132009-11-11 12:25:40 +01004427 {
4428 .cmd = NL80211_CMD_GET_SURVEY,
4429 .policy = nl80211_policy,
4430 .dumpit = nl80211_dump_survey,
4431 },
Johannes Berg55682962007-09-20 13:09:35 -04004432};
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004433static struct genl_multicast_group nl80211_mlme_mcgrp = {
4434 .name = "mlme",
4435};
Johannes Berg55682962007-09-20 13:09:35 -04004436
4437/* multicast groups */
4438static struct genl_multicast_group nl80211_config_mcgrp = {
4439 .name = "config",
4440};
Johannes Berg2a519312009-02-10 21:25:55 +01004441static struct genl_multicast_group nl80211_scan_mcgrp = {
4442 .name = "scan",
4443};
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04004444static struct genl_multicast_group nl80211_regulatory_mcgrp = {
4445 .name = "regulatory",
4446};
Johannes Berg55682962007-09-20 13:09:35 -04004447
4448/* notification functions */
4449
4450void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
4451{
4452 struct sk_buff *msg;
4453
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07004454 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04004455 if (!msg)
4456 return;
4457
4458 if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
4459 nlmsg_free(msg);
4460 return;
4461 }
4462
Johannes Berg463d0182009-07-14 00:33:35 +02004463 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4464 nl80211_config_mcgrp.id, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04004465}
4466
Johannes Berg362a4152009-05-24 16:43:15 +02004467static int nl80211_add_scan_req(struct sk_buff *msg,
4468 struct cfg80211_registered_device *rdev)
4469{
4470 struct cfg80211_scan_request *req = rdev->scan_req;
4471 struct nlattr *nest;
4472 int i;
4473
Johannes Berg667503dd2009-07-07 03:56:11 +02004474 ASSERT_RDEV_LOCK(rdev);
4475
Johannes Berg362a4152009-05-24 16:43:15 +02004476 if (WARN_ON(!req))
4477 return 0;
4478
4479 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
4480 if (!nest)
4481 goto nla_put_failure;
4482 for (i = 0; i < req->n_ssids; i++)
4483 NLA_PUT(msg, i, req->ssids[i].ssid_len, req->ssids[i].ssid);
4484 nla_nest_end(msg, nest);
4485
4486 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
4487 if (!nest)
4488 goto nla_put_failure;
4489 for (i = 0; i < req->n_channels; i++)
4490 NLA_PUT_U32(msg, i, req->channels[i]->center_freq);
4491 nla_nest_end(msg, nest);
4492
4493 if (req->ie)
4494 NLA_PUT(msg, NL80211_ATTR_IE, req->ie_len, req->ie);
4495
4496 return 0;
4497 nla_put_failure:
4498 return -ENOBUFS;
4499}
4500
Johannes Berga538e2d2009-06-16 19:56:42 +02004501static int nl80211_send_scan_msg(struct sk_buff *msg,
4502 struct cfg80211_registered_device *rdev,
4503 struct net_device *netdev,
4504 u32 pid, u32 seq, int flags,
4505 u32 cmd)
Johannes Berg2a519312009-02-10 21:25:55 +01004506{
4507 void *hdr;
4508
4509 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
4510 if (!hdr)
4511 return -1;
4512
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -05004513 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg2a519312009-02-10 21:25:55 +01004514 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
4515
Johannes Berg362a4152009-05-24 16:43:15 +02004516 /* ignore errors and send incomplete event anyway */
4517 nl80211_add_scan_req(msg, rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01004518
4519 return genlmsg_end(msg, hdr);
4520
4521 nla_put_failure:
4522 genlmsg_cancel(msg, hdr);
4523 return -EMSGSIZE;
4524}
4525
Johannes Berga538e2d2009-06-16 19:56:42 +02004526void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
4527 struct net_device *netdev)
4528{
4529 struct sk_buff *msg;
4530
4531 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
4532 if (!msg)
4533 return;
4534
4535 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
4536 NL80211_CMD_TRIGGER_SCAN) < 0) {
4537 nlmsg_free(msg);
4538 return;
4539 }
4540
Johannes Berg463d0182009-07-14 00:33:35 +02004541 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4542 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berga538e2d2009-06-16 19:56:42 +02004543}
4544
Johannes Berg2a519312009-02-10 21:25:55 +01004545void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
4546 struct net_device *netdev)
4547{
4548 struct sk_buff *msg;
4549
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07004550 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01004551 if (!msg)
4552 return;
4553
Johannes Berga538e2d2009-06-16 19:56:42 +02004554 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
4555 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01004556 nlmsg_free(msg);
4557 return;
4558 }
4559
Johannes Berg463d0182009-07-14 00:33:35 +02004560 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4561 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01004562}
4563
4564void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
4565 struct net_device *netdev)
4566{
4567 struct sk_buff *msg;
4568
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07004569 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01004570 if (!msg)
4571 return;
4572
Johannes Berga538e2d2009-06-16 19:56:42 +02004573 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
4574 NL80211_CMD_SCAN_ABORTED) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01004575 nlmsg_free(msg);
4576 return;
4577 }
4578
Johannes Berg463d0182009-07-14 00:33:35 +02004579 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4580 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01004581}
4582
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04004583/*
4584 * This can happen on global regulatory changes or device specific settings
4585 * based on custom world regulatory domains.
4586 */
4587void nl80211_send_reg_change_event(struct regulatory_request *request)
4588{
4589 struct sk_buff *msg;
4590 void *hdr;
4591
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07004592 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04004593 if (!msg)
4594 return;
4595
4596 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
4597 if (!hdr) {
4598 nlmsg_free(msg);
4599 return;
4600 }
4601
4602 /* Userspace can always count this one always being set */
4603 NLA_PUT_U8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator);
4604
4605 if (request->alpha2[0] == '0' && request->alpha2[1] == '0')
4606 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
4607 NL80211_REGDOM_TYPE_WORLD);
4608 else if (request->alpha2[0] == '9' && request->alpha2[1] == '9')
4609 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
4610 NL80211_REGDOM_TYPE_CUSTOM_WORLD);
4611 else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
4612 request->intersect)
4613 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
4614 NL80211_REGDOM_TYPE_INTERSECTION);
4615 else {
4616 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
4617 NL80211_REGDOM_TYPE_COUNTRY);
4618 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2);
4619 }
4620
4621 if (wiphy_idx_valid(request->wiphy_idx))
4622 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx);
4623
4624 if (genlmsg_end(msg, hdr) < 0) {
4625 nlmsg_free(msg);
4626 return;
4627 }
4628
Johannes Bergbc43b282009-07-25 10:54:13 +02004629 rcu_read_lock();
Johannes Berg463d0182009-07-14 00:33:35 +02004630 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
Johannes Bergbc43b282009-07-25 10:54:13 +02004631 GFP_ATOMIC);
4632 rcu_read_unlock();
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04004633
4634 return;
4635
4636nla_put_failure:
4637 genlmsg_cancel(msg, hdr);
4638 nlmsg_free(msg);
4639}
4640
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004641static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
4642 struct net_device *netdev,
4643 const u8 *buf, size_t len,
Johannes Berge6d6e342009-07-01 21:26:47 +02004644 enum nl80211_commands cmd, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004645{
4646 struct sk_buff *msg;
4647 void *hdr;
4648
Johannes Berge6d6e342009-07-01 21:26:47 +02004649 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004650 if (!msg)
4651 return;
4652
4653 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
4654 if (!hdr) {
4655 nlmsg_free(msg);
4656 return;
4657 }
4658
4659 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
4660 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
4661 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
4662
4663 if (genlmsg_end(msg, hdr) < 0) {
4664 nlmsg_free(msg);
4665 return;
4666 }
4667
Johannes Berg463d0182009-07-14 00:33:35 +02004668 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4669 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004670 return;
4671
4672 nla_put_failure:
4673 genlmsg_cancel(msg, hdr);
4674 nlmsg_free(msg);
4675}
4676
4677void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02004678 struct net_device *netdev, const u8 *buf,
4679 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004680{
4681 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02004682 NL80211_CMD_AUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004683}
4684
4685void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
4686 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02004687 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004688{
Johannes Berge6d6e342009-07-01 21:26:47 +02004689 nl80211_send_mlme_event(rdev, netdev, buf, len,
4690 NL80211_CMD_ASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004691}
4692
Jouni Malinen53b46b82009-03-27 20:53:56 +02004693void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02004694 struct net_device *netdev, const u8 *buf,
4695 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004696{
4697 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02004698 NL80211_CMD_DEAUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004699}
4700
Jouni Malinen53b46b82009-03-27 20:53:56 +02004701void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
4702 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02004703 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004704{
4705 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02004706 NL80211_CMD_DISASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004707}
4708
Luis R. Rodriguez1b06bb42009-05-02 00:34:48 -04004709static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
4710 struct net_device *netdev, int cmd,
Johannes Berge6d6e342009-07-01 21:26:47 +02004711 const u8 *addr, gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03004712{
4713 struct sk_buff *msg;
4714 void *hdr;
4715
Johannes Berge6d6e342009-07-01 21:26:47 +02004716 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03004717 if (!msg)
4718 return;
4719
4720 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
4721 if (!hdr) {
4722 nlmsg_free(msg);
4723 return;
4724 }
4725
4726 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
4727 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
4728 NLA_PUT_FLAG(msg, NL80211_ATTR_TIMED_OUT);
4729 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
4730
4731 if (genlmsg_end(msg, hdr) < 0) {
4732 nlmsg_free(msg);
4733 return;
4734 }
4735
Johannes Berg463d0182009-07-14 00:33:35 +02004736 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4737 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03004738 return;
4739
4740 nla_put_failure:
4741 genlmsg_cancel(msg, hdr);
4742 nlmsg_free(msg);
4743}
4744
4745void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02004746 struct net_device *netdev, const u8 *addr,
4747 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03004748{
4749 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE,
Johannes Berge6d6e342009-07-01 21:26:47 +02004750 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03004751}
4752
4753void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02004754 struct net_device *netdev, const u8 *addr,
4755 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03004756{
Johannes Berge6d6e342009-07-01 21:26:47 +02004757 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE,
4758 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03004759}
4760
Samuel Ortizb23aa672009-07-01 21:26:54 +02004761void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
4762 struct net_device *netdev, const u8 *bssid,
4763 const u8 *req_ie, size_t req_ie_len,
4764 const u8 *resp_ie, size_t resp_ie_len,
4765 u16 status, gfp_t gfp)
4766{
4767 struct sk_buff *msg;
4768 void *hdr;
4769
4770 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
4771 if (!msg)
4772 return;
4773
4774 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT);
4775 if (!hdr) {
4776 nlmsg_free(msg);
4777 return;
4778 }
4779
4780 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
4781 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
4782 if (bssid)
4783 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
4784 NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status);
4785 if (req_ie)
4786 NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
4787 if (resp_ie)
4788 NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
4789
4790 if (genlmsg_end(msg, hdr) < 0) {
4791 nlmsg_free(msg);
4792 return;
4793 }
4794
Johannes Berg463d0182009-07-14 00:33:35 +02004795 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4796 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004797 return;
4798
4799 nla_put_failure:
4800 genlmsg_cancel(msg, hdr);
4801 nlmsg_free(msg);
4802
4803}
4804
4805void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
4806 struct net_device *netdev, const u8 *bssid,
4807 const u8 *req_ie, size_t req_ie_len,
4808 const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
4809{
4810 struct sk_buff *msg;
4811 void *hdr;
4812
4813 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
4814 if (!msg)
4815 return;
4816
4817 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM);
4818 if (!hdr) {
4819 nlmsg_free(msg);
4820 return;
4821 }
4822
4823 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
4824 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
4825 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
4826 if (req_ie)
4827 NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
4828 if (resp_ie)
4829 NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
4830
4831 if (genlmsg_end(msg, hdr) < 0) {
4832 nlmsg_free(msg);
4833 return;
4834 }
4835
Johannes Berg463d0182009-07-14 00:33:35 +02004836 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4837 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004838 return;
4839
4840 nla_put_failure:
4841 genlmsg_cancel(msg, hdr);
4842 nlmsg_free(msg);
4843
4844}
4845
4846void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
4847 struct net_device *netdev, u16 reason,
Johannes Berg667503dd2009-07-07 03:56:11 +02004848 const u8 *ie, size_t ie_len, bool from_ap)
Samuel Ortizb23aa672009-07-01 21:26:54 +02004849{
4850 struct sk_buff *msg;
4851 void *hdr;
4852
Johannes Berg667503dd2009-07-07 03:56:11 +02004853 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004854 if (!msg)
4855 return;
4856
4857 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT);
4858 if (!hdr) {
4859 nlmsg_free(msg);
4860 return;
4861 }
4862
4863 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
4864 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
4865 if (from_ap && reason)
4866 NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason);
4867 if (from_ap)
4868 NLA_PUT_FLAG(msg, NL80211_ATTR_DISCONNECTED_BY_AP);
4869 if (ie)
4870 NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie);
4871
4872 if (genlmsg_end(msg, hdr) < 0) {
4873 nlmsg_free(msg);
4874 return;
4875 }
4876
Johannes Berg463d0182009-07-14 00:33:35 +02004877 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4878 nl80211_mlme_mcgrp.id, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004879 return;
4880
4881 nla_put_failure:
4882 genlmsg_cancel(msg, hdr);
4883 nlmsg_free(msg);
4884
4885}
4886
Johannes Berg04a773a2009-04-19 21:24:32 +02004887void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
4888 struct net_device *netdev, const u8 *bssid,
4889 gfp_t gfp)
4890{
4891 struct sk_buff *msg;
4892 void *hdr;
4893
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07004894 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02004895 if (!msg)
4896 return;
4897
4898 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS);
4899 if (!hdr) {
4900 nlmsg_free(msg);
4901 return;
4902 }
4903
4904 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
4905 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
4906 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
4907
4908 if (genlmsg_end(msg, hdr) < 0) {
4909 nlmsg_free(msg);
4910 return;
4911 }
4912
Johannes Berg463d0182009-07-14 00:33:35 +02004913 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4914 nl80211_mlme_mcgrp.id, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02004915 return;
4916
4917 nla_put_failure:
4918 genlmsg_cancel(msg, hdr);
4919 nlmsg_free(msg);
4920}
4921
Jouni Malinena3b8b052009-03-27 21:59:49 +02004922void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
4923 struct net_device *netdev, const u8 *addr,
4924 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +02004925 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +02004926{
4927 struct sk_buff *msg;
4928 void *hdr;
4929
Johannes Berge6d6e342009-07-01 21:26:47 +02004930 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02004931 if (!msg)
4932 return;
4933
4934 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
4935 if (!hdr) {
4936 nlmsg_free(msg);
4937 return;
4938 }
4939
4940 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
4941 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
4942 if (addr)
4943 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
4944 NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, key_type);
4945 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id);
4946 if (tsc)
4947 NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc);
4948
4949 if (genlmsg_end(msg, hdr) < 0) {
4950 nlmsg_free(msg);
4951 return;
4952 }
4953
Johannes Berg463d0182009-07-14 00:33:35 +02004954 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4955 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02004956 return;
4957
4958 nla_put_failure:
4959 genlmsg_cancel(msg, hdr);
4960 nlmsg_free(msg);
4961}
4962
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04004963void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
4964 struct ieee80211_channel *channel_before,
4965 struct ieee80211_channel *channel_after)
4966{
4967 struct sk_buff *msg;
4968 void *hdr;
4969 struct nlattr *nl_freq;
4970
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07004971 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04004972 if (!msg)
4973 return;
4974
4975 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
4976 if (!hdr) {
4977 nlmsg_free(msg);
4978 return;
4979 }
4980
4981 /*
4982 * Since we are applying the beacon hint to a wiphy we know its
4983 * wiphy_idx is valid
4984 */
4985 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy));
4986
4987 /* Before */
4988 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
4989 if (!nl_freq)
4990 goto nla_put_failure;
4991 if (nl80211_msg_put_channel(msg, channel_before))
4992 goto nla_put_failure;
4993 nla_nest_end(msg, nl_freq);
4994
4995 /* After */
4996 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
4997 if (!nl_freq)
4998 goto nla_put_failure;
4999 if (nl80211_msg_put_channel(msg, channel_after))
5000 goto nla_put_failure;
5001 nla_nest_end(msg, nl_freq);
5002
5003 if (genlmsg_end(msg, hdr) < 0) {
5004 nlmsg_free(msg);
5005 return;
5006 }
5007
Johannes Berg463d0182009-07-14 00:33:35 +02005008 rcu_read_lock();
5009 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
5010 GFP_ATOMIC);
5011 rcu_read_unlock();
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04005012
5013 return;
5014
5015nla_put_failure:
5016 genlmsg_cancel(msg, hdr);
5017 nlmsg_free(msg);
5018}
5019
Johannes Berg55682962007-09-20 13:09:35 -04005020/* initialisation/exit functions */
5021
5022int nl80211_init(void)
5023{
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00005024 int err;
Johannes Berg55682962007-09-20 13:09:35 -04005025
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00005026 err = genl_register_family_with_ops(&nl80211_fam,
5027 nl80211_ops, ARRAY_SIZE(nl80211_ops));
Johannes Berg55682962007-09-20 13:09:35 -04005028 if (err)
5029 return err;
5030
Johannes Berg55682962007-09-20 13:09:35 -04005031 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
5032 if (err)
5033 goto err_out;
5034
Johannes Berg2a519312009-02-10 21:25:55 +01005035 err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
5036 if (err)
5037 goto err_out;
5038
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04005039 err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
5040 if (err)
5041 goto err_out;
5042
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005043 err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
5044 if (err)
5045 goto err_out;
5046
Johannes Bergaff89a92009-07-01 21:26:51 +02005047#ifdef CONFIG_NL80211_TESTMODE
5048 err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp);
5049 if (err)
5050 goto err_out;
5051#endif
5052
Johannes Berg55682962007-09-20 13:09:35 -04005053 return 0;
5054 err_out:
5055 genl_unregister_family(&nl80211_fam);
5056 return err;
5057}
5058
5059void nl80211_exit(void)
5060{
5061 genl_unregister_family(&nl80211_fam);
5062}