blob: df5505b3930c49b70934db9c865dc7ac7003556b [file] [log] [blame]
Johannes Berg55682962007-09-20 13:09:35 -04001/*
2 * This is the new netlink-based wireless configuration interface.
3 *
Jouni Malinen026331c2010-02-15 12:53:10 +02004 * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
Johannes Berg55682962007-09-20 13:09:35 -04005 */
6
7#include <linux/if.h>
8#include <linux/module.h>
9#include <linux/err.h>
Johannes Berg55682962007-09-20 13:09:35 -040010#include <linux/list.h>
11#include <linux/if_ether.h>
12#include <linux/ieee80211.h>
13#include <linux/nl80211.h>
14#include <linux/rtnetlink.h>
15#include <linux/netlink.h>
Johannes Berg2a519312009-02-10 21:25:55 +010016#include <linux/etherdevice.h>
Johannes Berg463d0182009-07-14 00:33:35 +020017#include <net/net_namespace.h>
Johannes Berg55682962007-09-20 13:09:35 -040018#include <net/genetlink.h>
19#include <net/cfg80211.h>
Johannes Berg463d0182009-07-14 00:33:35 +020020#include <net/sock.h>
Johannes Berg55682962007-09-20 13:09:35 -040021#include "core.h"
22#include "nl80211.h"
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -070023#include "reg.h"
Johannes Berg55682962007-09-20 13:09:35 -040024
25/* the netlink family */
26static struct genl_family nl80211_fam = {
27 .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
28 .name = "nl80211", /* have users key off the name instead */
29 .hdrsize = 0, /* no private header */
30 .version = 1, /* no particular meaning now */
31 .maxattr = NL80211_ATTR_MAX,
Johannes Berg463d0182009-07-14 00:33:35 +020032 .netnsok = true,
Johannes Berg55682962007-09-20 13:09:35 -040033};
34
Johannes Berg79c97e92009-07-07 03:56:12 +020035/* internal helper: get rdev and dev */
Johannes Berg463d0182009-07-14 00:33:35 +020036static int get_rdev_dev_by_info_ifindex(struct genl_info *info,
Johannes Berg79c97e92009-07-07 03:56:12 +020037 struct cfg80211_registered_device **rdev,
Johannes Berg55682962007-09-20 13:09:35 -040038 struct net_device **dev)
39{
Johannes Berg463d0182009-07-14 00:33:35 +020040 struct nlattr **attrs = info->attrs;
Johannes Berg55682962007-09-20 13:09:35 -040041 int ifindex;
42
Johannes Bergbba95fe2008-07-29 13:22:51 +020043 if (!attrs[NL80211_ATTR_IFINDEX])
Johannes Berg55682962007-09-20 13:09:35 -040044 return -EINVAL;
45
Johannes Bergbba95fe2008-07-29 13:22:51 +020046 ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
Johannes Berg463d0182009-07-14 00:33:35 +020047 *dev = dev_get_by_index(genl_info_net(info), ifindex);
Johannes Berg55682962007-09-20 13:09:35 -040048 if (!*dev)
49 return -ENODEV;
50
Johannes Berg463d0182009-07-14 00:33:35 +020051 *rdev = cfg80211_get_dev_from_ifindex(genl_info_net(info), ifindex);
Johannes Berg79c97e92009-07-07 03:56:12 +020052 if (IS_ERR(*rdev)) {
Johannes Berg55682962007-09-20 13:09:35 -040053 dev_put(*dev);
Johannes Berg79c97e92009-07-07 03:56:12 +020054 return PTR_ERR(*rdev);
Johannes Berg55682962007-09-20 13:09:35 -040055 }
56
57 return 0;
58}
59
60/* policy for the attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +000061static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
Johannes Berg55682962007-09-20 13:09:35 -040062 [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
63 [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
David S. Miller079e24e2009-05-26 21:15:00 -070064 .len = 20-1 },
Jouni Malinen31888482008-10-30 16:59:24 +020065 [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
Jouni Malinen72bdcf32008-11-26 16:15:24 +020066 [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
Sujith094d05d2008-12-12 11:57:43 +053067 [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +020068 [NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 },
69 [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
70 [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
71 [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 },
Lukáš Turek81077e82009-12-21 22:50:47 +010072 [NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 },
Johannes Berg55682962007-09-20 13:09:35 -040073
74 [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
75 [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
76 [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
Johannes Berg41ade002007-12-19 02:03:29 +010077
78 [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN },
Johannes Berg3e5d7642009-07-07 14:37:26 +020079 [NL80211_ATTR_PREV_BSSID] = { .type = NLA_BINARY, .len = ETH_ALEN },
Johannes Berg41ade002007-12-19 02:03:29 +010080
Johannes Bergb9454e82009-07-08 13:29:08 +020081 [NL80211_ATTR_KEY] = { .type = NLA_NESTED, },
Johannes Berg41ade002007-12-19 02:03:29 +010082 [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
83 .len = WLAN_MAX_KEY_LEN },
84 [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
85 [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
86 [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
Jouni Malinen9f26a952009-05-15 12:38:32 +030087 [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
Johannes Berged1b6cc2007-12-19 02:03:32 +010088
89 [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
90 [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
91 [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY,
92 .len = IEEE80211_MAX_DATA_LEN },
93 [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY,
94 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg5727ef12007-12-19 02:03:34 +010095 [NL80211_ATTR_STA_AID] = { .type = NLA_U16 },
96 [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED },
97 [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 },
98 [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY,
99 .len = NL80211_MAX_SUPP_RATES },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100100 [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
Johannes Berg5727ef12007-12-19 02:03:34 +0100101 [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
Johannes Berg0a9542e2008-10-15 11:54:04 +0200102 [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100103 [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
104 .len = IEEE80211_MAX_MESH_ID_LEN },
105 [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300106
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -0700107 [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 },
108 [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED },
109
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300110 [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
111 [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
112 [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
Jouni Malinen90c97a02008-10-30 16:59:22 +0200113 [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
114 .len = NL80211_MAX_SUPP_RATES },
Jouni Malinen36aedc92008-08-25 11:58:58 +0300115
colin@cozybit.com93da9cc2008-10-21 12:03:48 -0700116 [NL80211_ATTR_MESH_PARAMS] = { .type = NLA_NESTED },
117
Jouni Malinen36aedc92008-08-25 11:58:58 +0300118 [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY,
119 .len = NL80211_HT_CAPABILITY_LEN },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +0200120
121 [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
122 [NL80211_ATTR_IE] = { .type = NLA_BINARY,
123 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg2a519312009-02-10 21:25:55 +0100124 [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
125 [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
Jouni Malinen636a5d32009-03-19 13:39:22 +0200126
127 [NL80211_ATTR_SSID] = { .type = NLA_BINARY,
128 .len = IEEE80211_MAX_SSID_LEN },
129 [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
130 [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
Johannes Berg04a773a2009-04-19 21:24:32 +0200131 [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
Jouni Malinen1965c852009-04-22 21:38:25 +0300132 [NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG },
Jouni Malinendc6382c2009-05-06 22:09:37 +0300133 [NL80211_ATTR_USE_MFP] = { .type = NLA_U32 },
Johannes Bergeccb8e82009-05-11 21:57:56 +0300134 [NL80211_ATTR_STA_FLAGS2] = {
135 .len = sizeof(struct nl80211_sta_flag_update),
136 },
Jouni Malinen3f77316c2009-05-11 21:57:57 +0300137 [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG },
Samuel Ortizb23aa672009-07-01 21:26:54 +0200138 [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG },
139 [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 },
140 [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
Johannes Berg463d0182009-07-14 00:33:35 +0200141 [NL80211_ATTR_PID] = { .type = NLA_U32 },
Felix Fietkau8b787642009-11-10 18:53:10 +0100142 [NL80211_ATTR_4ADDR] = { .type = NLA_U8 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100143 [NL80211_ATTR_PMKID] = { .type = NLA_BINARY,
144 .len = WLAN_PMKID_LEN },
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100145 [NL80211_ATTR_DURATION] = { .type = NLA_U32 },
146 [NL80211_ATTR_COOKIE] = { .type = NLA_U64 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +0200147 [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED },
Jouni Malinen026331c2010-02-15 12:53:10 +0200148 [NL80211_ATTR_FRAME] = { .type = NLA_BINARY,
149 .len = IEEE80211_MAX_DATA_LEN },
150 [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, },
Kalle Valoffb9eb32010-02-17 17:58:10 +0200151 [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +0200152 [NL80211_ATTR_CQM] = { .type = NLA_NESTED, },
Jouni Malinend5cdfac2010-04-04 09:37:19 +0300153 [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG },
Johannes Berg55682962007-09-20 13:09:35 -0400154};
155
Johannes Bergb9454e82009-07-08 13:29:08 +0200156/* policy for the attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +0000157static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = {
Johannes Bergfffd0932009-07-08 14:22:54 +0200158 [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN },
Johannes Bergb9454e82009-07-08 13:29:08 +0200159 [NL80211_KEY_IDX] = { .type = NLA_U8 },
160 [NL80211_KEY_CIPHER] = { .type = NLA_U32 },
161 [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
162 [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG },
163 [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
164};
165
Holger Schuriga0438972009-11-11 11:30:02 +0100166/* ifidx get helper */
167static int nl80211_get_ifidx(struct netlink_callback *cb)
168{
169 int res;
170
171 res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
172 nl80211_fam.attrbuf, nl80211_fam.maxattr,
173 nl80211_policy);
174 if (res)
175 return res;
176
177 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
178 return -EINVAL;
179
180 res = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
181 if (!res)
182 return -EINVAL;
183 return res;
184}
185
Johannes Bergf4a11bb2009-03-27 12:40:28 +0100186/* IE validation */
187static bool is_valid_ie_attr(const struct nlattr *attr)
188{
189 const u8 *pos;
190 int len;
191
192 if (!attr)
193 return true;
194
195 pos = nla_data(attr);
196 len = nla_len(attr);
197
198 while (len) {
199 u8 elemlen;
200
201 if (len < 2)
202 return false;
203 len -= 2;
204
205 elemlen = pos[1];
206 if (elemlen > len)
207 return false;
208
209 len -= elemlen;
210 pos += 2 + elemlen;
211 }
212
213 return true;
214}
215
Johannes Berg55682962007-09-20 13:09:35 -0400216/* message building helper */
217static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
218 int flags, u8 cmd)
219{
220 /* since there is no private header just add the generic one */
221 return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
222}
223
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400224static int nl80211_msg_put_channel(struct sk_buff *msg,
225 struct ieee80211_channel *chan)
226{
227 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
228 chan->center_freq);
229
230 if (chan->flags & IEEE80211_CHAN_DISABLED)
231 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
232 if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
233 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
234 if (chan->flags & IEEE80211_CHAN_NO_IBSS)
235 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
236 if (chan->flags & IEEE80211_CHAN_RADAR)
237 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
238
239 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
240 DBM_TO_MBM(chan->max_power));
241
242 return 0;
243
244 nla_put_failure:
245 return -ENOBUFS;
246}
247
Johannes Berg55682962007-09-20 13:09:35 -0400248/* netlink command implementations */
249
Johannes Bergb9454e82009-07-08 13:29:08 +0200250struct key_parse {
251 struct key_params p;
252 int idx;
253 bool def, defmgmt;
254};
255
256static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k)
257{
258 struct nlattr *tb[NL80211_KEY_MAX + 1];
259 int err = nla_parse_nested(tb, NL80211_KEY_MAX, key,
260 nl80211_key_policy);
261 if (err)
262 return err;
263
264 k->def = !!tb[NL80211_KEY_DEFAULT];
265 k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT];
266
267 if (tb[NL80211_KEY_IDX])
268 k->idx = nla_get_u8(tb[NL80211_KEY_IDX]);
269
270 if (tb[NL80211_KEY_DATA]) {
271 k->p.key = nla_data(tb[NL80211_KEY_DATA]);
272 k->p.key_len = nla_len(tb[NL80211_KEY_DATA]);
273 }
274
275 if (tb[NL80211_KEY_SEQ]) {
276 k->p.seq = nla_data(tb[NL80211_KEY_SEQ]);
277 k->p.seq_len = nla_len(tb[NL80211_KEY_SEQ]);
278 }
279
280 if (tb[NL80211_KEY_CIPHER])
281 k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]);
282
283 return 0;
284}
285
286static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k)
287{
288 if (info->attrs[NL80211_ATTR_KEY_DATA]) {
289 k->p.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
290 k->p.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
291 }
292
293 if (info->attrs[NL80211_ATTR_KEY_SEQ]) {
294 k->p.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]);
295 k->p.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]);
296 }
297
298 if (info->attrs[NL80211_ATTR_KEY_IDX])
299 k->idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
300
301 if (info->attrs[NL80211_ATTR_KEY_CIPHER])
302 k->p.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
303
304 k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT];
305 k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT];
306
307 return 0;
308}
309
310static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
311{
312 int err;
313
314 memset(k, 0, sizeof(*k));
315 k->idx = -1;
316
317 if (info->attrs[NL80211_ATTR_KEY])
318 err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k);
319 else
320 err = nl80211_parse_key_old(info, k);
321
322 if (err)
323 return err;
324
325 if (k->def && k->defmgmt)
326 return -EINVAL;
327
328 if (k->idx != -1) {
329 if (k->defmgmt) {
330 if (k->idx < 4 || k->idx > 5)
331 return -EINVAL;
332 } else if (k->def) {
333 if (k->idx < 0 || k->idx > 3)
334 return -EINVAL;
335 } else {
336 if (k->idx < 0 || k->idx > 5)
337 return -EINVAL;
338 }
339 }
340
341 return 0;
342}
343
Johannes Bergfffd0932009-07-08 14:22:54 +0200344static struct cfg80211_cached_keys *
345nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
346 struct nlattr *keys)
347{
348 struct key_parse parse;
349 struct nlattr *key;
350 struct cfg80211_cached_keys *result;
351 int rem, err, def = 0;
352
353 result = kzalloc(sizeof(*result), GFP_KERNEL);
354 if (!result)
355 return ERR_PTR(-ENOMEM);
356
357 result->def = -1;
358 result->defmgmt = -1;
359
360 nla_for_each_nested(key, keys, rem) {
361 memset(&parse, 0, sizeof(parse));
362 parse.idx = -1;
363
364 err = nl80211_parse_key_new(key, &parse);
365 if (err)
366 goto error;
367 err = -EINVAL;
368 if (!parse.p.key)
369 goto error;
370 if (parse.idx < 0 || parse.idx > 4)
371 goto error;
372 if (parse.def) {
373 if (def)
374 goto error;
375 def = 1;
376 result->def = parse.idx;
377 } else if (parse.defmgmt)
378 goto error;
379 err = cfg80211_validate_key_settings(rdev, &parse.p,
380 parse.idx, NULL);
381 if (err)
382 goto error;
383 result->params[parse.idx].cipher = parse.p.cipher;
384 result->params[parse.idx].key_len = parse.p.key_len;
385 result->params[parse.idx].key = result->data[parse.idx];
386 memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len);
387 }
388
389 return result;
390 error:
391 kfree(result);
392 return ERR_PTR(err);
393}
394
395static int nl80211_key_allowed(struct wireless_dev *wdev)
396{
397 ASSERT_WDEV_LOCK(wdev);
398
399 if (!netif_running(wdev->netdev))
400 return -ENETDOWN;
401
402 switch (wdev->iftype) {
403 case NL80211_IFTYPE_AP:
404 case NL80211_IFTYPE_AP_VLAN:
405 break;
406 case NL80211_IFTYPE_ADHOC:
407 if (!wdev->current_bss)
408 return -ENOLINK;
409 break;
410 case NL80211_IFTYPE_STATION:
411 if (wdev->sme_state != CFG80211_SME_CONNECTED)
412 return -ENOLINK;
413 break;
414 default:
415 return -EINVAL;
416 }
417
418 return 0;
419}
420
Johannes Berg55682962007-09-20 13:09:35 -0400421static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
422 struct cfg80211_registered_device *dev)
423{
424 void *hdr;
Johannes Bergee688b02008-01-24 19:38:39 +0100425 struct nlattr *nl_bands, *nl_band;
426 struct nlattr *nl_freqs, *nl_freq;
427 struct nlattr *nl_rates, *nl_rate;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700428 struct nlattr *nl_modes;
Johannes Berg8fdc6212009-03-14 09:34:01 +0100429 struct nlattr *nl_cmds;
Johannes Bergee688b02008-01-24 19:38:39 +0100430 enum ieee80211_band band;
431 struct ieee80211_channel *chan;
432 struct ieee80211_rate *rate;
433 int i;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700434 u16 ifmodes = dev->wiphy.interface_modes;
Johannes Berg55682962007-09-20 13:09:35 -0400435
436 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
437 if (!hdr)
438 return -1;
439
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -0500440 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400441 NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200442
Johannes Bergf5ea9122009-08-07 16:17:38 +0200443 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION,
444 cfg80211_rdev_list_generation);
445
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200446 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
447 dev->wiphy.retry_short);
448 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
449 dev->wiphy.retry_long);
450 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
451 dev->wiphy.frag_threshold);
452 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
453 dev->wiphy.rts_threshold);
Lukáš Turek81077e82009-12-21 22:50:47 +0100454 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
455 dev->wiphy.coverage_class);
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200456
Johannes Berg2a519312009-02-10 21:25:55 +0100457 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
458 dev->wiphy.max_scan_ssids);
Johannes Berg18a83652009-03-31 12:12:05 +0200459 NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
460 dev->wiphy.max_scan_ie_len);
Johannes Bergee688b02008-01-24 19:38:39 +0100461
Johannes Berg25e47c12009-04-02 20:14:06 +0200462 NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
463 sizeof(u32) * dev->wiphy.n_cipher_suites,
464 dev->wiphy.cipher_suites);
465
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100466 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
467 dev->wiphy.max_num_pmkids);
468
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700469 nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES);
470 if (!nl_modes)
471 goto nla_put_failure;
472
473 i = 0;
474 while (ifmodes) {
475 if (ifmodes & 1)
476 NLA_PUT_FLAG(msg, i);
477 ifmodes >>= 1;
478 i++;
479 }
480
481 nla_nest_end(msg, nl_modes);
482
Johannes Bergee688b02008-01-24 19:38:39 +0100483 nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
484 if (!nl_bands)
485 goto nla_put_failure;
486
487 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
488 if (!dev->wiphy.bands[band])
489 continue;
490
491 nl_band = nla_nest_start(msg, band);
492 if (!nl_band)
493 goto nla_put_failure;
494
Johannes Bergd51626d2008-10-09 12:20:13 +0200495 /* add HT info */
496 if (dev->wiphy.bands[band]->ht_cap.ht_supported) {
497 NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET,
498 sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
499 &dev->wiphy.bands[band]->ht_cap.mcs);
500 NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA,
501 dev->wiphy.bands[band]->ht_cap.cap);
502 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
503 dev->wiphy.bands[band]->ht_cap.ampdu_factor);
504 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
505 dev->wiphy.bands[band]->ht_cap.ampdu_density);
506 }
507
Johannes Bergee688b02008-01-24 19:38:39 +0100508 /* add frequencies */
509 nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
510 if (!nl_freqs)
511 goto nla_put_failure;
512
513 for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
514 nl_freq = nla_nest_start(msg, i);
515 if (!nl_freq)
516 goto nla_put_failure;
517
518 chan = &dev->wiphy.bands[band]->channels[i];
Johannes Bergee688b02008-01-24 19:38:39 +0100519
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400520 if (nl80211_msg_put_channel(msg, chan))
521 goto nla_put_failure;
Jouni Malinene2f367f262008-11-21 19:01:30 +0200522
Johannes Bergee688b02008-01-24 19:38:39 +0100523 nla_nest_end(msg, nl_freq);
524 }
525
526 nla_nest_end(msg, nl_freqs);
527
528 /* add bitrates */
529 nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
530 if (!nl_rates)
531 goto nla_put_failure;
532
533 for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
534 nl_rate = nla_nest_start(msg, i);
535 if (!nl_rate)
536 goto nla_put_failure;
537
538 rate = &dev->wiphy.bands[band]->bitrates[i];
539 NLA_PUT_U32(msg, NL80211_BITRATE_ATTR_RATE,
540 rate->bitrate);
541 if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
542 NLA_PUT_FLAG(msg,
543 NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE);
544
545 nla_nest_end(msg, nl_rate);
546 }
547
548 nla_nest_end(msg, nl_rates);
549
550 nla_nest_end(msg, nl_band);
551 }
552 nla_nest_end(msg, nl_bands);
553
Johannes Berg8fdc6212009-03-14 09:34:01 +0100554 nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
555 if (!nl_cmds)
556 goto nla_put_failure;
557
558 i = 0;
559#define CMD(op, n) \
560 do { \
561 if (dev->ops->op) { \
562 i++; \
563 NLA_PUT_U32(msg, i, NL80211_CMD_ ## n); \
564 } \
565 } while (0)
566
567 CMD(add_virtual_intf, NEW_INTERFACE);
568 CMD(change_virtual_intf, SET_INTERFACE);
569 CMD(add_key, NEW_KEY);
570 CMD(add_beacon, NEW_BEACON);
571 CMD(add_station, NEW_STATION);
572 CMD(add_mpath, NEW_MPATH);
573 CMD(set_mesh_params, SET_MESH_PARAMS);
574 CMD(change_bss, SET_BSS);
Jouni Malinen636a5d32009-03-19 13:39:22 +0200575 CMD(auth, AUTHENTICATE);
576 CMD(assoc, ASSOCIATE);
577 CMD(deauth, DEAUTHENTICATE);
578 CMD(disassoc, DISASSOCIATE);
Johannes Berg04a773a2009-04-19 21:24:32 +0200579 CMD(join_ibss, JOIN_IBSS);
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100580 CMD(set_pmksa, SET_PMKSA);
581 CMD(del_pmksa, DEL_PMKSA);
582 CMD(flush_pmksa, FLUSH_PMKSA);
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100583 CMD(remain_on_channel, REMAIN_ON_CHANNEL);
Jouni Malinen13ae75b2009-12-29 12:59:45 +0200584 CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
Jouni Malinen026331c2010-02-15 12:53:10 +0200585 CMD(action, ACTION);
Johannes Berg5be83de2009-11-19 00:56:28 +0100586 if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
Johannes Berg463d0182009-07-14 00:33:35 +0200587 i++;
588 NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS);
589 }
Johannes Berg8fdc6212009-03-14 09:34:01 +0100590
591#undef CMD
Samuel Ortizb23aa672009-07-01 21:26:54 +0200592
Johannes Berg6829c872009-07-02 09:13:27 +0200593 if (dev->ops->connect || dev->ops->auth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +0200594 i++;
595 NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT);
596 }
597
Johannes Berg6829c872009-07-02 09:13:27 +0200598 if (dev->ops->disconnect || dev->ops->deauth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +0200599 i++;
600 NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT);
601 }
602
Johannes Berg8fdc6212009-03-14 09:34:01 +0100603 nla_nest_end(msg, nl_cmds);
604
Johannes Berg55682962007-09-20 13:09:35 -0400605 return genlmsg_end(msg, hdr);
606
607 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700608 genlmsg_cancel(msg, hdr);
609 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400610}
611
612static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
613{
614 int idx = 0;
615 int start = cb->args[0];
616 struct cfg80211_registered_device *dev;
617
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500618 mutex_lock(&cfg80211_mutex);
Johannes Berg79c97e92009-07-07 03:56:12 +0200619 list_for_each_entry(dev, &cfg80211_rdev_list, list) {
Johannes Berg463d0182009-07-14 00:33:35 +0200620 if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
621 continue;
Julius Volzb4637272008-07-08 14:02:19 +0200622 if (++idx <= start)
Johannes Berg55682962007-09-20 13:09:35 -0400623 continue;
624 if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
625 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Julius Volzb4637272008-07-08 14:02:19 +0200626 dev) < 0) {
627 idx--;
Johannes Berg55682962007-09-20 13:09:35 -0400628 break;
Julius Volzb4637272008-07-08 14:02:19 +0200629 }
Johannes Berg55682962007-09-20 13:09:35 -0400630 }
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500631 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400632
633 cb->args[0] = idx;
634
635 return skb->len;
636}
637
638static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
639{
640 struct sk_buff *msg;
641 struct cfg80211_registered_device *dev;
642
643 dev = cfg80211_get_dev_from_info(info);
644 if (IS_ERR(dev))
645 return PTR_ERR(dev);
646
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -0700647 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -0400648 if (!msg)
649 goto out_err;
650
651 if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0)
652 goto out_free;
653
Johannes Berg4d0c8ae2009-07-07 03:56:09 +0200654 cfg80211_unlock_rdev(dev);
Johannes Berg55682962007-09-20 13:09:35 -0400655
Johannes Berg134e6372009-07-10 09:51:34 +0000656 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -0400657
658 out_free:
659 nlmsg_free(msg);
660 out_err:
Johannes Berg4d0c8ae2009-07-07 03:56:09 +0200661 cfg80211_unlock_rdev(dev);
Johannes Berg55682962007-09-20 13:09:35 -0400662 return -ENOBUFS;
663}
664
Jouni Malinen31888482008-10-30 16:59:24 +0200665static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
666 [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
667 [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
668 [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
669 [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
670 [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
671};
672
673static int parse_txq_params(struct nlattr *tb[],
674 struct ieee80211_txq_params *txq_params)
675{
676 if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] ||
677 !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
678 !tb[NL80211_TXQ_ATTR_AIFS])
679 return -EINVAL;
680
681 txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]);
682 txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
683 txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
684 txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
685 txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
686
687 return 0;
688}
689
Johannes Berg55682962007-09-20 13:09:35 -0400690static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
691{
692 struct cfg80211_registered_device *rdev;
Jouni Malinen31888482008-10-30 16:59:24 +0200693 int result = 0, rem_txq_params = 0;
694 struct nlattr *nl_txq_params;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200695 u32 changed;
696 u8 retry_short = 0, retry_long = 0;
697 u32 frag_threshold = 0, rts_threshold = 0;
Lukáš Turek81077e82009-12-21 22:50:47 +0100698 u8 coverage_class = 0;
Johannes Berg55682962007-09-20 13:09:35 -0400699
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100700 rtnl_lock();
Johannes Berg55682962007-09-20 13:09:35 -0400701
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100702 mutex_lock(&cfg80211_mutex);
703
Johannes Berg79c97e92009-07-07 03:56:12 +0200704 rdev = __cfg80211_rdev_from_info(info);
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100705 if (IS_ERR(rdev)) {
Jiri Slaby1f5fc702009-06-20 12:31:11 +0200706 mutex_unlock(&cfg80211_mutex);
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100707 result = PTR_ERR(rdev);
708 goto unlock;
709 }
710
711 mutex_lock(&rdev->mtx);
712
713 if (info->attrs[NL80211_ATTR_WIPHY_NAME])
Jouni Malinen31888482008-10-30 16:59:24 +0200714 result = cfg80211_dev_rename(
715 rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100716
717 mutex_unlock(&cfg80211_mutex);
718
719 if (result)
720 goto bad_res;
Johannes Berg55682962007-09-20 13:09:35 -0400721
Jouni Malinen31888482008-10-30 16:59:24 +0200722 if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
723 struct ieee80211_txq_params txq_params;
724 struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
725
726 if (!rdev->ops->set_txq_params) {
727 result = -EOPNOTSUPP;
728 goto bad_res;
729 }
730
731 nla_for_each_nested(nl_txq_params,
732 info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
733 rem_txq_params) {
734 nla_parse(tb, NL80211_TXQ_ATTR_MAX,
735 nla_data(nl_txq_params),
736 nla_len(nl_txq_params),
737 txq_params_policy);
738 result = parse_txq_params(tb, &txq_params);
739 if (result)
740 goto bad_res;
741
742 result = rdev->ops->set_txq_params(&rdev->wiphy,
743 &txq_params);
744 if (result)
745 goto bad_res;
746 }
747 }
748
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200749 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Sujith094d05d2008-12-12 11:57:43 +0530750 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
Luis R. Rodriguez294196a2009-05-02 00:37:20 -0400751 u32 freq;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200752
Johannes Berg306d6112008-12-08 12:39:04 +0100753 result = -EINVAL;
754
Sujith094d05d2008-12-12 11:57:43 +0530755 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
756 channel_type = nla_get_u32(info->attrs[
757 NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
758 if (channel_type != NL80211_CHAN_NO_HT &&
759 channel_type != NL80211_CHAN_HT20 &&
760 channel_type != NL80211_CHAN_HT40PLUS &&
761 channel_type != NL80211_CHAN_HT40MINUS)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200762 goto bad_res;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200763 }
764
765 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
Johannes Berg306d6112008-12-08 12:39:04 +0100766
Johannes Berg59bbb6f2009-08-07 17:22:35 +0200767 mutex_lock(&rdev->devlist_mtx);
Johannes Berg4b181142009-08-08 11:03:58 +0200768 result = rdev_set_freq(rdev, NULL, freq, channel_type);
Johannes Berg59bbb6f2009-08-07 17:22:35 +0200769 mutex_unlock(&rdev->devlist_mtx);
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200770 if (result)
771 goto bad_res;
772 }
773
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200774 changed = 0;
775
776 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {
777 retry_short = nla_get_u8(
778 info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]);
779 if (retry_short == 0) {
780 result = -EINVAL;
781 goto bad_res;
782 }
783 changed |= WIPHY_PARAM_RETRY_SHORT;
784 }
785
786 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {
787 retry_long = nla_get_u8(
788 info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]);
789 if (retry_long == 0) {
790 result = -EINVAL;
791 goto bad_res;
792 }
793 changed |= WIPHY_PARAM_RETRY_LONG;
794 }
795
796 if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
797 frag_threshold = nla_get_u32(
798 info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
799 if (frag_threshold < 256) {
800 result = -EINVAL;
801 goto bad_res;
802 }
803 if (frag_threshold != (u32) -1) {
804 /*
805 * Fragments (apart from the last one) are required to
806 * have even length. Make the fragmentation code
807 * simpler by stripping LSB should someone try to use
808 * odd threshold value.
809 */
810 frag_threshold &= ~0x1;
811 }
812 changed |= WIPHY_PARAM_FRAG_THRESHOLD;
813 }
814
815 if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
816 rts_threshold = nla_get_u32(
817 info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
818 changed |= WIPHY_PARAM_RTS_THRESHOLD;
819 }
820
Lukáš Turek81077e82009-12-21 22:50:47 +0100821 if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) {
822 coverage_class = nla_get_u8(
823 info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]);
824 changed |= WIPHY_PARAM_COVERAGE_CLASS;
825 }
826
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200827 if (changed) {
828 u8 old_retry_short, old_retry_long;
829 u32 old_frag_threshold, old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +0100830 u8 old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200831
832 if (!rdev->ops->set_wiphy_params) {
833 result = -EOPNOTSUPP;
834 goto bad_res;
835 }
836
837 old_retry_short = rdev->wiphy.retry_short;
838 old_retry_long = rdev->wiphy.retry_long;
839 old_frag_threshold = rdev->wiphy.frag_threshold;
840 old_rts_threshold = rdev->wiphy.rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +0100841 old_coverage_class = rdev->wiphy.coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200842
843 if (changed & WIPHY_PARAM_RETRY_SHORT)
844 rdev->wiphy.retry_short = retry_short;
845 if (changed & WIPHY_PARAM_RETRY_LONG)
846 rdev->wiphy.retry_long = retry_long;
847 if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
848 rdev->wiphy.frag_threshold = frag_threshold;
849 if (changed & WIPHY_PARAM_RTS_THRESHOLD)
850 rdev->wiphy.rts_threshold = rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +0100851 if (changed & WIPHY_PARAM_COVERAGE_CLASS)
852 rdev->wiphy.coverage_class = coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200853
854 result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed);
855 if (result) {
856 rdev->wiphy.retry_short = old_retry_short;
857 rdev->wiphy.retry_long = old_retry_long;
858 rdev->wiphy.frag_threshold = old_frag_threshold;
859 rdev->wiphy.rts_threshold = old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +0100860 rdev->wiphy.coverage_class = old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200861 }
862 }
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200863
Johannes Berg306d6112008-12-08 12:39:04 +0100864 bad_res:
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100865 mutex_unlock(&rdev->mtx);
866 unlock:
867 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400868 return result;
869}
870
871
872static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
Johannes Bergd7264052009-04-19 16:23:20 +0200873 struct cfg80211_registered_device *rdev,
Johannes Berg55682962007-09-20 13:09:35 -0400874 struct net_device *dev)
875{
876 void *hdr;
877
878 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
879 if (!hdr)
880 return -1;
881
882 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
Johannes Bergd7264052009-04-19 16:23:20 +0200883 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400884 NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
Johannes Berg60719ff2008-09-16 14:55:09 +0200885 NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype);
Johannes Bergf5ea9122009-08-07 16:17:38 +0200886
887 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION,
888 rdev->devlist_generation ^
889 (cfg80211_rdev_list_generation << 2));
890
Johannes Berg55682962007-09-20 13:09:35 -0400891 return genlmsg_end(msg, hdr);
892
893 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700894 genlmsg_cancel(msg, hdr);
895 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400896}
897
898static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
899{
900 int wp_idx = 0;
901 int if_idx = 0;
902 int wp_start = cb->args[0];
903 int if_start = cb->args[1];
Johannes Bergf5ea9122009-08-07 16:17:38 +0200904 struct cfg80211_registered_device *rdev;
Johannes Berg55682962007-09-20 13:09:35 -0400905 struct wireless_dev *wdev;
906
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500907 mutex_lock(&cfg80211_mutex);
Johannes Bergf5ea9122009-08-07 16:17:38 +0200908 list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
909 if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
Johannes Berg463d0182009-07-14 00:33:35 +0200910 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200911 if (wp_idx < wp_start) {
912 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400913 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200914 }
Johannes Berg55682962007-09-20 13:09:35 -0400915 if_idx = 0;
916
Johannes Bergf5ea9122009-08-07 16:17:38 +0200917 mutex_lock(&rdev->devlist_mtx);
918 list_for_each_entry(wdev, &rdev->netdev_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +0200919 if (if_idx < if_start) {
920 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400921 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200922 }
Johannes Berg55682962007-09-20 13:09:35 -0400923 if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
924 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Bergf5ea9122009-08-07 16:17:38 +0200925 rdev, wdev->netdev) < 0) {
926 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +0200927 goto out;
928 }
929 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400930 }
Johannes Bergf5ea9122009-08-07 16:17:38 +0200931 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +0200932
933 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400934 }
Johannes Bergbba95fe2008-07-29 13:22:51 +0200935 out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500936 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400937
938 cb->args[0] = wp_idx;
939 cb->args[1] = if_idx;
940
941 return skb->len;
942}
943
944static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
945{
946 struct sk_buff *msg;
947 struct cfg80211_registered_device *dev;
948 struct net_device *netdev;
949 int err;
950
Johannes Berg463d0182009-07-14 00:33:35 +0200951 err = get_rdev_dev_by_info_ifindex(info, &dev, &netdev);
Johannes Berg55682962007-09-20 13:09:35 -0400952 if (err)
953 return err;
954
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -0700955 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -0400956 if (!msg)
957 goto out_err;
958
Johannes Bergd7264052009-04-19 16:23:20 +0200959 if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0,
960 dev, netdev) < 0)
Johannes Berg55682962007-09-20 13:09:35 -0400961 goto out_free;
962
963 dev_put(netdev);
Johannes Berg4d0c8ae2009-07-07 03:56:09 +0200964 cfg80211_unlock_rdev(dev);
Johannes Berg55682962007-09-20 13:09:35 -0400965
Johannes Berg134e6372009-07-10 09:51:34 +0000966 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -0400967
968 out_free:
969 nlmsg_free(msg);
970 out_err:
971 dev_put(netdev);
Johannes Berg4d0c8ae2009-07-07 03:56:09 +0200972 cfg80211_unlock_rdev(dev);
Johannes Berg55682962007-09-20 13:09:35 -0400973 return -ENOBUFS;
974}
975
Michael Wu66f7ac52008-01-31 19:48:22 +0100976static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
977 [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
978 [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
979 [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
980 [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
981 [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
982};
983
984static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
985{
986 struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
987 int flag;
988
989 *mntrflags = 0;
990
991 if (!nla)
992 return -EINVAL;
993
994 if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX,
995 nla, mntr_flags_policy))
996 return -EINVAL;
997
998 for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
999 if (flags[flag])
1000 *mntrflags |= (1<<flag);
1001
1002 return 0;
1003}
1004
Johannes Berg9bc383d2009-11-19 11:55:19 +01001005static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev,
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001006 struct net_device *netdev, u8 use_4addr,
1007 enum nl80211_iftype iftype)
Johannes Berg9bc383d2009-11-19 11:55:19 +01001008{
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001009 if (!use_4addr) {
1010 if (netdev && netdev->br_port)
1011 return -EBUSY;
Johannes Berg9bc383d2009-11-19 11:55:19 +01001012 return 0;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001013 }
Johannes Berg9bc383d2009-11-19 11:55:19 +01001014
1015 switch (iftype) {
1016 case NL80211_IFTYPE_AP_VLAN:
1017 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_AP)
1018 return 0;
1019 break;
1020 case NL80211_IFTYPE_STATION:
1021 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION)
1022 return 0;
1023 break;
1024 default:
1025 break;
1026 }
1027
1028 return -EOPNOTSUPP;
1029}
1030
Johannes Berg55682962007-09-20 13:09:35 -04001031static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
1032{
Johannes Berg79c97e92009-07-07 03:56:12 +02001033 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001034 struct vif_params params;
Johannes Berge36d56b2009-06-09 21:04:43 +02001035 int err;
Johannes Berg04a773a2009-04-19 21:24:32 +02001036 enum nl80211_iftype otype, ntype;
Johannes Berg55682962007-09-20 13:09:35 -04001037 struct net_device *dev;
Johannes Berg92ffe052008-09-16 20:39:36 +02001038 u32 _flags, *flags = NULL;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001039 bool change = false;
Johannes Berg55682962007-09-20 13:09:35 -04001040
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001041 memset(&params, 0, sizeof(params));
1042
Johannes Berg3b858752009-03-12 09:55:09 +01001043 rtnl_lock();
1044
Johannes Berg463d0182009-07-14 00:33:35 +02001045 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg55682962007-09-20 13:09:35 -04001046 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001047 goto unlock_rtnl;
1048
Johannes Berg04a773a2009-04-19 21:24:32 +02001049 otype = ntype = dev->ieee80211_ptr->iftype;
Johannes Berg55682962007-09-20 13:09:35 -04001050
Johannes Berg723b0382008-09-16 20:22:09 +02001051 if (info->attrs[NL80211_ATTR_IFTYPE]) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001052 ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
Johannes Berg04a773a2009-04-19 21:24:32 +02001053 if (otype != ntype)
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001054 change = true;
Johannes Berg04a773a2009-04-19 21:24:32 +02001055 if (ntype > NL80211_IFTYPE_MAX) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001056 err = -EINVAL;
Johannes Berg723b0382008-09-16 20:22:09 +02001057 goto unlock;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001058 }
Johannes Berg723b0382008-09-16 20:22:09 +02001059 }
1060
Johannes Berg92ffe052008-09-16 20:39:36 +02001061 if (info->attrs[NL80211_ATTR_MESH_ID]) {
Johannes Berg04a773a2009-04-19 21:24:32 +02001062 if (ntype != NL80211_IFTYPE_MESH_POINT) {
Johannes Berg92ffe052008-09-16 20:39:36 +02001063 err = -EINVAL;
1064 goto unlock;
1065 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001066 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
1067 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001068 change = true;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001069 }
1070
Felix Fietkau8b787642009-11-10 18:53:10 +01001071 if (info->attrs[NL80211_ATTR_4ADDR]) {
1072 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
1073 change = true;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001074 err = nl80211_valid_4addr(rdev, dev, params.use_4addr, ntype);
Johannes Berg9bc383d2009-11-19 11:55:19 +01001075 if (err)
1076 goto unlock;
Felix Fietkau8b787642009-11-10 18:53:10 +01001077 } else {
1078 params.use_4addr = -1;
1079 }
1080
Johannes Berg92ffe052008-09-16 20:39:36 +02001081 if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
Johannes Berg04a773a2009-04-19 21:24:32 +02001082 if (ntype != NL80211_IFTYPE_MONITOR) {
Johannes Berg92ffe052008-09-16 20:39:36 +02001083 err = -EINVAL;
1084 goto unlock;
1085 }
1086 err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
1087 &_flags);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001088 if (err)
1089 goto unlock;
1090
1091 flags = &_flags;
1092 change = true;
Johannes Berg92ffe052008-09-16 20:39:36 +02001093 }
Johannes Berg3b858752009-03-12 09:55:09 +01001094
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001095 if (change)
Johannes Berg3d54d252009-08-21 14:51:05 +02001096 err = cfg80211_change_iface(rdev, dev, ntype, flags, &params);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001097 else
1098 err = 0;
Johannes Berg60719ff2008-09-16 14:55:09 +02001099
Johannes Berg9bc383d2009-11-19 11:55:19 +01001100 if (!err && params.use_4addr != -1)
1101 dev->ieee80211_ptr->use_4addr = params.use_4addr;
1102
Johannes Berg55682962007-09-20 13:09:35 -04001103 unlock:
Johannes Berge36d56b2009-06-09 21:04:43 +02001104 dev_put(dev);
Johannes Berg79c97e92009-07-07 03:56:12 +02001105 cfg80211_unlock_rdev(rdev);
Johannes Berg3b858752009-03-12 09:55:09 +01001106 unlock_rtnl:
1107 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -04001108 return err;
1109}
1110
1111static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
1112{
Johannes Berg79c97e92009-07-07 03:56:12 +02001113 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001114 struct vif_params params;
Johannes Berg55682962007-09-20 13:09:35 -04001115 int err;
1116 enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
Michael Wu66f7ac52008-01-31 19:48:22 +01001117 u32 flags;
Johannes Berg55682962007-09-20 13:09:35 -04001118
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001119 memset(&params, 0, sizeof(params));
1120
Johannes Berg55682962007-09-20 13:09:35 -04001121 if (!info->attrs[NL80211_ATTR_IFNAME])
1122 return -EINVAL;
1123
1124 if (info->attrs[NL80211_ATTR_IFTYPE]) {
1125 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
1126 if (type > NL80211_IFTYPE_MAX)
1127 return -EINVAL;
1128 }
1129
Johannes Berg3b858752009-03-12 09:55:09 +01001130 rtnl_lock();
1131
Johannes Berg79c97e92009-07-07 03:56:12 +02001132 rdev = cfg80211_get_dev_from_info(info);
1133 if (IS_ERR(rdev)) {
1134 err = PTR_ERR(rdev);
Johannes Berg3b858752009-03-12 09:55:09 +01001135 goto unlock_rtnl;
1136 }
Johannes Berg55682962007-09-20 13:09:35 -04001137
Johannes Berg79c97e92009-07-07 03:56:12 +02001138 if (!rdev->ops->add_virtual_intf ||
1139 !(rdev->wiphy.interface_modes & (1 << type))) {
Johannes Berg55682962007-09-20 13:09:35 -04001140 err = -EOPNOTSUPP;
1141 goto unlock;
1142 }
1143
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001144 if (type == NL80211_IFTYPE_MESH_POINT &&
1145 info->attrs[NL80211_ATTR_MESH_ID]) {
1146 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
1147 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
1148 }
1149
Johannes Berg9bc383d2009-11-19 11:55:19 +01001150 if (info->attrs[NL80211_ATTR_4ADDR]) {
Felix Fietkau8b787642009-11-10 18:53:10 +01001151 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001152 err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type);
Johannes Berg9bc383d2009-11-19 11:55:19 +01001153 if (err)
1154 goto unlock;
1155 }
Felix Fietkau8b787642009-11-10 18:53:10 +01001156
Michael Wu66f7ac52008-01-31 19:48:22 +01001157 err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
1158 info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
1159 &flags);
Johannes Berg79c97e92009-07-07 03:56:12 +02001160 err = rdev->ops->add_virtual_intf(&rdev->wiphy,
Michael Wu66f7ac52008-01-31 19:48:22 +01001161 nla_data(info->attrs[NL80211_ATTR_IFNAME]),
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001162 type, err ? NULL : &flags, &params);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001163
Johannes Berg55682962007-09-20 13:09:35 -04001164 unlock:
Johannes Berg79c97e92009-07-07 03:56:12 +02001165 cfg80211_unlock_rdev(rdev);
Johannes Berg3b858752009-03-12 09:55:09 +01001166 unlock_rtnl:
1167 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -04001168 return err;
1169}
1170
1171static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
1172{
Johannes Berg79c97e92009-07-07 03:56:12 +02001173 struct cfg80211_registered_device *rdev;
Johannes Berg463d0182009-07-14 00:33:35 +02001174 int err;
Johannes Berg55682962007-09-20 13:09:35 -04001175 struct net_device *dev;
1176
Johannes Berg3b858752009-03-12 09:55:09 +01001177 rtnl_lock();
1178
Johannes Berg463d0182009-07-14 00:33:35 +02001179 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg55682962007-09-20 13:09:35 -04001180 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001181 goto unlock_rtnl;
Johannes Berg55682962007-09-20 13:09:35 -04001182
Johannes Berg79c97e92009-07-07 03:56:12 +02001183 if (!rdev->ops->del_virtual_intf) {
Johannes Berg55682962007-09-20 13:09:35 -04001184 err = -EOPNOTSUPP;
1185 goto out;
1186 }
1187
Johannes Berg463d0182009-07-14 00:33:35 +02001188 err = rdev->ops->del_virtual_intf(&rdev->wiphy, dev);
Johannes Berg55682962007-09-20 13:09:35 -04001189
1190 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001191 cfg80211_unlock_rdev(rdev);
Johannes Berg463d0182009-07-14 00:33:35 +02001192 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001193 unlock_rtnl:
1194 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -04001195 return err;
1196}
1197
Johannes Berg41ade002007-12-19 02:03:29 +01001198struct get_key_cookie {
1199 struct sk_buff *msg;
1200 int error;
Johannes Bergb9454e82009-07-08 13:29:08 +02001201 int idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001202};
1203
1204static void get_key_callback(void *c, struct key_params *params)
1205{
Johannes Bergb9454e82009-07-08 13:29:08 +02001206 struct nlattr *key;
Johannes Berg41ade002007-12-19 02:03:29 +01001207 struct get_key_cookie *cookie = c;
1208
1209 if (params->key)
1210 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA,
1211 params->key_len, params->key);
1212
1213 if (params->seq)
1214 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ,
1215 params->seq_len, params->seq);
1216
1217 if (params->cipher)
1218 NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
1219 params->cipher);
1220
Johannes Bergb9454e82009-07-08 13:29:08 +02001221 key = nla_nest_start(cookie->msg, NL80211_ATTR_KEY);
1222 if (!key)
1223 goto nla_put_failure;
1224
1225 if (params->key)
1226 NLA_PUT(cookie->msg, NL80211_KEY_DATA,
1227 params->key_len, params->key);
1228
1229 if (params->seq)
1230 NLA_PUT(cookie->msg, NL80211_KEY_SEQ,
1231 params->seq_len, params->seq);
1232
1233 if (params->cipher)
1234 NLA_PUT_U32(cookie->msg, NL80211_KEY_CIPHER,
1235 params->cipher);
1236
1237 NLA_PUT_U8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx);
1238
1239 nla_nest_end(cookie->msg, key);
1240
Johannes Berg41ade002007-12-19 02:03:29 +01001241 return;
1242 nla_put_failure:
1243 cookie->error = 1;
1244}
1245
1246static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
1247{
Johannes Berg79c97e92009-07-07 03:56:12 +02001248 struct cfg80211_registered_device *rdev;
Johannes Berg41ade002007-12-19 02:03:29 +01001249 int err;
1250 struct net_device *dev;
1251 u8 key_idx = 0;
1252 u8 *mac_addr = NULL;
1253 struct get_key_cookie cookie = {
1254 .error = 0,
1255 };
1256 void *hdr;
1257 struct sk_buff *msg;
1258
1259 if (info->attrs[NL80211_ATTR_KEY_IDX])
1260 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1261
Jouni Malinen3cfcf6a2009-01-08 13:32:02 +02001262 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +01001263 return -EINVAL;
1264
1265 if (info->attrs[NL80211_ATTR_MAC])
1266 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1267
Johannes Berg3b858752009-03-12 09:55:09 +01001268 rtnl_lock();
1269
Johannes Berg463d0182009-07-14 00:33:35 +02001270 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001271 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001272 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001273
Johannes Berg79c97e92009-07-07 03:56:12 +02001274 if (!rdev->ops->get_key) {
Johannes Berg41ade002007-12-19 02:03:29 +01001275 err = -EOPNOTSUPP;
1276 goto out;
1277 }
1278
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001279 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg41ade002007-12-19 02:03:29 +01001280 if (!msg) {
1281 err = -ENOMEM;
1282 goto out;
1283 }
1284
1285 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
1286 NL80211_CMD_NEW_KEY);
1287
1288 if (IS_ERR(hdr)) {
1289 err = PTR_ERR(hdr);
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001290 goto free_msg;
Johannes Berg41ade002007-12-19 02:03:29 +01001291 }
1292
1293 cookie.msg = msg;
Johannes Bergb9454e82009-07-08 13:29:08 +02001294 cookie.idx = key_idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001295
1296 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1297 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
1298 if (mac_addr)
1299 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1300
Johannes Berg79c97e92009-07-07 03:56:12 +02001301 err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, mac_addr,
Johannes Berg41ade002007-12-19 02:03:29 +01001302 &cookie, get_key_callback);
Johannes Berg41ade002007-12-19 02:03:29 +01001303
1304 if (err)
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001305 goto free_msg;
Johannes Berg41ade002007-12-19 02:03:29 +01001306
1307 if (cookie.error)
1308 goto nla_put_failure;
1309
1310 genlmsg_end(msg, hdr);
Johannes Berg134e6372009-07-10 09:51:34 +00001311 err = genlmsg_reply(msg, info);
Johannes Berg41ade002007-12-19 02:03:29 +01001312 goto out;
1313
1314 nla_put_failure:
1315 err = -ENOBUFS;
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001316 free_msg:
Johannes Berg41ade002007-12-19 02:03:29 +01001317 nlmsg_free(msg);
1318 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001319 cfg80211_unlock_rdev(rdev);
Johannes Berg41ade002007-12-19 02:03:29 +01001320 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001321 unlock_rtnl:
1322 rtnl_unlock();
1323
Johannes Berg41ade002007-12-19 02:03:29 +01001324 return err;
1325}
1326
1327static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
1328{
Johannes Berg79c97e92009-07-07 03:56:12 +02001329 struct cfg80211_registered_device *rdev;
Johannes Bergb9454e82009-07-08 13:29:08 +02001330 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001331 int err;
1332 struct net_device *dev;
Jouni Malinen3cfcf6a2009-01-08 13:32:02 +02001333 int (*func)(struct wiphy *wiphy, struct net_device *netdev,
1334 u8 key_index);
Johannes Berg41ade002007-12-19 02:03:29 +01001335
Johannes Bergb9454e82009-07-08 13:29:08 +02001336 err = nl80211_parse_key(info, &key);
1337 if (err)
1338 return err;
1339
1340 if (key.idx < 0)
Johannes Berg41ade002007-12-19 02:03:29 +01001341 return -EINVAL;
1342
Johannes Bergb9454e82009-07-08 13:29:08 +02001343 /* only support setting default key */
1344 if (!key.def && !key.defmgmt)
Johannes Berg41ade002007-12-19 02:03:29 +01001345 return -EINVAL;
1346
Johannes Berg3b858752009-03-12 09:55:09 +01001347 rtnl_lock();
1348
Johannes Berg463d0182009-07-14 00:33:35 +02001349 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001350 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001351 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001352
Johannes Bergb9454e82009-07-08 13:29:08 +02001353 if (key.def)
Johannes Berg79c97e92009-07-07 03:56:12 +02001354 func = rdev->ops->set_default_key;
Jouni Malinen3cfcf6a2009-01-08 13:32:02 +02001355 else
Johannes Berg79c97e92009-07-07 03:56:12 +02001356 func = rdev->ops->set_default_mgmt_key;
Jouni Malinen3cfcf6a2009-01-08 13:32:02 +02001357
1358 if (!func) {
Johannes Berg41ade002007-12-19 02:03:29 +01001359 err = -EOPNOTSUPP;
1360 goto out;
1361 }
1362
Johannes Bergfffd0932009-07-08 14:22:54 +02001363 wdev_lock(dev->ieee80211_ptr);
1364 err = nl80211_key_allowed(dev->ieee80211_ptr);
1365 if (!err)
1366 err = func(&rdev->wiphy, dev, key.idx);
1367
Johannes Berg3d23e342009-09-29 23:27:28 +02001368#ifdef CONFIG_CFG80211_WEXT
Johannes Berg08645122009-05-11 13:54:58 +02001369 if (!err) {
Johannes Berg79c97e92009-07-07 03:56:12 +02001370 if (func == rdev->ops->set_default_key)
Johannes Bergb9454e82009-07-08 13:29:08 +02001371 dev->ieee80211_ptr->wext.default_key = key.idx;
Johannes Berg08645122009-05-11 13:54:58 +02001372 else
Johannes Bergb9454e82009-07-08 13:29:08 +02001373 dev->ieee80211_ptr->wext.default_mgmt_key = key.idx;
Johannes Berg08645122009-05-11 13:54:58 +02001374 }
1375#endif
Johannes Bergfffd0932009-07-08 14:22:54 +02001376 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01001377
1378 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001379 cfg80211_unlock_rdev(rdev);
Johannes Berg41ade002007-12-19 02:03:29 +01001380 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001381
1382 unlock_rtnl:
1383 rtnl_unlock();
1384
Johannes Berg41ade002007-12-19 02:03:29 +01001385 return err;
1386}
1387
1388static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
1389{
Johannes Berg79c97e92009-07-07 03:56:12 +02001390 struct cfg80211_registered_device *rdev;
Johannes Bergfffd0932009-07-08 14:22:54 +02001391 int err;
Johannes Berg41ade002007-12-19 02:03:29 +01001392 struct net_device *dev;
Johannes Bergb9454e82009-07-08 13:29:08 +02001393 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001394 u8 *mac_addr = NULL;
1395
Johannes Bergb9454e82009-07-08 13:29:08 +02001396 err = nl80211_parse_key(info, &key);
1397 if (err)
1398 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01001399
Johannes Bergb9454e82009-07-08 13:29:08 +02001400 if (!key.p.key)
Johannes Berg41ade002007-12-19 02:03:29 +01001401 return -EINVAL;
1402
Johannes Berg41ade002007-12-19 02:03:29 +01001403 if (info->attrs[NL80211_ATTR_MAC])
1404 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1405
Johannes Berg3b858752009-03-12 09:55:09 +01001406 rtnl_lock();
1407
Johannes Berg463d0182009-07-14 00:33:35 +02001408 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001409 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001410 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001411
Johannes Berg79c97e92009-07-07 03:56:12 +02001412 if (!rdev->ops->add_key) {
Johannes Berg41ade002007-12-19 02:03:29 +01001413 err = -EOPNOTSUPP;
1414 goto out;
1415 }
1416
Johannes Bergfffd0932009-07-08 14:22:54 +02001417 if (cfg80211_validate_key_settings(rdev, &key.p, key.idx, mac_addr)) {
1418 err = -EINVAL;
1419 goto out;
1420 }
1421
1422 wdev_lock(dev->ieee80211_ptr);
1423 err = nl80211_key_allowed(dev->ieee80211_ptr);
1424 if (!err)
1425 err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx,
1426 mac_addr, &key.p);
1427 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01001428
1429 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001430 cfg80211_unlock_rdev(rdev);
Johannes Berg41ade002007-12-19 02:03:29 +01001431 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001432 unlock_rtnl:
1433 rtnl_unlock();
1434
Johannes Berg41ade002007-12-19 02:03:29 +01001435 return err;
1436}
1437
1438static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
1439{
Johannes Berg79c97e92009-07-07 03:56:12 +02001440 struct cfg80211_registered_device *rdev;
Johannes Berg41ade002007-12-19 02:03:29 +01001441 int err;
1442 struct net_device *dev;
Johannes Berg41ade002007-12-19 02:03:29 +01001443 u8 *mac_addr = NULL;
Johannes Bergb9454e82009-07-08 13:29:08 +02001444 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001445
Johannes Bergb9454e82009-07-08 13:29:08 +02001446 err = nl80211_parse_key(info, &key);
1447 if (err)
1448 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01001449
1450 if (info->attrs[NL80211_ATTR_MAC])
1451 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1452
Johannes Berg3b858752009-03-12 09:55:09 +01001453 rtnl_lock();
1454
Johannes Berg463d0182009-07-14 00:33:35 +02001455 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001456 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001457 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001458
Johannes Berg79c97e92009-07-07 03:56:12 +02001459 if (!rdev->ops->del_key) {
Johannes Berg41ade002007-12-19 02:03:29 +01001460 err = -EOPNOTSUPP;
1461 goto out;
1462 }
1463
Johannes Bergfffd0932009-07-08 14:22:54 +02001464 wdev_lock(dev->ieee80211_ptr);
1465 err = nl80211_key_allowed(dev->ieee80211_ptr);
1466 if (!err)
1467 err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr);
Johannes Berg41ade002007-12-19 02:03:29 +01001468
Johannes Berg3d23e342009-09-29 23:27:28 +02001469#ifdef CONFIG_CFG80211_WEXT
Johannes Berg08645122009-05-11 13:54:58 +02001470 if (!err) {
Johannes Bergb9454e82009-07-08 13:29:08 +02001471 if (key.idx == dev->ieee80211_ptr->wext.default_key)
Johannes Berg08645122009-05-11 13:54:58 +02001472 dev->ieee80211_ptr->wext.default_key = -1;
Johannes Bergb9454e82009-07-08 13:29:08 +02001473 else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key)
Johannes Berg08645122009-05-11 13:54:58 +02001474 dev->ieee80211_ptr->wext.default_mgmt_key = -1;
1475 }
1476#endif
Johannes Bergfffd0932009-07-08 14:22:54 +02001477 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg08645122009-05-11 13:54:58 +02001478
Johannes Berg41ade002007-12-19 02:03:29 +01001479 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001480 cfg80211_unlock_rdev(rdev);
Johannes Berg41ade002007-12-19 02:03:29 +01001481 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001482
1483 unlock_rtnl:
1484 rtnl_unlock();
1485
Johannes Berg41ade002007-12-19 02:03:29 +01001486 return err;
1487}
1488
Johannes Berged1b6cc2007-12-19 02:03:32 +01001489static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
1490{
1491 int (*call)(struct wiphy *wiphy, struct net_device *dev,
1492 struct beacon_parameters *info);
Johannes Berg79c97e92009-07-07 03:56:12 +02001493 struct cfg80211_registered_device *rdev;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001494 int err;
1495 struct net_device *dev;
1496 struct beacon_parameters params;
1497 int haveinfo = 0;
1498
Johannes Bergf4a11bb2009-03-27 12:40:28 +01001499 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]))
1500 return -EINVAL;
1501
Johannes Berg3b858752009-03-12 09:55:09 +01001502 rtnl_lock();
1503
Johannes Berg463d0182009-07-14 00:33:35 +02001504 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001505 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001506 goto unlock_rtnl;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001507
Jouni Malineneec60b02009-03-20 21:21:19 +02001508 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
1509 err = -EOPNOTSUPP;
1510 goto out;
1511 }
1512
Johannes Berged1b6cc2007-12-19 02:03:32 +01001513 switch (info->genlhdr->cmd) {
1514 case NL80211_CMD_NEW_BEACON:
1515 /* these are required for NEW_BEACON */
1516 if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
1517 !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
1518 !info->attrs[NL80211_ATTR_BEACON_HEAD]) {
1519 err = -EINVAL;
1520 goto out;
1521 }
1522
Johannes Berg79c97e92009-07-07 03:56:12 +02001523 call = rdev->ops->add_beacon;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001524 break;
1525 case NL80211_CMD_SET_BEACON:
Johannes Berg79c97e92009-07-07 03:56:12 +02001526 call = rdev->ops->set_beacon;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001527 break;
1528 default:
1529 WARN_ON(1);
1530 err = -EOPNOTSUPP;
1531 goto out;
1532 }
1533
1534 if (!call) {
1535 err = -EOPNOTSUPP;
1536 goto out;
1537 }
1538
1539 memset(&params, 0, sizeof(params));
1540
1541 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
1542 params.interval =
1543 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
1544 haveinfo = 1;
1545 }
1546
1547 if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) {
1548 params.dtim_period =
1549 nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
1550 haveinfo = 1;
1551 }
1552
1553 if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
1554 params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
1555 params.head_len =
1556 nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
1557 haveinfo = 1;
1558 }
1559
1560 if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
1561 params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
1562 params.tail_len =
1563 nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
1564 haveinfo = 1;
1565 }
1566
1567 if (!haveinfo) {
1568 err = -EINVAL;
1569 goto out;
1570 }
1571
Johannes Berg79c97e92009-07-07 03:56:12 +02001572 err = call(&rdev->wiphy, dev, &params);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001573
1574 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001575 cfg80211_unlock_rdev(rdev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001576 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001577 unlock_rtnl:
1578 rtnl_unlock();
1579
Johannes Berged1b6cc2007-12-19 02:03:32 +01001580 return err;
1581}
1582
1583static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
1584{
Johannes Berg79c97e92009-07-07 03:56:12 +02001585 struct cfg80211_registered_device *rdev;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001586 int err;
1587 struct net_device *dev;
1588
Johannes Berg3b858752009-03-12 09:55:09 +01001589 rtnl_lock();
1590
Johannes Berg463d0182009-07-14 00:33:35 +02001591 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001592 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001593 goto unlock_rtnl;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001594
Johannes Berg79c97e92009-07-07 03:56:12 +02001595 if (!rdev->ops->del_beacon) {
Johannes Berged1b6cc2007-12-19 02:03:32 +01001596 err = -EOPNOTSUPP;
1597 goto out;
1598 }
1599
Jouni Malineneec60b02009-03-20 21:21:19 +02001600 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
1601 err = -EOPNOTSUPP;
1602 goto out;
1603 }
Johannes Berg79c97e92009-07-07 03:56:12 +02001604 err = rdev->ops->del_beacon(&rdev->wiphy, dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001605
1606 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001607 cfg80211_unlock_rdev(rdev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001608 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001609 unlock_rtnl:
1610 rtnl_unlock();
1611
Johannes Berged1b6cc2007-12-19 02:03:32 +01001612 return err;
1613}
1614
Johannes Berg5727ef12007-12-19 02:03:34 +01001615static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
1616 [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
1617 [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
1618 [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
Jouni Malinen0e467242009-05-11 21:57:55 +03001619 [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG },
Johannes Berg5727ef12007-12-19 02:03:34 +01001620};
1621
Johannes Bergeccb8e82009-05-11 21:57:56 +03001622static int parse_station_flags(struct genl_info *info,
1623 struct station_parameters *params)
Johannes Berg5727ef12007-12-19 02:03:34 +01001624{
1625 struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
Johannes Bergeccb8e82009-05-11 21:57:56 +03001626 struct nlattr *nla;
Johannes Berg5727ef12007-12-19 02:03:34 +01001627 int flag;
1628
Johannes Bergeccb8e82009-05-11 21:57:56 +03001629 /*
1630 * Try parsing the new attribute first so userspace
1631 * can specify both for older kernels.
1632 */
1633 nla = info->attrs[NL80211_ATTR_STA_FLAGS2];
1634 if (nla) {
1635 struct nl80211_sta_flag_update *sta_flags;
Johannes Berg5727ef12007-12-19 02:03:34 +01001636
Johannes Bergeccb8e82009-05-11 21:57:56 +03001637 sta_flags = nla_data(nla);
1638 params->sta_flags_mask = sta_flags->mask;
1639 params->sta_flags_set = sta_flags->set;
1640 if ((params->sta_flags_mask |
1641 params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID))
1642 return -EINVAL;
1643 return 0;
1644 }
1645
1646 /* if present, parse the old attribute */
1647
1648 nla = info->attrs[NL80211_ATTR_STA_FLAGS];
Johannes Berg5727ef12007-12-19 02:03:34 +01001649 if (!nla)
1650 return 0;
1651
1652 if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX,
1653 nla, sta_flags_policy))
1654 return -EINVAL;
1655
Johannes Bergeccb8e82009-05-11 21:57:56 +03001656 params->sta_flags_mask = (1 << __NL80211_STA_FLAG_AFTER_LAST) - 1;
1657 params->sta_flags_mask &= ~1;
Johannes Berg5727ef12007-12-19 02:03:34 +01001658
1659 for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++)
1660 if (flags[flag])
Johannes Bergeccb8e82009-05-11 21:57:56 +03001661 params->sta_flags_set |= (1<<flag);
Johannes Berg5727ef12007-12-19 02:03:34 +01001662
1663 return 0;
1664}
1665
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001666static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
1667 int flags, struct net_device *dev,
Johannes Berg98b62182009-12-23 13:15:44 +01001668 const u8 *mac_addr, struct station_info *sinfo)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001669{
1670 void *hdr;
Henning Rogge420e7fa2008-12-11 22:04:19 +01001671 struct nlattr *sinfoattr, *txrate;
1672 u16 bitrate;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001673
1674 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
1675 if (!hdr)
1676 return -1;
1677
1678 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1679 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1680
Johannes Bergf5ea9122009-08-07 16:17:38 +02001681 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, sinfo->generation);
1682
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001683 sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
1684 if (!sinfoattr)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001685 goto nla_put_failure;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001686 if (sinfo->filled & STATION_INFO_INACTIVE_TIME)
1687 NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME,
1688 sinfo->inactive_time);
1689 if (sinfo->filled & STATION_INFO_RX_BYTES)
1690 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES,
1691 sinfo->rx_bytes);
1692 if (sinfo->filled & STATION_INFO_TX_BYTES)
1693 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES,
1694 sinfo->tx_bytes);
1695 if (sinfo->filled & STATION_INFO_LLID)
1696 NLA_PUT_U16(msg, NL80211_STA_INFO_LLID,
1697 sinfo->llid);
1698 if (sinfo->filled & STATION_INFO_PLID)
1699 NLA_PUT_U16(msg, NL80211_STA_INFO_PLID,
1700 sinfo->plid);
1701 if (sinfo->filled & STATION_INFO_PLINK_STATE)
1702 NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
1703 sinfo->plink_state);
Henning Rogge420e7fa2008-12-11 22:04:19 +01001704 if (sinfo->filled & STATION_INFO_SIGNAL)
1705 NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL,
1706 sinfo->signal);
1707 if (sinfo->filled & STATION_INFO_TX_BITRATE) {
1708 txrate = nla_nest_start(msg, NL80211_STA_INFO_TX_BITRATE);
1709 if (!txrate)
1710 goto nla_put_failure;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001711
John W. Linville254416a2009-12-09 16:43:52 -05001712 /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */
1713 bitrate = cfg80211_calculate_bitrate(&sinfo->txrate);
Henning Rogge420e7fa2008-12-11 22:04:19 +01001714 if (bitrate > 0)
1715 NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
1716
1717 if (sinfo->txrate.flags & RATE_INFO_FLAGS_MCS)
1718 NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS,
1719 sinfo->txrate.mcs);
1720 if (sinfo->txrate.flags & RATE_INFO_FLAGS_40_MHZ_WIDTH)
1721 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH);
1722 if (sinfo->txrate.flags & RATE_INFO_FLAGS_SHORT_GI)
1723 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI);
1724
1725 nla_nest_end(msg, txrate);
1726 }
Jouni Malinen98c8a60a2009-02-17 13:24:57 +02001727 if (sinfo->filled & STATION_INFO_RX_PACKETS)
1728 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS,
1729 sinfo->rx_packets);
1730 if (sinfo->filled & STATION_INFO_TX_PACKETS)
1731 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS,
1732 sinfo->tx_packets);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001733 nla_nest_end(msg, sinfoattr);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001734
1735 return genlmsg_end(msg, hdr);
1736
1737 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001738 genlmsg_cancel(msg, hdr);
1739 return -EMSGSIZE;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001740}
1741
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001742static int nl80211_dump_station(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02001743 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001744{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001745 struct station_info sinfo;
1746 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001747 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001748 u8 mac_addr[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02001749 int ifidx = cb->args[0];
1750 int sta_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001751 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001752
Holger Schuriga0438972009-11-11 11:30:02 +01001753 if (!ifidx)
1754 ifidx = nl80211_get_ifidx(cb);
1755 if (ifidx < 0)
1756 return ifidx;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001757
Johannes Berg3b858752009-03-12 09:55:09 +01001758 rtnl_lock();
1759
Johannes Berg463d0182009-07-14 00:33:35 +02001760 netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
Johannes Berg3b858752009-03-12 09:55:09 +01001761 if (!netdev) {
1762 err = -ENODEV;
1763 goto out_rtnl;
1764 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001765
Johannes Berg463d0182009-07-14 00:33:35 +02001766 dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001767 if (IS_ERR(dev)) {
1768 err = PTR_ERR(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001769 goto out_rtnl;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001770 }
1771
1772 if (!dev->ops->dump_station) {
Jouni Malineneec60b02009-03-20 21:21:19 +02001773 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001774 goto out_err;
1775 }
1776
Johannes Bergbba95fe2008-07-29 13:22:51 +02001777 while (1) {
1778 err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
1779 mac_addr, &sinfo);
1780 if (err == -ENOENT)
1781 break;
1782 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001783 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001784
1785 if (nl80211_send_station(skb,
1786 NETLINK_CB(cb->skb).pid,
1787 cb->nlh->nlmsg_seq, NLM_F_MULTI,
1788 netdev, mac_addr,
1789 &sinfo) < 0)
1790 goto out;
1791
1792 sta_idx++;
1793 }
1794
1795
1796 out:
1797 cb->args[1] = sta_idx;
1798 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001799 out_err:
Johannes Berg4d0c8ae2009-07-07 03:56:09 +02001800 cfg80211_unlock_rdev(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001801 out_rtnl:
1802 rtnl_unlock();
Johannes Bergbba95fe2008-07-29 13:22:51 +02001803
1804 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001805}
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001806
Johannes Berg5727ef12007-12-19 02:03:34 +01001807static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
1808{
Johannes Berg79c97e92009-07-07 03:56:12 +02001809 struct cfg80211_registered_device *rdev;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001810 int err;
1811 struct net_device *dev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001812 struct station_info sinfo;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001813 struct sk_buff *msg;
1814 u8 *mac_addr = NULL;
1815
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001816 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001817
1818 if (!info->attrs[NL80211_ATTR_MAC])
1819 return -EINVAL;
1820
1821 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1822
Johannes Berg3b858752009-03-12 09:55:09 +01001823 rtnl_lock();
1824
Johannes Berg463d0182009-07-14 00:33:35 +02001825 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001826 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001827 goto out_rtnl;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001828
Johannes Berg79c97e92009-07-07 03:56:12 +02001829 if (!rdev->ops->get_station) {
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001830 err = -EOPNOTSUPP;
1831 goto out;
1832 }
1833
Johannes Berg79c97e92009-07-07 03:56:12 +02001834 err = rdev->ops->get_station(&rdev->wiphy, dev, mac_addr, &sinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001835 if (err)
1836 goto out;
1837
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001838 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001839 if (!msg)
1840 goto out;
1841
1842 if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001843 dev, mac_addr, &sinfo) < 0)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001844 goto out_free;
1845
Johannes Berg134e6372009-07-10 09:51:34 +00001846 err = genlmsg_reply(msg, info);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001847 goto out;
1848
1849 out_free:
1850 nlmsg_free(msg);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001851 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001852 cfg80211_unlock_rdev(rdev);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001853 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001854 out_rtnl:
1855 rtnl_unlock();
1856
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001857 return err;
Johannes Berg5727ef12007-12-19 02:03:34 +01001858}
1859
1860/*
Felix Fietkauc258d2d2009-11-11 17:23:31 +01001861 * Get vlan interface making sure it is running and on the right wiphy.
Johannes Berg5727ef12007-12-19 02:03:34 +01001862 */
Johannes Berg463d0182009-07-14 00:33:35 +02001863static int get_vlan(struct genl_info *info,
Johannes Berg5727ef12007-12-19 02:03:34 +01001864 struct cfg80211_registered_device *rdev,
1865 struct net_device **vlan)
1866{
Johannes Berg463d0182009-07-14 00:33:35 +02001867 struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN];
Johannes Berg5727ef12007-12-19 02:03:34 +01001868 *vlan = NULL;
1869
1870 if (vlanattr) {
Johannes Berg463d0182009-07-14 00:33:35 +02001871 *vlan = dev_get_by_index(genl_info_net(info),
1872 nla_get_u32(vlanattr));
Johannes Berg5727ef12007-12-19 02:03:34 +01001873 if (!*vlan)
1874 return -ENODEV;
1875 if (!(*vlan)->ieee80211_ptr)
1876 return -EINVAL;
1877 if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy)
1878 return -EINVAL;
Felix Fietkauc258d2d2009-11-11 17:23:31 +01001879 if (!netif_running(*vlan))
1880 return -ENETDOWN;
Johannes Berg5727ef12007-12-19 02:03:34 +01001881 }
1882 return 0;
1883}
1884
1885static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
1886{
Johannes Berg79c97e92009-07-07 03:56:12 +02001887 struct cfg80211_registered_device *rdev;
Johannes Berg5727ef12007-12-19 02:03:34 +01001888 int err;
1889 struct net_device *dev;
1890 struct station_parameters params;
1891 u8 *mac_addr = NULL;
1892
1893 memset(&params, 0, sizeof(params));
1894
1895 params.listen_interval = -1;
1896
1897 if (info->attrs[NL80211_ATTR_STA_AID])
1898 return -EINVAL;
1899
1900 if (!info->attrs[NL80211_ATTR_MAC])
1901 return -EINVAL;
1902
1903 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1904
1905 if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
1906 params.supported_rates =
1907 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1908 params.supported_rates_len =
1909 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1910 }
1911
1912 if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
1913 params.listen_interval =
1914 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
1915
Jouni Malinen36aedc92008-08-25 11:58:58 +03001916 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
1917 params.ht_capa =
1918 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
1919
Johannes Bergeccb8e82009-05-11 21:57:56 +03001920 if (parse_station_flags(info, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01001921 return -EINVAL;
1922
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001923 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
1924 params.plink_action =
1925 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
1926
Johannes Berg3b858752009-03-12 09:55:09 +01001927 rtnl_lock();
1928
Johannes Berg463d0182009-07-14 00:33:35 +02001929 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001930 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001931 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01001932
Johannes Berg463d0182009-07-14 00:33:35 +02001933 err = get_vlan(info, rdev, &params.vlan);
Johannes Berga97f4422009-06-18 17:23:43 +02001934 if (err)
Johannes Berg034d6552009-05-27 10:35:29 +02001935 goto out;
Johannes Berga97f4422009-06-18 17:23:43 +02001936
1937 /* validate settings */
1938 err = 0;
1939
1940 switch (dev->ieee80211_ptr->iftype) {
1941 case NL80211_IFTYPE_AP:
1942 case NL80211_IFTYPE_AP_VLAN:
1943 /* disallow mesh-specific things */
1944 if (params.plink_action)
1945 err = -EINVAL;
1946 break;
1947 case NL80211_IFTYPE_STATION:
1948 /* disallow everything but AUTHORIZED flag */
1949 if (params.plink_action)
1950 err = -EINVAL;
1951 if (params.vlan)
1952 err = -EINVAL;
1953 if (params.supported_rates)
1954 err = -EINVAL;
1955 if (params.ht_capa)
1956 err = -EINVAL;
1957 if (params.listen_interval >= 0)
1958 err = -EINVAL;
1959 if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
1960 err = -EINVAL;
1961 break;
1962 case NL80211_IFTYPE_MESH_POINT:
1963 /* disallow things mesh doesn't support */
1964 if (params.vlan)
1965 err = -EINVAL;
1966 if (params.ht_capa)
1967 err = -EINVAL;
1968 if (params.listen_interval >= 0)
1969 err = -EINVAL;
1970 if (params.supported_rates)
1971 err = -EINVAL;
1972 if (params.sta_flags_mask)
1973 err = -EINVAL;
1974 break;
1975 default:
1976 err = -EINVAL;
Johannes Berg034d6552009-05-27 10:35:29 +02001977 }
1978
Johannes Berg5727ef12007-12-19 02:03:34 +01001979 if (err)
1980 goto out;
1981
Johannes Berg79c97e92009-07-07 03:56:12 +02001982 if (!rdev->ops->change_station) {
Johannes Berg5727ef12007-12-19 02:03:34 +01001983 err = -EOPNOTSUPP;
1984 goto out;
1985 }
1986
Johannes Berg79c97e92009-07-07 03:56:12 +02001987 err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01001988
1989 out:
1990 if (params.vlan)
1991 dev_put(params.vlan);
Johannes Berg79c97e92009-07-07 03:56:12 +02001992 cfg80211_unlock_rdev(rdev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001993 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001994 out_rtnl:
1995 rtnl_unlock();
1996
Johannes Berg5727ef12007-12-19 02:03:34 +01001997 return err;
1998}
1999
2000static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
2001{
Johannes Berg79c97e92009-07-07 03:56:12 +02002002 struct cfg80211_registered_device *rdev;
Johannes Berg5727ef12007-12-19 02:03:34 +01002003 int err;
2004 struct net_device *dev;
2005 struct station_parameters params;
2006 u8 *mac_addr = NULL;
2007
2008 memset(&params, 0, sizeof(params));
2009
2010 if (!info->attrs[NL80211_ATTR_MAC])
2011 return -EINVAL;
2012
Johannes Berg5727ef12007-12-19 02:03:34 +01002013 if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
2014 return -EINVAL;
2015
2016 if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
2017 return -EINVAL;
2018
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002019 if (!info->attrs[NL80211_ATTR_STA_AID])
2020 return -EINVAL;
2021
Johannes Berg5727ef12007-12-19 02:03:34 +01002022 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2023 params.supported_rates =
2024 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2025 params.supported_rates_len =
2026 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2027 params.listen_interval =
2028 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
Johannes Berg51b50fb2009-05-24 16:42:30 +02002029
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002030 params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
2031 if (!params.aid || params.aid > IEEE80211_MAX_AID)
2032 return -EINVAL;
Johannes Berg51b50fb2009-05-24 16:42:30 +02002033
Jouni Malinen36aedc92008-08-25 11:58:58 +03002034 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
2035 params.ht_capa =
2036 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
Johannes Berg5727ef12007-12-19 02:03:34 +01002037
Johannes Bergeccb8e82009-05-11 21:57:56 +03002038 if (parse_station_flags(info, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01002039 return -EINVAL;
2040
Johannes Berg3b858752009-03-12 09:55:09 +01002041 rtnl_lock();
2042
Johannes Berg463d0182009-07-14 00:33:35 +02002043 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01002044 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002045 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01002046
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002047 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
2048 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN) {
2049 err = -EINVAL;
2050 goto out;
2051 }
2052
Johannes Berg463d0182009-07-14 00:33:35 +02002053 err = get_vlan(info, rdev, &params.vlan);
Johannes Berga97f4422009-06-18 17:23:43 +02002054 if (err)
Johannes Berge80cf852009-05-11 14:43:13 +02002055 goto out;
Johannes Berga97f4422009-06-18 17:23:43 +02002056
2057 /* validate settings */
2058 err = 0;
2059
Johannes Berg79c97e92009-07-07 03:56:12 +02002060 if (!rdev->ops->add_station) {
Johannes Berg5727ef12007-12-19 02:03:34 +01002061 err = -EOPNOTSUPP;
2062 goto out;
2063 }
2064
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002065 if (!netif_running(dev)) {
2066 err = -ENETDOWN;
2067 goto out;
2068 }
2069
Johannes Berg79c97e92009-07-07 03:56:12 +02002070 err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01002071
2072 out:
2073 if (params.vlan)
2074 dev_put(params.vlan);
Johannes Berg79c97e92009-07-07 03:56:12 +02002075 cfg80211_unlock_rdev(rdev);
Johannes Berg5727ef12007-12-19 02:03:34 +01002076 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002077 out_rtnl:
2078 rtnl_unlock();
2079
Johannes Berg5727ef12007-12-19 02:03:34 +01002080 return err;
2081}
2082
2083static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
2084{
Johannes Berg79c97e92009-07-07 03:56:12 +02002085 struct cfg80211_registered_device *rdev;
Johannes Berg5727ef12007-12-19 02:03:34 +01002086 int err;
2087 struct net_device *dev;
2088 u8 *mac_addr = NULL;
2089
2090 if (info->attrs[NL80211_ATTR_MAC])
2091 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2092
Johannes Berg3b858752009-03-12 09:55:09 +01002093 rtnl_lock();
2094
Johannes Berg463d0182009-07-14 00:33:35 +02002095 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01002096 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002097 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01002098
Johannes Berge80cf852009-05-11 14:43:13 +02002099 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Marco Porschd5d9de02010-03-30 10:00:16 +02002100 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
2101 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
Johannes Berge80cf852009-05-11 14:43:13 +02002102 err = -EINVAL;
2103 goto out;
2104 }
2105
Johannes Berg79c97e92009-07-07 03:56:12 +02002106 if (!rdev->ops->del_station) {
Johannes Berg5727ef12007-12-19 02:03:34 +01002107 err = -EOPNOTSUPP;
2108 goto out;
2109 }
2110
Johannes Berg79c97e92009-07-07 03:56:12 +02002111 err = rdev->ops->del_station(&rdev->wiphy, dev, mac_addr);
Johannes Berg5727ef12007-12-19 02:03:34 +01002112
2113 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002114 cfg80211_unlock_rdev(rdev);
Johannes Berg5727ef12007-12-19 02:03:34 +01002115 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002116 out_rtnl:
2117 rtnl_unlock();
2118
Johannes Berg5727ef12007-12-19 02:03:34 +01002119 return err;
2120}
2121
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002122static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq,
2123 int flags, struct net_device *dev,
2124 u8 *dst, u8 *next_hop,
2125 struct mpath_info *pinfo)
2126{
2127 void *hdr;
2128 struct nlattr *pinfoattr;
2129
2130 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
2131 if (!hdr)
2132 return -1;
2133
2134 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2135 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
2136 NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop);
2137
Johannes Bergf5ea9122009-08-07 16:17:38 +02002138 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, pinfo->generation);
2139
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002140 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
2141 if (!pinfoattr)
2142 goto nla_put_failure;
2143 if (pinfo->filled & MPATH_INFO_FRAME_QLEN)
2144 NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
2145 pinfo->frame_qlen);
Rui Paulod19b3bf2009-11-09 23:46:55 +00002146 if (pinfo->filled & MPATH_INFO_SN)
2147 NLA_PUT_U32(msg, NL80211_MPATH_INFO_SN,
2148 pinfo->sn);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002149 if (pinfo->filled & MPATH_INFO_METRIC)
2150 NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC,
2151 pinfo->metric);
2152 if (pinfo->filled & MPATH_INFO_EXPTIME)
2153 NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME,
2154 pinfo->exptime);
2155 if (pinfo->filled & MPATH_INFO_FLAGS)
2156 NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS,
2157 pinfo->flags);
2158 if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT)
2159 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
2160 pinfo->discovery_timeout);
2161 if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES)
2162 NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
2163 pinfo->discovery_retries);
2164
2165 nla_nest_end(msg, pinfoattr);
2166
2167 return genlmsg_end(msg, hdr);
2168
2169 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07002170 genlmsg_cancel(msg, hdr);
2171 return -EMSGSIZE;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002172}
2173
2174static int nl80211_dump_mpath(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02002175 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002176{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002177 struct mpath_info pinfo;
2178 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002179 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002180 u8 dst[ETH_ALEN];
2181 u8 next_hop[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02002182 int ifidx = cb->args[0];
2183 int path_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002184 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002185
Holger Schuriga0438972009-11-11 11:30:02 +01002186 if (!ifidx)
2187 ifidx = nl80211_get_ifidx(cb);
2188 if (ifidx < 0)
2189 return ifidx;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002190
Johannes Berg3b858752009-03-12 09:55:09 +01002191 rtnl_lock();
2192
Johannes Berg463d0182009-07-14 00:33:35 +02002193 netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
Johannes Berg3b858752009-03-12 09:55:09 +01002194 if (!netdev) {
2195 err = -ENODEV;
2196 goto out_rtnl;
2197 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002198
Johannes Berg463d0182009-07-14 00:33:35 +02002199 dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02002200 if (IS_ERR(dev)) {
2201 err = PTR_ERR(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002202 goto out_rtnl;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002203 }
2204
2205 if (!dev->ops->dump_mpath) {
Jouni Malineneec60b02009-03-20 21:21:19 +02002206 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002207 goto out_err;
2208 }
2209
Jouni Malineneec60b02009-03-20 21:21:19 +02002210 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2211 err = -EOPNOTSUPP;
Roel Kluin0448b5f2009-08-22 21:15:49 +02002212 goto out_err;
Jouni Malineneec60b02009-03-20 21:21:19 +02002213 }
2214
Johannes Bergbba95fe2008-07-29 13:22:51 +02002215 while (1) {
2216 err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
2217 dst, next_hop, &pinfo);
2218 if (err == -ENOENT)
2219 break;
2220 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002221 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002222
2223 if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid,
2224 cb->nlh->nlmsg_seq, NLM_F_MULTI,
2225 netdev, dst, next_hop,
2226 &pinfo) < 0)
2227 goto out;
2228
2229 path_idx++;
2230 }
2231
2232
2233 out:
2234 cb->args[1] = path_idx;
2235 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002236 out_err:
Johannes Berg4d0c8ae2009-07-07 03:56:09 +02002237 cfg80211_unlock_rdev(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002238 out_rtnl:
2239 rtnl_unlock();
Johannes Bergbba95fe2008-07-29 13:22:51 +02002240
2241 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002242}
2243
2244static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
2245{
Johannes Berg79c97e92009-07-07 03:56:12 +02002246 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002247 int err;
2248 struct net_device *dev;
2249 struct mpath_info pinfo;
2250 struct sk_buff *msg;
2251 u8 *dst = NULL;
2252 u8 next_hop[ETH_ALEN];
2253
2254 memset(&pinfo, 0, sizeof(pinfo));
2255
2256 if (!info->attrs[NL80211_ATTR_MAC])
2257 return -EINVAL;
2258
2259 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2260
Johannes Berg3b858752009-03-12 09:55:09 +01002261 rtnl_lock();
2262
Johannes Berg463d0182009-07-14 00:33:35 +02002263 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002264 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002265 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002266
Johannes Berg79c97e92009-07-07 03:56:12 +02002267 if (!rdev->ops->get_mpath) {
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002268 err = -EOPNOTSUPP;
2269 goto out;
2270 }
2271
Jouni Malineneec60b02009-03-20 21:21:19 +02002272 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2273 err = -EOPNOTSUPP;
2274 goto out;
2275 }
2276
Johannes Berg79c97e92009-07-07 03:56:12 +02002277 err = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, &pinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002278 if (err)
2279 goto out;
2280
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002281 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002282 if (!msg)
2283 goto out;
2284
2285 if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0,
2286 dev, dst, next_hop, &pinfo) < 0)
2287 goto out_free;
2288
Johannes Berg134e6372009-07-10 09:51:34 +00002289 err = genlmsg_reply(msg, info);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002290 goto out;
2291
2292 out_free:
2293 nlmsg_free(msg);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002294 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002295 cfg80211_unlock_rdev(rdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002296 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002297 out_rtnl:
2298 rtnl_unlock();
2299
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002300 return err;
2301}
2302
2303static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
2304{
Johannes Berg79c97e92009-07-07 03:56:12 +02002305 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002306 int err;
2307 struct net_device *dev;
2308 u8 *dst = NULL;
2309 u8 *next_hop = NULL;
2310
2311 if (!info->attrs[NL80211_ATTR_MAC])
2312 return -EINVAL;
2313
2314 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
2315 return -EINVAL;
2316
2317 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2318 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
2319
Johannes Berg3b858752009-03-12 09:55:09 +01002320 rtnl_lock();
2321
Johannes Berg463d0182009-07-14 00:33:35 +02002322 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002323 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002324 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002325
Johannes Berg79c97e92009-07-07 03:56:12 +02002326 if (!rdev->ops->change_mpath) {
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002327 err = -EOPNOTSUPP;
2328 goto out;
2329 }
2330
Jouni Malineneec60b02009-03-20 21:21:19 +02002331 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2332 err = -EOPNOTSUPP;
2333 goto out;
2334 }
2335
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002336 if (!netif_running(dev)) {
2337 err = -ENETDOWN;
2338 goto out;
2339 }
2340
Johannes Berg79c97e92009-07-07 03:56:12 +02002341 err = rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002342
2343 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002344 cfg80211_unlock_rdev(rdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002345 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002346 out_rtnl:
2347 rtnl_unlock();
2348
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002349 return err;
2350}
2351static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
2352{
Johannes Berg79c97e92009-07-07 03:56:12 +02002353 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002354 int err;
2355 struct net_device *dev;
2356 u8 *dst = NULL;
2357 u8 *next_hop = NULL;
2358
2359 if (!info->attrs[NL80211_ATTR_MAC])
2360 return -EINVAL;
2361
2362 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
2363 return -EINVAL;
2364
2365 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2366 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
2367
Johannes Berg3b858752009-03-12 09:55:09 +01002368 rtnl_lock();
2369
Johannes Berg463d0182009-07-14 00:33:35 +02002370 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002371 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002372 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002373
Johannes Berg79c97e92009-07-07 03:56:12 +02002374 if (!rdev->ops->add_mpath) {
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002375 err = -EOPNOTSUPP;
2376 goto out;
2377 }
2378
Jouni Malineneec60b02009-03-20 21:21:19 +02002379 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2380 err = -EOPNOTSUPP;
2381 goto out;
2382 }
2383
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002384 if (!netif_running(dev)) {
2385 err = -ENETDOWN;
2386 goto out;
2387 }
2388
Johannes Berg79c97e92009-07-07 03:56:12 +02002389 err = rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002390
2391 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002392 cfg80211_unlock_rdev(rdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002393 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002394 out_rtnl:
2395 rtnl_unlock();
2396
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002397 return err;
2398}
2399
2400static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
2401{
Johannes Berg79c97e92009-07-07 03:56:12 +02002402 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002403 int err;
2404 struct net_device *dev;
2405 u8 *dst = NULL;
2406
2407 if (info->attrs[NL80211_ATTR_MAC])
2408 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2409
Johannes Berg3b858752009-03-12 09:55:09 +01002410 rtnl_lock();
2411
Johannes Berg463d0182009-07-14 00:33:35 +02002412 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002413 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002414 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002415
Johannes Berg79c97e92009-07-07 03:56:12 +02002416 if (!rdev->ops->del_mpath) {
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002417 err = -EOPNOTSUPP;
2418 goto out;
2419 }
2420
Johannes Berg79c97e92009-07-07 03:56:12 +02002421 err = rdev->ops->del_mpath(&rdev->wiphy, dev, dst);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002422
2423 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002424 cfg80211_unlock_rdev(rdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002425 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002426 out_rtnl:
2427 rtnl_unlock();
2428
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002429 return err;
2430}
2431
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002432static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
2433{
Johannes Berg79c97e92009-07-07 03:56:12 +02002434 struct cfg80211_registered_device *rdev;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002435 int err;
2436 struct net_device *dev;
2437 struct bss_parameters params;
2438
2439 memset(&params, 0, sizeof(params));
2440 /* default to not changing parameters */
2441 params.use_cts_prot = -1;
2442 params.use_short_preamble = -1;
2443 params.use_short_slot_time = -1;
2444
2445 if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
2446 params.use_cts_prot =
2447 nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
2448 if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
2449 params.use_short_preamble =
2450 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
2451 if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
2452 params.use_short_slot_time =
2453 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
Jouni Malinen90c97a02008-10-30 16:59:22 +02002454 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
2455 params.basic_rates =
2456 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2457 params.basic_rates_len =
2458 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2459 }
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002460
Johannes Berg3b858752009-03-12 09:55:09 +01002461 rtnl_lock();
2462
Johannes Berg463d0182009-07-14 00:33:35 +02002463 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002464 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002465 goto out_rtnl;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002466
Johannes Berg79c97e92009-07-07 03:56:12 +02002467 if (!rdev->ops->change_bss) {
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002468 err = -EOPNOTSUPP;
2469 goto out;
2470 }
2471
Jouni Malineneec60b02009-03-20 21:21:19 +02002472 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
2473 err = -EOPNOTSUPP;
2474 goto out;
2475 }
2476
Johannes Berg79c97e92009-07-07 03:56:12 +02002477 err = rdev->ops->change_bss(&rdev->wiphy, dev, &params);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002478
2479 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002480 cfg80211_unlock_rdev(rdev);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002481 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002482 out_rtnl:
2483 rtnl_unlock();
2484
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002485 return err;
2486}
2487
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00002488static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002489 [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
2490 [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
2491 [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
2492 [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
2493 [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
2494 [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
2495};
2496
2497static int parse_reg_rule(struct nlattr *tb[],
2498 struct ieee80211_reg_rule *reg_rule)
2499{
2500 struct ieee80211_freq_range *freq_range = &reg_rule->freq_range;
2501 struct ieee80211_power_rule *power_rule = &reg_rule->power_rule;
2502
2503 if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
2504 return -EINVAL;
2505 if (!tb[NL80211_ATTR_FREQ_RANGE_START])
2506 return -EINVAL;
2507 if (!tb[NL80211_ATTR_FREQ_RANGE_END])
2508 return -EINVAL;
2509 if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
2510 return -EINVAL;
2511 if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
2512 return -EINVAL;
2513
2514 reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
2515
2516 freq_range->start_freq_khz =
2517 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
2518 freq_range->end_freq_khz =
2519 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
2520 freq_range->max_bandwidth_khz =
2521 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
2522
2523 power_rule->max_eirp =
2524 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
2525
2526 if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
2527 power_rule->max_antenna_gain =
2528 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
2529
2530 return 0;
2531}
2532
2533static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
2534{
2535 int r;
2536 char *data = NULL;
2537
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002538 /*
2539 * You should only get this when cfg80211 hasn't yet initialized
2540 * completely when built-in to the kernel right between the time
2541 * window between nl80211_init() and regulatory_init(), if that is
2542 * even possible.
2543 */
2544 mutex_lock(&cfg80211_mutex);
2545 if (unlikely(!cfg80211_regdomain)) {
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002546 mutex_unlock(&cfg80211_mutex);
2547 return -EINPROGRESS;
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002548 }
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002549 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002550
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002551 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
2552 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002553
2554 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
2555
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002556 r = regulatory_hint_user(data);
2557
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002558 return r;
2559}
2560
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002561static int nl80211_get_mesh_params(struct sk_buff *skb,
2562 struct genl_info *info)
2563{
Johannes Berg79c97e92009-07-07 03:56:12 +02002564 struct cfg80211_registered_device *rdev;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002565 struct mesh_config cur_params;
2566 int err;
2567 struct net_device *dev;
2568 void *hdr;
2569 struct nlattr *pinfoattr;
2570 struct sk_buff *msg;
2571
Johannes Berg3b858752009-03-12 09:55:09 +01002572 rtnl_lock();
2573
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002574 /* Look up our device */
Johannes Berg463d0182009-07-14 00:33:35 +02002575 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002576 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002577 goto out_rtnl;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002578
Johannes Berg79c97e92009-07-07 03:56:12 +02002579 if (!rdev->ops->get_mesh_params) {
Jouni Malinenf3f92582009-03-20 17:57:36 +02002580 err = -EOPNOTSUPP;
2581 goto out;
2582 }
2583
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002584 /* Get the mesh params */
Johannes Berg79c97e92009-07-07 03:56:12 +02002585 err = rdev->ops->get_mesh_params(&rdev->wiphy, dev, &cur_params);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002586 if (err)
2587 goto out;
2588
2589 /* Draw up a netlink message to send back */
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002590 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002591 if (!msg) {
2592 err = -ENOBUFS;
2593 goto out;
2594 }
2595 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
2596 NL80211_CMD_GET_MESH_PARAMS);
2597 if (!hdr)
2598 goto nla_put_failure;
2599 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS);
2600 if (!pinfoattr)
2601 goto nla_put_failure;
2602 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2603 NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
2604 cur_params.dot11MeshRetryTimeout);
2605 NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
2606 cur_params.dot11MeshConfirmTimeout);
2607 NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
2608 cur_params.dot11MeshHoldingTimeout);
2609 NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
2610 cur_params.dot11MeshMaxPeerLinks);
2611 NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES,
2612 cur_params.dot11MeshMaxRetries);
2613 NLA_PUT_U8(msg, NL80211_MESHCONF_TTL,
2614 cur_params.dot11MeshTTL);
2615 NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
2616 cur_params.auto_open_plinks);
2617 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
2618 cur_params.dot11MeshHWMPmaxPREQretries);
2619 NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
2620 cur_params.path_refresh_time);
2621 NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
2622 cur_params.min_discovery_timeout);
2623 NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
2624 cur_params.dot11MeshHWMPactivePathTimeout);
2625 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
2626 cur_params.dot11MeshHWMPpreqMinInterval);
2627 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
2628 cur_params.dot11MeshHWMPnetDiameterTraversalTime);
Rui Paulo63c57232009-11-09 23:46:57 +00002629 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_ROOTMODE,
2630 cur_params.dot11MeshHWMPRootMode);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002631 nla_nest_end(msg, pinfoattr);
2632 genlmsg_end(msg, hdr);
Johannes Berg134e6372009-07-10 09:51:34 +00002633 err = genlmsg_reply(msg, info);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002634 goto out;
2635
Johannes Berg3b858752009-03-12 09:55:09 +01002636 nla_put_failure:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002637 genlmsg_cancel(msg, hdr);
2638 err = -EMSGSIZE;
Johannes Berg3b858752009-03-12 09:55:09 +01002639 out:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002640 /* Cleanup */
Johannes Berg79c97e92009-07-07 03:56:12 +02002641 cfg80211_unlock_rdev(rdev);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002642 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002643 out_rtnl:
2644 rtnl_unlock();
2645
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002646 return err;
2647}
2648
2649#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \
2650do {\
2651 if (table[attr_num]) {\
2652 cfg.param = nla_fn(table[attr_num]); \
2653 mask |= (1 << (attr_num - 1)); \
2654 } \
2655} while (0);\
2656
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00002657static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = {
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002658 [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
2659 [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
2660 [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
2661 [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
2662 [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
2663 [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
2664 [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
2665
2666 [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
2667 [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
2668 [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
2669 [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
2670 [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
2671 [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
2672};
2673
2674static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
2675{
2676 int err;
2677 u32 mask;
Johannes Berg79c97e92009-07-07 03:56:12 +02002678 struct cfg80211_registered_device *rdev;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002679 struct net_device *dev;
2680 struct mesh_config cfg;
2681 struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
2682 struct nlattr *parent_attr;
2683
2684 parent_attr = info->attrs[NL80211_ATTR_MESH_PARAMS];
2685 if (!parent_attr)
2686 return -EINVAL;
2687 if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
2688 parent_attr, nl80211_meshconf_params_policy))
2689 return -EINVAL;
2690
Johannes Berg3b858752009-03-12 09:55:09 +01002691 rtnl_lock();
2692
Johannes Berg463d0182009-07-14 00:33:35 +02002693 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002694 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002695 goto out_rtnl;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002696
Johannes Berg79c97e92009-07-07 03:56:12 +02002697 if (!rdev->ops->set_mesh_params) {
Jouni Malinenf3f92582009-03-20 17:57:36 +02002698 err = -EOPNOTSUPP;
2699 goto out;
2700 }
2701
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002702 /* This makes sure that there aren't more than 32 mesh config
2703 * parameters (otherwise our bitfield scheme would not work.) */
2704 BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
2705
2706 /* Fill in the params struct */
2707 mask = 0;
2708 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout,
2709 mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16);
2710 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout,
2711 mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16);
2712 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout,
2713 mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16);
2714 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks,
2715 mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16);
2716 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries,
2717 mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8);
2718 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL,
2719 mask, NL80211_MESHCONF_TTL, nla_get_u8);
2720 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
2721 mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8);
2722 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
2723 mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
2724 nla_get_u8);
2725 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time,
2726 mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32);
2727 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout,
2728 mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
2729 nla_get_u16);
2730 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
2731 mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
2732 nla_get_u32);
2733 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
2734 mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
2735 nla_get_u16);
2736 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
2737 dot11MeshHWMPnetDiameterTraversalTime,
2738 mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
2739 nla_get_u16);
Rui Paulo63c57232009-11-09 23:46:57 +00002740 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
2741 dot11MeshHWMPRootMode, mask,
2742 NL80211_MESHCONF_HWMP_ROOTMODE,
2743 nla_get_u8);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002744
2745 /* Apply changes */
Johannes Berg79c97e92009-07-07 03:56:12 +02002746 err = rdev->ops->set_mesh_params(&rdev->wiphy, dev, &cfg, mask);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002747
Jouni Malinenf3f92582009-03-20 17:57:36 +02002748 out:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002749 /* cleanup */
Johannes Berg79c97e92009-07-07 03:56:12 +02002750 cfg80211_unlock_rdev(rdev);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002751 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002752 out_rtnl:
2753 rtnl_unlock();
2754
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002755 return err;
2756}
2757
2758#undef FILL_IN_MESH_PARAM_IF_SET
2759
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002760static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
2761{
2762 struct sk_buff *msg;
2763 void *hdr = NULL;
2764 struct nlattr *nl_reg_rules;
2765 unsigned int i;
2766 int err = -EINVAL;
2767
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002768 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002769
2770 if (!cfg80211_regdomain)
2771 goto out;
2772
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002773 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002774 if (!msg) {
2775 err = -ENOBUFS;
2776 goto out;
2777 }
2778
2779 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
2780 NL80211_CMD_GET_REG);
2781 if (!hdr)
2782 goto nla_put_failure;
2783
2784 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2,
2785 cfg80211_regdomain->alpha2);
2786
2787 nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
2788 if (!nl_reg_rules)
2789 goto nla_put_failure;
2790
2791 for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) {
2792 struct nlattr *nl_reg_rule;
2793 const struct ieee80211_reg_rule *reg_rule;
2794 const struct ieee80211_freq_range *freq_range;
2795 const struct ieee80211_power_rule *power_rule;
2796
2797 reg_rule = &cfg80211_regdomain->reg_rules[i];
2798 freq_range = &reg_rule->freq_range;
2799 power_rule = &reg_rule->power_rule;
2800
2801 nl_reg_rule = nla_nest_start(msg, i);
2802 if (!nl_reg_rule)
2803 goto nla_put_failure;
2804
2805 NLA_PUT_U32(msg, NL80211_ATTR_REG_RULE_FLAGS,
2806 reg_rule->flags);
2807 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_START,
2808 freq_range->start_freq_khz);
2809 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_END,
2810 freq_range->end_freq_khz);
2811 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
2812 freq_range->max_bandwidth_khz);
2813 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
2814 power_rule->max_antenna_gain);
2815 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
2816 power_rule->max_eirp);
2817
2818 nla_nest_end(msg, nl_reg_rule);
2819 }
2820
2821 nla_nest_end(msg, nl_reg_rules);
2822
2823 genlmsg_end(msg, hdr);
Johannes Berg134e6372009-07-10 09:51:34 +00002824 err = genlmsg_reply(msg, info);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002825 goto out;
2826
2827nla_put_failure:
2828 genlmsg_cancel(msg, hdr);
2829 err = -EMSGSIZE;
2830out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002831 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002832 return err;
2833}
2834
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002835static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
2836{
2837 struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
2838 struct nlattr *nl_reg_rule;
2839 char *alpha2 = NULL;
2840 int rem_reg_rules = 0, r = 0;
2841 u32 num_rules = 0, rule_idx = 0, size_of_regd;
2842 struct ieee80211_regdomain *rd = NULL;
2843
2844 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
2845 return -EINVAL;
2846
2847 if (!info->attrs[NL80211_ATTR_REG_RULES])
2848 return -EINVAL;
2849
2850 alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
2851
2852 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
2853 rem_reg_rules) {
2854 num_rules++;
2855 if (num_rules > NL80211_MAX_SUPP_REG_RULES)
Luis R. Rodriguez4776c6e2009-05-13 17:04:39 -04002856 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002857 }
2858
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04002859 mutex_lock(&cfg80211_mutex);
2860
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002861 if (!reg_is_valid_request(alpha2)) {
2862 r = -EINVAL;
2863 goto bad_reg;
2864 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002865
2866 size_of_regd = sizeof(struct ieee80211_regdomain) +
2867 (num_rules * sizeof(struct ieee80211_reg_rule));
2868
2869 rd = kzalloc(size_of_regd, GFP_KERNEL);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002870 if (!rd) {
2871 r = -ENOMEM;
2872 goto bad_reg;
2873 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002874
2875 rd->n_reg_rules = num_rules;
2876 rd->alpha2[0] = alpha2[0];
2877 rd->alpha2[1] = alpha2[1];
2878
2879 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
2880 rem_reg_rules) {
2881 nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
2882 nla_data(nl_reg_rule), nla_len(nl_reg_rule),
2883 reg_rule_policy);
2884 r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
2885 if (r)
2886 goto bad_reg;
2887
2888 rule_idx++;
2889
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002890 if (rule_idx > NL80211_MAX_SUPP_REG_RULES) {
2891 r = -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002892 goto bad_reg;
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002893 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002894 }
2895
2896 BUG_ON(rule_idx != num_rules);
2897
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002898 r = set_regdom(rd);
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04002899
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002900 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002901
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002902 return r;
2903
Johannes Bergd2372b32008-10-24 20:32:20 +02002904 bad_reg:
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04002905 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002906 kfree(rd);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002907 return r;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002908}
2909
Johannes Berg83f5e2c2009-06-17 17:41:49 +02002910static int validate_scan_freqs(struct nlattr *freqs)
2911{
2912 struct nlattr *attr1, *attr2;
2913 int n_channels = 0, tmp1, tmp2;
2914
2915 nla_for_each_nested(attr1, freqs, tmp1) {
2916 n_channels++;
2917 /*
2918 * Some hardware has a limited channel list for
2919 * scanning, and it is pretty much nonsensical
2920 * to scan for a channel twice, so disallow that
2921 * and don't require drivers to check that the
2922 * channel list they get isn't longer than what
2923 * they can scan, as long as they can scan all
2924 * the channels they registered at once.
2925 */
2926 nla_for_each_nested(attr2, freqs, tmp2)
2927 if (attr1 != attr2 &&
2928 nla_get_u32(attr1) == nla_get_u32(attr2))
2929 return 0;
2930 }
2931
2932 return n_channels;
2933}
2934
Johannes Berg2a519312009-02-10 21:25:55 +01002935static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
2936{
Johannes Berg79c97e92009-07-07 03:56:12 +02002937 struct cfg80211_registered_device *rdev;
Johannes Berg2a519312009-02-10 21:25:55 +01002938 struct net_device *dev;
2939 struct cfg80211_scan_request *request;
2940 struct cfg80211_ssid *ssid;
2941 struct ieee80211_channel *channel;
2942 struct nlattr *attr;
2943 struct wiphy *wiphy;
Johannes Berg83f5e2c2009-06-17 17:41:49 +02002944 int err, tmp, n_ssids = 0, n_channels, i;
Johannes Berg2a519312009-02-10 21:25:55 +01002945 enum ieee80211_band band;
Jouni Malinen70692ad2009-02-16 19:39:13 +02002946 size_t ie_len;
Johannes Berg2a519312009-02-10 21:25:55 +01002947
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002948 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
2949 return -EINVAL;
2950
Johannes Berg3b858752009-03-12 09:55:09 +01002951 rtnl_lock();
2952
Johannes Berg463d0182009-07-14 00:33:35 +02002953 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg2a519312009-02-10 21:25:55 +01002954 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002955 goto out_rtnl;
Johannes Berg2a519312009-02-10 21:25:55 +01002956
Johannes Berg79c97e92009-07-07 03:56:12 +02002957 wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01002958
Johannes Berg79c97e92009-07-07 03:56:12 +02002959 if (!rdev->ops->scan) {
Johannes Berg2a519312009-02-10 21:25:55 +01002960 err = -EOPNOTSUPP;
2961 goto out;
2962 }
2963
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002964 if (!netif_running(dev)) {
2965 err = -ENETDOWN;
2966 goto out;
2967 }
2968
Johannes Berg79c97e92009-07-07 03:56:12 +02002969 if (rdev->scan_req) {
Johannes Berg2a519312009-02-10 21:25:55 +01002970 err = -EBUSY;
Johannes Berg3b858752009-03-12 09:55:09 +01002971 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002972 }
2973
2974 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
Johannes Berg83f5e2c2009-06-17 17:41:49 +02002975 n_channels = validate_scan_freqs(
2976 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
Johannes Berg2a519312009-02-10 21:25:55 +01002977 if (!n_channels) {
2978 err = -EINVAL;
Johannes Berg3b858752009-03-12 09:55:09 +01002979 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002980 }
2981 } else {
Johannes Berg83f5e2c2009-06-17 17:41:49 +02002982 n_channels = 0;
2983
Johannes Berg2a519312009-02-10 21:25:55 +01002984 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
2985 if (wiphy->bands[band])
2986 n_channels += wiphy->bands[band]->n_channels;
2987 }
2988
2989 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
2990 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
2991 n_ssids++;
2992
2993 if (n_ssids > wiphy->max_scan_ssids) {
2994 err = -EINVAL;
Johannes Berg3b858752009-03-12 09:55:09 +01002995 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002996 }
2997
Jouni Malinen70692ad2009-02-16 19:39:13 +02002998 if (info->attrs[NL80211_ATTR_IE])
2999 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3000 else
3001 ie_len = 0;
3002
Johannes Berg18a83652009-03-31 12:12:05 +02003003 if (ie_len > wiphy->max_scan_ie_len) {
3004 err = -EINVAL;
3005 goto out;
3006 }
3007
Johannes Berg2a519312009-02-10 21:25:55 +01003008 request = kzalloc(sizeof(*request)
3009 + sizeof(*ssid) * n_ssids
Jouni Malinen70692ad2009-02-16 19:39:13 +02003010 + sizeof(channel) * n_channels
3011 + ie_len, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01003012 if (!request) {
3013 err = -ENOMEM;
Johannes Berg3b858752009-03-12 09:55:09 +01003014 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01003015 }
3016
Johannes Berg2a519312009-02-10 21:25:55 +01003017 if (n_ssids)
Johannes Berg5ba63532009-08-07 17:54:07 +02003018 request->ssids = (void *)&request->channels[n_channels];
Johannes Berg2a519312009-02-10 21:25:55 +01003019 request->n_ssids = n_ssids;
Jouni Malinen70692ad2009-02-16 19:39:13 +02003020 if (ie_len) {
3021 if (request->ssids)
3022 request->ie = (void *)(request->ssids + n_ssids);
3023 else
3024 request->ie = (void *)(request->channels + n_channels);
3025 }
Johannes Berg2a519312009-02-10 21:25:55 +01003026
Johannes Berg584991d2009-11-02 13:32:03 +01003027 i = 0;
Johannes Berg2a519312009-02-10 21:25:55 +01003028 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
3029 /* user specified, bail out if channel not found */
Johannes Berg2a519312009-02-10 21:25:55 +01003030 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
Johannes Berg584991d2009-11-02 13:32:03 +01003031 struct ieee80211_channel *chan;
3032
3033 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
3034
3035 if (!chan) {
Johannes Berg2a519312009-02-10 21:25:55 +01003036 err = -EINVAL;
3037 goto out_free;
3038 }
Johannes Berg584991d2009-11-02 13:32:03 +01003039
3040 /* ignore disabled channels */
3041 if (chan->flags & IEEE80211_CHAN_DISABLED)
3042 continue;
3043
3044 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01003045 i++;
3046 }
3047 } else {
3048 /* all channels */
Johannes Berg2a519312009-02-10 21:25:55 +01003049 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
3050 int j;
3051 if (!wiphy->bands[band])
3052 continue;
3053 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
Johannes Berg584991d2009-11-02 13:32:03 +01003054 struct ieee80211_channel *chan;
3055
3056 chan = &wiphy->bands[band]->channels[j];
3057
3058 if (chan->flags & IEEE80211_CHAN_DISABLED)
3059 continue;
3060
3061 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01003062 i++;
3063 }
3064 }
3065 }
3066
Johannes Berg584991d2009-11-02 13:32:03 +01003067 if (!i) {
3068 err = -EINVAL;
3069 goto out_free;
3070 }
3071
3072 request->n_channels = i;
3073
Johannes Berg2a519312009-02-10 21:25:55 +01003074 i = 0;
3075 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
3076 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
3077 if (request->ssids[i].ssid_len > IEEE80211_MAX_SSID_LEN) {
3078 err = -EINVAL;
3079 goto out_free;
3080 }
3081 memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
3082 request->ssids[i].ssid_len = nla_len(attr);
3083 i++;
3084 }
3085 }
3086
Jouni Malinen70692ad2009-02-16 19:39:13 +02003087 if (info->attrs[NL80211_ATTR_IE]) {
3088 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Johannes Bergde95a542009-04-01 11:58:36 +02003089 memcpy((void *)request->ie,
3090 nla_data(info->attrs[NL80211_ATTR_IE]),
Jouni Malinen70692ad2009-02-16 19:39:13 +02003091 request->ie_len);
3092 }
3093
Johannes Berg463d0182009-07-14 00:33:35 +02003094 request->dev = dev;
Johannes Berg79c97e92009-07-07 03:56:12 +02003095 request->wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01003096
Johannes Berg79c97e92009-07-07 03:56:12 +02003097 rdev->scan_req = request;
3098 err = rdev->ops->scan(&rdev->wiphy, dev, request);
Johannes Berg2a519312009-02-10 21:25:55 +01003099
Johannes Berg463d0182009-07-14 00:33:35 +02003100 if (!err) {
Johannes Berg79c97e92009-07-07 03:56:12 +02003101 nl80211_send_scan_start(rdev, dev);
Johannes Berg463d0182009-07-14 00:33:35 +02003102 dev_hold(dev);
3103 }
Johannes Berga538e2d2009-06-16 19:56:42 +02003104
Johannes Berg2a519312009-02-10 21:25:55 +01003105 out_free:
3106 if (err) {
Johannes Berg79c97e92009-07-07 03:56:12 +02003107 rdev->scan_req = NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01003108 kfree(request);
3109 }
Johannes Berg2a519312009-02-10 21:25:55 +01003110 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003111 cfg80211_unlock_rdev(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003112 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01003113 out_rtnl:
3114 rtnl_unlock();
3115
Johannes Berg2a519312009-02-10 21:25:55 +01003116 return err;
3117}
3118
3119static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
3120 struct cfg80211_registered_device *rdev,
Johannes Berg48ab9052009-07-10 18:42:31 +02003121 struct wireless_dev *wdev,
3122 struct cfg80211_internal_bss *intbss)
Johannes Berg2a519312009-02-10 21:25:55 +01003123{
Johannes Berg48ab9052009-07-10 18:42:31 +02003124 struct cfg80211_bss *res = &intbss->pub;
Johannes Berg2a519312009-02-10 21:25:55 +01003125 void *hdr;
3126 struct nlattr *bss;
Johannes Berg48ab9052009-07-10 18:42:31 +02003127 int i;
3128
3129 ASSERT_WDEV_LOCK(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003130
3131 hdr = nl80211hdr_put(msg, pid, seq, flags,
3132 NL80211_CMD_NEW_SCAN_RESULTS);
3133 if (!hdr)
3134 return -1;
3135
Johannes Bergf5ea9122009-08-07 16:17:38 +02003136 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation);
Johannes Berg48ab9052009-07-10 18:42:31 +02003137 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex);
Johannes Berg2a519312009-02-10 21:25:55 +01003138
3139 bss = nla_nest_start(msg, NL80211_ATTR_BSS);
3140 if (!bss)
3141 goto nla_put_failure;
3142 if (!is_zero_ether_addr(res->bssid))
3143 NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid);
3144 if (res->information_elements && res->len_information_elements)
3145 NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS,
3146 res->len_information_elements,
3147 res->information_elements);
Jouni Malinen34a6edd2010-01-06 16:19:24 +02003148 if (res->beacon_ies && res->len_beacon_ies &&
3149 res->beacon_ies != res->information_elements)
3150 NLA_PUT(msg, NL80211_BSS_BEACON_IES,
3151 res->len_beacon_ies, res->beacon_ies);
Johannes Berg2a519312009-02-10 21:25:55 +01003152 if (res->tsf)
3153 NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf);
3154 if (res->beacon_interval)
3155 NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval);
3156 NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability);
3157 NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq);
Holger Schurig7c896062009-09-24 12:21:01 +02003158 NLA_PUT_U32(msg, NL80211_BSS_SEEN_MS_AGO,
3159 jiffies_to_msecs(jiffies - intbss->ts));
Johannes Berg2a519312009-02-10 21:25:55 +01003160
Johannes Berg77965c92009-02-18 18:45:06 +01003161 switch (rdev->wiphy.signal_type) {
Johannes Berg2a519312009-02-10 21:25:55 +01003162 case CFG80211_SIGNAL_TYPE_MBM:
3163 NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal);
3164 break;
3165 case CFG80211_SIGNAL_TYPE_UNSPEC:
3166 NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal);
3167 break;
3168 default:
3169 break;
3170 }
3171
Johannes Berg48ab9052009-07-10 18:42:31 +02003172 switch (wdev->iftype) {
3173 case NL80211_IFTYPE_STATION:
3174 if (intbss == wdev->current_bss)
3175 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
3176 NL80211_BSS_STATUS_ASSOCIATED);
3177 else for (i = 0; i < MAX_AUTH_BSSES; i++) {
3178 if (intbss != wdev->auth_bsses[i])
3179 continue;
3180 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
3181 NL80211_BSS_STATUS_AUTHENTICATED);
3182 break;
3183 }
3184 break;
3185 case NL80211_IFTYPE_ADHOC:
3186 if (intbss == wdev->current_bss)
3187 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
3188 NL80211_BSS_STATUS_IBSS_JOINED);
3189 break;
3190 default:
3191 break;
3192 }
3193
Johannes Berg2a519312009-02-10 21:25:55 +01003194 nla_nest_end(msg, bss);
3195
3196 return genlmsg_end(msg, hdr);
3197
3198 nla_put_failure:
3199 genlmsg_cancel(msg, hdr);
3200 return -EMSGSIZE;
3201}
3202
3203static int nl80211_dump_scan(struct sk_buff *skb,
3204 struct netlink_callback *cb)
3205{
Johannes Berg48ab9052009-07-10 18:42:31 +02003206 struct cfg80211_registered_device *rdev;
3207 struct net_device *dev;
Johannes Berg2a519312009-02-10 21:25:55 +01003208 struct cfg80211_internal_bss *scan;
Johannes Berg48ab9052009-07-10 18:42:31 +02003209 struct wireless_dev *wdev;
Johannes Berg2a519312009-02-10 21:25:55 +01003210 int ifidx = cb->args[0];
3211 int start = cb->args[1], idx = 0;
3212 int err;
3213
Holger Schuriga0438972009-11-11 11:30:02 +01003214 if (!ifidx)
3215 ifidx = nl80211_get_ifidx(cb);
3216 if (ifidx < 0)
3217 return ifidx;
3218 cb->args[0] = ifidx;
Johannes Berg2a519312009-02-10 21:25:55 +01003219
Johannes Berg463d0182009-07-14 00:33:35 +02003220 dev = dev_get_by_index(sock_net(skb->sk), ifidx);
Johannes Berg48ab9052009-07-10 18:42:31 +02003221 if (!dev)
Johannes Berg2a519312009-02-10 21:25:55 +01003222 return -ENODEV;
3223
Johannes Berg463d0182009-07-14 00:33:35 +02003224 rdev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Johannes Berg48ab9052009-07-10 18:42:31 +02003225 if (IS_ERR(rdev)) {
3226 err = PTR_ERR(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003227 goto out_put_netdev;
3228 }
3229
Johannes Berg48ab9052009-07-10 18:42:31 +02003230 wdev = dev->ieee80211_ptr;
Johannes Berg2a519312009-02-10 21:25:55 +01003231
Johannes Berg48ab9052009-07-10 18:42:31 +02003232 wdev_lock(wdev);
3233 spin_lock_bh(&rdev->bss_lock);
3234 cfg80211_bss_expire(rdev);
3235
3236 list_for_each_entry(scan, &rdev->bss_list, list) {
Johannes Berg2a519312009-02-10 21:25:55 +01003237 if (++idx <= start)
3238 continue;
3239 if (nl80211_send_bss(skb,
3240 NETLINK_CB(cb->skb).pid,
3241 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Berg48ab9052009-07-10 18:42:31 +02003242 rdev, wdev, scan) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01003243 idx--;
3244 goto out;
3245 }
3246 }
3247
3248 out:
Johannes Berg48ab9052009-07-10 18:42:31 +02003249 spin_unlock_bh(&rdev->bss_lock);
3250 wdev_unlock(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003251
3252 cb->args[1] = idx;
3253 err = skb->len;
Johannes Berg48ab9052009-07-10 18:42:31 +02003254 cfg80211_unlock_rdev(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003255 out_put_netdev:
Johannes Berg48ab9052009-07-10 18:42:31 +02003256 dev_put(dev);
Johannes Berg2a519312009-02-10 21:25:55 +01003257
3258 return err;
3259}
3260
Holger Schurig61fa7132009-11-11 12:25:40 +01003261static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq,
3262 int flags, struct net_device *dev,
3263 struct survey_info *survey)
3264{
3265 void *hdr;
3266 struct nlattr *infoattr;
3267
3268 /* Survey without a channel doesn't make sense */
3269 if (!survey->channel)
3270 return -EINVAL;
3271
3272 hdr = nl80211hdr_put(msg, pid, seq, flags,
3273 NL80211_CMD_NEW_SURVEY_RESULTS);
3274 if (!hdr)
3275 return -ENOMEM;
3276
3277 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
3278
3279 infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO);
3280 if (!infoattr)
3281 goto nla_put_failure;
3282
3283 NLA_PUT_U32(msg, NL80211_SURVEY_INFO_FREQUENCY,
3284 survey->channel->center_freq);
3285 if (survey->filled & SURVEY_INFO_NOISE_DBM)
3286 NLA_PUT_U8(msg, NL80211_SURVEY_INFO_NOISE,
3287 survey->noise);
3288
3289 nla_nest_end(msg, infoattr);
3290
3291 return genlmsg_end(msg, hdr);
3292
3293 nla_put_failure:
3294 genlmsg_cancel(msg, hdr);
3295 return -EMSGSIZE;
3296}
3297
3298static int nl80211_dump_survey(struct sk_buff *skb,
3299 struct netlink_callback *cb)
3300{
3301 struct survey_info survey;
3302 struct cfg80211_registered_device *dev;
3303 struct net_device *netdev;
3304 int ifidx = cb->args[0];
3305 int survey_idx = cb->args[1];
3306 int res;
3307
3308 if (!ifidx)
3309 ifidx = nl80211_get_ifidx(cb);
3310 if (ifidx < 0)
3311 return ifidx;
3312 cb->args[0] = ifidx;
3313
3314 rtnl_lock();
3315
3316 netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
3317 if (!netdev) {
3318 res = -ENODEV;
3319 goto out_rtnl;
3320 }
3321
3322 dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
3323 if (IS_ERR(dev)) {
3324 res = PTR_ERR(dev);
3325 goto out_rtnl;
3326 }
3327
3328 if (!dev->ops->dump_survey) {
3329 res = -EOPNOTSUPP;
3330 goto out_err;
3331 }
3332
3333 while (1) {
3334 res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx,
3335 &survey);
3336 if (res == -ENOENT)
3337 break;
3338 if (res)
3339 goto out_err;
3340
3341 if (nl80211_send_survey(skb,
3342 NETLINK_CB(cb->skb).pid,
3343 cb->nlh->nlmsg_seq, NLM_F_MULTI,
3344 netdev,
3345 &survey) < 0)
3346 goto out;
3347 survey_idx++;
3348 }
3349
3350 out:
3351 cb->args[1] = survey_idx;
3352 res = skb->len;
3353 out_err:
3354 cfg80211_unlock_rdev(dev);
3355 out_rtnl:
3356 rtnl_unlock();
3357
3358 return res;
3359}
3360
Jouni Malinen255e7372009-03-20 21:21:17 +02003361static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
3362{
Samuel Ortizb23aa672009-07-01 21:26:54 +02003363 return auth_type <= NL80211_AUTHTYPE_MAX;
Jouni Malinen255e7372009-03-20 21:21:17 +02003364}
3365
Samuel Ortizb23aa672009-07-01 21:26:54 +02003366static bool nl80211_valid_wpa_versions(u32 wpa_versions)
3367{
3368 return !(wpa_versions & ~(NL80211_WPA_VERSION_1 |
3369 NL80211_WPA_VERSION_2));
3370}
3371
3372static bool nl80211_valid_akm_suite(u32 akm)
3373{
3374 return akm == WLAN_AKM_SUITE_8021X ||
3375 akm == WLAN_AKM_SUITE_PSK;
3376}
3377
3378static bool nl80211_valid_cipher_suite(u32 cipher)
3379{
3380 return cipher == WLAN_CIPHER_SUITE_WEP40 ||
3381 cipher == WLAN_CIPHER_SUITE_WEP104 ||
3382 cipher == WLAN_CIPHER_SUITE_TKIP ||
3383 cipher == WLAN_CIPHER_SUITE_CCMP ||
3384 cipher == WLAN_CIPHER_SUITE_AES_CMAC;
3385}
3386
3387
Jouni Malinen636a5d32009-03-19 13:39:22 +02003388static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
3389{
Johannes Berg79c97e92009-07-07 03:56:12 +02003390 struct cfg80211_registered_device *rdev;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003391 struct net_device *dev;
Johannes Berg19957bb2009-07-02 17:20:43 +02003392 struct ieee80211_channel *chan;
3393 const u8 *bssid, *ssid, *ie = NULL;
3394 int err, ssid_len, ie_len = 0;
3395 enum nl80211_auth_type auth_type;
Johannes Bergfffd0932009-07-08 14:22:54 +02003396 struct key_parse key;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03003397 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003398
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003399 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3400 return -EINVAL;
3401
3402 if (!info->attrs[NL80211_ATTR_MAC])
3403 return -EINVAL;
3404
Jouni Malinen17780922009-03-27 20:52:47 +02003405 if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
3406 return -EINVAL;
3407
Johannes Berg19957bb2009-07-02 17:20:43 +02003408 if (!info->attrs[NL80211_ATTR_SSID])
3409 return -EINVAL;
3410
3411 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
3412 return -EINVAL;
3413
Johannes Bergfffd0932009-07-08 14:22:54 +02003414 err = nl80211_parse_key(info, &key);
3415 if (err)
3416 return err;
3417
3418 if (key.idx >= 0) {
3419 if (!key.p.key || !key.p.key_len)
3420 return -EINVAL;
3421 if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 ||
3422 key.p.key_len != WLAN_KEY_LEN_WEP40) &&
3423 (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 ||
3424 key.p.key_len != WLAN_KEY_LEN_WEP104))
3425 return -EINVAL;
3426 if (key.idx > 4)
3427 return -EINVAL;
3428 } else {
3429 key.p.key_len = 0;
3430 key.p.key = NULL;
3431 }
3432
Jouni Malinen636a5d32009-03-19 13:39:22 +02003433 rtnl_lock();
3434
Johannes Berg463d0182009-07-14 00:33:35 +02003435 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003436 if (err)
3437 goto unlock_rtnl;
3438
Johannes Berg79c97e92009-07-07 03:56:12 +02003439 if (!rdev->ops->auth) {
Jouni Malinen636a5d32009-03-19 13:39:22 +02003440 err = -EOPNOTSUPP;
3441 goto out;
3442 }
3443
Jouni Malineneec60b02009-03-20 21:21:19 +02003444 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
3445 err = -EOPNOTSUPP;
3446 goto out;
3447 }
3448
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003449 if (!netif_running(dev)) {
3450 err = -ENETDOWN;
3451 goto out;
3452 }
3453
Johannes Berg19957bb2009-07-02 17:20:43 +02003454 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg79c97e92009-07-07 03:56:12 +02003455 chan = ieee80211_get_channel(&rdev->wiphy,
Johannes Berg19957bb2009-07-02 17:20:43 +02003456 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
3457 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) {
Jouni Malinen17780922009-03-27 20:52:47 +02003458 err = -EINVAL;
3459 goto out;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003460 }
3461
Johannes Berg19957bb2009-07-02 17:20:43 +02003462 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3463 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
3464
3465 if (info->attrs[NL80211_ATTR_IE]) {
3466 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3467 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3468 }
3469
3470 auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
3471 if (!nl80211_valid_auth_type(auth_type)) {
3472 err = -EINVAL;
3473 goto out;
3474 }
3475
Jouni Malinend5cdfac2010-04-04 09:37:19 +03003476 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
3477
Johannes Berg79c97e92009-07-07 03:56:12 +02003478 err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
Johannes Bergfffd0932009-07-08 14:22:54 +02003479 ssid, ssid_len, ie, ie_len,
Jouni Malinend5cdfac2010-04-04 09:37:19 +03003480 key.p.key, key.p.key_len, key.idx,
3481 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003482
3483out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003484 cfg80211_unlock_rdev(rdev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003485 dev_put(dev);
3486unlock_rtnl:
3487 rtnl_unlock();
3488 return err;
3489}
3490
Samuel Ortizb23aa672009-07-01 21:26:54 +02003491static int nl80211_crypto_settings(struct genl_info *info,
Johannes Berg3dc27d22009-07-02 21:36:37 +02003492 struct cfg80211_crypto_settings *settings,
3493 int cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02003494{
Johannes Bergc0b2bbd2009-07-25 16:54:36 +02003495 memset(settings, 0, sizeof(*settings));
3496
Samuel Ortizb23aa672009-07-01 21:26:54 +02003497 settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT];
3498
3499 if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) {
3500 void *data;
3501 int len, i;
3502
3503 data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
3504 len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
3505 settings->n_ciphers_pairwise = len / sizeof(u32);
3506
3507 if (len % sizeof(u32))
3508 return -EINVAL;
3509
Johannes Berg3dc27d22009-07-02 21:36:37 +02003510 if (settings->n_ciphers_pairwise > cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02003511 return -EINVAL;
3512
3513 memcpy(settings->ciphers_pairwise, data, len);
3514
3515 for (i = 0; i < settings->n_ciphers_pairwise; i++)
3516 if (!nl80211_valid_cipher_suite(
3517 settings->ciphers_pairwise[i]))
3518 return -EINVAL;
3519 }
3520
3521 if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) {
3522 settings->cipher_group =
3523 nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]);
3524 if (!nl80211_valid_cipher_suite(settings->cipher_group))
3525 return -EINVAL;
3526 }
3527
3528 if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) {
3529 settings->wpa_versions =
3530 nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]);
3531 if (!nl80211_valid_wpa_versions(settings->wpa_versions))
3532 return -EINVAL;
3533 }
3534
3535 if (info->attrs[NL80211_ATTR_AKM_SUITES]) {
3536 void *data;
3537 int len, i;
3538
3539 data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]);
3540 len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]);
3541 settings->n_akm_suites = len / sizeof(u32);
3542
3543 if (len % sizeof(u32))
3544 return -EINVAL;
3545
3546 memcpy(settings->akm_suites, data, len);
3547
3548 for (i = 0; i < settings->n_ciphers_pairwise; i++)
3549 if (!nl80211_valid_akm_suite(settings->akm_suites[i]))
3550 return -EINVAL;
3551 }
3552
3553 return 0;
3554}
3555
Jouni Malinen636a5d32009-03-19 13:39:22 +02003556static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
3557{
Johannes Berg19957bb2009-07-02 17:20:43 +02003558 struct cfg80211_registered_device *rdev;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003559 struct net_device *dev;
Jouni Malinen27e310c2010-01-19 10:53:30 -08003560 struct wireless_dev *wdev;
Johannes Berg19957bb2009-07-02 17:20:43 +02003561 struct cfg80211_crypto_settings crypto;
Johannes Berg59bbb6f2009-08-07 17:22:35 +02003562 struct ieee80211_channel *chan, *fixedchan;
Johannes Berg3e5d7642009-07-07 14:37:26 +02003563 const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +02003564 int err, ssid_len, ie_len = 0;
3565 bool use_mfp = false;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003566
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003567 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3568 return -EINVAL;
3569
3570 if (!info->attrs[NL80211_ATTR_MAC] ||
Johannes Berg19957bb2009-07-02 17:20:43 +02003571 !info->attrs[NL80211_ATTR_SSID] ||
3572 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003573 return -EINVAL;
3574
Jouni Malinen636a5d32009-03-19 13:39:22 +02003575 rtnl_lock();
3576
Johannes Berg463d0182009-07-14 00:33:35 +02003577 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003578 if (err)
3579 goto unlock_rtnl;
3580
Johannes Berg19957bb2009-07-02 17:20:43 +02003581 if (!rdev->ops->assoc) {
Jouni Malinen636a5d32009-03-19 13:39:22 +02003582 err = -EOPNOTSUPP;
3583 goto out;
3584 }
3585
Jouni Malineneec60b02009-03-20 21:21:19 +02003586 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
3587 err = -EOPNOTSUPP;
3588 goto out;
3589 }
3590
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003591 if (!netif_running(dev)) {
3592 err = -ENETDOWN;
3593 goto out;
3594 }
3595
Johannes Berg19957bb2009-07-02 17:20:43 +02003596 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003597
Johannes Berg19957bb2009-07-02 17:20:43 +02003598 chan = ieee80211_get_channel(&rdev->wiphy,
3599 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
3600 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) {
3601 err = -EINVAL;
3602 goto out;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003603 }
3604
Johannes Berg59bbb6f2009-08-07 17:22:35 +02003605 mutex_lock(&rdev->devlist_mtx);
Jouni Malinen27e310c2010-01-19 10:53:30 -08003606 wdev = dev->ieee80211_ptr;
3607 fixedchan = rdev_fixed_channel(rdev, wdev);
Johannes Berg59bbb6f2009-08-07 17:22:35 +02003608 if (fixedchan && chan != fixedchan) {
3609 err = -EBUSY;
3610 mutex_unlock(&rdev->devlist_mtx);
3611 goto out;
3612 }
3613 mutex_unlock(&rdev->devlist_mtx);
3614
Johannes Berg19957bb2009-07-02 17:20:43 +02003615 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3616 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003617
3618 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02003619 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3620 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003621 }
3622
Jouni Malinendc6382c2009-05-06 22:09:37 +03003623 if (info->attrs[NL80211_ATTR_USE_MFP]) {
Johannes Berg4f5dadc2009-07-07 03:56:10 +02003624 enum nl80211_mfp mfp =
Jouni Malinendc6382c2009-05-06 22:09:37 +03003625 nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
Johannes Berg4f5dadc2009-07-07 03:56:10 +02003626 if (mfp == NL80211_MFP_REQUIRED)
Johannes Berg19957bb2009-07-02 17:20:43 +02003627 use_mfp = true;
Johannes Berg4f5dadc2009-07-07 03:56:10 +02003628 else if (mfp != NL80211_MFP_NO) {
Jouni Malinendc6382c2009-05-06 22:09:37 +03003629 err = -EINVAL;
3630 goto out;
3631 }
3632 }
3633
Johannes Berg3e5d7642009-07-07 14:37:26 +02003634 if (info->attrs[NL80211_ATTR_PREV_BSSID])
3635 prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
3636
Johannes Berg3dc27d22009-07-02 21:36:37 +02003637 err = nl80211_crypto_settings(info, &crypto, 1);
Samuel Ortizb23aa672009-07-01 21:26:54 +02003638 if (!err)
Johannes Berg3e5d7642009-07-07 14:37:26 +02003639 err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
3640 ssid, ssid_len, ie, ie_len, use_mfp,
Johannes Berg19957bb2009-07-02 17:20:43 +02003641 &crypto);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003642
3643out:
Johannes Berg4d0c8ae2009-07-07 03:56:09 +02003644 cfg80211_unlock_rdev(rdev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003645 dev_put(dev);
3646unlock_rtnl:
3647 rtnl_unlock();
3648 return err;
3649}
3650
3651static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
3652{
Johannes Berg79c97e92009-07-07 03:56:12 +02003653 struct cfg80211_registered_device *rdev;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003654 struct net_device *dev;
Johannes Berg19957bb2009-07-02 17:20:43 +02003655 const u8 *ie = NULL, *bssid;
3656 int err, ie_len = 0;
3657 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03003658 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003659
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003660 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3661 return -EINVAL;
3662
3663 if (!info->attrs[NL80211_ATTR_MAC])
3664 return -EINVAL;
3665
3666 if (!info->attrs[NL80211_ATTR_REASON_CODE])
3667 return -EINVAL;
3668
Jouni Malinen636a5d32009-03-19 13:39:22 +02003669 rtnl_lock();
3670
Johannes Berg463d0182009-07-14 00:33:35 +02003671 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003672 if (err)
3673 goto unlock_rtnl;
3674
Johannes Berg79c97e92009-07-07 03:56:12 +02003675 if (!rdev->ops->deauth) {
Jouni Malinen636a5d32009-03-19 13:39:22 +02003676 err = -EOPNOTSUPP;
3677 goto out;
3678 }
3679
Jouni Malineneec60b02009-03-20 21:21:19 +02003680 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
3681 err = -EOPNOTSUPP;
3682 goto out;
3683 }
3684
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003685 if (!netif_running(dev)) {
3686 err = -ENETDOWN;
3687 goto out;
3688 }
3689
Johannes Berg19957bb2009-07-02 17:20:43 +02003690 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003691
Johannes Berg19957bb2009-07-02 17:20:43 +02003692 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
3693 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003694 /* Reason Code 0 is reserved */
3695 err = -EINVAL;
3696 goto out;
Jouni Malinen255e7372009-03-20 21:21:17 +02003697 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02003698
3699 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02003700 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3701 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003702 }
3703
Jouni Malinend5cdfac2010-04-04 09:37:19 +03003704 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
3705
3706 err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
3707 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003708
3709out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003710 cfg80211_unlock_rdev(rdev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003711 dev_put(dev);
3712unlock_rtnl:
3713 rtnl_unlock();
3714 return err;
3715}
3716
3717static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
3718{
Johannes Berg79c97e92009-07-07 03:56:12 +02003719 struct cfg80211_registered_device *rdev;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003720 struct net_device *dev;
Johannes Berg19957bb2009-07-02 17:20:43 +02003721 const u8 *ie = NULL, *bssid;
3722 int err, ie_len = 0;
3723 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03003724 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003725
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003726 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3727 return -EINVAL;
3728
3729 if (!info->attrs[NL80211_ATTR_MAC])
3730 return -EINVAL;
3731
3732 if (!info->attrs[NL80211_ATTR_REASON_CODE])
3733 return -EINVAL;
3734
Jouni Malinen636a5d32009-03-19 13:39:22 +02003735 rtnl_lock();
3736
Johannes Berg463d0182009-07-14 00:33:35 +02003737 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003738 if (err)
3739 goto unlock_rtnl;
3740
Johannes Berg79c97e92009-07-07 03:56:12 +02003741 if (!rdev->ops->disassoc) {
Jouni Malinen636a5d32009-03-19 13:39:22 +02003742 err = -EOPNOTSUPP;
3743 goto out;
3744 }
3745
Jouni Malineneec60b02009-03-20 21:21:19 +02003746 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
3747 err = -EOPNOTSUPP;
3748 goto out;
3749 }
3750
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003751 if (!netif_running(dev)) {
3752 err = -ENETDOWN;
3753 goto out;
3754 }
3755
Johannes Berg19957bb2009-07-02 17:20:43 +02003756 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003757
Johannes Berg19957bb2009-07-02 17:20:43 +02003758 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
3759 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003760 /* Reason Code 0 is reserved */
3761 err = -EINVAL;
3762 goto out;
Jouni Malinen255e7372009-03-20 21:21:17 +02003763 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02003764
3765 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02003766 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3767 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003768 }
3769
Jouni Malinend5cdfac2010-04-04 09:37:19 +03003770 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
3771
3772 err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
3773 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003774
3775out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003776 cfg80211_unlock_rdev(rdev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003777 dev_put(dev);
3778unlock_rtnl:
3779 rtnl_unlock();
3780 return err;
3781}
3782
Johannes Berg04a773a2009-04-19 21:24:32 +02003783static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
3784{
Johannes Berg79c97e92009-07-07 03:56:12 +02003785 struct cfg80211_registered_device *rdev;
Johannes Berg04a773a2009-04-19 21:24:32 +02003786 struct net_device *dev;
3787 struct cfg80211_ibss_params ibss;
3788 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02003789 struct cfg80211_cached_keys *connkeys = NULL;
Johannes Berg04a773a2009-04-19 21:24:32 +02003790 int err;
3791
Johannes Berg8e30bc52009-04-22 17:45:38 +02003792 memset(&ibss, 0, sizeof(ibss));
3793
Johannes Berg04a773a2009-04-19 21:24:32 +02003794 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3795 return -EINVAL;
3796
3797 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
3798 !info->attrs[NL80211_ATTR_SSID] ||
3799 !nla_len(info->attrs[NL80211_ATTR_SSID]))
3800 return -EINVAL;
3801
Johannes Berg8e30bc52009-04-22 17:45:38 +02003802 ibss.beacon_interval = 100;
3803
3804 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
3805 ibss.beacon_interval =
3806 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
3807 if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000)
3808 return -EINVAL;
3809 }
3810
Johannes Berg04a773a2009-04-19 21:24:32 +02003811 rtnl_lock();
3812
Johannes Berg463d0182009-07-14 00:33:35 +02003813 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg04a773a2009-04-19 21:24:32 +02003814 if (err)
3815 goto unlock_rtnl;
3816
Johannes Berg79c97e92009-07-07 03:56:12 +02003817 if (!rdev->ops->join_ibss) {
Johannes Berg04a773a2009-04-19 21:24:32 +02003818 err = -EOPNOTSUPP;
3819 goto out;
3820 }
3821
3822 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
3823 err = -EOPNOTSUPP;
3824 goto out;
3825 }
3826
3827 if (!netif_running(dev)) {
3828 err = -ENETDOWN;
3829 goto out;
3830 }
3831
Johannes Berg79c97e92009-07-07 03:56:12 +02003832 wiphy = &rdev->wiphy;
Johannes Berg04a773a2009-04-19 21:24:32 +02003833
3834 if (info->attrs[NL80211_ATTR_MAC])
3835 ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
3836 ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3837 ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
3838
3839 if (info->attrs[NL80211_ATTR_IE]) {
3840 ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3841 ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3842 }
3843
3844 ibss.channel = ieee80211_get_channel(wiphy,
3845 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
3846 if (!ibss.channel ||
3847 ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
3848 ibss.channel->flags & IEEE80211_CHAN_DISABLED) {
3849 err = -EINVAL;
3850 goto out;
3851 }
3852
3853 ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
Johannes Bergfffd0932009-07-08 14:22:54 +02003854 ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
Johannes Berg04a773a2009-04-19 21:24:32 +02003855
Johannes Bergfffd0932009-07-08 14:22:54 +02003856 if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
3857 connkeys = nl80211_parse_connkeys(rdev,
3858 info->attrs[NL80211_ATTR_KEYS]);
3859 if (IS_ERR(connkeys)) {
3860 err = PTR_ERR(connkeys);
3861 connkeys = NULL;
3862 goto out;
3863 }
3864 }
3865
3866 err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
Johannes Berg04a773a2009-04-19 21:24:32 +02003867
3868out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003869 cfg80211_unlock_rdev(rdev);
Johannes Berg04a773a2009-04-19 21:24:32 +02003870 dev_put(dev);
3871unlock_rtnl:
Johannes Bergfffd0932009-07-08 14:22:54 +02003872 if (err)
3873 kfree(connkeys);
Johannes Berg04a773a2009-04-19 21:24:32 +02003874 rtnl_unlock();
3875 return err;
3876}
3877
3878static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
3879{
Johannes Berg79c97e92009-07-07 03:56:12 +02003880 struct cfg80211_registered_device *rdev;
Johannes Berg04a773a2009-04-19 21:24:32 +02003881 struct net_device *dev;
3882 int err;
3883
3884 rtnl_lock();
3885
Johannes Berg463d0182009-07-14 00:33:35 +02003886 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg04a773a2009-04-19 21:24:32 +02003887 if (err)
3888 goto unlock_rtnl;
3889
Johannes Berg79c97e92009-07-07 03:56:12 +02003890 if (!rdev->ops->leave_ibss) {
Johannes Berg04a773a2009-04-19 21:24:32 +02003891 err = -EOPNOTSUPP;
3892 goto out;
3893 }
3894
3895 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
3896 err = -EOPNOTSUPP;
3897 goto out;
3898 }
3899
3900 if (!netif_running(dev)) {
3901 err = -ENETDOWN;
3902 goto out;
3903 }
3904
Johannes Berg79c97e92009-07-07 03:56:12 +02003905 err = cfg80211_leave_ibss(rdev, dev, false);
Johannes Berg04a773a2009-04-19 21:24:32 +02003906
3907out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003908 cfg80211_unlock_rdev(rdev);
Johannes Berg04a773a2009-04-19 21:24:32 +02003909 dev_put(dev);
3910unlock_rtnl:
3911 rtnl_unlock();
3912 return err;
3913}
3914
Johannes Bergaff89a92009-07-01 21:26:51 +02003915#ifdef CONFIG_NL80211_TESTMODE
3916static struct genl_multicast_group nl80211_testmode_mcgrp = {
3917 .name = "testmode",
3918};
3919
3920static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
3921{
3922 struct cfg80211_registered_device *rdev;
3923 int err;
3924
3925 if (!info->attrs[NL80211_ATTR_TESTDATA])
3926 return -EINVAL;
3927
3928 rtnl_lock();
3929
3930 rdev = cfg80211_get_dev_from_info(info);
3931 if (IS_ERR(rdev)) {
3932 err = PTR_ERR(rdev);
3933 goto unlock_rtnl;
3934 }
3935
3936 err = -EOPNOTSUPP;
3937 if (rdev->ops->testmode_cmd) {
3938 rdev->testmode_info = info;
3939 err = rdev->ops->testmode_cmd(&rdev->wiphy,
3940 nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
3941 nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
3942 rdev->testmode_info = NULL;
3943 }
3944
Johannes Berg4d0c8ae2009-07-07 03:56:09 +02003945 cfg80211_unlock_rdev(rdev);
Johannes Bergaff89a92009-07-01 21:26:51 +02003946
3947 unlock_rtnl:
3948 rtnl_unlock();
3949 return err;
3950}
3951
3952static struct sk_buff *
3953__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
3954 int approxlen, u32 pid, u32 seq, gfp_t gfp)
3955{
3956 struct sk_buff *skb;
3957 void *hdr;
3958 struct nlattr *data;
3959
3960 skb = nlmsg_new(approxlen + 100, gfp);
3961 if (!skb)
3962 return NULL;
3963
3964 hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE);
3965 if (!hdr) {
3966 kfree_skb(skb);
3967 return NULL;
3968 }
3969
3970 NLA_PUT_U32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
3971 data = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
3972
3973 ((void **)skb->cb)[0] = rdev;
3974 ((void **)skb->cb)[1] = hdr;
3975 ((void **)skb->cb)[2] = data;
3976
3977 return skb;
3978
3979 nla_put_failure:
3980 kfree_skb(skb);
3981 return NULL;
3982}
3983
3984struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
3985 int approxlen)
3986{
3987 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
3988
3989 if (WARN_ON(!rdev->testmode_info))
3990 return NULL;
3991
3992 return __cfg80211_testmode_alloc_skb(rdev, approxlen,
3993 rdev->testmode_info->snd_pid,
3994 rdev->testmode_info->snd_seq,
3995 GFP_KERNEL);
3996}
3997EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb);
3998
3999int cfg80211_testmode_reply(struct sk_buff *skb)
4000{
4001 struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
4002 void *hdr = ((void **)skb->cb)[1];
4003 struct nlattr *data = ((void **)skb->cb)[2];
4004
4005 if (WARN_ON(!rdev->testmode_info)) {
4006 kfree_skb(skb);
4007 return -EINVAL;
4008 }
4009
4010 nla_nest_end(skb, data);
4011 genlmsg_end(skb, hdr);
4012 return genlmsg_reply(skb, rdev->testmode_info);
4013}
4014EXPORT_SYMBOL(cfg80211_testmode_reply);
4015
4016struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
4017 int approxlen, gfp_t gfp)
4018{
4019 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
4020
4021 return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp);
4022}
4023EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb);
4024
4025void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
4026{
4027 void *hdr = ((void **)skb->cb)[1];
4028 struct nlattr *data = ((void **)skb->cb)[2];
4029
4030 nla_nest_end(skb, data);
4031 genlmsg_end(skb, hdr);
4032 genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp);
4033}
4034EXPORT_SYMBOL(cfg80211_testmode_event);
4035#endif
4036
Samuel Ortizb23aa672009-07-01 21:26:54 +02004037static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
4038{
Johannes Berg79c97e92009-07-07 03:56:12 +02004039 struct cfg80211_registered_device *rdev;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004040 struct net_device *dev;
4041 struct cfg80211_connect_params connect;
4042 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02004043 struct cfg80211_cached_keys *connkeys = NULL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004044 int err;
4045
4046 memset(&connect, 0, sizeof(connect));
4047
4048 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4049 return -EINVAL;
4050
4051 if (!info->attrs[NL80211_ATTR_SSID] ||
4052 !nla_len(info->attrs[NL80211_ATTR_SSID]))
4053 return -EINVAL;
4054
4055 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
4056 connect.auth_type =
4057 nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
4058 if (!nl80211_valid_auth_type(connect.auth_type))
4059 return -EINVAL;
4060 } else
4061 connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
4062
4063 connect.privacy = info->attrs[NL80211_ATTR_PRIVACY];
4064
Johannes Berg3dc27d22009-07-02 21:36:37 +02004065 err = nl80211_crypto_settings(info, &connect.crypto,
4066 NL80211_MAX_NR_CIPHER_SUITES);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004067 if (err)
4068 return err;
4069 rtnl_lock();
4070
Johannes Berg463d0182009-07-14 00:33:35 +02004071 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004072 if (err)
4073 goto unlock_rtnl;
4074
4075 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
4076 err = -EOPNOTSUPP;
4077 goto out;
4078 }
4079
4080 if (!netif_running(dev)) {
4081 err = -ENETDOWN;
4082 goto out;
4083 }
4084
Johannes Berg79c97e92009-07-07 03:56:12 +02004085 wiphy = &rdev->wiphy;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004086
Samuel Ortizb23aa672009-07-01 21:26:54 +02004087 if (info->attrs[NL80211_ATTR_MAC])
4088 connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
4089 connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4090 connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4091
4092 if (info->attrs[NL80211_ATTR_IE]) {
4093 connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4094 connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4095 }
4096
4097 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
4098 connect.channel =
4099 ieee80211_get_channel(wiphy,
4100 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
4101 if (!connect.channel ||
4102 connect.channel->flags & IEEE80211_CHAN_DISABLED) {
4103 err = -EINVAL;
4104 goto out;
4105 }
4106 }
4107
Johannes Bergfffd0932009-07-08 14:22:54 +02004108 if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
4109 connkeys = nl80211_parse_connkeys(rdev,
4110 info->attrs[NL80211_ATTR_KEYS]);
4111 if (IS_ERR(connkeys)) {
4112 err = PTR_ERR(connkeys);
4113 connkeys = NULL;
4114 goto out;
4115 }
4116 }
4117
4118 err = cfg80211_connect(rdev, dev, &connect, connkeys);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004119
4120out:
Johannes Berg79c97e92009-07-07 03:56:12 +02004121 cfg80211_unlock_rdev(rdev);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004122 dev_put(dev);
4123unlock_rtnl:
Johannes Bergfffd0932009-07-08 14:22:54 +02004124 if (err)
4125 kfree(connkeys);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004126 rtnl_unlock();
4127 return err;
4128}
4129
4130static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
4131{
Johannes Berg79c97e92009-07-07 03:56:12 +02004132 struct cfg80211_registered_device *rdev;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004133 struct net_device *dev;
4134 int err;
4135 u16 reason;
4136
4137 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4138 reason = WLAN_REASON_DEAUTH_LEAVING;
4139 else
4140 reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4141
4142 if (reason == 0)
4143 return -EINVAL;
4144
4145 rtnl_lock();
4146
Johannes Berg463d0182009-07-14 00:33:35 +02004147 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004148 if (err)
4149 goto unlock_rtnl;
4150
4151 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
4152 err = -EOPNOTSUPP;
4153 goto out;
4154 }
4155
4156 if (!netif_running(dev)) {
4157 err = -ENETDOWN;
4158 goto out;
4159 }
4160
Johannes Berg79c97e92009-07-07 03:56:12 +02004161 err = cfg80211_disconnect(rdev, dev, reason, true);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004162
4163out:
Johannes Berg79c97e92009-07-07 03:56:12 +02004164 cfg80211_unlock_rdev(rdev);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004165 dev_put(dev);
4166unlock_rtnl:
4167 rtnl_unlock();
4168 return err;
4169}
4170
Johannes Berg463d0182009-07-14 00:33:35 +02004171static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
4172{
4173 struct cfg80211_registered_device *rdev;
4174 struct net *net;
4175 int err;
4176 u32 pid;
4177
4178 if (!info->attrs[NL80211_ATTR_PID])
4179 return -EINVAL;
4180
4181 pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
4182
4183 rtnl_lock();
4184
4185 rdev = cfg80211_get_dev_from_info(info);
4186 if (IS_ERR(rdev)) {
4187 err = PTR_ERR(rdev);
Johannes Berg8a8e05e2009-10-08 21:02:02 +02004188 goto out_rtnl;
Johannes Berg463d0182009-07-14 00:33:35 +02004189 }
4190
4191 net = get_net_ns_by_pid(pid);
4192 if (IS_ERR(net)) {
4193 err = PTR_ERR(net);
4194 goto out;
4195 }
4196
4197 err = 0;
4198
4199 /* check if anything to do */
4200 if (net_eq(wiphy_net(&rdev->wiphy), net))
4201 goto out_put_net;
4202
4203 err = cfg80211_switch_netns(rdev, net);
4204 out_put_net:
4205 put_net(net);
4206 out:
4207 cfg80211_unlock_rdev(rdev);
Johannes Berg8a8e05e2009-10-08 21:02:02 +02004208 out_rtnl:
Johannes Berg463d0182009-07-14 00:33:35 +02004209 rtnl_unlock();
4210 return err;
4211}
4212
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004213static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info)
4214{
4215 struct cfg80211_registered_device *rdev;
4216 int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev,
4217 struct cfg80211_pmksa *pmksa) = NULL;
4218 int err;
4219 struct net_device *dev;
4220 struct cfg80211_pmksa pmksa;
4221
4222 memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
4223
4224 if (!info->attrs[NL80211_ATTR_MAC])
4225 return -EINVAL;
4226
4227 if (!info->attrs[NL80211_ATTR_PMKID])
4228 return -EINVAL;
4229
4230 rtnl_lock();
4231
4232 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
4233 if (err)
4234 goto out_rtnl;
4235
4236 pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]);
4237 pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
4238
4239 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
4240 err = -EOPNOTSUPP;
4241 goto out;
4242 }
4243
4244 switch (info->genlhdr->cmd) {
4245 case NL80211_CMD_SET_PMKSA:
4246 rdev_ops = rdev->ops->set_pmksa;
4247 break;
4248 case NL80211_CMD_DEL_PMKSA:
4249 rdev_ops = rdev->ops->del_pmksa;
4250 break;
4251 default:
4252 WARN_ON(1);
4253 break;
4254 }
4255
4256 if (!rdev_ops) {
4257 err = -EOPNOTSUPP;
4258 goto out;
4259 }
4260
4261 err = rdev_ops(&rdev->wiphy, dev, &pmksa);
4262
4263 out:
4264 cfg80211_unlock_rdev(rdev);
4265 dev_put(dev);
4266 out_rtnl:
4267 rtnl_unlock();
4268
4269 return err;
4270}
4271
4272static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info)
4273{
4274 struct cfg80211_registered_device *rdev;
4275 int err;
4276 struct net_device *dev;
4277
4278 rtnl_lock();
4279
4280 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
4281 if (err)
4282 goto out_rtnl;
4283
4284 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
4285 err = -EOPNOTSUPP;
4286 goto out;
4287 }
4288
4289 if (!rdev->ops->flush_pmksa) {
4290 err = -EOPNOTSUPP;
4291 goto out;
4292 }
4293
4294 err = rdev->ops->flush_pmksa(&rdev->wiphy, dev);
4295
4296 out:
4297 cfg80211_unlock_rdev(rdev);
4298 dev_put(dev);
4299 out_rtnl:
4300 rtnl_unlock();
4301
4302 return err;
4303
4304}
4305
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004306static int nl80211_remain_on_channel(struct sk_buff *skb,
4307 struct genl_info *info)
4308{
4309 struct cfg80211_registered_device *rdev;
4310 struct net_device *dev;
4311 struct ieee80211_channel *chan;
4312 struct sk_buff *msg;
4313 void *hdr;
4314 u64 cookie;
4315 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
4316 u32 freq, duration;
4317 int err;
4318
4319 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
4320 !info->attrs[NL80211_ATTR_DURATION])
4321 return -EINVAL;
4322
4323 duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
4324
4325 /*
4326 * We should be on that channel for at least one jiffie,
4327 * and more than 5 seconds seems excessive.
4328 */
4329 if (!duration || !msecs_to_jiffies(duration) || duration > 5000)
4330 return -EINVAL;
4331
4332 rtnl_lock();
4333
4334 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
4335 if (err)
4336 goto unlock_rtnl;
4337
4338 if (!rdev->ops->remain_on_channel) {
4339 err = -EOPNOTSUPP;
4340 goto out;
4341 }
4342
4343 if (!netif_running(dev)) {
4344 err = -ENETDOWN;
4345 goto out;
4346 }
4347
4348 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
4349 channel_type = nla_get_u32(
4350 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
4351 if (channel_type != NL80211_CHAN_NO_HT &&
4352 channel_type != NL80211_CHAN_HT20 &&
4353 channel_type != NL80211_CHAN_HT40PLUS &&
4354 channel_type != NL80211_CHAN_HT40MINUS)
4355 err = -EINVAL;
4356 goto out;
4357 }
4358
4359 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
4360 chan = rdev_freq_to_chan(rdev, freq, channel_type);
4361 if (chan == NULL) {
4362 err = -EINVAL;
4363 goto out;
4364 }
4365
4366 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
4367 if (!msg) {
4368 err = -ENOMEM;
4369 goto out;
4370 }
4371
4372 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
4373 NL80211_CMD_REMAIN_ON_CHANNEL);
4374
4375 if (IS_ERR(hdr)) {
4376 err = PTR_ERR(hdr);
4377 goto free_msg;
4378 }
4379
4380 err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan,
4381 channel_type, duration, &cookie);
4382
4383 if (err)
4384 goto free_msg;
4385
4386 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
4387
4388 genlmsg_end(msg, hdr);
4389 err = genlmsg_reply(msg, info);
4390 goto out;
4391
4392 nla_put_failure:
4393 err = -ENOBUFS;
4394 free_msg:
4395 nlmsg_free(msg);
4396 out:
4397 cfg80211_unlock_rdev(rdev);
4398 dev_put(dev);
4399 unlock_rtnl:
4400 rtnl_unlock();
4401 return err;
4402}
4403
4404static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,
4405 struct genl_info *info)
4406{
4407 struct cfg80211_registered_device *rdev;
4408 struct net_device *dev;
4409 u64 cookie;
4410 int err;
4411
4412 if (!info->attrs[NL80211_ATTR_COOKIE])
4413 return -EINVAL;
4414
4415 rtnl_lock();
4416
4417 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
4418 if (err)
4419 goto unlock_rtnl;
4420
4421 if (!rdev->ops->cancel_remain_on_channel) {
4422 err = -EOPNOTSUPP;
4423 goto out;
4424 }
4425
4426 if (!netif_running(dev)) {
4427 err = -ENETDOWN;
4428 goto out;
4429 }
4430
4431 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
4432
4433 err = rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie);
4434
4435 out:
4436 cfg80211_unlock_rdev(rdev);
4437 dev_put(dev);
4438 unlock_rtnl:
4439 rtnl_unlock();
4440 return err;
4441}
4442
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004443static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
4444 u8 *rates, u8 rates_len)
4445{
4446 u8 i;
4447 u32 mask = 0;
4448
4449 for (i = 0; i < rates_len; i++) {
4450 int rate = (rates[i] & 0x7f) * 5;
4451 int ridx;
4452 for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
4453 struct ieee80211_rate *srate =
4454 &sband->bitrates[ridx];
4455 if (rate == srate->bitrate) {
4456 mask |= 1 << ridx;
4457 break;
4458 }
4459 }
4460 if (ridx == sband->n_bitrates)
4461 return 0; /* rate not found */
4462 }
4463
4464 return mask;
4465}
4466
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00004467static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004468 [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
4469 .len = NL80211_MAX_SUPP_RATES },
4470};
4471
4472static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
4473 struct genl_info *info)
4474{
4475 struct nlattr *tb[NL80211_TXRATE_MAX + 1];
4476 struct cfg80211_registered_device *rdev;
4477 struct cfg80211_bitrate_mask mask;
4478 int err, rem, i;
4479 struct net_device *dev;
4480 struct nlattr *tx_rates;
4481 struct ieee80211_supported_band *sband;
4482
4483 if (info->attrs[NL80211_ATTR_TX_RATES] == NULL)
4484 return -EINVAL;
4485
4486 rtnl_lock();
4487
4488 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
4489 if (err)
4490 goto unlock_rtnl;
4491
4492 if (!rdev->ops->set_bitrate_mask) {
4493 err = -EOPNOTSUPP;
4494 goto unlock;
4495 }
4496
4497 memset(&mask, 0, sizeof(mask));
4498 /* Default to all rates enabled */
4499 for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
4500 sband = rdev->wiphy.bands[i];
4501 mask.control[i].legacy =
4502 sband ? (1 << sband->n_bitrates) - 1 : 0;
4503 }
4504
4505 /*
4506 * The nested attribute uses enum nl80211_band as the index. This maps
4507 * directly to the enum ieee80211_band values used in cfg80211.
4508 */
4509 nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem)
4510 {
4511 enum ieee80211_band band = nla_type(tx_rates);
4512 if (band < 0 || band >= IEEE80211_NUM_BANDS) {
4513 err = -EINVAL;
4514 goto unlock;
4515 }
4516 sband = rdev->wiphy.bands[band];
4517 if (sband == NULL) {
4518 err = -EINVAL;
4519 goto unlock;
4520 }
4521 nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
4522 nla_len(tx_rates), nl80211_txattr_policy);
4523 if (tb[NL80211_TXRATE_LEGACY]) {
4524 mask.control[band].legacy = rateset_to_mask(
4525 sband,
4526 nla_data(tb[NL80211_TXRATE_LEGACY]),
4527 nla_len(tb[NL80211_TXRATE_LEGACY]));
4528 if (mask.control[band].legacy == 0) {
4529 err = -EINVAL;
4530 goto unlock;
4531 }
4532 }
4533 }
4534
4535 err = rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask);
4536
4537 unlock:
4538 dev_put(dev);
4539 cfg80211_unlock_rdev(rdev);
4540 unlock_rtnl:
4541 rtnl_unlock();
4542 return err;
4543}
4544
Jouni Malinen026331c2010-02-15 12:53:10 +02004545static int nl80211_register_action(struct sk_buff *skb, struct genl_info *info)
4546{
4547 struct cfg80211_registered_device *rdev;
4548 struct net_device *dev;
4549 int err;
4550
4551 if (!info->attrs[NL80211_ATTR_FRAME_MATCH])
4552 return -EINVAL;
4553
4554 if (nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]) < 1)
4555 return -EINVAL;
4556
4557 rtnl_lock();
4558
4559 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
4560 if (err)
4561 goto unlock_rtnl;
4562
4563 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
4564 err = -EOPNOTSUPP;
4565 goto out;
4566 }
4567
4568 /* not much point in registering if we can't reply */
4569 if (!rdev->ops->action) {
4570 err = -EOPNOTSUPP;
4571 goto out;
4572 }
4573
4574 err = cfg80211_mlme_register_action(dev->ieee80211_ptr, info->snd_pid,
4575 nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]),
4576 nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]));
4577 out:
4578 cfg80211_unlock_rdev(rdev);
4579 dev_put(dev);
4580 unlock_rtnl:
4581 rtnl_unlock();
4582 return err;
4583}
4584
4585static int nl80211_action(struct sk_buff *skb, struct genl_info *info)
4586{
4587 struct cfg80211_registered_device *rdev;
4588 struct net_device *dev;
4589 struct ieee80211_channel *chan;
4590 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
4591 u32 freq;
4592 int err;
4593 void *hdr;
4594 u64 cookie;
4595 struct sk_buff *msg;
4596
4597 if (!info->attrs[NL80211_ATTR_FRAME] ||
4598 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
4599 return -EINVAL;
4600
4601 rtnl_lock();
4602
4603 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
4604 if (err)
4605 goto unlock_rtnl;
4606
4607 if (!rdev->ops->action) {
4608 err = -EOPNOTSUPP;
4609 goto out;
4610 }
4611
4612 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
4613 err = -EOPNOTSUPP;
4614 goto out;
4615 }
4616
4617 if (!netif_running(dev)) {
4618 err = -ENETDOWN;
4619 goto out;
4620 }
4621
4622 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
4623 channel_type = nla_get_u32(
4624 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
4625 if (channel_type != NL80211_CHAN_NO_HT &&
4626 channel_type != NL80211_CHAN_HT20 &&
4627 channel_type != NL80211_CHAN_HT40PLUS &&
4628 channel_type != NL80211_CHAN_HT40MINUS)
4629 err = -EINVAL;
4630 goto out;
4631 }
4632
4633 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
4634 chan = rdev_freq_to_chan(rdev, freq, channel_type);
4635 if (chan == NULL) {
4636 err = -EINVAL;
4637 goto out;
4638 }
4639
4640 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
4641 if (!msg) {
4642 err = -ENOMEM;
4643 goto out;
4644 }
4645
4646 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
4647 NL80211_CMD_ACTION);
4648
4649 if (IS_ERR(hdr)) {
4650 err = PTR_ERR(hdr);
4651 goto free_msg;
4652 }
4653 err = cfg80211_mlme_action(rdev, dev, chan, channel_type,
4654 nla_data(info->attrs[NL80211_ATTR_FRAME]),
4655 nla_len(info->attrs[NL80211_ATTR_FRAME]),
4656 &cookie);
4657 if (err)
4658 goto free_msg;
4659
4660 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
4661
4662 genlmsg_end(msg, hdr);
4663 err = genlmsg_reply(msg, info);
4664 goto out;
4665
4666 nla_put_failure:
4667 err = -ENOBUFS;
4668 free_msg:
4669 nlmsg_free(msg);
4670 out:
4671 cfg80211_unlock_rdev(rdev);
4672 dev_put(dev);
4673unlock_rtnl:
4674 rtnl_unlock();
4675 return err;
4676}
4677
Kalle Valoffb9eb32010-02-17 17:58:10 +02004678static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info)
4679{
4680 struct cfg80211_registered_device *rdev;
4681 struct wireless_dev *wdev;
4682 struct net_device *dev;
4683 u8 ps_state;
4684 bool state;
4685 int err;
4686
4687 if (!info->attrs[NL80211_ATTR_PS_STATE]) {
4688 err = -EINVAL;
4689 goto out;
4690 }
4691
4692 ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]);
4693
4694 if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED) {
4695 err = -EINVAL;
4696 goto out;
4697 }
4698
4699 rtnl_lock();
4700
4701 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
4702 if (err)
4703 goto unlock_rdev;
4704
4705 wdev = dev->ieee80211_ptr;
4706
4707 if (!rdev->ops->set_power_mgmt) {
4708 err = -EOPNOTSUPP;
4709 goto unlock_rdev;
4710 }
4711
4712 state = (ps_state == NL80211_PS_ENABLED) ? true : false;
4713
4714 if (state == wdev->ps)
4715 goto unlock_rdev;
4716
4717 wdev->ps = state;
4718
4719 if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, wdev->ps,
4720 wdev->ps_timeout))
4721 /* assume this means it's off */
4722 wdev->ps = false;
4723
4724unlock_rdev:
4725 cfg80211_unlock_rdev(rdev);
4726 dev_put(dev);
4727 rtnl_unlock();
4728
4729out:
4730 return err;
4731}
4732
4733static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)
4734{
4735 struct cfg80211_registered_device *rdev;
4736 enum nl80211_ps_state ps_state;
4737 struct wireless_dev *wdev;
4738 struct net_device *dev;
4739 struct sk_buff *msg;
4740 void *hdr;
4741 int err;
4742
4743 rtnl_lock();
4744
4745 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
4746 if (err)
4747 goto unlock_rtnl;
4748
4749 wdev = dev->ieee80211_ptr;
4750
4751 if (!rdev->ops->set_power_mgmt) {
4752 err = -EOPNOTSUPP;
4753 goto out;
4754 }
4755
4756 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
4757 if (!msg) {
4758 err = -ENOMEM;
4759 goto out;
4760 }
4761
4762 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
4763 NL80211_CMD_GET_POWER_SAVE);
4764 if (!hdr) {
4765 err = -ENOMEM;
4766 goto free_msg;
4767 }
4768
4769 if (wdev->ps)
4770 ps_state = NL80211_PS_ENABLED;
4771 else
4772 ps_state = NL80211_PS_DISABLED;
4773
4774 NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state);
4775
4776 genlmsg_end(msg, hdr);
4777 err = genlmsg_reply(msg, info);
4778 goto out;
4779
4780nla_put_failure:
4781 err = -ENOBUFS;
4782
4783free_msg:
4784 nlmsg_free(msg);
4785
4786out:
4787 cfg80211_unlock_rdev(rdev);
4788 dev_put(dev);
4789
4790unlock_rtnl:
4791 rtnl_unlock();
4792
4793 return err;
4794}
4795
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02004796static struct nla_policy
4797nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
4798 [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
4799 [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
4800 [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
4801};
4802
4803static int nl80211_set_cqm_rssi(struct genl_info *info,
4804 s32 threshold, u32 hysteresis)
4805{
4806 struct cfg80211_registered_device *rdev;
4807 struct wireless_dev *wdev;
4808 struct net_device *dev;
4809 int err;
4810
4811 if (threshold > 0)
4812 return -EINVAL;
4813
4814 rtnl_lock();
4815
4816 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
4817 if (err)
4818 goto unlock_rdev;
4819
4820 wdev = dev->ieee80211_ptr;
4821
4822 if (!rdev->ops->set_cqm_rssi_config) {
4823 err = -EOPNOTSUPP;
4824 goto unlock_rdev;
4825 }
4826
4827 if (wdev->iftype != NL80211_IFTYPE_STATION) {
4828 err = -EOPNOTSUPP;
4829 goto unlock_rdev;
4830 }
4831
4832 err = rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev,
4833 threshold, hysteresis);
4834
4835unlock_rdev:
4836 cfg80211_unlock_rdev(rdev);
4837 dev_put(dev);
4838 rtnl_unlock();
4839
4840 return err;
4841}
4842
4843static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
4844{
4845 struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1];
4846 struct nlattr *cqm;
4847 int err;
4848
4849 cqm = info->attrs[NL80211_ATTR_CQM];
4850 if (!cqm) {
4851 err = -EINVAL;
4852 goto out;
4853 }
4854
4855 err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
4856 nl80211_attr_cqm_policy);
4857 if (err)
4858 goto out;
4859
4860 if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
4861 attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
4862 s32 threshold;
4863 u32 hysteresis;
4864 threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
4865 hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
4866 err = nl80211_set_cqm_rssi(info, threshold, hysteresis);
4867 } else
4868 err = -EINVAL;
4869
4870out:
4871 return err;
4872}
4873
Johannes Berg55682962007-09-20 13:09:35 -04004874static struct genl_ops nl80211_ops[] = {
4875 {
4876 .cmd = NL80211_CMD_GET_WIPHY,
4877 .doit = nl80211_get_wiphy,
4878 .dumpit = nl80211_dump_wiphy,
4879 .policy = nl80211_policy,
4880 /* can be retrieved by unprivileged users */
4881 },
4882 {
4883 .cmd = NL80211_CMD_SET_WIPHY,
4884 .doit = nl80211_set_wiphy,
4885 .policy = nl80211_policy,
4886 .flags = GENL_ADMIN_PERM,
4887 },
4888 {
4889 .cmd = NL80211_CMD_GET_INTERFACE,
4890 .doit = nl80211_get_interface,
4891 .dumpit = nl80211_dump_interface,
4892 .policy = nl80211_policy,
4893 /* can be retrieved by unprivileged users */
4894 },
4895 {
4896 .cmd = NL80211_CMD_SET_INTERFACE,
4897 .doit = nl80211_set_interface,
4898 .policy = nl80211_policy,
4899 .flags = GENL_ADMIN_PERM,
4900 },
4901 {
4902 .cmd = NL80211_CMD_NEW_INTERFACE,
4903 .doit = nl80211_new_interface,
4904 .policy = nl80211_policy,
4905 .flags = GENL_ADMIN_PERM,
4906 },
4907 {
4908 .cmd = NL80211_CMD_DEL_INTERFACE,
4909 .doit = nl80211_del_interface,
4910 .policy = nl80211_policy,
4911 .flags = GENL_ADMIN_PERM,
4912 },
Johannes Berg41ade002007-12-19 02:03:29 +01004913 {
4914 .cmd = NL80211_CMD_GET_KEY,
4915 .doit = nl80211_get_key,
4916 .policy = nl80211_policy,
4917 .flags = GENL_ADMIN_PERM,
4918 },
4919 {
4920 .cmd = NL80211_CMD_SET_KEY,
4921 .doit = nl80211_set_key,
4922 .policy = nl80211_policy,
4923 .flags = GENL_ADMIN_PERM,
4924 },
4925 {
4926 .cmd = NL80211_CMD_NEW_KEY,
4927 .doit = nl80211_new_key,
4928 .policy = nl80211_policy,
4929 .flags = GENL_ADMIN_PERM,
4930 },
4931 {
4932 .cmd = NL80211_CMD_DEL_KEY,
4933 .doit = nl80211_del_key,
4934 .policy = nl80211_policy,
4935 .flags = GENL_ADMIN_PERM,
4936 },
Johannes Berged1b6cc2007-12-19 02:03:32 +01004937 {
4938 .cmd = NL80211_CMD_SET_BEACON,
4939 .policy = nl80211_policy,
4940 .flags = GENL_ADMIN_PERM,
4941 .doit = nl80211_addset_beacon,
4942 },
4943 {
4944 .cmd = NL80211_CMD_NEW_BEACON,
4945 .policy = nl80211_policy,
4946 .flags = GENL_ADMIN_PERM,
4947 .doit = nl80211_addset_beacon,
4948 },
4949 {
4950 .cmd = NL80211_CMD_DEL_BEACON,
4951 .policy = nl80211_policy,
4952 .flags = GENL_ADMIN_PERM,
4953 .doit = nl80211_del_beacon,
4954 },
Johannes Berg5727ef12007-12-19 02:03:34 +01004955 {
4956 .cmd = NL80211_CMD_GET_STATION,
4957 .doit = nl80211_get_station,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004958 .dumpit = nl80211_dump_station,
Johannes Berg5727ef12007-12-19 02:03:34 +01004959 .policy = nl80211_policy,
Johannes Berg5727ef12007-12-19 02:03:34 +01004960 },
4961 {
4962 .cmd = NL80211_CMD_SET_STATION,
4963 .doit = nl80211_set_station,
4964 .policy = nl80211_policy,
4965 .flags = GENL_ADMIN_PERM,
4966 },
4967 {
4968 .cmd = NL80211_CMD_NEW_STATION,
4969 .doit = nl80211_new_station,
4970 .policy = nl80211_policy,
4971 .flags = GENL_ADMIN_PERM,
4972 },
4973 {
4974 .cmd = NL80211_CMD_DEL_STATION,
4975 .doit = nl80211_del_station,
4976 .policy = nl80211_policy,
4977 .flags = GENL_ADMIN_PERM,
4978 },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004979 {
4980 .cmd = NL80211_CMD_GET_MPATH,
4981 .doit = nl80211_get_mpath,
4982 .dumpit = nl80211_dump_mpath,
4983 .policy = nl80211_policy,
4984 .flags = GENL_ADMIN_PERM,
4985 },
4986 {
4987 .cmd = NL80211_CMD_SET_MPATH,
4988 .doit = nl80211_set_mpath,
4989 .policy = nl80211_policy,
4990 .flags = GENL_ADMIN_PERM,
4991 },
4992 {
4993 .cmd = NL80211_CMD_NEW_MPATH,
4994 .doit = nl80211_new_mpath,
4995 .policy = nl80211_policy,
4996 .flags = GENL_ADMIN_PERM,
4997 },
4998 {
4999 .cmd = NL80211_CMD_DEL_MPATH,
5000 .doit = nl80211_del_mpath,
5001 .policy = nl80211_policy,
5002 .flags = GENL_ADMIN_PERM,
5003 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +03005004 {
5005 .cmd = NL80211_CMD_SET_BSS,
5006 .doit = nl80211_set_bss,
5007 .policy = nl80211_policy,
5008 .flags = GENL_ADMIN_PERM,
5009 },
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07005010 {
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08005011 .cmd = NL80211_CMD_GET_REG,
5012 .doit = nl80211_get_reg,
5013 .policy = nl80211_policy,
5014 /* can be retrieved by unprivileged users */
5015 },
5016 {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07005017 .cmd = NL80211_CMD_SET_REG,
5018 .doit = nl80211_set_reg,
5019 .policy = nl80211_policy,
5020 .flags = GENL_ADMIN_PERM,
5021 },
5022 {
5023 .cmd = NL80211_CMD_REQ_SET_REG,
5024 .doit = nl80211_req_set_reg,
5025 .policy = nl80211_policy,
5026 .flags = GENL_ADMIN_PERM,
5027 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07005028 {
5029 .cmd = NL80211_CMD_GET_MESH_PARAMS,
5030 .doit = nl80211_get_mesh_params,
5031 .policy = nl80211_policy,
5032 /* can be retrieved by unprivileged users */
5033 },
5034 {
5035 .cmd = NL80211_CMD_SET_MESH_PARAMS,
5036 .doit = nl80211_set_mesh_params,
5037 .policy = nl80211_policy,
5038 .flags = GENL_ADMIN_PERM,
5039 },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +02005040 {
Johannes Berg2a519312009-02-10 21:25:55 +01005041 .cmd = NL80211_CMD_TRIGGER_SCAN,
5042 .doit = nl80211_trigger_scan,
5043 .policy = nl80211_policy,
5044 .flags = GENL_ADMIN_PERM,
5045 },
5046 {
5047 .cmd = NL80211_CMD_GET_SCAN,
5048 .policy = nl80211_policy,
5049 .dumpit = nl80211_dump_scan,
5050 },
Jouni Malinen636a5d32009-03-19 13:39:22 +02005051 {
5052 .cmd = NL80211_CMD_AUTHENTICATE,
5053 .doit = nl80211_authenticate,
5054 .policy = nl80211_policy,
5055 .flags = GENL_ADMIN_PERM,
5056 },
5057 {
5058 .cmd = NL80211_CMD_ASSOCIATE,
5059 .doit = nl80211_associate,
5060 .policy = nl80211_policy,
5061 .flags = GENL_ADMIN_PERM,
5062 },
5063 {
5064 .cmd = NL80211_CMD_DEAUTHENTICATE,
5065 .doit = nl80211_deauthenticate,
5066 .policy = nl80211_policy,
5067 .flags = GENL_ADMIN_PERM,
5068 },
5069 {
5070 .cmd = NL80211_CMD_DISASSOCIATE,
5071 .doit = nl80211_disassociate,
5072 .policy = nl80211_policy,
5073 .flags = GENL_ADMIN_PERM,
5074 },
Johannes Berg04a773a2009-04-19 21:24:32 +02005075 {
5076 .cmd = NL80211_CMD_JOIN_IBSS,
5077 .doit = nl80211_join_ibss,
5078 .policy = nl80211_policy,
5079 .flags = GENL_ADMIN_PERM,
5080 },
5081 {
5082 .cmd = NL80211_CMD_LEAVE_IBSS,
5083 .doit = nl80211_leave_ibss,
5084 .policy = nl80211_policy,
5085 .flags = GENL_ADMIN_PERM,
5086 },
Johannes Bergaff89a92009-07-01 21:26:51 +02005087#ifdef CONFIG_NL80211_TESTMODE
5088 {
5089 .cmd = NL80211_CMD_TESTMODE,
5090 .doit = nl80211_testmode_do,
5091 .policy = nl80211_policy,
5092 .flags = GENL_ADMIN_PERM,
5093 },
5094#endif
Samuel Ortizb23aa672009-07-01 21:26:54 +02005095 {
5096 .cmd = NL80211_CMD_CONNECT,
5097 .doit = nl80211_connect,
5098 .policy = nl80211_policy,
5099 .flags = GENL_ADMIN_PERM,
5100 },
5101 {
5102 .cmd = NL80211_CMD_DISCONNECT,
5103 .doit = nl80211_disconnect,
5104 .policy = nl80211_policy,
5105 .flags = GENL_ADMIN_PERM,
5106 },
Johannes Berg463d0182009-07-14 00:33:35 +02005107 {
5108 .cmd = NL80211_CMD_SET_WIPHY_NETNS,
5109 .doit = nl80211_wiphy_netns,
5110 .policy = nl80211_policy,
5111 .flags = GENL_ADMIN_PERM,
5112 },
Holger Schurig61fa7132009-11-11 12:25:40 +01005113 {
5114 .cmd = NL80211_CMD_GET_SURVEY,
5115 .policy = nl80211_policy,
5116 .dumpit = nl80211_dump_survey,
5117 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005118 {
5119 .cmd = NL80211_CMD_SET_PMKSA,
5120 .doit = nl80211_setdel_pmksa,
5121 .policy = nl80211_policy,
5122 .flags = GENL_ADMIN_PERM,
5123 },
5124 {
5125 .cmd = NL80211_CMD_DEL_PMKSA,
5126 .doit = nl80211_setdel_pmksa,
5127 .policy = nl80211_policy,
5128 .flags = GENL_ADMIN_PERM,
5129 },
5130 {
5131 .cmd = NL80211_CMD_FLUSH_PMKSA,
5132 .doit = nl80211_flush_pmksa,
5133 .policy = nl80211_policy,
5134 .flags = GENL_ADMIN_PERM,
5135 },
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005136 {
5137 .cmd = NL80211_CMD_REMAIN_ON_CHANNEL,
5138 .doit = nl80211_remain_on_channel,
5139 .policy = nl80211_policy,
5140 .flags = GENL_ADMIN_PERM,
5141 },
5142 {
5143 .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
5144 .doit = nl80211_cancel_remain_on_channel,
5145 .policy = nl80211_policy,
5146 .flags = GENL_ADMIN_PERM,
5147 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005148 {
5149 .cmd = NL80211_CMD_SET_TX_BITRATE_MASK,
5150 .doit = nl80211_set_tx_bitrate_mask,
5151 .policy = nl80211_policy,
5152 .flags = GENL_ADMIN_PERM,
5153 },
Jouni Malinen026331c2010-02-15 12:53:10 +02005154 {
5155 .cmd = NL80211_CMD_REGISTER_ACTION,
5156 .doit = nl80211_register_action,
5157 .policy = nl80211_policy,
5158 .flags = GENL_ADMIN_PERM,
5159 },
5160 {
5161 .cmd = NL80211_CMD_ACTION,
5162 .doit = nl80211_action,
5163 .policy = nl80211_policy,
5164 .flags = GENL_ADMIN_PERM,
5165 },
Kalle Valoffb9eb32010-02-17 17:58:10 +02005166 {
5167 .cmd = NL80211_CMD_SET_POWER_SAVE,
5168 .doit = nl80211_set_power_save,
5169 .policy = nl80211_policy,
5170 .flags = GENL_ADMIN_PERM,
5171 },
5172 {
5173 .cmd = NL80211_CMD_GET_POWER_SAVE,
5174 .doit = nl80211_get_power_save,
5175 .policy = nl80211_policy,
5176 /* can be retrieved by unprivileged users */
5177 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005178 {
5179 .cmd = NL80211_CMD_SET_CQM,
5180 .doit = nl80211_set_cqm,
5181 .policy = nl80211_policy,
5182 .flags = GENL_ADMIN_PERM,
5183 },
Johannes Berg55682962007-09-20 13:09:35 -04005184};
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005185
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005186static struct genl_multicast_group nl80211_mlme_mcgrp = {
5187 .name = "mlme",
5188};
Johannes Berg55682962007-09-20 13:09:35 -04005189
5190/* multicast groups */
5191static struct genl_multicast_group nl80211_config_mcgrp = {
5192 .name = "config",
5193};
Johannes Berg2a519312009-02-10 21:25:55 +01005194static struct genl_multicast_group nl80211_scan_mcgrp = {
5195 .name = "scan",
5196};
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04005197static struct genl_multicast_group nl80211_regulatory_mcgrp = {
5198 .name = "regulatory",
5199};
Johannes Berg55682962007-09-20 13:09:35 -04005200
5201/* notification functions */
5202
5203void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
5204{
5205 struct sk_buff *msg;
5206
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07005207 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04005208 if (!msg)
5209 return;
5210
5211 if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
5212 nlmsg_free(msg);
5213 return;
5214 }
5215
Johannes Berg463d0182009-07-14 00:33:35 +02005216 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5217 nl80211_config_mcgrp.id, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04005218}
5219
Johannes Berg362a4152009-05-24 16:43:15 +02005220static int nl80211_add_scan_req(struct sk_buff *msg,
5221 struct cfg80211_registered_device *rdev)
5222{
5223 struct cfg80211_scan_request *req = rdev->scan_req;
5224 struct nlattr *nest;
5225 int i;
5226
Johannes Berg667503d2009-07-07 03:56:11 +02005227 ASSERT_RDEV_LOCK(rdev);
5228
Johannes Berg362a4152009-05-24 16:43:15 +02005229 if (WARN_ON(!req))
5230 return 0;
5231
5232 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
5233 if (!nest)
5234 goto nla_put_failure;
5235 for (i = 0; i < req->n_ssids; i++)
5236 NLA_PUT(msg, i, req->ssids[i].ssid_len, req->ssids[i].ssid);
5237 nla_nest_end(msg, nest);
5238
5239 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
5240 if (!nest)
5241 goto nla_put_failure;
5242 for (i = 0; i < req->n_channels; i++)
5243 NLA_PUT_U32(msg, i, req->channels[i]->center_freq);
5244 nla_nest_end(msg, nest);
5245
5246 if (req->ie)
5247 NLA_PUT(msg, NL80211_ATTR_IE, req->ie_len, req->ie);
5248
5249 return 0;
5250 nla_put_failure:
5251 return -ENOBUFS;
5252}
5253
Johannes Berga538e2d2009-06-16 19:56:42 +02005254static int nl80211_send_scan_msg(struct sk_buff *msg,
5255 struct cfg80211_registered_device *rdev,
5256 struct net_device *netdev,
5257 u32 pid, u32 seq, int flags,
5258 u32 cmd)
Johannes Berg2a519312009-02-10 21:25:55 +01005259{
5260 void *hdr;
5261
5262 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
5263 if (!hdr)
5264 return -1;
5265
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -05005266 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg2a519312009-02-10 21:25:55 +01005267 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5268
Johannes Berg362a4152009-05-24 16:43:15 +02005269 /* ignore errors and send incomplete event anyway */
5270 nl80211_add_scan_req(msg, rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01005271
5272 return genlmsg_end(msg, hdr);
5273
5274 nla_put_failure:
5275 genlmsg_cancel(msg, hdr);
5276 return -EMSGSIZE;
5277}
5278
Johannes Berga538e2d2009-06-16 19:56:42 +02005279void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
5280 struct net_device *netdev)
5281{
5282 struct sk_buff *msg;
5283
5284 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
5285 if (!msg)
5286 return;
5287
5288 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
5289 NL80211_CMD_TRIGGER_SCAN) < 0) {
5290 nlmsg_free(msg);
5291 return;
5292 }
5293
Johannes Berg463d0182009-07-14 00:33:35 +02005294 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5295 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berga538e2d2009-06-16 19:56:42 +02005296}
5297
Johannes Berg2a519312009-02-10 21:25:55 +01005298void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
5299 struct net_device *netdev)
5300{
5301 struct sk_buff *msg;
5302
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07005303 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01005304 if (!msg)
5305 return;
5306
Johannes Berga538e2d2009-06-16 19:56:42 +02005307 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
5308 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01005309 nlmsg_free(msg);
5310 return;
5311 }
5312
Johannes Berg463d0182009-07-14 00:33:35 +02005313 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5314 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01005315}
5316
5317void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
5318 struct net_device *netdev)
5319{
5320 struct sk_buff *msg;
5321
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07005322 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01005323 if (!msg)
5324 return;
5325
Johannes Berga538e2d2009-06-16 19:56:42 +02005326 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
5327 NL80211_CMD_SCAN_ABORTED) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01005328 nlmsg_free(msg);
5329 return;
5330 }
5331
Johannes Berg463d0182009-07-14 00:33:35 +02005332 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5333 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01005334}
5335
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04005336/*
5337 * This can happen on global regulatory changes or device specific settings
5338 * based on custom world regulatory domains.
5339 */
5340void nl80211_send_reg_change_event(struct regulatory_request *request)
5341{
5342 struct sk_buff *msg;
5343 void *hdr;
5344
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07005345 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04005346 if (!msg)
5347 return;
5348
5349 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
5350 if (!hdr) {
5351 nlmsg_free(msg);
5352 return;
5353 }
5354
5355 /* Userspace can always count this one always being set */
5356 NLA_PUT_U8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator);
5357
5358 if (request->alpha2[0] == '0' && request->alpha2[1] == '0')
5359 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
5360 NL80211_REGDOM_TYPE_WORLD);
5361 else if (request->alpha2[0] == '9' && request->alpha2[1] == '9')
5362 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
5363 NL80211_REGDOM_TYPE_CUSTOM_WORLD);
5364 else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
5365 request->intersect)
5366 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
5367 NL80211_REGDOM_TYPE_INTERSECTION);
5368 else {
5369 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
5370 NL80211_REGDOM_TYPE_COUNTRY);
5371 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2);
5372 }
5373
5374 if (wiphy_idx_valid(request->wiphy_idx))
5375 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx);
5376
5377 if (genlmsg_end(msg, hdr) < 0) {
5378 nlmsg_free(msg);
5379 return;
5380 }
5381
Johannes Bergbc43b282009-07-25 10:54:13 +02005382 rcu_read_lock();
Johannes Berg463d0182009-07-14 00:33:35 +02005383 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
Johannes Bergbc43b282009-07-25 10:54:13 +02005384 GFP_ATOMIC);
5385 rcu_read_unlock();
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04005386
5387 return;
5388
5389nla_put_failure:
5390 genlmsg_cancel(msg, hdr);
5391 nlmsg_free(msg);
5392}
5393
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005394static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
5395 struct net_device *netdev,
5396 const u8 *buf, size_t len,
Johannes Berge6d6e342009-07-01 21:26:47 +02005397 enum nl80211_commands cmd, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005398{
5399 struct sk_buff *msg;
5400 void *hdr;
5401
Johannes Berge6d6e342009-07-01 21:26:47 +02005402 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005403 if (!msg)
5404 return;
5405
5406 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
5407 if (!hdr) {
5408 nlmsg_free(msg);
5409 return;
5410 }
5411
5412 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5413 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5414 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
5415
5416 if (genlmsg_end(msg, hdr) < 0) {
5417 nlmsg_free(msg);
5418 return;
5419 }
5420
Johannes Berg463d0182009-07-14 00:33:35 +02005421 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5422 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005423 return;
5424
5425 nla_put_failure:
5426 genlmsg_cancel(msg, hdr);
5427 nlmsg_free(msg);
5428}
5429
5430void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02005431 struct net_device *netdev, const u8 *buf,
5432 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005433{
5434 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02005435 NL80211_CMD_AUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005436}
5437
5438void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
5439 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02005440 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005441{
Johannes Berge6d6e342009-07-01 21:26:47 +02005442 nl80211_send_mlme_event(rdev, netdev, buf, len,
5443 NL80211_CMD_ASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005444}
5445
Jouni Malinen53b46b82009-03-27 20:53:56 +02005446void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02005447 struct net_device *netdev, const u8 *buf,
5448 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005449{
5450 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02005451 NL80211_CMD_DEAUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005452}
5453
Jouni Malinen53b46b82009-03-27 20:53:56 +02005454void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
5455 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02005456 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005457{
5458 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02005459 NL80211_CMD_DISASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005460}
5461
Luis R. Rodriguez1b06bb42009-05-02 00:34:48 -04005462static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
5463 struct net_device *netdev, int cmd,
Johannes Berge6d6e342009-07-01 21:26:47 +02005464 const u8 *addr, gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03005465{
5466 struct sk_buff *msg;
5467 void *hdr;
5468
Johannes Berge6d6e342009-07-01 21:26:47 +02005469 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03005470 if (!msg)
5471 return;
5472
5473 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
5474 if (!hdr) {
5475 nlmsg_free(msg);
5476 return;
5477 }
5478
5479 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5480 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5481 NLA_PUT_FLAG(msg, NL80211_ATTR_TIMED_OUT);
5482 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
5483
5484 if (genlmsg_end(msg, hdr) < 0) {
5485 nlmsg_free(msg);
5486 return;
5487 }
5488
Johannes Berg463d0182009-07-14 00:33:35 +02005489 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5490 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03005491 return;
5492
5493 nla_put_failure:
5494 genlmsg_cancel(msg, hdr);
5495 nlmsg_free(msg);
5496}
5497
5498void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02005499 struct net_device *netdev, const u8 *addr,
5500 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03005501{
5502 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE,
Johannes Berge6d6e342009-07-01 21:26:47 +02005503 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03005504}
5505
5506void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02005507 struct net_device *netdev, const u8 *addr,
5508 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03005509{
Johannes Berge6d6e342009-07-01 21:26:47 +02005510 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE,
5511 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03005512}
5513
Samuel Ortizb23aa672009-07-01 21:26:54 +02005514void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
5515 struct net_device *netdev, const u8 *bssid,
5516 const u8 *req_ie, size_t req_ie_len,
5517 const u8 *resp_ie, size_t resp_ie_len,
5518 u16 status, gfp_t gfp)
5519{
5520 struct sk_buff *msg;
5521 void *hdr;
5522
5523 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
5524 if (!msg)
5525 return;
5526
5527 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT);
5528 if (!hdr) {
5529 nlmsg_free(msg);
5530 return;
5531 }
5532
5533 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5534 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5535 if (bssid)
5536 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
5537 NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status);
5538 if (req_ie)
5539 NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
5540 if (resp_ie)
5541 NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
5542
5543 if (genlmsg_end(msg, hdr) < 0) {
5544 nlmsg_free(msg);
5545 return;
5546 }
5547
Johannes Berg463d0182009-07-14 00:33:35 +02005548 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5549 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005550 return;
5551
5552 nla_put_failure:
5553 genlmsg_cancel(msg, hdr);
5554 nlmsg_free(msg);
5555
5556}
5557
5558void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
5559 struct net_device *netdev, const u8 *bssid,
5560 const u8 *req_ie, size_t req_ie_len,
5561 const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
5562{
5563 struct sk_buff *msg;
5564 void *hdr;
5565
5566 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
5567 if (!msg)
5568 return;
5569
5570 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM);
5571 if (!hdr) {
5572 nlmsg_free(msg);
5573 return;
5574 }
5575
5576 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5577 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5578 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
5579 if (req_ie)
5580 NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
5581 if (resp_ie)
5582 NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
5583
5584 if (genlmsg_end(msg, hdr) < 0) {
5585 nlmsg_free(msg);
5586 return;
5587 }
5588
Johannes Berg463d0182009-07-14 00:33:35 +02005589 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5590 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005591 return;
5592
5593 nla_put_failure:
5594 genlmsg_cancel(msg, hdr);
5595 nlmsg_free(msg);
5596
5597}
5598
5599void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
5600 struct net_device *netdev, u16 reason,
Johannes Berg667503d2009-07-07 03:56:11 +02005601 const u8 *ie, size_t ie_len, bool from_ap)
Samuel Ortizb23aa672009-07-01 21:26:54 +02005602{
5603 struct sk_buff *msg;
5604 void *hdr;
5605
Johannes Berg667503d2009-07-07 03:56:11 +02005606 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005607 if (!msg)
5608 return;
5609
5610 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT);
5611 if (!hdr) {
5612 nlmsg_free(msg);
5613 return;
5614 }
5615
5616 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5617 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5618 if (from_ap && reason)
5619 NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason);
5620 if (from_ap)
5621 NLA_PUT_FLAG(msg, NL80211_ATTR_DISCONNECTED_BY_AP);
5622 if (ie)
5623 NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie);
5624
5625 if (genlmsg_end(msg, hdr) < 0) {
5626 nlmsg_free(msg);
5627 return;
5628 }
5629
Johannes Berg463d0182009-07-14 00:33:35 +02005630 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5631 nl80211_mlme_mcgrp.id, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005632 return;
5633
5634 nla_put_failure:
5635 genlmsg_cancel(msg, hdr);
5636 nlmsg_free(msg);
5637
5638}
5639
Johannes Berg04a773a2009-04-19 21:24:32 +02005640void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
5641 struct net_device *netdev, const u8 *bssid,
5642 gfp_t gfp)
5643{
5644 struct sk_buff *msg;
5645 void *hdr;
5646
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07005647 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02005648 if (!msg)
5649 return;
5650
5651 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS);
5652 if (!hdr) {
5653 nlmsg_free(msg);
5654 return;
5655 }
5656
5657 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5658 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5659 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
5660
5661 if (genlmsg_end(msg, hdr) < 0) {
5662 nlmsg_free(msg);
5663 return;
5664 }
5665
Johannes Berg463d0182009-07-14 00:33:35 +02005666 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5667 nl80211_mlme_mcgrp.id, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02005668 return;
5669
5670 nla_put_failure:
5671 genlmsg_cancel(msg, hdr);
5672 nlmsg_free(msg);
5673}
5674
Jouni Malinena3b8b052009-03-27 21:59:49 +02005675void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
5676 struct net_device *netdev, const u8 *addr,
5677 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +02005678 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +02005679{
5680 struct sk_buff *msg;
5681 void *hdr;
5682
Johannes Berge6d6e342009-07-01 21:26:47 +02005683 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02005684 if (!msg)
5685 return;
5686
5687 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
5688 if (!hdr) {
5689 nlmsg_free(msg);
5690 return;
5691 }
5692
5693 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5694 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5695 if (addr)
5696 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
5697 NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, key_type);
5698 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id);
5699 if (tsc)
5700 NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc);
5701
5702 if (genlmsg_end(msg, hdr) < 0) {
5703 nlmsg_free(msg);
5704 return;
5705 }
5706
Johannes Berg463d0182009-07-14 00:33:35 +02005707 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5708 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02005709 return;
5710
5711 nla_put_failure:
5712 genlmsg_cancel(msg, hdr);
5713 nlmsg_free(msg);
5714}
5715
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04005716void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
5717 struct ieee80211_channel *channel_before,
5718 struct ieee80211_channel *channel_after)
5719{
5720 struct sk_buff *msg;
5721 void *hdr;
5722 struct nlattr *nl_freq;
5723
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07005724 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04005725 if (!msg)
5726 return;
5727
5728 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
5729 if (!hdr) {
5730 nlmsg_free(msg);
5731 return;
5732 }
5733
5734 /*
5735 * Since we are applying the beacon hint to a wiphy we know its
5736 * wiphy_idx is valid
5737 */
5738 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy));
5739
5740 /* Before */
5741 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
5742 if (!nl_freq)
5743 goto nla_put_failure;
5744 if (nl80211_msg_put_channel(msg, channel_before))
5745 goto nla_put_failure;
5746 nla_nest_end(msg, nl_freq);
5747
5748 /* After */
5749 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
5750 if (!nl_freq)
5751 goto nla_put_failure;
5752 if (nl80211_msg_put_channel(msg, channel_after))
5753 goto nla_put_failure;
5754 nla_nest_end(msg, nl_freq);
5755
5756 if (genlmsg_end(msg, hdr) < 0) {
5757 nlmsg_free(msg);
5758 return;
5759 }
5760
Johannes Berg463d0182009-07-14 00:33:35 +02005761 rcu_read_lock();
5762 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
5763 GFP_ATOMIC);
5764 rcu_read_unlock();
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04005765
5766 return;
5767
5768nla_put_failure:
5769 genlmsg_cancel(msg, hdr);
5770 nlmsg_free(msg);
5771}
5772
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005773static void nl80211_send_remain_on_chan_event(
5774 int cmd, struct cfg80211_registered_device *rdev,
5775 struct net_device *netdev, u64 cookie,
5776 struct ieee80211_channel *chan,
5777 enum nl80211_channel_type channel_type,
5778 unsigned int duration, gfp_t gfp)
5779{
5780 struct sk_buff *msg;
5781 void *hdr;
5782
5783 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
5784 if (!msg)
5785 return;
5786
5787 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
5788 if (!hdr) {
5789 nlmsg_free(msg);
5790 return;
5791 }
5792
5793 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5794 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5795 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq);
5796 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type);
5797 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
5798
5799 if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL)
5800 NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration);
5801
5802 if (genlmsg_end(msg, hdr) < 0) {
5803 nlmsg_free(msg);
5804 return;
5805 }
5806
5807 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5808 nl80211_mlme_mcgrp.id, gfp);
5809 return;
5810
5811 nla_put_failure:
5812 genlmsg_cancel(msg, hdr);
5813 nlmsg_free(msg);
5814}
5815
5816void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
5817 struct net_device *netdev, u64 cookie,
5818 struct ieee80211_channel *chan,
5819 enum nl80211_channel_type channel_type,
5820 unsigned int duration, gfp_t gfp)
5821{
5822 nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
5823 rdev, netdev, cookie, chan,
5824 channel_type, duration, gfp);
5825}
5826
5827void nl80211_send_remain_on_channel_cancel(
5828 struct cfg80211_registered_device *rdev, struct net_device *netdev,
5829 u64 cookie, struct ieee80211_channel *chan,
5830 enum nl80211_channel_type channel_type, gfp_t gfp)
5831{
5832 nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
5833 rdev, netdev, cookie, chan,
5834 channel_type, 0, gfp);
5835}
5836
Johannes Berg98b62182009-12-23 13:15:44 +01005837void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
5838 struct net_device *dev, const u8 *mac_addr,
5839 struct station_info *sinfo, gfp_t gfp)
5840{
5841 struct sk_buff *msg;
5842
5843 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
5844 if (!msg)
5845 return;
5846
5847 if (nl80211_send_station(msg, 0, 0, 0, dev, mac_addr, sinfo) < 0) {
5848 nlmsg_free(msg);
5849 return;
5850 }
5851
5852 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5853 nl80211_mlme_mcgrp.id, gfp);
5854}
5855
Jouni Malinen026331c2010-02-15 12:53:10 +02005856int nl80211_send_action(struct cfg80211_registered_device *rdev,
5857 struct net_device *netdev, u32 nlpid,
5858 int freq, const u8 *buf, size_t len, gfp_t gfp)
5859{
5860 struct sk_buff *msg;
5861 void *hdr;
5862 int err;
5863
5864 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
5865 if (!msg)
5866 return -ENOMEM;
5867
5868 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ACTION);
5869 if (!hdr) {
5870 nlmsg_free(msg);
5871 return -ENOMEM;
5872 }
5873
5874 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5875 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5876 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
5877 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
5878
5879 err = genlmsg_end(msg, hdr);
5880 if (err < 0) {
5881 nlmsg_free(msg);
5882 return err;
5883 }
5884
5885 err = genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
5886 if (err < 0)
5887 return err;
5888 return 0;
5889
5890 nla_put_failure:
5891 genlmsg_cancel(msg, hdr);
5892 nlmsg_free(msg);
5893 return -ENOBUFS;
5894}
5895
5896void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev,
5897 struct net_device *netdev, u64 cookie,
5898 const u8 *buf, size_t len, bool ack,
5899 gfp_t gfp)
5900{
5901 struct sk_buff *msg;
5902 void *hdr;
5903
5904 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
5905 if (!msg)
5906 return;
5907
5908 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ACTION_TX_STATUS);
5909 if (!hdr) {
5910 nlmsg_free(msg);
5911 return;
5912 }
5913
5914 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5915 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5916 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
5917 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
5918 if (ack)
5919 NLA_PUT_FLAG(msg, NL80211_ATTR_ACK);
5920
5921 if (genlmsg_end(msg, hdr) < 0) {
5922 nlmsg_free(msg);
5923 return;
5924 }
5925
5926 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
5927 return;
5928
5929 nla_put_failure:
5930 genlmsg_cancel(msg, hdr);
5931 nlmsg_free(msg);
5932}
5933
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005934void
5935nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
5936 struct net_device *netdev,
5937 enum nl80211_cqm_rssi_threshold_event rssi_event,
5938 gfp_t gfp)
5939{
5940 struct sk_buff *msg;
5941 struct nlattr *pinfoattr;
5942 void *hdr;
5943
5944 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
5945 if (!msg)
5946 return;
5947
5948 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
5949 if (!hdr) {
5950 nlmsg_free(msg);
5951 return;
5952 }
5953
5954 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5955 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5956
5957 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
5958 if (!pinfoattr)
5959 goto nla_put_failure;
5960
5961 NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
5962 rssi_event);
5963
5964 nla_nest_end(msg, pinfoattr);
5965
5966 if (genlmsg_end(msg, hdr) < 0) {
5967 nlmsg_free(msg);
5968 return;
5969 }
5970
5971 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5972 nl80211_mlme_mcgrp.id, gfp);
5973 return;
5974
5975 nla_put_failure:
5976 genlmsg_cancel(msg, hdr);
5977 nlmsg_free(msg);
5978}
5979
Jouni Malinen026331c2010-02-15 12:53:10 +02005980static int nl80211_netlink_notify(struct notifier_block * nb,
5981 unsigned long state,
5982 void *_notify)
5983{
5984 struct netlink_notify *notify = _notify;
5985 struct cfg80211_registered_device *rdev;
5986 struct wireless_dev *wdev;
5987
5988 if (state != NETLINK_URELEASE)
5989 return NOTIFY_DONE;
5990
5991 rcu_read_lock();
5992
5993 list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list)
5994 list_for_each_entry_rcu(wdev, &rdev->netdev_list, list)
5995 cfg80211_mlme_unregister_actions(wdev, notify->pid);
5996
5997 rcu_read_unlock();
5998
5999 return NOTIFY_DONE;
6000}
6001
6002static struct notifier_block nl80211_netlink_notifier = {
6003 .notifier_call = nl80211_netlink_notify,
6004};
6005
Johannes Berg55682962007-09-20 13:09:35 -04006006/* initialisation/exit functions */
6007
6008int nl80211_init(void)
6009{
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00006010 int err;
Johannes Berg55682962007-09-20 13:09:35 -04006011
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00006012 err = genl_register_family_with_ops(&nl80211_fam,
6013 nl80211_ops, ARRAY_SIZE(nl80211_ops));
Johannes Berg55682962007-09-20 13:09:35 -04006014 if (err)
6015 return err;
6016
Johannes Berg55682962007-09-20 13:09:35 -04006017 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
6018 if (err)
6019 goto err_out;
6020
Johannes Berg2a519312009-02-10 21:25:55 +01006021 err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
6022 if (err)
6023 goto err_out;
6024
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04006025 err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
6026 if (err)
6027 goto err_out;
6028
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006029 err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
6030 if (err)
6031 goto err_out;
6032
Johannes Bergaff89a92009-07-01 21:26:51 +02006033#ifdef CONFIG_NL80211_TESTMODE
6034 err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp);
6035 if (err)
6036 goto err_out;
6037#endif
6038
Jouni Malinen026331c2010-02-15 12:53:10 +02006039 err = netlink_register_notifier(&nl80211_netlink_notifier);
6040 if (err)
6041 goto err_out;
6042
Johannes Berg55682962007-09-20 13:09:35 -04006043 return 0;
6044 err_out:
6045 genl_unregister_family(&nl80211_fam);
6046 return err;
6047}
6048
6049void nl80211_exit(void)
6050{
Jouni Malinen026331c2010-02-15 12:53:10 +02006051 netlink_unregister_notifier(&nl80211_netlink_notifier);
Johannes Berg55682962007-09-20 13:09:35 -04006052 genl_unregister_family(&nl80211_fam);
6053}