blob: 0cd548267d4a717a11e636dce9d548dd03ebd7ea [file] [log] [blame]
Johannes Berg55682962007-09-20 13:09:35 -04001/*
2 * This is the new netlink-based wireless configuration interface.
3 *
Johannes Berg08645122009-05-11 13:54:58 +02004 * Copyright 2006-2009 Johannes Berg <johannes@sipsolutions.net>
Johannes Berg55682962007-09-20 13:09:35 -04005 */
6
7#include <linux/if.h>
8#include <linux/module.h>
9#include <linux/err.h>
Johannes Berg55682962007-09-20 13:09:35 -040010#include <linux/list.h>
11#include <linux/if_ether.h>
12#include <linux/ieee80211.h>
13#include <linux/nl80211.h>
14#include <linux/rtnetlink.h>
15#include <linux/netlink.h>
Johannes Berg2a519312009-02-10 21:25:55 +010016#include <linux/etherdevice.h>
Johannes Berg463d0182009-07-14 00:33:35 +020017#include <net/net_namespace.h>
Johannes Berg55682962007-09-20 13:09:35 -040018#include <net/genetlink.h>
19#include <net/cfg80211.h>
Johannes Berg463d0182009-07-14 00:33:35 +020020#include <net/sock.h>
Johannes Berg55682962007-09-20 13:09:35 -040021#include "core.h"
22#include "nl80211.h"
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -070023#include "reg.h"
Johannes Berg55682962007-09-20 13:09:35 -040024
25/* the netlink family */
26static struct genl_family nl80211_fam = {
27 .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
28 .name = "nl80211", /* have users key off the name instead */
29 .hdrsize = 0, /* no private header */
30 .version = 1, /* no particular meaning now */
31 .maxattr = NL80211_ATTR_MAX,
Johannes Berg463d0182009-07-14 00:33:35 +020032 .netnsok = true,
Johannes Berg55682962007-09-20 13:09:35 -040033};
34
Johannes Berg79c97e92009-07-07 03:56:12 +020035/* internal helper: get rdev and dev */
Johannes Berg463d0182009-07-14 00:33:35 +020036static int get_rdev_dev_by_info_ifindex(struct genl_info *info,
Johannes Berg79c97e92009-07-07 03:56:12 +020037 struct cfg80211_registered_device **rdev,
Johannes Berg55682962007-09-20 13:09:35 -040038 struct net_device **dev)
39{
Johannes Berg463d0182009-07-14 00:33:35 +020040 struct nlattr **attrs = info->attrs;
Johannes Berg55682962007-09-20 13:09:35 -040041 int ifindex;
42
Johannes Bergbba95fe2008-07-29 13:22:51 +020043 if (!attrs[NL80211_ATTR_IFINDEX])
Johannes Berg55682962007-09-20 13:09:35 -040044 return -EINVAL;
45
Johannes Bergbba95fe2008-07-29 13:22:51 +020046 ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
Johannes Berg463d0182009-07-14 00:33:35 +020047 *dev = dev_get_by_index(genl_info_net(info), ifindex);
Johannes Berg55682962007-09-20 13:09:35 -040048 if (!*dev)
49 return -ENODEV;
50
Johannes Berg463d0182009-07-14 00:33:35 +020051 *rdev = cfg80211_get_dev_from_ifindex(genl_info_net(info), ifindex);
Johannes Berg79c97e92009-07-07 03:56:12 +020052 if (IS_ERR(*rdev)) {
Johannes Berg55682962007-09-20 13:09:35 -040053 dev_put(*dev);
Johannes Berg79c97e92009-07-07 03:56:12 +020054 return PTR_ERR(*rdev);
Johannes Berg55682962007-09-20 13:09:35 -040055 }
56
57 return 0;
58}
59
60/* policy for the attributes */
61static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
62 [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
63 [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
David S. Miller079e24e2009-05-26 21:15:00 -070064 .len = 20-1 },
Jouni Malinen31888482008-10-30 16:59:24 +020065 [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
Jouni Malinen72bdcf32008-11-26 16:15:24 +020066 [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
Sujith094d05d2008-12-12 11:57:43 +053067 [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +020068 [NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 },
69 [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
70 [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
71 [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 },
Johannes Berg55682962007-09-20 13:09:35 -040072
73 [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
74 [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
75 [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
Johannes Berg41ade002007-12-19 02:03:29 +010076
77 [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN },
Johannes Berg3e5d7642009-07-07 14:37:26 +020078 [NL80211_ATTR_PREV_BSSID] = { .type = NLA_BINARY, .len = ETH_ALEN },
Johannes Berg41ade002007-12-19 02:03:29 +010079
Johannes Bergb9454e82009-07-08 13:29:08 +020080 [NL80211_ATTR_KEY] = { .type = NLA_NESTED, },
Johannes Berg41ade002007-12-19 02:03:29 +010081 [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
82 .len = WLAN_MAX_KEY_LEN },
83 [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
84 [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
85 [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
Jouni Malinen9f26a952009-05-15 12:38:32 +030086 [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
Johannes Berged1b6cc2007-12-19 02:03:32 +010087
88 [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
89 [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
90 [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY,
91 .len = IEEE80211_MAX_DATA_LEN },
92 [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY,
93 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg5727ef12007-12-19 02:03:34 +010094 [NL80211_ATTR_STA_AID] = { .type = NLA_U16 },
95 [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED },
96 [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 },
97 [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY,
98 .len = NL80211_MAX_SUPP_RATES },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +010099 [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
Johannes Berg5727ef12007-12-19 02:03:34 +0100100 [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
Johannes Berg0a9542e2008-10-15 11:54:04 +0200101 [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100102 [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
103 .len = IEEE80211_MAX_MESH_ID_LEN },
104 [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300105
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -0700106 [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 },
107 [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED },
108
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300109 [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
110 [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
111 [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
Jouni Malinen90c97a02008-10-30 16:59:22 +0200112 [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
113 .len = NL80211_MAX_SUPP_RATES },
Jouni Malinen36aedc92008-08-25 11:58:58 +0300114
colin@cozybit.com93da9cc2008-10-21 12:03:48 -0700115 [NL80211_ATTR_MESH_PARAMS] = { .type = NLA_NESTED },
116
Jouni Malinen36aedc92008-08-25 11:58:58 +0300117 [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY,
118 .len = NL80211_HT_CAPABILITY_LEN },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +0200119
120 [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
121 [NL80211_ATTR_IE] = { .type = NLA_BINARY,
122 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg2a519312009-02-10 21:25:55 +0100123 [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
124 [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
Jouni Malinen636a5d32009-03-19 13:39:22 +0200125
126 [NL80211_ATTR_SSID] = { .type = NLA_BINARY,
127 .len = IEEE80211_MAX_SSID_LEN },
128 [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
129 [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
Johannes Berg04a773a2009-04-19 21:24:32 +0200130 [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
Jouni Malinen1965c852009-04-22 21:38:25 +0300131 [NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG },
Jouni Malinendc6382c2009-05-06 22:09:37 +0300132 [NL80211_ATTR_USE_MFP] = { .type = NLA_U32 },
Johannes Bergeccb8e82009-05-11 21:57:56 +0300133 [NL80211_ATTR_STA_FLAGS2] = {
134 .len = sizeof(struct nl80211_sta_flag_update),
135 },
Jouni Malinen3f77316c2009-05-11 21:57:57 +0300136 [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG },
Samuel Ortizb23aa672009-07-01 21:26:54 +0200137 [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG },
138 [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 },
139 [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
Johannes Berg463d0182009-07-14 00:33:35 +0200140 [NL80211_ATTR_PID] = { .type = NLA_U32 },
Johannes Berg55682962007-09-20 13:09:35 -0400141};
142
Johannes Bergb9454e82009-07-08 13:29:08 +0200143/* policy for the attributes */
144static struct nla_policy
145nl80211_key_policy[NL80211_KEY_MAX + 1] __read_mostly = {
Johannes Bergfffd0932009-07-08 14:22:54 +0200146 [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN },
Johannes Bergb9454e82009-07-08 13:29:08 +0200147 [NL80211_KEY_IDX] = { .type = NLA_U8 },
148 [NL80211_KEY_CIPHER] = { .type = NLA_U32 },
149 [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
150 [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG },
151 [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
152};
153
Johannes Bergf4a11bb2009-03-27 12:40:28 +0100154/* IE validation */
155static bool is_valid_ie_attr(const struct nlattr *attr)
156{
157 const u8 *pos;
158 int len;
159
160 if (!attr)
161 return true;
162
163 pos = nla_data(attr);
164 len = nla_len(attr);
165
166 while (len) {
167 u8 elemlen;
168
169 if (len < 2)
170 return false;
171 len -= 2;
172
173 elemlen = pos[1];
174 if (elemlen > len)
175 return false;
176
177 len -= elemlen;
178 pos += 2 + elemlen;
179 }
180
181 return true;
182}
183
Johannes Berg55682962007-09-20 13:09:35 -0400184/* message building helper */
185static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
186 int flags, u8 cmd)
187{
188 /* since there is no private header just add the generic one */
189 return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
190}
191
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400192static int nl80211_msg_put_channel(struct sk_buff *msg,
193 struct ieee80211_channel *chan)
194{
195 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
196 chan->center_freq);
197
198 if (chan->flags & IEEE80211_CHAN_DISABLED)
199 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
200 if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
201 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
202 if (chan->flags & IEEE80211_CHAN_NO_IBSS)
203 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
204 if (chan->flags & IEEE80211_CHAN_RADAR)
205 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
206
207 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
208 DBM_TO_MBM(chan->max_power));
209
210 return 0;
211
212 nla_put_failure:
213 return -ENOBUFS;
214}
215
Johannes Berg55682962007-09-20 13:09:35 -0400216/* netlink command implementations */
217
Johannes Bergb9454e82009-07-08 13:29:08 +0200218struct key_parse {
219 struct key_params p;
220 int idx;
221 bool def, defmgmt;
222};
223
224static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k)
225{
226 struct nlattr *tb[NL80211_KEY_MAX + 1];
227 int err = nla_parse_nested(tb, NL80211_KEY_MAX, key,
228 nl80211_key_policy);
229 if (err)
230 return err;
231
232 k->def = !!tb[NL80211_KEY_DEFAULT];
233 k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT];
234
235 if (tb[NL80211_KEY_IDX])
236 k->idx = nla_get_u8(tb[NL80211_KEY_IDX]);
237
238 if (tb[NL80211_KEY_DATA]) {
239 k->p.key = nla_data(tb[NL80211_KEY_DATA]);
240 k->p.key_len = nla_len(tb[NL80211_KEY_DATA]);
241 }
242
243 if (tb[NL80211_KEY_SEQ]) {
244 k->p.seq = nla_data(tb[NL80211_KEY_SEQ]);
245 k->p.seq_len = nla_len(tb[NL80211_KEY_SEQ]);
246 }
247
248 if (tb[NL80211_KEY_CIPHER])
249 k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]);
250
251 return 0;
252}
253
254static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k)
255{
256 if (info->attrs[NL80211_ATTR_KEY_DATA]) {
257 k->p.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
258 k->p.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
259 }
260
261 if (info->attrs[NL80211_ATTR_KEY_SEQ]) {
262 k->p.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]);
263 k->p.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]);
264 }
265
266 if (info->attrs[NL80211_ATTR_KEY_IDX])
267 k->idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
268
269 if (info->attrs[NL80211_ATTR_KEY_CIPHER])
270 k->p.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
271
272 k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT];
273 k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT];
274
275 return 0;
276}
277
278static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
279{
280 int err;
281
282 memset(k, 0, sizeof(*k));
283 k->idx = -1;
284
285 if (info->attrs[NL80211_ATTR_KEY])
286 err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k);
287 else
288 err = nl80211_parse_key_old(info, k);
289
290 if (err)
291 return err;
292
293 if (k->def && k->defmgmt)
294 return -EINVAL;
295
296 if (k->idx != -1) {
297 if (k->defmgmt) {
298 if (k->idx < 4 || k->idx > 5)
299 return -EINVAL;
300 } else if (k->def) {
301 if (k->idx < 0 || k->idx > 3)
302 return -EINVAL;
303 } else {
304 if (k->idx < 0 || k->idx > 5)
305 return -EINVAL;
306 }
307 }
308
309 return 0;
310}
311
Johannes Bergfffd0932009-07-08 14:22:54 +0200312static struct cfg80211_cached_keys *
313nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
314 struct nlattr *keys)
315{
316 struct key_parse parse;
317 struct nlattr *key;
318 struct cfg80211_cached_keys *result;
319 int rem, err, def = 0;
320
321 result = kzalloc(sizeof(*result), GFP_KERNEL);
322 if (!result)
323 return ERR_PTR(-ENOMEM);
324
325 result->def = -1;
326 result->defmgmt = -1;
327
328 nla_for_each_nested(key, keys, rem) {
329 memset(&parse, 0, sizeof(parse));
330 parse.idx = -1;
331
332 err = nl80211_parse_key_new(key, &parse);
333 if (err)
334 goto error;
335 err = -EINVAL;
336 if (!parse.p.key)
337 goto error;
338 if (parse.idx < 0 || parse.idx > 4)
339 goto error;
340 if (parse.def) {
341 if (def)
342 goto error;
343 def = 1;
344 result->def = parse.idx;
345 } else if (parse.defmgmt)
346 goto error;
347 err = cfg80211_validate_key_settings(rdev, &parse.p,
348 parse.idx, NULL);
349 if (err)
350 goto error;
351 result->params[parse.idx].cipher = parse.p.cipher;
352 result->params[parse.idx].key_len = parse.p.key_len;
353 result->params[parse.idx].key = result->data[parse.idx];
354 memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len);
355 }
356
357 return result;
358 error:
359 kfree(result);
360 return ERR_PTR(err);
361}
362
363static int nl80211_key_allowed(struct wireless_dev *wdev)
364{
365 ASSERT_WDEV_LOCK(wdev);
366
367 if (!netif_running(wdev->netdev))
368 return -ENETDOWN;
369
370 switch (wdev->iftype) {
371 case NL80211_IFTYPE_AP:
372 case NL80211_IFTYPE_AP_VLAN:
373 break;
374 case NL80211_IFTYPE_ADHOC:
375 if (!wdev->current_bss)
376 return -ENOLINK;
377 break;
378 case NL80211_IFTYPE_STATION:
379 if (wdev->sme_state != CFG80211_SME_CONNECTED)
380 return -ENOLINK;
381 break;
382 default:
383 return -EINVAL;
384 }
385
386 return 0;
387}
388
Johannes Berg55682962007-09-20 13:09:35 -0400389static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
390 struct cfg80211_registered_device *dev)
391{
392 void *hdr;
Johannes Bergee688b002008-01-24 19:38:39 +0100393 struct nlattr *nl_bands, *nl_band;
394 struct nlattr *nl_freqs, *nl_freq;
395 struct nlattr *nl_rates, *nl_rate;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700396 struct nlattr *nl_modes;
Johannes Berg8fdc6212009-03-14 09:34:01 +0100397 struct nlattr *nl_cmds;
Johannes Bergee688b002008-01-24 19:38:39 +0100398 enum ieee80211_band band;
399 struct ieee80211_channel *chan;
400 struct ieee80211_rate *rate;
401 int i;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700402 u16 ifmodes = dev->wiphy.interface_modes;
Johannes Berg55682962007-09-20 13:09:35 -0400403
404 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
405 if (!hdr)
406 return -1;
407
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -0500408 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400409 NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200410
411 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
412 dev->wiphy.retry_short);
413 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
414 dev->wiphy.retry_long);
415 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
416 dev->wiphy.frag_threshold);
417 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
418 dev->wiphy.rts_threshold);
419
Johannes Berg2a519312009-02-10 21:25:55 +0100420 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
421 dev->wiphy.max_scan_ssids);
Johannes Berg18a83652009-03-31 12:12:05 +0200422 NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
423 dev->wiphy.max_scan_ie_len);
Johannes Bergee688b002008-01-24 19:38:39 +0100424
Johannes Berg25e47c12009-04-02 20:14:06 +0200425 NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
426 sizeof(u32) * dev->wiphy.n_cipher_suites,
427 dev->wiphy.cipher_suites);
428
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700429 nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES);
430 if (!nl_modes)
431 goto nla_put_failure;
432
433 i = 0;
434 while (ifmodes) {
435 if (ifmodes & 1)
436 NLA_PUT_FLAG(msg, i);
437 ifmodes >>= 1;
438 i++;
439 }
440
441 nla_nest_end(msg, nl_modes);
442
Johannes Bergee688b002008-01-24 19:38:39 +0100443 nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
444 if (!nl_bands)
445 goto nla_put_failure;
446
447 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
448 if (!dev->wiphy.bands[band])
449 continue;
450
451 nl_band = nla_nest_start(msg, band);
452 if (!nl_band)
453 goto nla_put_failure;
454
Johannes Bergd51626d2008-10-09 12:20:13 +0200455 /* add HT info */
456 if (dev->wiphy.bands[band]->ht_cap.ht_supported) {
457 NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET,
458 sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
459 &dev->wiphy.bands[band]->ht_cap.mcs);
460 NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA,
461 dev->wiphy.bands[band]->ht_cap.cap);
462 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
463 dev->wiphy.bands[band]->ht_cap.ampdu_factor);
464 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
465 dev->wiphy.bands[band]->ht_cap.ampdu_density);
466 }
467
Johannes Bergee688b002008-01-24 19:38:39 +0100468 /* add frequencies */
469 nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
470 if (!nl_freqs)
471 goto nla_put_failure;
472
473 for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
474 nl_freq = nla_nest_start(msg, i);
475 if (!nl_freq)
476 goto nla_put_failure;
477
478 chan = &dev->wiphy.bands[band]->channels[i];
Johannes Bergee688b002008-01-24 19:38:39 +0100479
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400480 if (nl80211_msg_put_channel(msg, chan))
481 goto nla_put_failure;
Jouni Malinene2f367f262008-11-21 19:01:30 +0200482
Johannes Bergee688b002008-01-24 19:38:39 +0100483 nla_nest_end(msg, nl_freq);
484 }
485
486 nla_nest_end(msg, nl_freqs);
487
488 /* add bitrates */
489 nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
490 if (!nl_rates)
491 goto nla_put_failure;
492
493 for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
494 nl_rate = nla_nest_start(msg, i);
495 if (!nl_rate)
496 goto nla_put_failure;
497
498 rate = &dev->wiphy.bands[band]->bitrates[i];
499 NLA_PUT_U32(msg, NL80211_BITRATE_ATTR_RATE,
500 rate->bitrate);
501 if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
502 NLA_PUT_FLAG(msg,
503 NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE);
504
505 nla_nest_end(msg, nl_rate);
506 }
507
508 nla_nest_end(msg, nl_rates);
509
510 nla_nest_end(msg, nl_band);
511 }
512 nla_nest_end(msg, nl_bands);
513
Johannes Berg8fdc6212009-03-14 09:34:01 +0100514 nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
515 if (!nl_cmds)
516 goto nla_put_failure;
517
518 i = 0;
519#define CMD(op, n) \
520 do { \
521 if (dev->ops->op) { \
522 i++; \
523 NLA_PUT_U32(msg, i, NL80211_CMD_ ## n); \
524 } \
525 } while (0)
526
527 CMD(add_virtual_intf, NEW_INTERFACE);
528 CMD(change_virtual_intf, SET_INTERFACE);
529 CMD(add_key, NEW_KEY);
530 CMD(add_beacon, NEW_BEACON);
531 CMD(add_station, NEW_STATION);
532 CMD(add_mpath, NEW_MPATH);
533 CMD(set_mesh_params, SET_MESH_PARAMS);
534 CMD(change_bss, SET_BSS);
Jouni Malinen636a5d32009-03-19 13:39:22 +0200535 CMD(auth, AUTHENTICATE);
536 CMD(assoc, ASSOCIATE);
537 CMD(deauth, DEAUTHENTICATE);
538 CMD(disassoc, DISASSOCIATE);
Johannes Berg04a773a2009-04-19 21:24:32 +0200539 CMD(join_ibss, JOIN_IBSS);
Johannes Berg463d0182009-07-14 00:33:35 +0200540 if (dev->wiphy.netnsok) {
541 i++;
542 NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS);
543 }
Johannes Berg8fdc6212009-03-14 09:34:01 +0100544
545#undef CMD
Samuel Ortizb23aa672009-07-01 21:26:54 +0200546
Johannes Berg6829c872009-07-02 09:13:27 +0200547 if (dev->ops->connect || dev->ops->auth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +0200548 i++;
549 NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT);
550 }
551
Johannes Berg6829c872009-07-02 09:13:27 +0200552 if (dev->ops->disconnect || dev->ops->deauth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +0200553 i++;
554 NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT);
555 }
556
Johannes Berg8fdc6212009-03-14 09:34:01 +0100557 nla_nest_end(msg, nl_cmds);
558
Johannes Berg55682962007-09-20 13:09:35 -0400559 return genlmsg_end(msg, hdr);
560
561 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700562 genlmsg_cancel(msg, hdr);
563 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400564}
565
566static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
567{
568 int idx = 0;
569 int start = cb->args[0];
570 struct cfg80211_registered_device *dev;
571
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500572 mutex_lock(&cfg80211_mutex);
Johannes Berg79c97e92009-07-07 03:56:12 +0200573 list_for_each_entry(dev, &cfg80211_rdev_list, list) {
Johannes Berg463d0182009-07-14 00:33:35 +0200574 if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
575 continue;
Julius Volzb4637272008-07-08 14:02:19 +0200576 if (++idx <= start)
Johannes Berg55682962007-09-20 13:09:35 -0400577 continue;
578 if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
579 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Julius Volzb4637272008-07-08 14:02:19 +0200580 dev) < 0) {
581 idx--;
Johannes Berg55682962007-09-20 13:09:35 -0400582 break;
Julius Volzb4637272008-07-08 14:02:19 +0200583 }
Johannes Berg55682962007-09-20 13:09:35 -0400584 }
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500585 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400586
587 cb->args[0] = idx;
588
589 return skb->len;
590}
591
592static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
593{
594 struct sk_buff *msg;
595 struct cfg80211_registered_device *dev;
596
597 dev = cfg80211_get_dev_from_info(info);
598 if (IS_ERR(dev))
599 return PTR_ERR(dev);
600
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -0700601 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -0400602 if (!msg)
603 goto out_err;
604
605 if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0)
606 goto out_free;
607
Johannes Berg4d0c8ae2009-07-07 03:56:09 +0200608 cfg80211_unlock_rdev(dev);
Johannes Berg55682962007-09-20 13:09:35 -0400609
Johannes Berg134e6372009-07-10 09:51:34 +0000610 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -0400611
612 out_free:
613 nlmsg_free(msg);
614 out_err:
Johannes Berg4d0c8ae2009-07-07 03:56:09 +0200615 cfg80211_unlock_rdev(dev);
Johannes Berg55682962007-09-20 13:09:35 -0400616 return -ENOBUFS;
617}
618
Jouni Malinen31888482008-10-30 16:59:24 +0200619static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
620 [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
621 [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
622 [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
623 [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
624 [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
625};
626
627static int parse_txq_params(struct nlattr *tb[],
628 struct ieee80211_txq_params *txq_params)
629{
630 if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] ||
631 !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
632 !tb[NL80211_TXQ_ATTR_AIFS])
633 return -EINVAL;
634
635 txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]);
636 txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
637 txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
638 txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
639 txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
640
641 return 0;
642}
643
Johannes Berg55682962007-09-20 13:09:35 -0400644static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
645{
646 struct cfg80211_registered_device *rdev;
Jouni Malinen31888482008-10-30 16:59:24 +0200647 int result = 0, rem_txq_params = 0;
648 struct nlattr *nl_txq_params;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200649 u32 changed;
650 u8 retry_short = 0, retry_long = 0;
651 u32 frag_threshold = 0, rts_threshold = 0;
Johannes Berg55682962007-09-20 13:09:35 -0400652
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100653 rtnl_lock();
Johannes Berg55682962007-09-20 13:09:35 -0400654
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100655 mutex_lock(&cfg80211_mutex);
656
Johannes Berg79c97e92009-07-07 03:56:12 +0200657 rdev = __cfg80211_rdev_from_info(info);
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100658 if (IS_ERR(rdev)) {
Jiri Slaby1f5fc702009-06-20 12:31:11 +0200659 mutex_unlock(&cfg80211_mutex);
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100660 result = PTR_ERR(rdev);
661 goto unlock;
662 }
663
664 mutex_lock(&rdev->mtx);
665
666 if (info->attrs[NL80211_ATTR_WIPHY_NAME])
Jouni Malinen31888482008-10-30 16:59:24 +0200667 result = cfg80211_dev_rename(
668 rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100669
670 mutex_unlock(&cfg80211_mutex);
671
672 if (result)
673 goto bad_res;
Johannes Berg55682962007-09-20 13:09:35 -0400674
Jouni Malinen31888482008-10-30 16:59:24 +0200675 if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
676 struct ieee80211_txq_params txq_params;
677 struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
678
679 if (!rdev->ops->set_txq_params) {
680 result = -EOPNOTSUPP;
681 goto bad_res;
682 }
683
684 nla_for_each_nested(nl_txq_params,
685 info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
686 rem_txq_params) {
687 nla_parse(tb, NL80211_TXQ_ATTR_MAX,
688 nla_data(nl_txq_params),
689 nla_len(nl_txq_params),
690 txq_params_policy);
691 result = parse_txq_params(tb, &txq_params);
692 if (result)
693 goto bad_res;
694
695 result = rdev->ops->set_txq_params(&rdev->wiphy,
696 &txq_params);
697 if (result)
698 goto bad_res;
699 }
700 }
701
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200702 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Sujith094d05d2008-12-12 11:57:43 +0530703 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200704 struct ieee80211_channel *chan;
Johannes Berg306d6112008-12-08 12:39:04 +0100705 struct ieee80211_sta_ht_cap *ht_cap;
Luis R. Rodriguez294196a2009-05-02 00:37:20 -0400706 u32 freq;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200707
708 if (!rdev->ops->set_channel) {
709 result = -EOPNOTSUPP;
710 goto bad_res;
711 }
712
Johannes Berg306d6112008-12-08 12:39:04 +0100713 result = -EINVAL;
714
Sujith094d05d2008-12-12 11:57:43 +0530715 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
716 channel_type = nla_get_u32(info->attrs[
717 NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
718 if (channel_type != NL80211_CHAN_NO_HT &&
719 channel_type != NL80211_CHAN_HT20 &&
720 channel_type != NL80211_CHAN_HT40PLUS &&
721 channel_type != NL80211_CHAN_HT40MINUS)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200722 goto bad_res;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200723 }
724
725 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
726 chan = ieee80211_get_channel(&rdev->wiphy, freq);
Johannes Berg306d6112008-12-08 12:39:04 +0100727
728 /* Primary channel not allowed */
729 if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200730 goto bad_res;
Johannes Berg306d6112008-12-08 12:39:04 +0100731
Luis R. Rodriguez294196a2009-05-02 00:37:20 -0400732 if (channel_type == NL80211_CHAN_HT40MINUS &&
733 (chan->flags & IEEE80211_CHAN_NO_HT40MINUS))
734 goto bad_res;
735 else if (channel_type == NL80211_CHAN_HT40PLUS &&
736 (chan->flags & IEEE80211_CHAN_NO_HT40PLUS))
737 goto bad_res;
738
739 /*
740 * At this point we know if that if HT40 was requested
741 * we are allowed to use it and the extension channel
742 * exists.
743 */
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200744
Johannes Berg306d6112008-12-08 12:39:04 +0100745 ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
746
Luis R. Rodriguez294196a2009-05-02 00:37:20 -0400747 /* no HT capabilities or intolerant */
748 if (channel_type != NL80211_CHAN_NO_HT) {
749 if (!ht_cap->ht_supported)
750 goto bad_res;
Johannes Berg306d6112008-12-08 12:39:04 +0100751 if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
752 (ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200753 goto bad_res;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200754 }
755
756 result = rdev->ops->set_channel(&rdev->wiphy, chan,
Sujith094d05d2008-12-12 11:57:43 +0530757 channel_type);
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200758 if (result)
759 goto bad_res;
Johannes Berg0e82ffe2009-07-27 12:01:50 +0200760
761 rdev->channel = chan;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200762 }
763
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200764 changed = 0;
765
766 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {
767 retry_short = nla_get_u8(
768 info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]);
769 if (retry_short == 0) {
770 result = -EINVAL;
771 goto bad_res;
772 }
773 changed |= WIPHY_PARAM_RETRY_SHORT;
774 }
775
776 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {
777 retry_long = nla_get_u8(
778 info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]);
779 if (retry_long == 0) {
780 result = -EINVAL;
781 goto bad_res;
782 }
783 changed |= WIPHY_PARAM_RETRY_LONG;
784 }
785
786 if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
787 frag_threshold = nla_get_u32(
788 info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
789 if (frag_threshold < 256) {
790 result = -EINVAL;
791 goto bad_res;
792 }
793 if (frag_threshold != (u32) -1) {
794 /*
795 * Fragments (apart from the last one) are required to
796 * have even length. Make the fragmentation code
797 * simpler by stripping LSB should someone try to use
798 * odd threshold value.
799 */
800 frag_threshold &= ~0x1;
801 }
802 changed |= WIPHY_PARAM_FRAG_THRESHOLD;
803 }
804
805 if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
806 rts_threshold = nla_get_u32(
807 info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
808 changed |= WIPHY_PARAM_RTS_THRESHOLD;
809 }
810
811 if (changed) {
812 u8 old_retry_short, old_retry_long;
813 u32 old_frag_threshold, old_rts_threshold;
814
815 if (!rdev->ops->set_wiphy_params) {
816 result = -EOPNOTSUPP;
817 goto bad_res;
818 }
819
820 old_retry_short = rdev->wiphy.retry_short;
821 old_retry_long = rdev->wiphy.retry_long;
822 old_frag_threshold = rdev->wiphy.frag_threshold;
823 old_rts_threshold = rdev->wiphy.rts_threshold;
824
825 if (changed & WIPHY_PARAM_RETRY_SHORT)
826 rdev->wiphy.retry_short = retry_short;
827 if (changed & WIPHY_PARAM_RETRY_LONG)
828 rdev->wiphy.retry_long = retry_long;
829 if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
830 rdev->wiphy.frag_threshold = frag_threshold;
831 if (changed & WIPHY_PARAM_RTS_THRESHOLD)
832 rdev->wiphy.rts_threshold = rts_threshold;
833
834 result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed);
835 if (result) {
836 rdev->wiphy.retry_short = old_retry_short;
837 rdev->wiphy.retry_long = old_retry_long;
838 rdev->wiphy.frag_threshold = old_frag_threshold;
839 rdev->wiphy.rts_threshold = old_rts_threshold;
840 }
841 }
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200842
Johannes Berg306d6112008-12-08 12:39:04 +0100843 bad_res:
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100844 mutex_unlock(&rdev->mtx);
845 unlock:
846 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400847 return result;
848}
849
850
851static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
Johannes Bergd7264052009-04-19 16:23:20 +0200852 struct cfg80211_registered_device *rdev,
Johannes Berg55682962007-09-20 13:09:35 -0400853 struct net_device *dev)
854{
855 void *hdr;
856
857 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
858 if (!hdr)
859 return -1;
860
861 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
Johannes Bergd7264052009-04-19 16:23:20 +0200862 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400863 NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
Johannes Berg60719ff2008-09-16 14:55:09 +0200864 NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype);
Johannes Berg55682962007-09-20 13:09:35 -0400865 return genlmsg_end(msg, hdr);
866
867 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700868 genlmsg_cancel(msg, hdr);
869 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400870}
871
872static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
873{
874 int wp_idx = 0;
875 int if_idx = 0;
876 int wp_start = cb->args[0];
877 int if_start = cb->args[1];
878 struct cfg80211_registered_device *dev;
879 struct wireless_dev *wdev;
880
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500881 mutex_lock(&cfg80211_mutex);
Johannes Berg79c97e92009-07-07 03:56:12 +0200882 list_for_each_entry(dev, &cfg80211_rdev_list, list) {
Johannes Berg463d0182009-07-14 00:33:35 +0200883 if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
884 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200885 if (wp_idx < wp_start) {
886 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400887 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200888 }
Johannes Berg55682962007-09-20 13:09:35 -0400889 if_idx = 0;
890
891 mutex_lock(&dev->devlist_mtx);
892 list_for_each_entry(wdev, &dev->netdev_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +0200893 if (if_idx < if_start) {
894 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400895 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200896 }
Johannes Berg55682962007-09-20 13:09:35 -0400897 if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
898 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Bergd7264052009-04-19 16:23:20 +0200899 dev, wdev->netdev) < 0) {
Johannes Bergbba95fe2008-07-29 13:22:51 +0200900 mutex_unlock(&dev->devlist_mtx);
901 goto out;
902 }
903 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400904 }
905 mutex_unlock(&dev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +0200906
907 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400908 }
Johannes Bergbba95fe2008-07-29 13:22:51 +0200909 out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500910 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400911
912 cb->args[0] = wp_idx;
913 cb->args[1] = if_idx;
914
915 return skb->len;
916}
917
918static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
919{
920 struct sk_buff *msg;
921 struct cfg80211_registered_device *dev;
922 struct net_device *netdev;
923 int err;
924
Johannes Berg463d0182009-07-14 00:33:35 +0200925 err = get_rdev_dev_by_info_ifindex(info, &dev, &netdev);
Johannes Berg55682962007-09-20 13:09:35 -0400926 if (err)
927 return err;
928
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -0700929 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -0400930 if (!msg)
931 goto out_err;
932
Johannes Bergd7264052009-04-19 16:23:20 +0200933 if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0,
934 dev, netdev) < 0)
Johannes Berg55682962007-09-20 13:09:35 -0400935 goto out_free;
936
937 dev_put(netdev);
Johannes Berg4d0c8ae2009-07-07 03:56:09 +0200938 cfg80211_unlock_rdev(dev);
Johannes Berg55682962007-09-20 13:09:35 -0400939
Johannes Berg134e6372009-07-10 09:51:34 +0000940 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -0400941
942 out_free:
943 nlmsg_free(msg);
944 out_err:
945 dev_put(netdev);
Johannes Berg4d0c8ae2009-07-07 03:56:09 +0200946 cfg80211_unlock_rdev(dev);
Johannes Berg55682962007-09-20 13:09:35 -0400947 return -ENOBUFS;
948}
949
Michael Wu66f7ac52008-01-31 19:48:22 +0100950static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
951 [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
952 [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
953 [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
954 [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
955 [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
956};
957
958static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
959{
960 struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
961 int flag;
962
963 *mntrflags = 0;
964
965 if (!nla)
966 return -EINVAL;
967
968 if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX,
969 nla, mntr_flags_policy))
970 return -EINVAL;
971
972 for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
973 if (flags[flag])
974 *mntrflags |= (1<<flag);
975
976 return 0;
977}
978
Johannes Berg55682962007-09-20 13:09:35 -0400979static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
980{
Johannes Berg79c97e92009-07-07 03:56:12 +0200981 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100982 struct vif_params params;
Johannes Berge36d56b2009-06-09 21:04:43 +0200983 int err;
Johannes Berg04a773a2009-04-19 21:24:32 +0200984 enum nl80211_iftype otype, ntype;
Johannes Berg55682962007-09-20 13:09:35 -0400985 struct net_device *dev;
Johannes Berg92ffe052008-09-16 20:39:36 +0200986 u32 _flags, *flags = NULL;
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100987 bool change = false;
Johannes Berg55682962007-09-20 13:09:35 -0400988
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100989 memset(&params, 0, sizeof(params));
990
Johannes Berg3b858752009-03-12 09:55:09 +0100991 rtnl_lock();
992
Johannes Berg463d0182009-07-14 00:33:35 +0200993 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg55682962007-09-20 13:09:35 -0400994 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +0100995 goto unlock_rtnl;
996
Johannes Berg04a773a2009-04-19 21:24:32 +0200997 otype = ntype = dev->ieee80211_ptr->iftype;
Johannes Berg55682962007-09-20 13:09:35 -0400998
Johannes Berg723b0382008-09-16 20:22:09 +0200999 if (info->attrs[NL80211_ATTR_IFTYPE]) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001000 ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
Johannes Berg04a773a2009-04-19 21:24:32 +02001001 if (otype != ntype)
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001002 change = true;
Johannes Berg04a773a2009-04-19 21:24:32 +02001003 if (ntype > NL80211_IFTYPE_MAX) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001004 err = -EINVAL;
Johannes Berg723b0382008-09-16 20:22:09 +02001005 goto unlock;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001006 }
Johannes Berg723b0382008-09-16 20:22:09 +02001007 }
1008
Johannes Berg79c97e92009-07-07 03:56:12 +02001009 if (!rdev->ops->change_virtual_intf ||
1010 !(rdev->wiphy.interface_modes & (1 << ntype))) {
Johannes Berg55682962007-09-20 13:09:35 -04001011 err = -EOPNOTSUPP;
1012 goto unlock;
1013 }
1014
Johannes Berg92ffe052008-09-16 20:39:36 +02001015 if (info->attrs[NL80211_ATTR_MESH_ID]) {
Johannes Berg04a773a2009-04-19 21:24:32 +02001016 if (ntype != NL80211_IFTYPE_MESH_POINT) {
Johannes Berg92ffe052008-09-16 20:39:36 +02001017 err = -EINVAL;
1018 goto unlock;
1019 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001020 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
1021 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001022 change = true;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001023 }
1024
Johannes Berg92ffe052008-09-16 20:39:36 +02001025 if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
Johannes Berg04a773a2009-04-19 21:24:32 +02001026 if (ntype != NL80211_IFTYPE_MONITOR) {
Johannes Berg92ffe052008-09-16 20:39:36 +02001027 err = -EINVAL;
1028 goto unlock;
1029 }
1030 err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
1031 &_flags);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001032 if (err)
1033 goto unlock;
1034
1035 flags = &_flags;
1036 change = true;
Johannes Berg92ffe052008-09-16 20:39:36 +02001037 }
Johannes Berg3b858752009-03-12 09:55:09 +01001038
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001039 if (change)
Johannes Berg79c97e92009-07-07 03:56:12 +02001040 err = rdev->ops->change_virtual_intf(&rdev->wiphy, dev,
Johannes Berg04a773a2009-04-19 21:24:32 +02001041 ntype, flags, &params);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001042 else
1043 err = 0;
Johannes Berg60719ff2008-09-16 14:55:09 +02001044
Johannes Berge36d56b2009-06-09 21:04:43 +02001045 WARN_ON(!err && dev->ieee80211_ptr->iftype != ntype);
Johannes Berg04a773a2009-04-19 21:24:32 +02001046
Johannes Berge36d56b2009-06-09 21:04:43 +02001047 if (!err && (ntype != otype)) {
Johannes Berg04a773a2009-04-19 21:24:32 +02001048 if (otype == NL80211_IFTYPE_ADHOC)
Johannes Berg9d308422009-04-20 18:43:46 +02001049 cfg80211_clear_ibss(dev, false);
Johannes Berg04a773a2009-04-19 21:24:32 +02001050 }
Johannes Berg60719ff2008-09-16 14:55:09 +02001051
Johannes Berg55682962007-09-20 13:09:35 -04001052 unlock:
Johannes Berge36d56b2009-06-09 21:04:43 +02001053 dev_put(dev);
Johannes Berg79c97e92009-07-07 03:56:12 +02001054 cfg80211_unlock_rdev(rdev);
Johannes Berg3b858752009-03-12 09:55:09 +01001055 unlock_rtnl:
1056 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -04001057 return err;
1058}
1059
1060static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
1061{
Johannes Berg79c97e92009-07-07 03:56:12 +02001062 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001063 struct vif_params params;
Johannes Berg55682962007-09-20 13:09:35 -04001064 int err;
1065 enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
Michael Wu66f7ac52008-01-31 19:48:22 +01001066 u32 flags;
Johannes Berg55682962007-09-20 13:09:35 -04001067
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001068 memset(&params, 0, sizeof(params));
1069
Johannes Berg55682962007-09-20 13:09:35 -04001070 if (!info->attrs[NL80211_ATTR_IFNAME])
1071 return -EINVAL;
1072
1073 if (info->attrs[NL80211_ATTR_IFTYPE]) {
1074 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
1075 if (type > NL80211_IFTYPE_MAX)
1076 return -EINVAL;
1077 }
1078
Johannes Berg3b858752009-03-12 09:55:09 +01001079 rtnl_lock();
1080
Johannes Berg79c97e92009-07-07 03:56:12 +02001081 rdev = cfg80211_get_dev_from_info(info);
1082 if (IS_ERR(rdev)) {
1083 err = PTR_ERR(rdev);
Johannes Berg3b858752009-03-12 09:55:09 +01001084 goto unlock_rtnl;
1085 }
Johannes Berg55682962007-09-20 13:09:35 -04001086
Johannes Berg79c97e92009-07-07 03:56:12 +02001087 if (!rdev->ops->add_virtual_intf ||
1088 !(rdev->wiphy.interface_modes & (1 << type))) {
Johannes Berg55682962007-09-20 13:09:35 -04001089 err = -EOPNOTSUPP;
1090 goto unlock;
1091 }
1092
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001093 if (type == NL80211_IFTYPE_MESH_POINT &&
1094 info->attrs[NL80211_ATTR_MESH_ID]) {
1095 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
1096 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
1097 }
1098
Michael Wu66f7ac52008-01-31 19:48:22 +01001099 err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
1100 info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
1101 &flags);
Johannes Berg79c97e92009-07-07 03:56:12 +02001102 err = rdev->ops->add_virtual_intf(&rdev->wiphy,
Michael Wu66f7ac52008-01-31 19:48:22 +01001103 nla_data(info->attrs[NL80211_ATTR_IFNAME]),
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001104 type, err ? NULL : &flags, &params);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001105
Johannes Berg55682962007-09-20 13:09:35 -04001106 unlock:
Johannes Berg79c97e92009-07-07 03:56:12 +02001107 cfg80211_unlock_rdev(rdev);
Johannes Berg3b858752009-03-12 09:55:09 +01001108 unlock_rtnl:
1109 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -04001110 return err;
1111}
1112
1113static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
1114{
Johannes Berg79c97e92009-07-07 03:56:12 +02001115 struct cfg80211_registered_device *rdev;
Johannes Berg463d0182009-07-14 00:33:35 +02001116 int err;
Johannes Berg55682962007-09-20 13:09:35 -04001117 struct net_device *dev;
1118
Johannes Berg3b858752009-03-12 09:55:09 +01001119 rtnl_lock();
1120
Johannes Berg463d0182009-07-14 00:33:35 +02001121 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg55682962007-09-20 13:09:35 -04001122 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001123 goto unlock_rtnl;
Johannes Berg55682962007-09-20 13:09:35 -04001124
Johannes Berg79c97e92009-07-07 03:56:12 +02001125 if (!rdev->ops->del_virtual_intf) {
Johannes Berg55682962007-09-20 13:09:35 -04001126 err = -EOPNOTSUPP;
1127 goto out;
1128 }
1129
Johannes Berg463d0182009-07-14 00:33:35 +02001130 err = rdev->ops->del_virtual_intf(&rdev->wiphy, dev);
Johannes Berg55682962007-09-20 13:09:35 -04001131
1132 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001133 cfg80211_unlock_rdev(rdev);
Johannes Berg463d0182009-07-14 00:33:35 +02001134 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001135 unlock_rtnl:
1136 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -04001137 return err;
1138}
1139
Johannes Berg41ade002007-12-19 02:03:29 +01001140struct get_key_cookie {
1141 struct sk_buff *msg;
1142 int error;
Johannes Bergb9454e82009-07-08 13:29:08 +02001143 int idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001144};
1145
1146static void get_key_callback(void *c, struct key_params *params)
1147{
Johannes Bergb9454e82009-07-08 13:29:08 +02001148 struct nlattr *key;
Johannes Berg41ade002007-12-19 02:03:29 +01001149 struct get_key_cookie *cookie = c;
1150
1151 if (params->key)
1152 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA,
1153 params->key_len, params->key);
1154
1155 if (params->seq)
1156 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ,
1157 params->seq_len, params->seq);
1158
1159 if (params->cipher)
1160 NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
1161 params->cipher);
1162
Johannes Bergb9454e82009-07-08 13:29:08 +02001163 key = nla_nest_start(cookie->msg, NL80211_ATTR_KEY);
1164 if (!key)
1165 goto nla_put_failure;
1166
1167 if (params->key)
1168 NLA_PUT(cookie->msg, NL80211_KEY_DATA,
1169 params->key_len, params->key);
1170
1171 if (params->seq)
1172 NLA_PUT(cookie->msg, NL80211_KEY_SEQ,
1173 params->seq_len, params->seq);
1174
1175 if (params->cipher)
1176 NLA_PUT_U32(cookie->msg, NL80211_KEY_CIPHER,
1177 params->cipher);
1178
1179 NLA_PUT_U8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx);
1180
1181 nla_nest_end(cookie->msg, key);
1182
Johannes Berg41ade002007-12-19 02:03:29 +01001183 return;
1184 nla_put_failure:
1185 cookie->error = 1;
1186}
1187
1188static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
1189{
Johannes Berg79c97e92009-07-07 03:56:12 +02001190 struct cfg80211_registered_device *rdev;
Johannes Berg41ade002007-12-19 02:03:29 +01001191 int err;
1192 struct net_device *dev;
1193 u8 key_idx = 0;
1194 u8 *mac_addr = NULL;
1195 struct get_key_cookie cookie = {
1196 .error = 0,
1197 };
1198 void *hdr;
1199 struct sk_buff *msg;
1200
1201 if (info->attrs[NL80211_ATTR_KEY_IDX])
1202 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1203
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001204 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +01001205 return -EINVAL;
1206
1207 if (info->attrs[NL80211_ATTR_MAC])
1208 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1209
Johannes Berg3b858752009-03-12 09:55:09 +01001210 rtnl_lock();
1211
Johannes Berg463d0182009-07-14 00:33:35 +02001212 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001213 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001214 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001215
Johannes Berg79c97e92009-07-07 03:56:12 +02001216 if (!rdev->ops->get_key) {
Johannes Berg41ade002007-12-19 02:03:29 +01001217 err = -EOPNOTSUPP;
1218 goto out;
1219 }
1220
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001221 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg41ade002007-12-19 02:03:29 +01001222 if (!msg) {
1223 err = -ENOMEM;
1224 goto out;
1225 }
1226
1227 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
1228 NL80211_CMD_NEW_KEY);
1229
1230 if (IS_ERR(hdr)) {
1231 err = PTR_ERR(hdr);
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001232 goto free_msg;
Johannes Berg41ade002007-12-19 02:03:29 +01001233 }
1234
1235 cookie.msg = msg;
Johannes Bergb9454e82009-07-08 13:29:08 +02001236 cookie.idx = key_idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001237
1238 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1239 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
1240 if (mac_addr)
1241 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1242
Johannes Berg79c97e92009-07-07 03:56:12 +02001243 err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, mac_addr,
Johannes Berg41ade002007-12-19 02:03:29 +01001244 &cookie, get_key_callback);
Johannes Berg41ade002007-12-19 02:03:29 +01001245
1246 if (err)
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001247 goto free_msg;
Johannes Berg41ade002007-12-19 02:03:29 +01001248
1249 if (cookie.error)
1250 goto nla_put_failure;
1251
1252 genlmsg_end(msg, hdr);
Johannes Berg134e6372009-07-10 09:51:34 +00001253 err = genlmsg_reply(msg, info);
Johannes Berg41ade002007-12-19 02:03:29 +01001254 goto out;
1255
1256 nla_put_failure:
1257 err = -ENOBUFS;
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001258 free_msg:
Johannes Berg41ade002007-12-19 02:03:29 +01001259 nlmsg_free(msg);
1260 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001261 cfg80211_unlock_rdev(rdev);
Johannes Berg41ade002007-12-19 02:03:29 +01001262 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001263 unlock_rtnl:
1264 rtnl_unlock();
1265
Johannes Berg41ade002007-12-19 02:03:29 +01001266 return err;
1267}
1268
1269static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
1270{
Johannes Berg79c97e92009-07-07 03:56:12 +02001271 struct cfg80211_registered_device *rdev;
Johannes Bergb9454e82009-07-08 13:29:08 +02001272 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001273 int err;
1274 struct net_device *dev;
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001275 int (*func)(struct wiphy *wiphy, struct net_device *netdev,
1276 u8 key_index);
Johannes Berg41ade002007-12-19 02:03:29 +01001277
Johannes Bergb9454e82009-07-08 13:29:08 +02001278 err = nl80211_parse_key(info, &key);
1279 if (err)
1280 return err;
1281
1282 if (key.idx < 0)
Johannes Berg41ade002007-12-19 02:03:29 +01001283 return -EINVAL;
1284
Johannes Bergb9454e82009-07-08 13:29:08 +02001285 /* only support setting default key */
1286 if (!key.def && !key.defmgmt)
Johannes Berg41ade002007-12-19 02:03:29 +01001287 return -EINVAL;
1288
Johannes Berg3b858752009-03-12 09:55:09 +01001289 rtnl_lock();
1290
Johannes Berg463d0182009-07-14 00:33:35 +02001291 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001292 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001293 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001294
Johannes Bergb9454e82009-07-08 13:29:08 +02001295 if (key.def)
Johannes Berg79c97e92009-07-07 03:56:12 +02001296 func = rdev->ops->set_default_key;
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001297 else
Johannes Berg79c97e92009-07-07 03:56:12 +02001298 func = rdev->ops->set_default_mgmt_key;
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001299
1300 if (!func) {
Johannes Berg41ade002007-12-19 02:03:29 +01001301 err = -EOPNOTSUPP;
1302 goto out;
1303 }
1304
Johannes Bergfffd0932009-07-08 14:22:54 +02001305 wdev_lock(dev->ieee80211_ptr);
1306 err = nl80211_key_allowed(dev->ieee80211_ptr);
1307 if (!err)
1308 err = func(&rdev->wiphy, dev, key.idx);
1309
Johannes Berg08645122009-05-11 13:54:58 +02001310#ifdef CONFIG_WIRELESS_EXT
1311 if (!err) {
Johannes Berg79c97e92009-07-07 03:56:12 +02001312 if (func == rdev->ops->set_default_key)
Johannes Bergb9454e82009-07-08 13:29:08 +02001313 dev->ieee80211_ptr->wext.default_key = key.idx;
Johannes Berg08645122009-05-11 13:54:58 +02001314 else
Johannes Bergb9454e82009-07-08 13:29:08 +02001315 dev->ieee80211_ptr->wext.default_mgmt_key = key.idx;
Johannes Berg08645122009-05-11 13:54:58 +02001316 }
1317#endif
Johannes Bergfffd0932009-07-08 14:22:54 +02001318 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01001319
1320 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001321 cfg80211_unlock_rdev(rdev);
Johannes Berg41ade002007-12-19 02:03:29 +01001322 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001323
1324 unlock_rtnl:
1325 rtnl_unlock();
1326
Johannes Berg41ade002007-12-19 02:03:29 +01001327 return err;
1328}
1329
1330static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
1331{
Johannes Berg79c97e92009-07-07 03:56:12 +02001332 struct cfg80211_registered_device *rdev;
Johannes Bergfffd0932009-07-08 14:22:54 +02001333 int err;
Johannes Berg41ade002007-12-19 02:03:29 +01001334 struct net_device *dev;
Johannes Bergb9454e82009-07-08 13:29:08 +02001335 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001336 u8 *mac_addr = NULL;
1337
Johannes Bergb9454e82009-07-08 13:29:08 +02001338 err = nl80211_parse_key(info, &key);
1339 if (err)
1340 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01001341
Johannes Bergb9454e82009-07-08 13:29:08 +02001342 if (!key.p.key)
Johannes Berg41ade002007-12-19 02:03:29 +01001343 return -EINVAL;
1344
Johannes Berg41ade002007-12-19 02:03:29 +01001345 if (info->attrs[NL80211_ATTR_MAC])
1346 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1347
Johannes Berg3b858752009-03-12 09:55:09 +01001348 rtnl_lock();
1349
Johannes Berg463d0182009-07-14 00:33:35 +02001350 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001351 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001352 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001353
Johannes Berg79c97e92009-07-07 03:56:12 +02001354 if (!rdev->ops->add_key) {
Johannes Berg41ade002007-12-19 02:03:29 +01001355 err = -EOPNOTSUPP;
1356 goto out;
1357 }
1358
Johannes Bergfffd0932009-07-08 14:22:54 +02001359 if (cfg80211_validate_key_settings(rdev, &key.p, key.idx, mac_addr)) {
1360 err = -EINVAL;
1361 goto out;
1362 }
1363
1364 wdev_lock(dev->ieee80211_ptr);
1365 err = nl80211_key_allowed(dev->ieee80211_ptr);
1366 if (!err)
1367 err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx,
1368 mac_addr, &key.p);
1369 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01001370
1371 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001372 cfg80211_unlock_rdev(rdev);
Johannes Berg41ade002007-12-19 02:03:29 +01001373 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001374 unlock_rtnl:
1375 rtnl_unlock();
1376
Johannes Berg41ade002007-12-19 02:03:29 +01001377 return err;
1378}
1379
1380static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
1381{
Johannes Berg79c97e92009-07-07 03:56:12 +02001382 struct cfg80211_registered_device *rdev;
Johannes Berg41ade002007-12-19 02:03:29 +01001383 int err;
1384 struct net_device *dev;
Johannes Berg41ade002007-12-19 02:03:29 +01001385 u8 *mac_addr = NULL;
Johannes Bergb9454e82009-07-08 13:29:08 +02001386 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001387
Johannes Bergb9454e82009-07-08 13:29:08 +02001388 err = nl80211_parse_key(info, &key);
1389 if (err)
1390 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01001391
1392 if (info->attrs[NL80211_ATTR_MAC])
1393 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1394
Johannes Berg3b858752009-03-12 09:55:09 +01001395 rtnl_lock();
1396
Johannes Berg463d0182009-07-14 00:33:35 +02001397 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001398 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001399 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001400
Johannes Berg79c97e92009-07-07 03:56:12 +02001401 if (!rdev->ops->del_key) {
Johannes Berg41ade002007-12-19 02:03:29 +01001402 err = -EOPNOTSUPP;
1403 goto out;
1404 }
1405
Johannes Bergfffd0932009-07-08 14:22:54 +02001406 wdev_lock(dev->ieee80211_ptr);
1407 err = nl80211_key_allowed(dev->ieee80211_ptr);
1408 if (!err)
1409 err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr);
Johannes Berg41ade002007-12-19 02:03:29 +01001410
Johannes Berg08645122009-05-11 13:54:58 +02001411#ifdef CONFIG_WIRELESS_EXT
1412 if (!err) {
Johannes Bergb9454e82009-07-08 13:29:08 +02001413 if (key.idx == dev->ieee80211_ptr->wext.default_key)
Johannes Berg08645122009-05-11 13:54:58 +02001414 dev->ieee80211_ptr->wext.default_key = -1;
Johannes Bergb9454e82009-07-08 13:29:08 +02001415 else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key)
Johannes Berg08645122009-05-11 13:54:58 +02001416 dev->ieee80211_ptr->wext.default_mgmt_key = -1;
1417 }
1418#endif
Johannes Bergfffd0932009-07-08 14:22:54 +02001419 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg08645122009-05-11 13:54:58 +02001420
Johannes Berg41ade002007-12-19 02:03:29 +01001421 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001422 cfg80211_unlock_rdev(rdev);
Johannes Berg41ade002007-12-19 02:03:29 +01001423 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001424
1425 unlock_rtnl:
1426 rtnl_unlock();
1427
Johannes Berg41ade002007-12-19 02:03:29 +01001428 return err;
1429}
1430
Johannes Berged1b6cc2007-12-19 02:03:32 +01001431static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
1432{
1433 int (*call)(struct wiphy *wiphy, struct net_device *dev,
1434 struct beacon_parameters *info);
Johannes Berg79c97e92009-07-07 03:56:12 +02001435 struct cfg80211_registered_device *rdev;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001436 int err;
1437 struct net_device *dev;
1438 struct beacon_parameters params;
1439 int haveinfo = 0;
1440
Johannes Bergf4a11bb2009-03-27 12:40:28 +01001441 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]))
1442 return -EINVAL;
1443
Johannes Berg3b858752009-03-12 09:55:09 +01001444 rtnl_lock();
1445
Johannes Berg463d0182009-07-14 00:33:35 +02001446 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001447 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001448 goto unlock_rtnl;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001449
Jouni Malineneec60b02009-03-20 21:21:19 +02001450 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
1451 err = -EOPNOTSUPP;
1452 goto out;
1453 }
1454
Johannes Berged1b6cc2007-12-19 02:03:32 +01001455 switch (info->genlhdr->cmd) {
1456 case NL80211_CMD_NEW_BEACON:
1457 /* these are required for NEW_BEACON */
1458 if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
1459 !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
1460 !info->attrs[NL80211_ATTR_BEACON_HEAD]) {
1461 err = -EINVAL;
1462 goto out;
1463 }
1464
Johannes Berg79c97e92009-07-07 03:56:12 +02001465 call = rdev->ops->add_beacon;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001466 break;
1467 case NL80211_CMD_SET_BEACON:
Johannes Berg79c97e92009-07-07 03:56:12 +02001468 call = rdev->ops->set_beacon;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001469 break;
1470 default:
1471 WARN_ON(1);
1472 err = -EOPNOTSUPP;
1473 goto out;
1474 }
1475
1476 if (!call) {
1477 err = -EOPNOTSUPP;
1478 goto out;
1479 }
1480
1481 memset(&params, 0, sizeof(params));
1482
1483 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
1484 params.interval =
1485 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
1486 haveinfo = 1;
1487 }
1488
1489 if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) {
1490 params.dtim_period =
1491 nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
1492 haveinfo = 1;
1493 }
1494
1495 if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
1496 params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
1497 params.head_len =
1498 nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
1499 haveinfo = 1;
1500 }
1501
1502 if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
1503 params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
1504 params.tail_len =
1505 nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
1506 haveinfo = 1;
1507 }
1508
1509 if (!haveinfo) {
1510 err = -EINVAL;
1511 goto out;
1512 }
1513
Johannes Berg79c97e92009-07-07 03:56:12 +02001514 err = call(&rdev->wiphy, dev, &params);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001515
1516 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001517 cfg80211_unlock_rdev(rdev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001518 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001519 unlock_rtnl:
1520 rtnl_unlock();
1521
Johannes Berged1b6cc2007-12-19 02:03:32 +01001522 return err;
1523}
1524
1525static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
1526{
Johannes Berg79c97e92009-07-07 03:56:12 +02001527 struct cfg80211_registered_device *rdev;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001528 int err;
1529 struct net_device *dev;
1530
Johannes Berg3b858752009-03-12 09:55:09 +01001531 rtnl_lock();
1532
Johannes Berg463d0182009-07-14 00:33:35 +02001533 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001534 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001535 goto unlock_rtnl;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001536
Johannes Berg79c97e92009-07-07 03:56:12 +02001537 if (!rdev->ops->del_beacon) {
Johannes Berged1b6cc2007-12-19 02:03:32 +01001538 err = -EOPNOTSUPP;
1539 goto out;
1540 }
1541
Jouni Malineneec60b02009-03-20 21:21:19 +02001542 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
1543 err = -EOPNOTSUPP;
1544 goto out;
1545 }
Johannes Berg79c97e92009-07-07 03:56:12 +02001546 err = rdev->ops->del_beacon(&rdev->wiphy, dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001547
1548 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001549 cfg80211_unlock_rdev(rdev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001550 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001551 unlock_rtnl:
1552 rtnl_unlock();
1553
Johannes Berged1b6cc2007-12-19 02:03:32 +01001554 return err;
1555}
1556
Johannes Berg5727ef12007-12-19 02:03:34 +01001557static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
1558 [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
1559 [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
1560 [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
Jouni Malinen0e467242009-05-11 21:57:55 +03001561 [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG },
Johannes Berg5727ef12007-12-19 02:03:34 +01001562};
1563
Johannes Bergeccb8e82009-05-11 21:57:56 +03001564static int parse_station_flags(struct genl_info *info,
1565 struct station_parameters *params)
Johannes Berg5727ef12007-12-19 02:03:34 +01001566{
1567 struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
Johannes Bergeccb8e82009-05-11 21:57:56 +03001568 struct nlattr *nla;
Johannes Berg5727ef12007-12-19 02:03:34 +01001569 int flag;
1570
Johannes Bergeccb8e82009-05-11 21:57:56 +03001571 /*
1572 * Try parsing the new attribute first so userspace
1573 * can specify both for older kernels.
1574 */
1575 nla = info->attrs[NL80211_ATTR_STA_FLAGS2];
1576 if (nla) {
1577 struct nl80211_sta_flag_update *sta_flags;
Johannes Berg5727ef12007-12-19 02:03:34 +01001578
Johannes Bergeccb8e82009-05-11 21:57:56 +03001579 sta_flags = nla_data(nla);
1580 params->sta_flags_mask = sta_flags->mask;
1581 params->sta_flags_set = sta_flags->set;
1582 if ((params->sta_flags_mask |
1583 params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID))
1584 return -EINVAL;
1585 return 0;
1586 }
1587
1588 /* if present, parse the old attribute */
1589
1590 nla = info->attrs[NL80211_ATTR_STA_FLAGS];
Johannes Berg5727ef12007-12-19 02:03:34 +01001591 if (!nla)
1592 return 0;
1593
1594 if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX,
1595 nla, sta_flags_policy))
1596 return -EINVAL;
1597
Johannes Bergeccb8e82009-05-11 21:57:56 +03001598 params->sta_flags_mask = (1 << __NL80211_STA_FLAG_AFTER_LAST) - 1;
1599 params->sta_flags_mask &= ~1;
Johannes Berg5727ef12007-12-19 02:03:34 +01001600
1601 for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++)
1602 if (flags[flag])
Johannes Bergeccb8e82009-05-11 21:57:56 +03001603 params->sta_flags_set |= (1<<flag);
Johannes Berg5727ef12007-12-19 02:03:34 +01001604
1605 return 0;
1606}
1607
Henning Rogge420e7fa2008-12-11 22:04:19 +01001608static u16 nl80211_calculate_bitrate(struct rate_info *rate)
1609{
1610 int modulation, streams, bitrate;
1611
1612 if (!(rate->flags & RATE_INFO_FLAGS_MCS))
1613 return rate->legacy;
1614
1615 /* the formula below does only work for MCS values smaller than 32 */
1616 if (rate->mcs >= 32)
1617 return 0;
1618
1619 modulation = rate->mcs & 7;
1620 streams = (rate->mcs >> 3) + 1;
1621
1622 bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ?
1623 13500000 : 6500000;
1624
1625 if (modulation < 4)
1626 bitrate *= (modulation + 1);
1627 else if (modulation == 4)
1628 bitrate *= (modulation + 2);
1629 else
1630 bitrate *= (modulation + 3);
1631
1632 bitrate *= streams;
1633
1634 if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
1635 bitrate = (bitrate / 9) * 10;
1636
1637 /* do NOT round down here */
1638 return (bitrate + 50000) / 100000;
1639}
1640
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001641static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
1642 int flags, struct net_device *dev,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001643 u8 *mac_addr, struct station_info *sinfo)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001644{
1645 void *hdr;
Henning Rogge420e7fa2008-12-11 22:04:19 +01001646 struct nlattr *sinfoattr, *txrate;
1647 u16 bitrate;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001648
1649 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
1650 if (!hdr)
1651 return -1;
1652
1653 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1654 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1655
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001656 sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
1657 if (!sinfoattr)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001658 goto nla_put_failure;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001659 if (sinfo->filled & STATION_INFO_INACTIVE_TIME)
1660 NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME,
1661 sinfo->inactive_time);
1662 if (sinfo->filled & STATION_INFO_RX_BYTES)
1663 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES,
1664 sinfo->rx_bytes);
1665 if (sinfo->filled & STATION_INFO_TX_BYTES)
1666 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES,
1667 sinfo->tx_bytes);
1668 if (sinfo->filled & STATION_INFO_LLID)
1669 NLA_PUT_U16(msg, NL80211_STA_INFO_LLID,
1670 sinfo->llid);
1671 if (sinfo->filled & STATION_INFO_PLID)
1672 NLA_PUT_U16(msg, NL80211_STA_INFO_PLID,
1673 sinfo->plid);
1674 if (sinfo->filled & STATION_INFO_PLINK_STATE)
1675 NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
1676 sinfo->plink_state);
Henning Rogge420e7fa2008-12-11 22:04:19 +01001677 if (sinfo->filled & STATION_INFO_SIGNAL)
1678 NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL,
1679 sinfo->signal);
1680 if (sinfo->filled & STATION_INFO_TX_BITRATE) {
1681 txrate = nla_nest_start(msg, NL80211_STA_INFO_TX_BITRATE);
1682 if (!txrate)
1683 goto nla_put_failure;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001684
Henning Rogge420e7fa2008-12-11 22:04:19 +01001685 /* nl80211_calculate_bitrate will return 0 for mcs >= 32 */
1686 bitrate = nl80211_calculate_bitrate(&sinfo->txrate);
1687 if (bitrate > 0)
1688 NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
1689
1690 if (sinfo->txrate.flags & RATE_INFO_FLAGS_MCS)
1691 NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS,
1692 sinfo->txrate.mcs);
1693 if (sinfo->txrate.flags & RATE_INFO_FLAGS_40_MHZ_WIDTH)
1694 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH);
1695 if (sinfo->txrate.flags & RATE_INFO_FLAGS_SHORT_GI)
1696 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI);
1697
1698 nla_nest_end(msg, txrate);
1699 }
Jouni Malinen98c8a60a2009-02-17 13:24:57 +02001700 if (sinfo->filled & STATION_INFO_RX_PACKETS)
1701 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS,
1702 sinfo->rx_packets);
1703 if (sinfo->filled & STATION_INFO_TX_PACKETS)
1704 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS,
1705 sinfo->tx_packets);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001706 nla_nest_end(msg, sinfoattr);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001707
1708 return genlmsg_end(msg, hdr);
1709
1710 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001711 genlmsg_cancel(msg, hdr);
1712 return -EMSGSIZE;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001713}
1714
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001715static int nl80211_dump_station(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02001716 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001717{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001718 struct station_info sinfo;
1719 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001720 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001721 u8 mac_addr[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02001722 int ifidx = cb->args[0];
1723 int sta_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001724 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001725
Johannes Bergbba95fe2008-07-29 13:22:51 +02001726 if (!ifidx) {
1727 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
1728 nl80211_fam.attrbuf, nl80211_fam.maxattr,
1729 nl80211_policy);
1730 if (err)
1731 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001732
Johannes Bergbba95fe2008-07-29 13:22:51 +02001733 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
1734 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001735
Johannes Bergbba95fe2008-07-29 13:22:51 +02001736 ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
1737 if (!ifidx)
1738 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001739 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001740
Johannes Berg3b858752009-03-12 09:55:09 +01001741 rtnl_lock();
1742
Johannes Berg463d0182009-07-14 00:33:35 +02001743 netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
Johannes Berg3b858752009-03-12 09:55:09 +01001744 if (!netdev) {
1745 err = -ENODEV;
1746 goto out_rtnl;
1747 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001748
Johannes Berg463d0182009-07-14 00:33:35 +02001749 dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001750 if (IS_ERR(dev)) {
1751 err = PTR_ERR(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001752 goto out_rtnl;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001753 }
1754
1755 if (!dev->ops->dump_station) {
Jouni Malineneec60b02009-03-20 21:21:19 +02001756 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001757 goto out_err;
1758 }
1759
Johannes Bergbba95fe2008-07-29 13:22:51 +02001760 while (1) {
1761 err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
1762 mac_addr, &sinfo);
1763 if (err == -ENOENT)
1764 break;
1765 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001766 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001767
1768 if (nl80211_send_station(skb,
1769 NETLINK_CB(cb->skb).pid,
1770 cb->nlh->nlmsg_seq, NLM_F_MULTI,
1771 netdev, mac_addr,
1772 &sinfo) < 0)
1773 goto out;
1774
1775 sta_idx++;
1776 }
1777
1778
1779 out:
1780 cb->args[1] = sta_idx;
1781 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001782 out_err:
Johannes Berg4d0c8ae2009-07-07 03:56:09 +02001783 cfg80211_unlock_rdev(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001784 out_rtnl:
1785 rtnl_unlock();
Johannes Bergbba95fe2008-07-29 13:22:51 +02001786
1787 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001788}
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001789
Johannes Berg5727ef12007-12-19 02:03:34 +01001790static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
1791{
Johannes Berg79c97e92009-07-07 03:56:12 +02001792 struct cfg80211_registered_device *rdev;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001793 int err;
1794 struct net_device *dev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001795 struct station_info sinfo;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001796 struct sk_buff *msg;
1797 u8 *mac_addr = NULL;
1798
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001799 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001800
1801 if (!info->attrs[NL80211_ATTR_MAC])
1802 return -EINVAL;
1803
1804 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1805
Johannes Berg3b858752009-03-12 09:55:09 +01001806 rtnl_lock();
1807
Johannes Berg463d0182009-07-14 00:33:35 +02001808 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001809 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001810 goto out_rtnl;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001811
Johannes Berg79c97e92009-07-07 03:56:12 +02001812 if (!rdev->ops->get_station) {
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001813 err = -EOPNOTSUPP;
1814 goto out;
1815 }
1816
Johannes Berg79c97e92009-07-07 03:56:12 +02001817 err = rdev->ops->get_station(&rdev->wiphy, dev, mac_addr, &sinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001818 if (err)
1819 goto out;
1820
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001821 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001822 if (!msg)
1823 goto out;
1824
1825 if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001826 dev, mac_addr, &sinfo) < 0)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001827 goto out_free;
1828
Johannes Berg134e6372009-07-10 09:51:34 +00001829 err = genlmsg_reply(msg, info);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001830 goto out;
1831
1832 out_free:
1833 nlmsg_free(msg);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001834 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02001835 cfg80211_unlock_rdev(rdev);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001836 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001837 out_rtnl:
1838 rtnl_unlock();
1839
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001840 return err;
Johannes Berg5727ef12007-12-19 02:03:34 +01001841}
1842
1843/*
1844 * Get vlan interface making sure it is on the right wiphy.
1845 */
Johannes Berg463d0182009-07-14 00:33:35 +02001846static int get_vlan(struct genl_info *info,
Johannes Berg5727ef12007-12-19 02:03:34 +01001847 struct cfg80211_registered_device *rdev,
1848 struct net_device **vlan)
1849{
Johannes Berg463d0182009-07-14 00:33:35 +02001850 struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN];
Johannes Berg5727ef12007-12-19 02:03:34 +01001851 *vlan = NULL;
1852
1853 if (vlanattr) {
Johannes Berg463d0182009-07-14 00:33:35 +02001854 *vlan = dev_get_by_index(genl_info_net(info),
1855 nla_get_u32(vlanattr));
Johannes Berg5727ef12007-12-19 02:03:34 +01001856 if (!*vlan)
1857 return -ENODEV;
1858 if (!(*vlan)->ieee80211_ptr)
1859 return -EINVAL;
1860 if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy)
1861 return -EINVAL;
1862 }
1863 return 0;
1864}
1865
1866static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
1867{
Johannes Berg79c97e92009-07-07 03:56:12 +02001868 struct cfg80211_registered_device *rdev;
Johannes Berg5727ef12007-12-19 02:03:34 +01001869 int err;
1870 struct net_device *dev;
1871 struct station_parameters params;
1872 u8 *mac_addr = NULL;
1873
1874 memset(&params, 0, sizeof(params));
1875
1876 params.listen_interval = -1;
1877
1878 if (info->attrs[NL80211_ATTR_STA_AID])
1879 return -EINVAL;
1880
1881 if (!info->attrs[NL80211_ATTR_MAC])
1882 return -EINVAL;
1883
1884 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1885
1886 if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
1887 params.supported_rates =
1888 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1889 params.supported_rates_len =
1890 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1891 }
1892
1893 if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
1894 params.listen_interval =
1895 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
1896
Jouni Malinen36aedc92008-08-25 11:58:58 +03001897 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
1898 params.ht_capa =
1899 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
1900
Johannes Bergeccb8e82009-05-11 21:57:56 +03001901 if (parse_station_flags(info, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01001902 return -EINVAL;
1903
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001904 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
1905 params.plink_action =
1906 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
1907
Johannes Berg3b858752009-03-12 09:55:09 +01001908 rtnl_lock();
1909
Johannes Berg463d0182009-07-14 00:33:35 +02001910 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001911 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001912 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01001913
Johannes Berg463d0182009-07-14 00:33:35 +02001914 err = get_vlan(info, rdev, &params.vlan);
Johannes Berga97f4422009-06-18 17:23:43 +02001915 if (err)
Johannes Berg034d6552009-05-27 10:35:29 +02001916 goto out;
Johannes Berga97f4422009-06-18 17:23:43 +02001917
1918 /* validate settings */
1919 err = 0;
1920
1921 switch (dev->ieee80211_ptr->iftype) {
1922 case NL80211_IFTYPE_AP:
1923 case NL80211_IFTYPE_AP_VLAN:
1924 /* disallow mesh-specific things */
1925 if (params.plink_action)
1926 err = -EINVAL;
1927 break;
1928 case NL80211_IFTYPE_STATION:
1929 /* disallow everything but AUTHORIZED flag */
1930 if (params.plink_action)
1931 err = -EINVAL;
1932 if (params.vlan)
1933 err = -EINVAL;
1934 if (params.supported_rates)
1935 err = -EINVAL;
1936 if (params.ht_capa)
1937 err = -EINVAL;
1938 if (params.listen_interval >= 0)
1939 err = -EINVAL;
1940 if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
1941 err = -EINVAL;
1942 break;
1943 case NL80211_IFTYPE_MESH_POINT:
1944 /* disallow things mesh doesn't support */
1945 if (params.vlan)
1946 err = -EINVAL;
1947 if (params.ht_capa)
1948 err = -EINVAL;
1949 if (params.listen_interval >= 0)
1950 err = -EINVAL;
1951 if (params.supported_rates)
1952 err = -EINVAL;
1953 if (params.sta_flags_mask)
1954 err = -EINVAL;
1955 break;
1956 default:
1957 err = -EINVAL;
Johannes Berg034d6552009-05-27 10:35:29 +02001958 }
1959
Johannes Berg5727ef12007-12-19 02:03:34 +01001960 if (err)
1961 goto out;
1962
Johannes Berg79c97e92009-07-07 03:56:12 +02001963 if (!rdev->ops->change_station) {
Johannes Berg5727ef12007-12-19 02:03:34 +01001964 err = -EOPNOTSUPP;
1965 goto out;
1966 }
1967
Johannes Berg79c97e92009-07-07 03:56:12 +02001968 err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01001969
1970 out:
1971 if (params.vlan)
1972 dev_put(params.vlan);
Johannes Berg79c97e92009-07-07 03:56:12 +02001973 cfg80211_unlock_rdev(rdev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001974 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001975 out_rtnl:
1976 rtnl_unlock();
1977
Johannes Berg5727ef12007-12-19 02:03:34 +01001978 return err;
1979}
1980
1981static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
1982{
Johannes Berg79c97e92009-07-07 03:56:12 +02001983 struct cfg80211_registered_device *rdev;
Johannes Berg5727ef12007-12-19 02:03:34 +01001984 int err;
1985 struct net_device *dev;
1986 struct station_parameters params;
1987 u8 *mac_addr = NULL;
1988
1989 memset(&params, 0, sizeof(params));
1990
1991 if (!info->attrs[NL80211_ATTR_MAC])
1992 return -EINVAL;
1993
Johannes Berg5727ef12007-12-19 02:03:34 +01001994 if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
1995 return -EINVAL;
1996
1997 if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
1998 return -EINVAL;
1999
2000 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2001 params.supported_rates =
2002 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2003 params.supported_rates_len =
2004 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2005 params.listen_interval =
2006 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
Johannes Berg51b50fb2009-05-24 16:42:30 +02002007
Johannes Berga97f4422009-06-18 17:23:43 +02002008 if (info->attrs[NL80211_ATTR_STA_AID]) {
2009 params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
2010 if (!params.aid || params.aid > IEEE80211_MAX_AID)
2011 return -EINVAL;
2012 }
Johannes Berg51b50fb2009-05-24 16:42:30 +02002013
Jouni Malinen36aedc92008-08-25 11:58:58 +03002014 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
2015 params.ht_capa =
2016 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
Johannes Berg5727ef12007-12-19 02:03:34 +01002017
Johannes Bergeccb8e82009-05-11 21:57:56 +03002018 if (parse_station_flags(info, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01002019 return -EINVAL;
2020
Johannes Berg3b858752009-03-12 09:55:09 +01002021 rtnl_lock();
2022
Johannes Berg463d0182009-07-14 00:33:35 +02002023 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01002024 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002025 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01002026
Johannes Berg463d0182009-07-14 00:33:35 +02002027 err = get_vlan(info, rdev, &params.vlan);
Johannes Berga97f4422009-06-18 17:23:43 +02002028 if (err)
Johannes Berge80cf852009-05-11 14:43:13 +02002029 goto out;
Johannes Berga97f4422009-06-18 17:23:43 +02002030
2031 /* validate settings */
2032 err = 0;
2033
2034 switch (dev->ieee80211_ptr->iftype) {
2035 case NL80211_IFTYPE_AP:
2036 case NL80211_IFTYPE_AP_VLAN:
2037 /* all ok but must have AID */
2038 if (!params.aid)
2039 err = -EINVAL;
2040 break;
2041 case NL80211_IFTYPE_MESH_POINT:
2042 /* disallow things mesh doesn't support */
2043 if (params.vlan)
2044 err = -EINVAL;
2045 if (params.aid)
2046 err = -EINVAL;
2047 if (params.ht_capa)
2048 err = -EINVAL;
2049 if (params.listen_interval >= 0)
2050 err = -EINVAL;
2051 if (params.supported_rates)
2052 err = -EINVAL;
2053 if (params.sta_flags_mask)
2054 err = -EINVAL;
2055 break;
2056 default:
2057 err = -EINVAL;
Johannes Berge80cf852009-05-11 14:43:13 +02002058 }
2059
Johannes Berg5727ef12007-12-19 02:03:34 +01002060 if (err)
2061 goto out;
2062
Johannes Berg79c97e92009-07-07 03:56:12 +02002063 if (!rdev->ops->add_station) {
Johannes Berg5727ef12007-12-19 02:03:34 +01002064 err = -EOPNOTSUPP;
2065 goto out;
2066 }
2067
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002068 if (!netif_running(dev)) {
2069 err = -ENETDOWN;
2070 goto out;
2071 }
2072
Johannes Berg79c97e92009-07-07 03:56:12 +02002073 err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01002074
2075 out:
2076 if (params.vlan)
2077 dev_put(params.vlan);
Johannes Berg79c97e92009-07-07 03:56:12 +02002078 cfg80211_unlock_rdev(rdev);
Johannes Berg5727ef12007-12-19 02:03:34 +01002079 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002080 out_rtnl:
2081 rtnl_unlock();
2082
Johannes Berg5727ef12007-12-19 02:03:34 +01002083 return err;
2084}
2085
2086static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
2087{
Johannes Berg79c97e92009-07-07 03:56:12 +02002088 struct cfg80211_registered_device *rdev;
Johannes Berg5727ef12007-12-19 02:03:34 +01002089 int err;
2090 struct net_device *dev;
2091 u8 *mac_addr = NULL;
2092
2093 if (info->attrs[NL80211_ATTR_MAC])
2094 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2095
Johannes Berg3b858752009-03-12 09:55:09 +01002096 rtnl_lock();
2097
Johannes Berg463d0182009-07-14 00:33:35 +02002098 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01002099 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002100 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01002101
Johannes Berge80cf852009-05-11 14:43:13 +02002102 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Andrey Yurovsky155cc9e2009-06-16 11:31:04 -07002103 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
2104 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
Johannes Berge80cf852009-05-11 14:43:13 +02002105 err = -EINVAL;
2106 goto out;
2107 }
2108
Johannes Berg79c97e92009-07-07 03:56:12 +02002109 if (!rdev->ops->del_station) {
Johannes Berg5727ef12007-12-19 02:03:34 +01002110 err = -EOPNOTSUPP;
2111 goto out;
2112 }
2113
Johannes Berg79c97e92009-07-07 03:56:12 +02002114 err = rdev->ops->del_station(&rdev->wiphy, dev, mac_addr);
Johannes Berg5727ef12007-12-19 02:03:34 +01002115
2116 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002117 cfg80211_unlock_rdev(rdev);
Johannes Berg5727ef12007-12-19 02:03:34 +01002118 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002119 out_rtnl:
2120 rtnl_unlock();
2121
Johannes Berg5727ef12007-12-19 02:03:34 +01002122 return err;
2123}
2124
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002125static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq,
2126 int flags, struct net_device *dev,
2127 u8 *dst, u8 *next_hop,
2128 struct mpath_info *pinfo)
2129{
2130 void *hdr;
2131 struct nlattr *pinfoattr;
2132
2133 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
2134 if (!hdr)
2135 return -1;
2136
2137 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2138 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
2139 NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop);
2140
2141 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
2142 if (!pinfoattr)
2143 goto nla_put_failure;
2144 if (pinfo->filled & MPATH_INFO_FRAME_QLEN)
2145 NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
2146 pinfo->frame_qlen);
2147 if (pinfo->filled & MPATH_INFO_DSN)
2148 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DSN,
2149 pinfo->dsn);
2150 if (pinfo->filled & MPATH_INFO_METRIC)
2151 NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC,
2152 pinfo->metric);
2153 if (pinfo->filled & MPATH_INFO_EXPTIME)
2154 NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME,
2155 pinfo->exptime);
2156 if (pinfo->filled & MPATH_INFO_FLAGS)
2157 NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS,
2158 pinfo->flags);
2159 if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT)
2160 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
2161 pinfo->discovery_timeout);
2162 if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES)
2163 NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
2164 pinfo->discovery_retries);
2165
2166 nla_nest_end(msg, pinfoattr);
2167
2168 return genlmsg_end(msg, hdr);
2169
2170 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07002171 genlmsg_cancel(msg, hdr);
2172 return -EMSGSIZE;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002173}
2174
2175static int nl80211_dump_mpath(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02002176 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002177{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002178 struct mpath_info pinfo;
2179 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002180 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002181 u8 dst[ETH_ALEN];
2182 u8 next_hop[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02002183 int ifidx = cb->args[0];
2184 int path_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002185 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002186
Johannes Bergbba95fe2008-07-29 13:22:51 +02002187 if (!ifidx) {
2188 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
2189 nl80211_fam.attrbuf, nl80211_fam.maxattr,
2190 nl80211_policy);
2191 if (err)
2192 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002193
Johannes Bergbba95fe2008-07-29 13:22:51 +02002194 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
2195 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002196
Johannes Bergbba95fe2008-07-29 13:22:51 +02002197 ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
2198 if (!ifidx)
2199 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002200 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002201
Johannes Berg3b858752009-03-12 09:55:09 +01002202 rtnl_lock();
2203
Johannes Berg463d0182009-07-14 00:33:35 +02002204 netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
Johannes Berg3b858752009-03-12 09:55:09 +01002205 if (!netdev) {
2206 err = -ENODEV;
2207 goto out_rtnl;
2208 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002209
Johannes Berg463d0182009-07-14 00:33:35 +02002210 dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02002211 if (IS_ERR(dev)) {
2212 err = PTR_ERR(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002213 goto out_rtnl;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002214 }
2215
2216 if (!dev->ops->dump_mpath) {
Jouni Malineneec60b02009-03-20 21:21:19 +02002217 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002218 goto out_err;
2219 }
2220
Jouni Malineneec60b02009-03-20 21:21:19 +02002221 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2222 err = -EOPNOTSUPP;
2223 goto out;
2224 }
2225
Johannes Bergbba95fe2008-07-29 13:22:51 +02002226 while (1) {
2227 err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
2228 dst, next_hop, &pinfo);
2229 if (err == -ENOENT)
2230 break;
2231 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002232 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002233
2234 if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid,
2235 cb->nlh->nlmsg_seq, NLM_F_MULTI,
2236 netdev, dst, next_hop,
2237 &pinfo) < 0)
2238 goto out;
2239
2240 path_idx++;
2241 }
2242
2243
2244 out:
2245 cb->args[1] = path_idx;
2246 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002247 out_err:
Johannes Berg4d0c8ae2009-07-07 03:56:09 +02002248 cfg80211_unlock_rdev(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002249 out_rtnl:
2250 rtnl_unlock();
Johannes Bergbba95fe2008-07-29 13:22:51 +02002251
2252 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002253}
2254
2255static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
2256{
Johannes Berg79c97e92009-07-07 03:56:12 +02002257 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002258 int err;
2259 struct net_device *dev;
2260 struct mpath_info pinfo;
2261 struct sk_buff *msg;
2262 u8 *dst = NULL;
2263 u8 next_hop[ETH_ALEN];
2264
2265 memset(&pinfo, 0, sizeof(pinfo));
2266
2267 if (!info->attrs[NL80211_ATTR_MAC])
2268 return -EINVAL;
2269
2270 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2271
Johannes Berg3b858752009-03-12 09:55:09 +01002272 rtnl_lock();
2273
Johannes Berg463d0182009-07-14 00:33:35 +02002274 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002275 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002276 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002277
Johannes Berg79c97e92009-07-07 03:56:12 +02002278 if (!rdev->ops->get_mpath) {
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002279 err = -EOPNOTSUPP;
2280 goto out;
2281 }
2282
Jouni Malineneec60b02009-03-20 21:21:19 +02002283 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2284 err = -EOPNOTSUPP;
2285 goto out;
2286 }
2287
Johannes Berg79c97e92009-07-07 03:56:12 +02002288 err = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, &pinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002289 if (err)
2290 goto out;
2291
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002292 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002293 if (!msg)
2294 goto out;
2295
2296 if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0,
2297 dev, dst, next_hop, &pinfo) < 0)
2298 goto out_free;
2299
Johannes Berg134e6372009-07-10 09:51:34 +00002300 err = genlmsg_reply(msg, info);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002301 goto out;
2302
2303 out_free:
2304 nlmsg_free(msg);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002305 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002306 cfg80211_unlock_rdev(rdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002307 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002308 out_rtnl:
2309 rtnl_unlock();
2310
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002311 return err;
2312}
2313
2314static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
2315{
Johannes Berg79c97e92009-07-07 03:56:12 +02002316 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002317 int err;
2318 struct net_device *dev;
2319 u8 *dst = NULL;
2320 u8 *next_hop = NULL;
2321
2322 if (!info->attrs[NL80211_ATTR_MAC])
2323 return -EINVAL;
2324
2325 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
2326 return -EINVAL;
2327
2328 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2329 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
2330
Johannes Berg3b858752009-03-12 09:55:09 +01002331 rtnl_lock();
2332
Johannes Berg463d0182009-07-14 00:33:35 +02002333 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002334 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002335 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002336
Johannes Berg79c97e92009-07-07 03:56:12 +02002337 if (!rdev->ops->change_mpath) {
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002338 err = -EOPNOTSUPP;
2339 goto out;
2340 }
2341
Jouni Malineneec60b02009-03-20 21:21:19 +02002342 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2343 err = -EOPNOTSUPP;
2344 goto out;
2345 }
2346
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002347 if (!netif_running(dev)) {
2348 err = -ENETDOWN;
2349 goto out;
2350 }
2351
Johannes Berg79c97e92009-07-07 03:56:12 +02002352 err = rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002353
2354 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002355 cfg80211_unlock_rdev(rdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002356 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002357 out_rtnl:
2358 rtnl_unlock();
2359
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002360 return err;
2361}
2362static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
2363{
Johannes Berg79c97e92009-07-07 03:56:12 +02002364 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002365 int err;
2366 struct net_device *dev;
2367 u8 *dst = NULL;
2368 u8 *next_hop = NULL;
2369
2370 if (!info->attrs[NL80211_ATTR_MAC])
2371 return -EINVAL;
2372
2373 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
2374 return -EINVAL;
2375
2376 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2377 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
2378
Johannes Berg3b858752009-03-12 09:55:09 +01002379 rtnl_lock();
2380
Johannes Berg463d0182009-07-14 00:33:35 +02002381 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002382 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002383 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002384
Johannes Berg79c97e92009-07-07 03:56:12 +02002385 if (!rdev->ops->add_mpath) {
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002386 err = -EOPNOTSUPP;
2387 goto out;
2388 }
2389
Jouni Malineneec60b02009-03-20 21:21:19 +02002390 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2391 err = -EOPNOTSUPP;
2392 goto out;
2393 }
2394
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002395 if (!netif_running(dev)) {
2396 err = -ENETDOWN;
2397 goto out;
2398 }
2399
Johannes Berg79c97e92009-07-07 03:56:12 +02002400 err = rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002401
2402 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002403 cfg80211_unlock_rdev(rdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002404 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002405 out_rtnl:
2406 rtnl_unlock();
2407
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002408 return err;
2409}
2410
2411static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
2412{
Johannes Berg79c97e92009-07-07 03:56:12 +02002413 struct cfg80211_registered_device *rdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002414 int err;
2415 struct net_device *dev;
2416 u8 *dst = NULL;
2417
2418 if (info->attrs[NL80211_ATTR_MAC])
2419 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2420
Johannes Berg3b858752009-03-12 09:55:09 +01002421 rtnl_lock();
2422
Johannes Berg463d0182009-07-14 00:33:35 +02002423 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002424 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002425 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002426
Johannes Berg79c97e92009-07-07 03:56:12 +02002427 if (!rdev->ops->del_mpath) {
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002428 err = -EOPNOTSUPP;
2429 goto out;
2430 }
2431
Johannes Berg79c97e92009-07-07 03:56:12 +02002432 err = rdev->ops->del_mpath(&rdev->wiphy, dev, dst);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002433
2434 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002435 cfg80211_unlock_rdev(rdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002436 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002437 out_rtnl:
2438 rtnl_unlock();
2439
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002440 return err;
2441}
2442
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002443static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
2444{
Johannes Berg79c97e92009-07-07 03:56:12 +02002445 struct cfg80211_registered_device *rdev;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002446 int err;
2447 struct net_device *dev;
2448 struct bss_parameters params;
2449
2450 memset(&params, 0, sizeof(params));
2451 /* default to not changing parameters */
2452 params.use_cts_prot = -1;
2453 params.use_short_preamble = -1;
2454 params.use_short_slot_time = -1;
2455
2456 if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
2457 params.use_cts_prot =
2458 nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
2459 if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
2460 params.use_short_preamble =
2461 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
2462 if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
2463 params.use_short_slot_time =
2464 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
Jouni Malinen90c97a02008-10-30 16:59:22 +02002465 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
2466 params.basic_rates =
2467 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2468 params.basic_rates_len =
2469 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2470 }
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002471
Johannes Berg3b858752009-03-12 09:55:09 +01002472 rtnl_lock();
2473
Johannes Berg463d0182009-07-14 00:33:35 +02002474 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002475 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002476 goto out_rtnl;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002477
Johannes Berg79c97e92009-07-07 03:56:12 +02002478 if (!rdev->ops->change_bss) {
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002479 err = -EOPNOTSUPP;
2480 goto out;
2481 }
2482
Jouni Malineneec60b02009-03-20 21:21:19 +02002483 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
2484 err = -EOPNOTSUPP;
2485 goto out;
2486 }
2487
Johannes Berg79c97e92009-07-07 03:56:12 +02002488 err = rdev->ops->change_bss(&rdev->wiphy, dev, &params);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002489
2490 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02002491 cfg80211_unlock_rdev(rdev);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002492 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002493 out_rtnl:
2494 rtnl_unlock();
2495
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002496 return err;
2497}
2498
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002499static const struct nla_policy
2500 reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
2501 [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
2502 [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
2503 [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
2504 [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
2505 [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
2506 [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
2507};
2508
2509static int parse_reg_rule(struct nlattr *tb[],
2510 struct ieee80211_reg_rule *reg_rule)
2511{
2512 struct ieee80211_freq_range *freq_range = &reg_rule->freq_range;
2513 struct ieee80211_power_rule *power_rule = &reg_rule->power_rule;
2514
2515 if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
2516 return -EINVAL;
2517 if (!tb[NL80211_ATTR_FREQ_RANGE_START])
2518 return -EINVAL;
2519 if (!tb[NL80211_ATTR_FREQ_RANGE_END])
2520 return -EINVAL;
2521 if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
2522 return -EINVAL;
2523 if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
2524 return -EINVAL;
2525
2526 reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
2527
2528 freq_range->start_freq_khz =
2529 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
2530 freq_range->end_freq_khz =
2531 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
2532 freq_range->max_bandwidth_khz =
2533 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
2534
2535 power_rule->max_eirp =
2536 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
2537
2538 if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
2539 power_rule->max_antenna_gain =
2540 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
2541
2542 return 0;
2543}
2544
2545static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
2546{
2547 int r;
2548 char *data = NULL;
2549
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002550 /*
2551 * You should only get this when cfg80211 hasn't yet initialized
2552 * completely when built-in to the kernel right between the time
2553 * window between nl80211_init() and regulatory_init(), if that is
2554 * even possible.
2555 */
2556 mutex_lock(&cfg80211_mutex);
2557 if (unlikely(!cfg80211_regdomain)) {
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002558 mutex_unlock(&cfg80211_mutex);
2559 return -EINPROGRESS;
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002560 }
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002561 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002562
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002563 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
2564 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002565
2566 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
2567
2568#ifdef CONFIG_WIRELESS_OLD_REGULATORY
2569 /* We ignore world regdom requests with the old regdom setup */
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002570 if (is_world_regdom(data))
2571 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002572#endif
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002573
2574 r = regulatory_hint_user(data);
2575
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002576 return r;
2577}
2578
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002579static int nl80211_get_mesh_params(struct sk_buff *skb,
2580 struct genl_info *info)
2581{
Johannes Berg79c97e92009-07-07 03:56:12 +02002582 struct cfg80211_registered_device *rdev;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002583 struct mesh_config cur_params;
2584 int err;
2585 struct net_device *dev;
2586 void *hdr;
2587 struct nlattr *pinfoattr;
2588 struct sk_buff *msg;
2589
Johannes Berg3b858752009-03-12 09:55:09 +01002590 rtnl_lock();
2591
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002592 /* Look up our device */
Johannes Berg463d0182009-07-14 00:33:35 +02002593 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002594 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002595 goto out_rtnl;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002596
Johannes Berg79c97e92009-07-07 03:56:12 +02002597 if (!rdev->ops->get_mesh_params) {
Jouni Malinenf3f92582009-03-20 17:57:36 +02002598 err = -EOPNOTSUPP;
2599 goto out;
2600 }
2601
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002602 /* Get the mesh params */
Johannes Berg79c97e92009-07-07 03:56:12 +02002603 err = rdev->ops->get_mesh_params(&rdev->wiphy, dev, &cur_params);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002604 if (err)
2605 goto out;
2606
2607 /* Draw up a netlink message to send back */
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002608 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002609 if (!msg) {
2610 err = -ENOBUFS;
2611 goto out;
2612 }
2613 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
2614 NL80211_CMD_GET_MESH_PARAMS);
2615 if (!hdr)
2616 goto nla_put_failure;
2617 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS);
2618 if (!pinfoattr)
2619 goto nla_put_failure;
2620 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2621 NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
2622 cur_params.dot11MeshRetryTimeout);
2623 NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
2624 cur_params.dot11MeshConfirmTimeout);
2625 NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
2626 cur_params.dot11MeshHoldingTimeout);
2627 NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
2628 cur_params.dot11MeshMaxPeerLinks);
2629 NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES,
2630 cur_params.dot11MeshMaxRetries);
2631 NLA_PUT_U8(msg, NL80211_MESHCONF_TTL,
2632 cur_params.dot11MeshTTL);
2633 NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
2634 cur_params.auto_open_plinks);
2635 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
2636 cur_params.dot11MeshHWMPmaxPREQretries);
2637 NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
2638 cur_params.path_refresh_time);
2639 NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
2640 cur_params.min_discovery_timeout);
2641 NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
2642 cur_params.dot11MeshHWMPactivePathTimeout);
2643 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
2644 cur_params.dot11MeshHWMPpreqMinInterval);
2645 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
2646 cur_params.dot11MeshHWMPnetDiameterTraversalTime);
2647 nla_nest_end(msg, pinfoattr);
2648 genlmsg_end(msg, hdr);
Johannes Berg134e6372009-07-10 09:51:34 +00002649 err = genlmsg_reply(msg, info);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002650 goto out;
2651
Johannes Berg3b858752009-03-12 09:55:09 +01002652 nla_put_failure:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002653 genlmsg_cancel(msg, hdr);
2654 err = -EMSGSIZE;
Johannes Berg3b858752009-03-12 09:55:09 +01002655 out:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002656 /* Cleanup */
Johannes Berg79c97e92009-07-07 03:56:12 +02002657 cfg80211_unlock_rdev(rdev);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002658 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002659 out_rtnl:
2660 rtnl_unlock();
2661
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002662 return err;
2663}
2664
2665#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \
2666do {\
2667 if (table[attr_num]) {\
2668 cfg.param = nla_fn(table[attr_num]); \
2669 mask |= (1 << (attr_num - 1)); \
2670 } \
2671} while (0);\
2672
2673static struct nla_policy
2674nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] __read_mostly = {
2675 [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
2676 [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
2677 [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
2678 [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
2679 [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
2680 [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
2681 [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
2682
2683 [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
2684 [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
2685 [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
2686 [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
2687 [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
2688 [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
2689};
2690
2691static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
2692{
2693 int err;
2694 u32 mask;
Johannes Berg79c97e92009-07-07 03:56:12 +02002695 struct cfg80211_registered_device *rdev;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002696 struct net_device *dev;
2697 struct mesh_config cfg;
2698 struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
2699 struct nlattr *parent_attr;
2700
2701 parent_attr = info->attrs[NL80211_ATTR_MESH_PARAMS];
2702 if (!parent_attr)
2703 return -EINVAL;
2704 if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
2705 parent_attr, nl80211_meshconf_params_policy))
2706 return -EINVAL;
2707
Johannes Berg3b858752009-03-12 09:55:09 +01002708 rtnl_lock();
2709
Johannes Berg463d0182009-07-14 00:33:35 +02002710 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002711 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002712 goto out_rtnl;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002713
Johannes Berg79c97e92009-07-07 03:56:12 +02002714 if (!rdev->ops->set_mesh_params) {
Jouni Malinenf3f92582009-03-20 17:57:36 +02002715 err = -EOPNOTSUPP;
2716 goto out;
2717 }
2718
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002719 /* This makes sure that there aren't more than 32 mesh config
2720 * parameters (otherwise our bitfield scheme would not work.) */
2721 BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
2722
2723 /* Fill in the params struct */
2724 mask = 0;
2725 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout,
2726 mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16);
2727 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout,
2728 mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16);
2729 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout,
2730 mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16);
2731 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks,
2732 mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16);
2733 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries,
2734 mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8);
2735 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL,
2736 mask, NL80211_MESHCONF_TTL, nla_get_u8);
2737 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
2738 mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8);
2739 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
2740 mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
2741 nla_get_u8);
2742 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time,
2743 mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32);
2744 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout,
2745 mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
2746 nla_get_u16);
2747 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
2748 mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
2749 nla_get_u32);
2750 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
2751 mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
2752 nla_get_u16);
2753 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
2754 dot11MeshHWMPnetDiameterTraversalTime,
2755 mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
2756 nla_get_u16);
2757
2758 /* Apply changes */
Johannes Berg79c97e92009-07-07 03:56:12 +02002759 err = rdev->ops->set_mesh_params(&rdev->wiphy, dev, &cfg, mask);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002760
Jouni Malinenf3f92582009-03-20 17:57:36 +02002761 out:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002762 /* cleanup */
Johannes Berg79c97e92009-07-07 03:56:12 +02002763 cfg80211_unlock_rdev(rdev);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002764 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002765 out_rtnl:
2766 rtnl_unlock();
2767
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002768 return err;
2769}
2770
2771#undef FILL_IN_MESH_PARAM_IF_SET
2772
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002773static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
2774{
2775 struct sk_buff *msg;
2776 void *hdr = NULL;
2777 struct nlattr *nl_reg_rules;
2778 unsigned int i;
2779 int err = -EINVAL;
2780
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002781 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002782
2783 if (!cfg80211_regdomain)
2784 goto out;
2785
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002786 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002787 if (!msg) {
2788 err = -ENOBUFS;
2789 goto out;
2790 }
2791
2792 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
2793 NL80211_CMD_GET_REG);
2794 if (!hdr)
2795 goto nla_put_failure;
2796
2797 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2,
2798 cfg80211_regdomain->alpha2);
2799
2800 nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
2801 if (!nl_reg_rules)
2802 goto nla_put_failure;
2803
2804 for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) {
2805 struct nlattr *nl_reg_rule;
2806 const struct ieee80211_reg_rule *reg_rule;
2807 const struct ieee80211_freq_range *freq_range;
2808 const struct ieee80211_power_rule *power_rule;
2809
2810 reg_rule = &cfg80211_regdomain->reg_rules[i];
2811 freq_range = &reg_rule->freq_range;
2812 power_rule = &reg_rule->power_rule;
2813
2814 nl_reg_rule = nla_nest_start(msg, i);
2815 if (!nl_reg_rule)
2816 goto nla_put_failure;
2817
2818 NLA_PUT_U32(msg, NL80211_ATTR_REG_RULE_FLAGS,
2819 reg_rule->flags);
2820 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_START,
2821 freq_range->start_freq_khz);
2822 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_END,
2823 freq_range->end_freq_khz);
2824 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
2825 freq_range->max_bandwidth_khz);
2826 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
2827 power_rule->max_antenna_gain);
2828 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
2829 power_rule->max_eirp);
2830
2831 nla_nest_end(msg, nl_reg_rule);
2832 }
2833
2834 nla_nest_end(msg, nl_reg_rules);
2835
2836 genlmsg_end(msg, hdr);
Johannes Berg134e6372009-07-10 09:51:34 +00002837 err = genlmsg_reply(msg, info);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002838 goto out;
2839
2840nla_put_failure:
2841 genlmsg_cancel(msg, hdr);
2842 err = -EMSGSIZE;
2843out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002844 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002845 return err;
2846}
2847
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002848static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
2849{
2850 struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
2851 struct nlattr *nl_reg_rule;
2852 char *alpha2 = NULL;
2853 int rem_reg_rules = 0, r = 0;
2854 u32 num_rules = 0, rule_idx = 0, size_of_regd;
2855 struct ieee80211_regdomain *rd = NULL;
2856
2857 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
2858 return -EINVAL;
2859
2860 if (!info->attrs[NL80211_ATTR_REG_RULES])
2861 return -EINVAL;
2862
2863 alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
2864
2865 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
2866 rem_reg_rules) {
2867 num_rules++;
2868 if (num_rules > NL80211_MAX_SUPP_REG_RULES)
Luis R. Rodriguez4776c6e2009-05-13 17:04:39 -04002869 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002870 }
2871
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04002872 mutex_lock(&cfg80211_mutex);
2873
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002874 if (!reg_is_valid_request(alpha2)) {
2875 r = -EINVAL;
2876 goto bad_reg;
2877 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002878
2879 size_of_regd = sizeof(struct ieee80211_regdomain) +
2880 (num_rules * sizeof(struct ieee80211_reg_rule));
2881
2882 rd = kzalloc(size_of_regd, GFP_KERNEL);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002883 if (!rd) {
2884 r = -ENOMEM;
2885 goto bad_reg;
2886 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002887
2888 rd->n_reg_rules = num_rules;
2889 rd->alpha2[0] = alpha2[0];
2890 rd->alpha2[1] = alpha2[1];
2891
2892 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
2893 rem_reg_rules) {
2894 nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
2895 nla_data(nl_reg_rule), nla_len(nl_reg_rule),
2896 reg_rule_policy);
2897 r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
2898 if (r)
2899 goto bad_reg;
2900
2901 rule_idx++;
2902
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002903 if (rule_idx > NL80211_MAX_SUPP_REG_RULES) {
2904 r = -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002905 goto bad_reg;
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002906 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002907 }
2908
2909 BUG_ON(rule_idx != num_rules);
2910
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002911 r = set_regdom(rd);
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04002912
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002913 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002914
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002915 return r;
2916
Johannes Bergd2372b32008-10-24 20:32:20 +02002917 bad_reg:
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04002918 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002919 kfree(rd);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04002920 return r;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002921}
2922
Johannes Berg83f5e2c2009-06-17 17:41:49 +02002923static int validate_scan_freqs(struct nlattr *freqs)
2924{
2925 struct nlattr *attr1, *attr2;
2926 int n_channels = 0, tmp1, tmp2;
2927
2928 nla_for_each_nested(attr1, freqs, tmp1) {
2929 n_channels++;
2930 /*
2931 * Some hardware has a limited channel list for
2932 * scanning, and it is pretty much nonsensical
2933 * to scan for a channel twice, so disallow that
2934 * and don't require drivers to check that the
2935 * channel list they get isn't longer than what
2936 * they can scan, as long as they can scan all
2937 * the channels they registered at once.
2938 */
2939 nla_for_each_nested(attr2, freqs, tmp2)
2940 if (attr1 != attr2 &&
2941 nla_get_u32(attr1) == nla_get_u32(attr2))
2942 return 0;
2943 }
2944
2945 return n_channels;
2946}
2947
Johannes Berg2a519312009-02-10 21:25:55 +01002948static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
2949{
Johannes Berg79c97e92009-07-07 03:56:12 +02002950 struct cfg80211_registered_device *rdev;
Johannes Berg2a519312009-02-10 21:25:55 +01002951 struct net_device *dev;
2952 struct cfg80211_scan_request *request;
2953 struct cfg80211_ssid *ssid;
2954 struct ieee80211_channel *channel;
2955 struct nlattr *attr;
2956 struct wiphy *wiphy;
Johannes Berg83f5e2c2009-06-17 17:41:49 +02002957 int err, tmp, n_ssids = 0, n_channels, i;
Johannes Berg2a519312009-02-10 21:25:55 +01002958 enum ieee80211_band band;
Jouni Malinen70692ad2009-02-16 19:39:13 +02002959 size_t ie_len;
Johannes Berg2a519312009-02-10 21:25:55 +01002960
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002961 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
2962 return -EINVAL;
2963
Johannes Berg3b858752009-03-12 09:55:09 +01002964 rtnl_lock();
2965
Johannes Berg463d0182009-07-14 00:33:35 +02002966 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg2a519312009-02-10 21:25:55 +01002967 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002968 goto out_rtnl;
Johannes Berg2a519312009-02-10 21:25:55 +01002969
Johannes Berg79c97e92009-07-07 03:56:12 +02002970 wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01002971
Johannes Berg79c97e92009-07-07 03:56:12 +02002972 if (!rdev->ops->scan) {
Johannes Berg2a519312009-02-10 21:25:55 +01002973 err = -EOPNOTSUPP;
2974 goto out;
2975 }
2976
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002977 if (!netif_running(dev)) {
2978 err = -ENETDOWN;
2979 goto out;
2980 }
2981
Johannes Berg79c97e92009-07-07 03:56:12 +02002982 if (rdev->scan_req) {
Johannes Berg2a519312009-02-10 21:25:55 +01002983 err = -EBUSY;
Johannes Berg3b858752009-03-12 09:55:09 +01002984 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002985 }
2986
2987 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
Johannes Berg83f5e2c2009-06-17 17:41:49 +02002988 n_channels = validate_scan_freqs(
2989 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
Johannes Berg2a519312009-02-10 21:25:55 +01002990 if (!n_channels) {
2991 err = -EINVAL;
Johannes Berg3b858752009-03-12 09:55:09 +01002992 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002993 }
2994 } else {
Johannes Berg83f5e2c2009-06-17 17:41:49 +02002995 n_channels = 0;
2996
Johannes Berg2a519312009-02-10 21:25:55 +01002997 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
2998 if (wiphy->bands[band])
2999 n_channels += wiphy->bands[band]->n_channels;
3000 }
3001
3002 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
3003 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
3004 n_ssids++;
3005
3006 if (n_ssids > wiphy->max_scan_ssids) {
3007 err = -EINVAL;
Johannes Berg3b858752009-03-12 09:55:09 +01003008 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01003009 }
3010
Jouni Malinen70692ad2009-02-16 19:39:13 +02003011 if (info->attrs[NL80211_ATTR_IE])
3012 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3013 else
3014 ie_len = 0;
3015
Johannes Berg18a83652009-03-31 12:12:05 +02003016 if (ie_len > wiphy->max_scan_ie_len) {
3017 err = -EINVAL;
3018 goto out;
3019 }
3020
Johannes Berg2a519312009-02-10 21:25:55 +01003021 request = kzalloc(sizeof(*request)
3022 + sizeof(*ssid) * n_ssids
Jouni Malinen70692ad2009-02-16 19:39:13 +02003023 + sizeof(channel) * n_channels
3024 + ie_len, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01003025 if (!request) {
3026 err = -ENOMEM;
Johannes Berg3b858752009-03-12 09:55:09 +01003027 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01003028 }
3029
3030 request->channels = (void *)((char *)request + sizeof(*request));
3031 request->n_channels = n_channels;
3032 if (n_ssids)
3033 request->ssids = (void *)(request->channels + n_channels);
3034 request->n_ssids = n_ssids;
Jouni Malinen70692ad2009-02-16 19:39:13 +02003035 if (ie_len) {
3036 if (request->ssids)
3037 request->ie = (void *)(request->ssids + n_ssids);
3038 else
3039 request->ie = (void *)(request->channels + n_channels);
3040 }
Johannes Berg2a519312009-02-10 21:25:55 +01003041
3042 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
3043 /* user specified, bail out if channel not found */
3044 request->n_channels = n_channels;
3045 i = 0;
3046 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
3047 request->channels[i] = ieee80211_get_channel(wiphy, nla_get_u32(attr));
3048 if (!request->channels[i]) {
3049 err = -EINVAL;
3050 goto out_free;
3051 }
3052 i++;
3053 }
3054 } else {
3055 /* all channels */
3056 i = 0;
3057 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
3058 int j;
3059 if (!wiphy->bands[band])
3060 continue;
3061 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
3062 request->channels[i] = &wiphy->bands[band]->channels[j];
3063 i++;
3064 }
3065 }
3066 }
3067
3068 i = 0;
3069 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
3070 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
3071 if (request->ssids[i].ssid_len > IEEE80211_MAX_SSID_LEN) {
3072 err = -EINVAL;
3073 goto out_free;
3074 }
3075 memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
3076 request->ssids[i].ssid_len = nla_len(attr);
3077 i++;
3078 }
3079 }
3080
Jouni Malinen70692ad2009-02-16 19:39:13 +02003081 if (info->attrs[NL80211_ATTR_IE]) {
3082 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Johannes Bergde95a542009-04-01 11:58:36 +02003083 memcpy((void *)request->ie,
3084 nla_data(info->attrs[NL80211_ATTR_IE]),
Jouni Malinen70692ad2009-02-16 19:39:13 +02003085 request->ie_len);
3086 }
3087
Johannes Berg463d0182009-07-14 00:33:35 +02003088 request->dev = dev;
Johannes Berg79c97e92009-07-07 03:56:12 +02003089 request->wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01003090
Johannes Berg79c97e92009-07-07 03:56:12 +02003091 rdev->scan_req = request;
3092 err = rdev->ops->scan(&rdev->wiphy, dev, request);
Johannes Berg2a519312009-02-10 21:25:55 +01003093
Johannes Berg463d0182009-07-14 00:33:35 +02003094 if (!err) {
Johannes Berg79c97e92009-07-07 03:56:12 +02003095 nl80211_send_scan_start(rdev, dev);
Johannes Berg463d0182009-07-14 00:33:35 +02003096 dev_hold(dev);
3097 }
Johannes Berga538e2d2009-06-16 19:56:42 +02003098
Johannes Berg2a519312009-02-10 21:25:55 +01003099 out_free:
3100 if (err) {
Johannes Berg79c97e92009-07-07 03:56:12 +02003101 rdev->scan_req = NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01003102 kfree(request);
3103 }
Johannes Berg2a519312009-02-10 21:25:55 +01003104 out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003105 cfg80211_unlock_rdev(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003106 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01003107 out_rtnl:
3108 rtnl_unlock();
3109
Johannes Berg2a519312009-02-10 21:25:55 +01003110 return err;
3111}
3112
3113static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
3114 struct cfg80211_registered_device *rdev,
Johannes Berg48ab9052009-07-10 18:42:31 +02003115 struct wireless_dev *wdev,
3116 struct cfg80211_internal_bss *intbss)
Johannes Berg2a519312009-02-10 21:25:55 +01003117{
Johannes Berg48ab9052009-07-10 18:42:31 +02003118 struct cfg80211_bss *res = &intbss->pub;
Johannes Berg2a519312009-02-10 21:25:55 +01003119 void *hdr;
3120 struct nlattr *bss;
Johannes Berg48ab9052009-07-10 18:42:31 +02003121 int i;
3122
3123 ASSERT_WDEV_LOCK(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003124
3125 hdr = nl80211hdr_put(msg, pid, seq, flags,
3126 NL80211_CMD_NEW_SCAN_RESULTS);
3127 if (!hdr)
3128 return -1;
3129
3130 NLA_PUT_U32(msg, NL80211_ATTR_SCAN_GENERATION,
3131 rdev->bss_generation);
Johannes Berg48ab9052009-07-10 18:42:31 +02003132 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex);
Johannes Berg2a519312009-02-10 21:25:55 +01003133
3134 bss = nla_nest_start(msg, NL80211_ATTR_BSS);
3135 if (!bss)
3136 goto nla_put_failure;
3137 if (!is_zero_ether_addr(res->bssid))
3138 NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid);
3139 if (res->information_elements && res->len_information_elements)
3140 NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS,
3141 res->len_information_elements,
3142 res->information_elements);
3143 if (res->tsf)
3144 NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf);
3145 if (res->beacon_interval)
3146 NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval);
3147 NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability);
3148 NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq);
3149
Johannes Berg77965c92009-02-18 18:45:06 +01003150 switch (rdev->wiphy.signal_type) {
Johannes Berg2a519312009-02-10 21:25:55 +01003151 case CFG80211_SIGNAL_TYPE_MBM:
3152 NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal);
3153 break;
3154 case CFG80211_SIGNAL_TYPE_UNSPEC:
3155 NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal);
3156 break;
3157 default:
3158 break;
3159 }
3160
Johannes Berg48ab9052009-07-10 18:42:31 +02003161 switch (wdev->iftype) {
3162 case NL80211_IFTYPE_STATION:
3163 if (intbss == wdev->current_bss)
3164 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
3165 NL80211_BSS_STATUS_ASSOCIATED);
3166 else for (i = 0; i < MAX_AUTH_BSSES; i++) {
3167 if (intbss != wdev->auth_bsses[i])
3168 continue;
3169 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
3170 NL80211_BSS_STATUS_AUTHENTICATED);
3171 break;
3172 }
3173 break;
3174 case NL80211_IFTYPE_ADHOC:
3175 if (intbss == wdev->current_bss)
3176 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
3177 NL80211_BSS_STATUS_IBSS_JOINED);
3178 break;
3179 default:
3180 break;
3181 }
3182
Johannes Berg2a519312009-02-10 21:25:55 +01003183 nla_nest_end(msg, bss);
3184
3185 return genlmsg_end(msg, hdr);
3186
3187 nla_put_failure:
3188 genlmsg_cancel(msg, hdr);
3189 return -EMSGSIZE;
3190}
3191
3192static int nl80211_dump_scan(struct sk_buff *skb,
3193 struct netlink_callback *cb)
3194{
Johannes Berg48ab9052009-07-10 18:42:31 +02003195 struct cfg80211_registered_device *rdev;
3196 struct net_device *dev;
Johannes Berg2a519312009-02-10 21:25:55 +01003197 struct cfg80211_internal_bss *scan;
Johannes Berg48ab9052009-07-10 18:42:31 +02003198 struct wireless_dev *wdev;
Johannes Berg2a519312009-02-10 21:25:55 +01003199 int ifidx = cb->args[0];
3200 int start = cb->args[1], idx = 0;
3201 int err;
3202
3203 if (!ifidx) {
3204 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
3205 nl80211_fam.attrbuf, nl80211_fam.maxattr,
3206 nl80211_policy);
3207 if (err)
3208 return err;
3209
3210 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
3211 return -EINVAL;
3212
3213 ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
3214 if (!ifidx)
3215 return -EINVAL;
3216 cb->args[0] = ifidx;
3217 }
3218
Johannes Berg463d0182009-07-14 00:33:35 +02003219 dev = dev_get_by_index(sock_net(skb->sk), ifidx);
Johannes Berg48ab9052009-07-10 18:42:31 +02003220 if (!dev)
Johannes Berg2a519312009-02-10 21:25:55 +01003221 return -ENODEV;
3222
Johannes Berg463d0182009-07-14 00:33:35 +02003223 rdev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Johannes Berg48ab9052009-07-10 18:42:31 +02003224 if (IS_ERR(rdev)) {
3225 err = PTR_ERR(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003226 goto out_put_netdev;
3227 }
3228
Johannes Berg48ab9052009-07-10 18:42:31 +02003229 wdev = dev->ieee80211_ptr;
Johannes Berg2a519312009-02-10 21:25:55 +01003230
Johannes Berg48ab9052009-07-10 18:42:31 +02003231 wdev_lock(wdev);
3232 spin_lock_bh(&rdev->bss_lock);
3233 cfg80211_bss_expire(rdev);
3234
3235 list_for_each_entry(scan, &rdev->bss_list, list) {
Johannes Berg2a519312009-02-10 21:25:55 +01003236 if (++idx <= start)
3237 continue;
3238 if (nl80211_send_bss(skb,
3239 NETLINK_CB(cb->skb).pid,
3240 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Berg48ab9052009-07-10 18:42:31 +02003241 rdev, wdev, scan) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01003242 idx--;
3243 goto out;
3244 }
3245 }
3246
3247 out:
Johannes Berg48ab9052009-07-10 18:42:31 +02003248 spin_unlock_bh(&rdev->bss_lock);
3249 wdev_unlock(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003250
3251 cb->args[1] = idx;
3252 err = skb->len;
Johannes Berg48ab9052009-07-10 18:42:31 +02003253 cfg80211_unlock_rdev(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003254 out_put_netdev:
Johannes Berg48ab9052009-07-10 18:42:31 +02003255 dev_put(dev);
Johannes Berg2a519312009-02-10 21:25:55 +01003256
3257 return err;
3258}
3259
Jouni Malinen255e7372009-03-20 21:21:17 +02003260static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
3261{
Samuel Ortizb23aa672009-07-01 21:26:54 +02003262 return auth_type <= NL80211_AUTHTYPE_MAX;
Jouni Malinen255e7372009-03-20 21:21:17 +02003263}
3264
Samuel Ortizb23aa672009-07-01 21:26:54 +02003265static bool nl80211_valid_wpa_versions(u32 wpa_versions)
3266{
3267 return !(wpa_versions & ~(NL80211_WPA_VERSION_1 |
3268 NL80211_WPA_VERSION_2));
3269}
3270
3271static bool nl80211_valid_akm_suite(u32 akm)
3272{
3273 return akm == WLAN_AKM_SUITE_8021X ||
3274 akm == WLAN_AKM_SUITE_PSK;
3275}
3276
3277static bool nl80211_valid_cipher_suite(u32 cipher)
3278{
3279 return cipher == WLAN_CIPHER_SUITE_WEP40 ||
3280 cipher == WLAN_CIPHER_SUITE_WEP104 ||
3281 cipher == WLAN_CIPHER_SUITE_TKIP ||
3282 cipher == WLAN_CIPHER_SUITE_CCMP ||
3283 cipher == WLAN_CIPHER_SUITE_AES_CMAC;
3284}
3285
3286
Jouni Malinen636a5d32009-03-19 13:39:22 +02003287static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
3288{
Johannes Berg79c97e92009-07-07 03:56:12 +02003289 struct cfg80211_registered_device *rdev;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003290 struct net_device *dev;
Johannes Berg19957bb2009-07-02 17:20:43 +02003291 struct ieee80211_channel *chan;
3292 const u8 *bssid, *ssid, *ie = NULL;
3293 int err, ssid_len, ie_len = 0;
3294 enum nl80211_auth_type auth_type;
Johannes Bergfffd0932009-07-08 14:22:54 +02003295 struct key_parse key;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003296
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003297 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3298 return -EINVAL;
3299
3300 if (!info->attrs[NL80211_ATTR_MAC])
3301 return -EINVAL;
3302
Jouni Malinen17780922009-03-27 20:52:47 +02003303 if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
3304 return -EINVAL;
3305
Johannes Berg19957bb2009-07-02 17:20:43 +02003306 if (!info->attrs[NL80211_ATTR_SSID])
3307 return -EINVAL;
3308
3309 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
3310 return -EINVAL;
3311
Johannes Bergfffd0932009-07-08 14:22:54 +02003312 err = nl80211_parse_key(info, &key);
3313 if (err)
3314 return err;
3315
3316 if (key.idx >= 0) {
3317 if (!key.p.key || !key.p.key_len)
3318 return -EINVAL;
3319 if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 ||
3320 key.p.key_len != WLAN_KEY_LEN_WEP40) &&
3321 (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 ||
3322 key.p.key_len != WLAN_KEY_LEN_WEP104))
3323 return -EINVAL;
3324 if (key.idx > 4)
3325 return -EINVAL;
3326 } else {
3327 key.p.key_len = 0;
3328 key.p.key = NULL;
3329 }
3330
Jouni Malinen636a5d32009-03-19 13:39:22 +02003331 rtnl_lock();
3332
Johannes Berg463d0182009-07-14 00:33:35 +02003333 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003334 if (err)
3335 goto unlock_rtnl;
3336
Johannes Berg79c97e92009-07-07 03:56:12 +02003337 if (!rdev->ops->auth) {
Jouni Malinen636a5d32009-03-19 13:39:22 +02003338 err = -EOPNOTSUPP;
3339 goto out;
3340 }
3341
Jouni Malineneec60b02009-03-20 21:21:19 +02003342 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
3343 err = -EOPNOTSUPP;
3344 goto out;
3345 }
3346
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003347 if (!netif_running(dev)) {
3348 err = -ENETDOWN;
3349 goto out;
3350 }
3351
Johannes Berg19957bb2009-07-02 17:20:43 +02003352 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg79c97e92009-07-07 03:56:12 +02003353 chan = ieee80211_get_channel(&rdev->wiphy,
Johannes Berg19957bb2009-07-02 17:20:43 +02003354 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
3355 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) {
Jouni Malinen17780922009-03-27 20:52:47 +02003356 err = -EINVAL;
3357 goto out;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003358 }
3359
Johannes Berg19957bb2009-07-02 17:20:43 +02003360 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3361 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
3362
3363 if (info->attrs[NL80211_ATTR_IE]) {
3364 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3365 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3366 }
3367
3368 auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
3369 if (!nl80211_valid_auth_type(auth_type)) {
3370 err = -EINVAL;
3371 goto out;
3372 }
3373
Johannes Berg79c97e92009-07-07 03:56:12 +02003374 err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
Johannes Bergfffd0932009-07-08 14:22:54 +02003375 ssid, ssid_len, ie, ie_len,
3376 key.p.key, key.p.key_len, key.idx);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003377
3378out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003379 cfg80211_unlock_rdev(rdev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003380 dev_put(dev);
3381unlock_rtnl:
3382 rtnl_unlock();
3383 return err;
3384}
3385
Samuel Ortizb23aa672009-07-01 21:26:54 +02003386static int nl80211_crypto_settings(struct genl_info *info,
Johannes Berg3dc27d22009-07-02 21:36:37 +02003387 struct cfg80211_crypto_settings *settings,
3388 int cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02003389{
Johannes Bergc0b2bbd2009-07-25 16:54:36 +02003390 memset(settings, 0, sizeof(*settings));
3391
Samuel Ortizb23aa672009-07-01 21:26:54 +02003392 settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT];
3393
3394 if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) {
3395 void *data;
3396 int len, i;
3397
3398 data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
3399 len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
3400 settings->n_ciphers_pairwise = len / sizeof(u32);
3401
3402 if (len % sizeof(u32))
3403 return -EINVAL;
3404
Johannes Berg3dc27d22009-07-02 21:36:37 +02003405 if (settings->n_ciphers_pairwise > cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02003406 return -EINVAL;
3407
3408 memcpy(settings->ciphers_pairwise, data, len);
3409
3410 for (i = 0; i < settings->n_ciphers_pairwise; i++)
3411 if (!nl80211_valid_cipher_suite(
3412 settings->ciphers_pairwise[i]))
3413 return -EINVAL;
3414 }
3415
3416 if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) {
3417 settings->cipher_group =
3418 nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]);
3419 if (!nl80211_valid_cipher_suite(settings->cipher_group))
3420 return -EINVAL;
3421 }
3422
3423 if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) {
3424 settings->wpa_versions =
3425 nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]);
3426 if (!nl80211_valid_wpa_versions(settings->wpa_versions))
3427 return -EINVAL;
3428 }
3429
3430 if (info->attrs[NL80211_ATTR_AKM_SUITES]) {
3431 void *data;
3432 int len, i;
3433
3434 data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]);
3435 len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]);
3436 settings->n_akm_suites = len / sizeof(u32);
3437
3438 if (len % sizeof(u32))
3439 return -EINVAL;
3440
3441 memcpy(settings->akm_suites, data, len);
3442
3443 for (i = 0; i < settings->n_ciphers_pairwise; i++)
3444 if (!nl80211_valid_akm_suite(settings->akm_suites[i]))
3445 return -EINVAL;
3446 }
3447
3448 return 0;
3449}
3450
Jouni Malinen636a5d32009-03-19 13:39:22 +02003451static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
3452{
Johannes Berg19957bb2009-07-02 17:20:43 +02003453 struct cfg80211_registered_device *rdev;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003454 struct net_device *dev;
Johannes Berg19957bb2009-07-02 17:20:43 +02003455 struct cfg80211_crypto_settings crypto;
3456 struct ieee80211_channel *chan;
Johannes Berg3e5d7642009-07-07 14:37:26 +02003457 const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +02003458 int err, ssid_len, ie_len = 0;
3459 bool use_mfp = false;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003460
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003461 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3462 return -EINVAL;
3463
3464 if (!info->attrs[NL80211_ATTR_MAC] ||
Johannes Berg19957bb2009-07-02 17:20:43 +02003465 !info->attrs[NL80211_ATTR_SSID] ||
3466 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003467 return -EINVAL;
3468
Jouni Malinen636a5d32009-03-19 13:39:22 +02003469 rtnl_lock();
3470
Johannes Berg463d0182009-07-14 00:33:35 +02003471 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003472 if (err)
3473 goto unlock_rtnl;
3474
Johannes Berg19957bb2009-07-02 17:20:43 +02003475 if (!rdev->ops->assoc) {
Jouni Malinen636a5d32009-03-19 13:39:22 +02003476 err = -EOPNOTSUPP;
3477 goto out;
3478 }
3479
Jouni Malineneec60b02009-03-20 21:21:19 +02003480 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
3481 err = -EOPNOTSUPP;
3482 goto out;
3483 }
3484
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003485 if (!netif_running(dev)) {
3486 err = -ENETDOWN;
3487 goto out;
3488 }
3489
Johannes Berg19957bb2009-07-02 17:20:43 +02003490 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003491
Johannes Berg19957bb2009-07-02 17:20:43 +02003492 chan = ieee80211_get_channel(&rdev->wiphy,
3493 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
3494 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) {
3495 err = -EINVAL;
3496 goto out;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003497 }
3498
Johannes Berg19957bb2009-07-02 17:20:43 +02003499 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3500 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003501
3502 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02003503 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3504 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003505 }
3506
Jouni Malinendc6382c2009-05-06 22:09:37 +03003507 if (info->attrs[NL80211_ATTR_USE_MFP]) {
Johannes Berg4f5dadc2009-07-07 03:56:10 +02003508 enum nl80211_mfp mfp =
Jouni Malinendc6382c2009-05-06 22:09:37 +03003509 nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
Johannes Berg4f5dadc2009-07-07 03:56:10 +02003510 if (mfp == NL80211_MFP_REQUIRED)
Johannes Berg19957bb2009-07-02 17:20:43 +02003511 use_mfp = true;
Johannes Berg4f5dadc2009-07-07 03:56:10 +02003512 else if (mfp != NL80211_MFP_NO) {
Jouni Malinendc6382c2009-05-06 22:09:37 +03003513 err = -EINVAL;
3514 goto out;
3515 }
3516 }
3517
Johannes Berg3e5d7642009-07-07 14:37:26 +02003518 if (info->attrs[NL80211_ATTR_PREV_BSSID])
3519 prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
3520
Johannes Berg3dc27d22009-07-02 21:36:37 +02003521 err = nl80211_crypto_settings(info, &crypto, 1);
Samuel Ortizb23aa672009-07-01 21:26:54 +02003522 if (!err)
Johannes Berg3e5d7642009-07-07 14:37:26 +02003523 err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
3524 ssid, ssid_len, ie, ie_len, use_mfp,
Johannes Berg19957bb2009-07-02 17:20:43 +02003525 &crypto);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003526
3527out:
Johannes Berg4d0c8ae2009-07-07 03:56:09 +02003528 cfg80211_unlock_rdev(rdev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003529 dev_put(dev);
3530unlock_rtnl:
3531 rtnl_unlock();
3532 return err;
3533}
3534
3535static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
3536{
Johannes Berg79c97e92009-07-07 03:56:12 +02003537 struct cfg80211_registered_device *rdev;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003538 struct net_device *dev;
Johannes Berg19957bb2009-07-02 17:20:43 +02003539 const u8 *ie = NULL, *bssid;
3540 int err, ie_len = 0;
3541 u16 reason_code;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003542
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003543 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3544 return -EINVAL;
3545
3546 if (!info->attrs[NL80211_ATTR_MAC])
3547 return -EINVAL;
3548
3549 if (!info->attrs[NL80211_ATTR_REASON_CODE])
3550 return -EINVAL;
3551
Jouni Malinen636a5d32009-03-19 13:39:22 +02003552 rtnl_lock();
3553
Johannes Berg463d0182009-07-14 00:33:35 +02003554 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003555 if (err)
3556 goto unlock_rtnl;
3557
Johannes Berg79c97e92009-07-07 03:56:12 +02003558 if (!rdev->ops->deauth) {
Jouni Malinen636a5d32009-03-19 13:39:22 +02003559 err = -EOPNOTSUPP;
3560 goto out;
3561 }
3562
Jouni Malineneec60b02009-03-20 21:21:19 +02003563 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
3564 err = -EOPNOTSUPP;
3565 goto out;
3566 }
3567
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003568 if (!netif_running(dev)) {
3569 err = -ENETDOWN;
3570 goto out;
3571 }
3572
Johannes Berg19957bb2009-07-02 17:20:43 +02003573 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003574
Johannes Berg19957bb2009-07-02 17:20:43 +02003575 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
3576 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003577 /* Reason Code 0 is reserved */
3578 err = -EINVAL;
3579 goto out;
Jouni Malinen255e7372009-03-20 21:21:17 +02003580 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02003581
3582 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02003583 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3584 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003585 }
3586
Johannes Berg79c97e92009-07-07 03:56:12 +02003587 err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003588
3589out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003590 cfg80211_unlock_rdev(rdev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003591 dev_put(dev);
3592unlock_rtnl:
3593 rtnl_unlock();
3594 return err;
3595}
3596
3597static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
3598{
Johannes Berg79c97e92009-07-07 03:56:12 +02003599 struct cfg80211_registered_device *rdev;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003600 struct net_device *dev;
Johannes Berg19957bb2009-07-02 17:20:43 +02003601 const u8 *ie = NULL, *bssid;
3602 int err, ie_len = 0;
3603 u16 reason_code;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003604
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003605 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3606 return -EINVAL;
3607
3608 if (!info->attrs[NL80211_ATTR_MAC])
3609 return -EINVAL;
3610
3611 if (!info->attrs[NL80211_ATTR_REASON_CODE])
3612 return -EINVAL;
3613
Jouni Malinen636a5d32009-03-19 13:39:22 +02003614 rtnl_lock();
3615
Johannes Berg463d0182009-07-14 00:33:35 +02003616 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003617 if (err)
3618 goto unlock_rtnl;
3619
Johannes Berg79c97e92009-07-07 03:56:12 +02003620 if (!rdev->ops->disassoc) {
Jouni Malinen636a5d32009-03-19 13:39:22 +02003621 err = -EOPNOTSUPP;
3622 goto out;
3623 }
3624
Jouni Malineneec60b02009-03-20 21:21:19 +02003625 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
3626 err = -EOPNOTSUPP;
3627 goto out;
3628 }
3629
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003630 if (!netif_running(dev)) {
3631 err = -ENETDOWN;
3632 goto out;
3633 }
3634
Johannes Berg19957bb2009-07-02 17:20:43 +02003635 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003636
Johannes Berg19957bb2009-07-02 17:20:43 +02003637 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
3638 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003639 /* Reason Code 0 is reserved */
3640 err = -EINVAL;
3641 goto out;
Jouni Malinen255e7372009-03-20 21:21:17 +02003642 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02003643
3644 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02003645 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3646 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003647 }
3648
Johannes Berg79c97e92009-07-07 03:56:12 +02003649 err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003650
3651out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003652 cfg80211_unlock_rdev(rdev);
Jouni Malinen636a5d32009-03-19 13:39:22 +02003653 dev_put(dev);
3654unlock_rtnl:
3655 rtnl_unlock();
3656 return err;
3657}
3658
Johannes Berg04a773a2009-04-19 21:24:32 +02003659static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
3660{
Johannes Berg79c97e92009-07-07 03:56:12 +02003661 struct cfg80211_registered_device *rdev;
Johannes Berg04a773a2009-04-19 21:24:32 +02003662 struct net_device *dev;
3663 struct cfg80211_ibss_params ibss;
3664 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02003665 struct cfg80211_cached_keys *connkeys = NULL;
Johannes Berg04a773a2009-04-19 21:24:32 +02003666 int err;
3667
Johannes Berg8e30bc52009-04-22 17:45:38 +02003668 memset(&ibss, 0, sizeof(ibss));
3669
Johannes Berg04a773a2009-04-19 21:24:32 +02003670 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3671 return -EINVAL;
3672
3673 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
3674 !info->attrs[NL80211_ATTR_SSID] ||
3675 !nla_len(info->attrs[NL80211_ATTR_SSID]))
3676 return -EINVAL;
3677
Johannes Berg8e30bc52009-04-22 17:45:38 +02003678 ibss.beacon_interval = 100;
3679
3680 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
3681 ibss.beacon_interval =
3682 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
3683 if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000)
3684 return -EINVAL;
3685 }
3686
Johannes Berg04a773a2009-04-19 21:24:32 +02003687 rtnl_lock();
3688
Johannes Berg463d0182009-07-14 00:33:35 +02003689 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg04a773a2009-04-19 21:24:32 +02003690 if (err)
3691 goto unlock_rtnl;
3692
Johannes Berg79c97e92009-07-07 03:56:12 +02003693 if (!rdev->ops->join_ibss) {
Johannes Berg04a773a2009-04-19 21:24:32 +02003694 err = -EOPNOTSUPP;
3695 goto out;
3696 }
3697
3698 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
3699 err = -EOPNOTSUPP;
3700 goto out;
3701 }
3702
3703 if (!netif_running(dev)) {
3704 err = -ENETDOWN;
3705 goto out;
3706 }
3707
Johannes Berg79c97e92009-07-07 03:56:12 +02003708 wiphy = &rdev->wiphy;
Johannes Berg04a773a2009-04-19 21:24:32 +02003709
3710 if (info->attrs[NL80211_ATTR_MAC])
3711 ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
3712 ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3713 ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
3714
3715 if (info->attrs[NL80211_ATTR_IE]) {
3716 ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3717 ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3718 }
3719
3720 ibss.channel = ieee80211_get_channel(wiphy,
3721 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
3722 if (!ibss.channel ||
3723 ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
3724 ibss.channel->flags & IEEE80211_CHAN_DISABLED) {
3725 err = -EINVAL;
3726 goto out;
3727 }
3728
3729 ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
Johannes Bergfffd0932009-07-08 14:22:54 +02003730 ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
Johannes Berg04a773a2009-04-19 21:24:32 +02003731
Johannes Bergfffd0932009-07-08 14:22:54 +02003732 if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
3733 connkeys = nl80211_parse_connkeys(rdev,
3734 info->attrs[NL80211_ATTR_KEYS]);
3735 if (IS_ERR(connkeys)) {
3736 err = PTR_ERR(connkeys);
3737 connkeys = NULL;
3738 goto out;
3739 }
3740 }
3741
3742 err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
Johannes Berg04a773a2009-04-19 21:24:32 +02003743
3744out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003745 cfg80211_unlock_rdev(rdev);
Johannes Berg04a773a2009-04-19 21:24:32 +02003746 dev_put(dev);
3747unlock_rtnl:
Johannes Bergfffd0932009-07-08 14:22:54 +02003748 if (err)
3749 kfree(connkeys);
Johannes Berg04a773a2009-04-19 21:24:32 +02003750 rtnl_unlock();
3751 return err;
3752}
3753
3754static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
3755{
Johannes Berg79c97e92009-07-07 03:56:12 +02003756 struct cfg80211_registered_device *rdev;
Johannes Berg04a773a2009-04-19 21:24:32 +02003757 struct net_device *dev;
3758 int err;
3759
3760 rtnl_lock();
3761
Johannes Berg463d0182009-07-14 00:33:35 +02003762 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Johannes Berg04a773a2009-04-19 21:24:32 +02003763 if (err)
3764 goto unlock_rtnl;
3765
Johannes Berg79c97e92009-07-07 03:56:12 +02003766 if (!rdev->ops->leave_ibss) {
Johannes Berg04a773a2009-04-19 21:24:32 +02003767 err = -EOPNOTSUPP;
3768 goto out;
3769 }
3770
3771 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
3772 err = -EOPNOTSUPP;
3773 goto out;
3774 }
3775
3776 if (!netif_running(dev)) {
3777 err = -ENETDOWN;
3778 goto out;
3779 }
3780
Johannes Berg79c97e92009-07-07 03:56:12 +02003781 err = cfg80211_leave_ibss(rdev, dev, false);
Johannes Berg04a773a2009-04-19 21:24:32 +02003782
3783out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003784 cfg80211_unlock_rdev(rdev);
Johannes Berg04a773a2009-04-19 21:24:32 +02003785 dev_put(dev);
3786unlock_rtnl:
3787 rtnl_unlock();
3788 return err;
3789}
3790
Johannes Bergaff89a92009-07-01 21:26:51 +02003791#ifdef CONFIG_NL80211_TESTMODE
3792static struct genl_multicast_group nl80211_testmode_mcgrp = {
3793 .name = "testmode",
3794};
3795
3796static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
3797{
3798 struct cfg80211_registered_device *rdev;
3799 int err;
3800
3801 if (!info->attrs[NL80211_ATTR_TESTDATA])
3802 return -EINVAL;
3803
3804 rtnl_lock();
3805
3806 rdev = cfg80211_get_dev_from_info(info);
3807 if (IS_ERR(rdev)) {
3808 err = PTR_ERR(rdev);
3809 goto unlock_rtnl;
3810 }
3811
3812 err = -EOPNOTSUPP;
3813 if (rdev->ops->testmode_cmd) {
3814 rdev->testmode_info = info;
3815 err = rdev->ops->testmode_cmd(&rdev->wiphy,
3816 nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
3817 nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
3818 rdev->testmode_info = NULL;
3819 }
3820
Johannes Berg4d0c8ae2009-07-07 03:56:09 +02003821 cfg80211_unlock_rdev(rdev);
Johannes Bergaff89a92009-07-01 21:26:51 +02003822
3823 unlock_rtnl:
3824 rtnl_unlock();
3825 return err;
3826}
3827
3828static struct sk_buff *
3829__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
3830 int approxlen, u32 pid, u32 seq, gfp_t gfp)
3831{
3832 struct sk_buff *skb;
3833 void *hdr;
3834 struct nlattr *data;
3835
3836 skb = nlmsg_new(approxlen + 100, gfp);
3837 if (!skb)
3838 return NULL;
3839
3840 hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE);
3841 if (!hdr) {
3842 kfree_skb(skb);
3843 return NULL;
3844 }
3845
3846 NLA_PUT_U32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
3847 data = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
3848
3849 ((void **)skb->cb)[0] = rdev;
3850 ((void **)skb->cb)[1] = hdr;
3851 ((void **)skb->cb)[2] = data;
3852
3853 return skb;
3854
3855 nla_put_failure:
3856 kfree_skb(skb);
3857 return NULL;
3858}
3859
3860struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
3861 int approxlen)
3862{
3863 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
3864
3865 if (WARN_ON(!rdev->testmode_info))
3866 return NULL;
3867
3868 return __cfg80211_testmode_alloc_skb(rdev, approxlen,
3869 rdev->testmode_info->snd_pid,
3870 rdev->testmode_info->snd_seq,
3871 GFP_KERNEL);
3872}
3873EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb);
3874
3875int cfg80211_testmode_reply(struct sk_buff *skb)
3876{
3877 struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
3878 void *hdr = ((void **)skb->cb)[1];
3879 struct nlattr *data = ((void **)skb->cb)[2];
3880
3881 if (WARN_ON(!rdev->testmode_info)) {
3882 kfree_skb(skb);
3883 return -EINVAL;
3884 }
3885
3886 nla_nest_end(skb, data);
3887 genlmsg_end(skb, hdr);
3888 return genlmsg_reply(skb, rdev->testmode_info);
3889}
3890EXPORT_SYMBOL(cfg80211_testmode_reply);
3891
3892struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
3893 int approxlen, gfp_t gfp)
3894{
3895 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
3896
3897 return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp);
3898}
3899EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb);
3900
3901void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
3902{
3903 void *hdr = ((void **)skb->cb)[1];
3904 struct nlattr *data = ((void **)skb->cb)[2];
3905
3906 nla_nest_end(skb, data);
3907 genlmsg_end(skb, hdr);
3908 genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp);
3909}
3910EXPORT_SYMBOL(cfg80211_testmode_event);
3911#endif
3912
Samuel Ortizb23aa672009-07-01 21:26:54 +02003913static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
3914{
Johannes Berg79c97e92009-07-07 03:56:12 +02003915 struct cfg80211_registered_device *rdev;
Samuel Ortizb23aa672009-07-01 21:26:54 +02003916 struct net_device *dev;
3917 struct cfg80211_connect_params connect;
3918 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02003919 struct cfg80211_cached_keys *connkeys = NULL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02003920 int err;
3921
3922 memset(&connect, 0, sizeof(connect));
3923
3924 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3925 return -EINVAL;
3926
3927 if (!info->attrs[NL80211_ATTR_SSID] ||
3928 !nla_len(info->attrs[NL80211_ATTR_SSID]))
3929 return -EINVAL;
3930
3931 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
3932 connect.auth_type =
3933 nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
3934 if (!nl80211_valid_auth_type(connect.auth_type))
3935 return -EINVAL;
3936 } else
3937 connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
3938
3939 connect.privacy = info->attrs[NL80211_ATTR_PRIVACY];
3940
Johannes Berg3dc27d22009-07-02 21:36:37 +02003941 err = nl80211_crypto_settings(info, &connect.crypto,
3942 NL80211_MAX_NR_CIPHER_SUITES);
Samuel Ortizb23aa672009-07-01 21:26:54 +02003943 if (err)
3944 return err;
3945 rtnl_lock();
3946
Johannes Berg463d0182009-07-14 00:33:35 +02003947 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Samuel Ortizb23aa672009-07-01 21:26:54 +02003948 if (err)
3949 goto unlock_rtnl;
3950
3951 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
3952 err = -EOPNOTSUPP;
3953 goto out;
3954 }
3955
3956 if (!netif_running(dev)) {
3957 err = -ENETDOWN;
3958 goto out;
3959 }
3960
Johannes Berg79c97e92009-07-07 03:56:12 +02003961 wiphy = &rdev->wiphy;
Samuel Ortizb23aa672009-07-01 21:26:54 +02003962
Samuel Ortizb23aa672009-07-01 21:26:54 +02003963 if (info->attrs[NL80211_ATTR_MAC])
3964 connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
3965 connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3966 connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
3967
3968 if (info->attrs[NL80211_ATTR_IE]) {
3969 connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3970 connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3971 }
3972
3973 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
3974 connect.channel =
3975 ieee80211_get_channel(wiphy,
3976 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
3977 if (!connect.channel ||
3978 connect.channel->flags & IEEE80211_CHAN_DISABLED) {
3979 err = -EINVAL;
3980 goto out;
3981 }
3982 }
3983
Johannes Bergfffd0932009-07-08 14:22:54 +02003984 if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
3985 connkeys = nl80211_parse_connkeys(rdev,
3986 info->attrs[NL80211_ATTR_KEYS]);
3987 if (IS_ERR(connkeys)) {
3988 err = PTR_ERR(connkeys);
3989 connkeys = NULL;
3990 goto out;
3991 }
3992 }
3993
3994 err = cfg80211_connect(rdev, dev, &connect, connkeys);
Samuel Ortizb23aa672009-07-01 21:26:54 +02003995
3996out:
Johannes Berg79c97e92009-07-07 03:56:12 +02003997 cfg80211_unlock_rdev(rdev);
Samuel Ortizb23aa672009-07-01 21:26:54 +02003998 dev_put(dev);
3999unlock_rtnl:
Johannes Bergfffd0932009-07-08 14:22:54 +02004000 if (err)
4001 kfree(connkeys);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004002 rtnl_unlock();
4003 return err;
4004}
4005
4006static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
4007{
Johannes Berg79c97e92009-07-07 03:56:12 +02004008 struct cfg80211_registered_device *rdev;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004009 struct net_device *dev;
4010 int err;
4011 u16 reason;
4012
4013 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4014 reason = WLAN_REASON_DEAUTH_LEAVING;
4015 else
4016 reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4017
4018 if (reason == 0)
4019 return -EINVAL;
4020
4021 rtnl_lock();
4022
Johannes Berg463d0182009-07-14 00:33:35 +02004023 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004024 if (err)
4025 goto unlock_rtnl;
4026
4027 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
4028 err = -EOPNOTSUPP;
4029 goto out;
4030 }
4031
4032 if (!netif_running(dev)) {
4033 err = -ENETDOWN;
4034 goto out;
4035 }
4036
Johannes Berg79c97e92009-07-07 03:56:12 +02004037 err = cfg80211_disconnect(rdev, dev, reason, true);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004038
4039out:
Johannes Berg79c97e92009-07-07 03:56:12 +02004040 cfg80211_unlock_rdev(rdev);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004041 dev_put(dev);
4042unlock_rtnl:
4043 rtnl_unlock();
4044 return err;
4045}
4046
Johannes Berg463d0182009-07-14 00:33:35 +02004047static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
4048{
4049 struct cfg80211_registered_device *rdev;
4050 struct net *net;
4051 int err;
4052 u32 pid;
4053
4054 if (!info->attrs[NL80211_ATTR_PID])
4055 return -EINVAL;
4056
4057 pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
4058
4059 rtnl_lock();
4060
4061 rdev = cfg80211_get_dev_from_info(info);
4062 if (IS_ERR(rdev)) {
4063 err = PTR_ERR(rdev);
4064 goto out;
4065 }
4066
4067 net = get_net_ns_by_pid(pid);
4068 if (IS_ERR(net)) {
4069 err = PTR_ERR(net);
4070 goto out;
4071 }
4072
4073 err = 0;
4074
4075 /* check if anything to do */
4076 if (net_eq(wiphy_net(&rdev->wiphy), net))
4077 goto out_put_net;
4078
4079 err = cfg80211_switch_netns(rdev, net);
4080 out_put_net:
4081 put_net(net);
4082 out:
4083 cfg80211_unlock_rdev(rdev);
4084 rtnl_unlock();
4085 return err;
4086}
4087
Johannes Berg55682962007-09-20 13:09:35 -04004088static struct genl_ops nl80211_ops[] = {
4089 {
4090 .cmd = NL80211_CMD_GET_WIPHY,
4091 .doit = nl80211_get_wiphy,
4092 .dumpit = nl80211_dump_wiphy,
4093 .policy = nl80211_policy,
4094 /* can be retrieved by unprivileged users */
4095 },
4096 {
4097 .cmd = NL80211_CMD_SET_WIPHY,
4098 .doit = nl80211_set_wiphy,
4099 .policy = nl80211_policy,
4100 .flags = GENL_ADMIN_PERM,
4101 },
4102 {
4103 .cmd = NL80211_CMD_GET_INTERFACE,
4104 .doit = nl80211_get_interface,
4105 .dumpit = nl80211_dump_interface,
4106 .policy = nl80211_policy,
4107 /* can be retrieved by unprivileged users */
4108 },
4109 {
4110 .cmd = NL80211_CMD_SET_INTERFACE,
4111 .doit = nl80211_set_interface,
4112 .policy = nl80211_policy,
4113 .flags = GENL_ADMIN_PERM,
4114 },
4115 {
4116 .cmd = NL80211_CMD_NEW_INTERFACE,
4117 .doit = nl80211_new_interface,
4118 .policy = nl80211_policy,
4119 .flags = GENL_ADMIN_PERM,
4120 },
4121 {
4122 .cmd = NL80211_CMD_DEL_INTERFACE,
4123 .doit = nl80211_del_interface,
4124 .policy = nl80211_policy,
4125 .flags = GENL_ADMIN_PERM,
4126 },
Johannes Berg41ade002007-12-19 02:03:29 +01004127 {
4128 .cmd = NL80211_CMD_GET_KEY,
4129 .doit = nl80211_get_key,
4130 .policy = nl80211_policy,
4131 .flags = GENL_ADMIN_PERM,
4132 },
4133 {
4134 .cmd = NL80211_CMD_SET_KEY,
4135 .doit = nl80211_set_key,
4136 .policy = nl80211_policy,
4137 .flags = GENL_ADMIN_PERM,
4138 },
4139 {
4140 .cmd = NL80211_CMD_NEW_KEY,
4141 .doit = nl80211_new_key,
4142 .policy = nl80211_policy,
4143 .flags = GENL_ADMIN_PERM,
4144 },
4145 {
4146 .cmd = NL80211_CMD_DEL_KEY,
4147 .doit = nl80211_del_key,
4148 .policy = nl80211_policy,
4149 .flags = GENL_ADMIN_PERM,
4150 },
Johannes Berged1b6cc2007-12-19 02:03:32 +01004151 {
4152 .cmd = NL80211_CMD_SET_BEACON,
4153 .policy = nl80211_policy,
4154 .flags = GENL_ADMIN_PERM,
4155 .doit = nl80211_addset_beacon,
4156 },
4157 {
4158 .cmd = NL80211_CMD_NEW_BEACON,
4159 .policy = nl80211_policy,
4160 .flags = GENL_ADMIN_PERM,
4161 .doit = nl80211_addset_beacon,
4162 },
4163 {
4164 .cmd = NL80211_CMD_DEL_BEACON,
4165 .policy = nl80211_policy,
4166 .flags = GENL_ADMIN_PERM,
4167 .doit = nl80211_del_beacon,
4168 },
Johannes Berg5727ef12007-12-19 02:03:34 +01004169 {
4170 .cmd = NL80211_CMD_GET_STATION,
4171 .doit = nl80211_get_station,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004172 .dumpit = nl80211_dump_station,
Johannes Berg5727ef12007-12-19 02:03:34 +01004173 .policy = nl80211_policy,
Johannes Berg5727ef12007-12-19 02:03:34 +01004174 },
4175 {
4176 .cmd = NL80211_CMD_SET_STATION,
4177 .doit = nl80211_set_station,
4178 .policy = nl80211_policy,
4179 .flags = GENL_ADMIN_PERM,
4180 },
4181 {
4182 .cmd = NL80211_CMD_NEW_STATION,
4183 .doit = nl80211_new_station,
4184 .policy = nl80211_policy,
4185 .flags = GENL_ADMIN_PERM,
4186 },
4187 {
4188 .cmd = NL80211_CMD_DEL_STATION,
4189 .doit = nl80211_del_station,
4190 .policy = nl80211_policy,
4191 .flags = GENL_ADMIN_PERM,
4192 },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01004193 {
4194 .cmd = NL80211_CMD_GET_MPATH,
4195 .doit = nl80211_get_mpath,
4196 .dumpit = nl80211_dump_mpath,
4197 .policy = nl80211_policy,
4198 .flags = GENL_ADMIN_PERM,
4199 },
4200 {
4201 .cmd = NL80211_CMD_SET_MPATH,
4202 .doit = nl80211_set_mpath,
4203 .policy = nl80211_policy,
4204 .flags = GENL_ADMIN_PERM,
4205 },
4206 {
4207 .cmd = NL80211_CMD_NEW_MPATH,
4208 .doit = nl80211_new_mpath,
4209 .policy = nl80211_policy,
4210 .flags = GENL_ADMIN_PERM,
4211 },
4212 {
4213 .cmd = NL80211_CMD_DEL_MPATH,
4214 .doit = nl80211_del_mpath,
4215 .policy = nl80211_policy,
4216 .flags = GENL_ADMIN_PERM,
4217 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +03004218 {
4219 .cmd = NL80211_CMD_SET_BSS,
4220 .doit = nl80211_set_bss,
4221 .policy = nl80211_policy,
4222 .flags = GENL_ADMIN_PERM,
4223 },
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004224 {
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08004225 .cmd = NL80211_CMD_GET_REG,
4226 .doit = nl80211_get_reg,
4227 .policy = nl80211_policy,
4228 /* can be retrieved by unprivileged users */
4229 },
4230 {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07004231 .cmd = NL80211_CMD_SET_REG,
4232 .doit = nl80211_set_reg,
4233 .policy = nl80211_policy,
4234 .flags = GENL_ADMIN_PERM,
4235 },
4236 {
4237 .cmd = NL80211_CMD_REQ_SET_REG,
4238 .doit = nl80211_req_set_reg,
4239 .policy = nl80211_policy,
4240 .flags = GENL_ADMIN_PERM,
4241 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07004242 {
4243 .cmd = NL80211_CMD_GET_MESH_PARAMS,
4244 .doit = nl80211_get_mesh_params,
4245 .policy = nl80211_policy,
4246 /* can be retrieved by unprivileged users */
4247 },
4248 {
4249 .cmd = NL80211_CMD_SET_MESH_PARAMS,
4250 .doit = nl80211_set_mesh_params,
4251 .policy = nl80211_policy,
4252 .flags = GENL_ADMIN_PERM,
4253 },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +02004254 {
Johannes Berg2a519312009-02-10 21:25:55 +01004255 .cmd = NL80211_CMD_TRIGGER_SCAN,
4256 .doit = nl80211_trigger_scan,
4257 .policy = nl80211_policy,
4258 .flags = GENL_ADMIN_PERM,
4259 },
4260 {
4261 .cmd = NL80211_CMD_GET_SCAN,
4262 .policy = nl80211_policy,
4263 .dumpit = nl80211_dump_scan,
4264 },
Jouni Malinen636a5d32009-03-19 13:39:22 +02004265 {
4266 .cmd = NL80211_CMD_AUTHENTICATE,
4267 .doit = nl80211_authenticate,
4268 .policy = nl80211_policy,
4269 .flags = GENL_ADMIN_PERM,
4270 },
4271 {
4272 .cmd = NL80211_CMD_ASSOCIATE,
4273 .doit = nl80211_associate,
4274 .policy = nl80211_policy,
4275 .flags = GENL_ADMIN_PERM,
4276 },
4277 {
4278 .cmd = NL80211_CMD_DEAUTHENTICATE,
4279 .doit = nl80211_deauthenticate,
4280 .policy = nl80211_policy,
4281 .flags = GENL_ADMIN_PERM,
4282 },
4283 {
4284 .cmd = NL80211_CMD_DISASSOCIATE,
4285 .doit = nl80211_disassociate,
4286 .policy = nl80211_policy,
4287 .flags = GENL_ADMIN_PERM,
4288 },
Johannes Berg04a773a2009-04-19 21:24:32 +02004289 {
4290 .cmd = NL80211_CMD_JOIN_IBSS,
4291 .doit = nl80211_join_ibss,
4292 .policy = nl80211_policy,
4293 .flags = GENL_ADMIN_PERM,
4294 },
4295 {
4296 .cmd = NL80211_CMD_LEAVE_IBSS,
4297 .doit = nl80211_leave_ibss,
4298 .policy = nl80211_policy,
4299 .flags = GENL_ADMIN_PERM,
4300 },
Johannes Bergaff89a92009-07-01 21:26:51 +02004301#ifdef CONFIG_NL80211_TESTMODE
4302 {
4303 .cmd = NL80211_CMD_TESTMODE,
4304 .doit = nl80211_testmode_do,
4305 .policy = nl80211_policy,
4306 .flags = GENL_ADMIN_PERM,
4307 },
4308#endif
Samuel Ortizb23aa672009-07-01 21:26:54 +02004309 {
4310 .cmd = NL80211_CMD_CONNECT,
4311 .doit = nl80211_connect,
4312 .policy = nl80211_policy,
4313 .flags = GENL_ADMIN_PERM,
4314 },
4315 {
4316 .cmd = NL80211_CMD_DISCONNECT,
4317 .doit = nl80211_disconnect,
4318 .policy = nl80211_policy,
4319 .flags = GENL_ADMIN_PERM,
4320 },
Johannes Berg463d0182009-07-14 00:33:35 +02004321 {
4322 .cmd = NL80211_CMD_SET_WIPHY_NETNS,
4323 .doit = nl80211_wiphy_netns,
4324 .policy = nl80211_policy,
4325 .flags = GENL_ADMIN_PERM,
4326 },
Johannes Berg55682962007-09-20 13:09:35 -04004327};
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004328static struct genl_multicast_group nl80211_mlme_mcgrp = {
4329 .name = "mlme",
4330};
Johannes Berg55682962007-09-20 13:09:35 -04004331
4332/* multicast groups */
4333static struct genl_multicast_group nl80211_config_mcgrp = {
4334 .name = "config",
4335};
Johannes Berg2a519312009-02-10 21:25:55 +01004336static struct genl_multicast_group nl80211_scan_mcgrp = {
4337 .name = "scan",
4338};
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04004339static struct genl_multicast_group nl80211_regulatory_mcgrp = {
4340 .name = "regulatory",
4341};
Johannes Berg55682962007-09-20 13:09:35 -04004342
4343/* notification functions */
4344
4345void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
4346{
4347 struct sk_buff *msg;
4348
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07004349 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04004350 if (!msg)
4351 return;
4352
4353 if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
4354 nlmsg_free(msg);
4355 return;
4356 }
4357
Johannes Berg463d0182009-07-14 00:33:35 +02004358 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4359 nl80211_config_mcgrp.id, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04004360}
4361
Johannes Berg362a4152009-05-24 16:43:15 +02004362static int nl80211_add_scan_req(struct sk_buff *msg,
4363 struct cfg80211_registered_device *rdev)
4364{
4365 struct cfg80211_scan_request *req = rdev->scan_req;
4366 struct nlattr *nest;
4367 int i;
4368
Johannes Berg667503d2009-07-07 03:56:11 +02004369 ASSERT_RDEV_LOCK(rdev);
4370
Johannes Berg362a4152009-05-24 16:43:15 +02004371 if (WARN_ON(!req))
4372 return 0;
4373
4374 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
4375 if (!nest)
4376 goto nla_put_failure;
4377 for (i = 0; i < req->n_ssids; i++)
4378 NLA_PUT(msg, i, req->ssids[i].ssid_len, req->ssids[i].ssid);
4379 nla_nest_end(msg, nest);
4380
4381 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
4382 if (!nest)
4383 goto nla_put_failure;
4384 for (i = 0; i < req->n_channels; i++)
4385 NLA_PUT_U32(msg, i, req->channels[i]->center_freq);
4386 nla_nest_end(msg, nest);
4387
4388 if (req->ie)
4389 NLA_PUT(msg, NL80211_ATTR_IE, req->ie_len, req->ie);
4390
4391 return 0;
4392 nla_put_failure:
4393 return -ENOBUFS;
4394}
4395
Johannes Berga538e2d2009-06-16 19:56:42 +02004396static int nl80211_send_scan_msg(struct sk_buff *msg,
4397 struct cfg80211_registered_device *rdev,
4398 struct net_device *netdev,
4399 u32 pid, u32 seq, int flags,
4400 u32 cmd)
Johannes Berg2a519312009-02-10 21:25:55 +01004401{
4402 void *hdr;
4403
4404 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
4405 if (!hdr)
4406 return -1;
4407
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -05004408 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg2a519312009-02-10 21:25:55 +01004409 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
4410
Johannes Berg362a4152009-05-24 16:43:15 +02004411 /* ignore errors and send incomplete event anyway */
4412 nl80211_add_scan_req(msg, rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01004413
4414 return genlmsg_end(msg, hdr);
4415
4416 nla_put_failure:
4417 genlmsg_cancel(msg, hdr);
4418 return -EMSGSIZE;
4419}
4420
Johannes Berga538e2d2009-06-16 19:56:42 +02004421void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
4422 struct net_device *netdev)
4423{
4424 struct sk_buff *msg;
4425
4426 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
4427 if (!msg)
4428 return;
4429
4430 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
4431 NL80211_CMD_TRIGGER_SCAN) < 0) {
4432 nlmsg_free(msg);
4433 return;
4434 }
4435
Johannes Berg463d0182009-07-14 00:33:35 +02004436 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4437 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berga538e2d2009-06-16 19:56:42 +02004438}
4439
Johannes Berg2a519312009-02-10 21:25:55 +01004440void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
4441 struct net_device *netdev)
4442{
4443 struct sk_buff *msg;
4444
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07004445 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01004446 if (!msg)
4447 return;
4448
Johannes Berga538e2d2009-06-16 19:56:42 +02004449 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
4450 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01004451 nlmsg_free(msg);
4452 return;
4453 }
4454
Johannes Berg463d0182009-07-14 00:33:35 +02004455 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4456 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01004457}
4458
4459void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
4460 struct net_device *netdev)
4461{
4462 struct sk_buff *msg;
4463
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07004464 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01004465 if (!msg)
4466 return;
4467
Johannes Berga538e2d2009-06-16 19:56:42 +02004468 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
4469 NL80211_CMD_SCAN_ABORTED) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01004470 nlmsg_free(msg);
4471 return;
4472 }
4473
Johannes Berg463d0182009-07-14 00:33:35 +02004474 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4475 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01004476}
4477
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04004478/*
4479 * This can happen on global regulatory changes or device specific settings
4480 * based on custom world regulatory domains.
4481 */
4482void nl80211_send_reg_change_event(struct regulatory_request *request)
4483{
4484 struct sk_buff *msg;
4485 void *hdr;
4486
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07004487 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04004488 if (!msg)
4489 return;
4490
4491 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
4492 if (!hdr) {
4493 nlmsg_free(msg);
4494 return;
4495 }
4496
4497 /* Userspace can always count this one always being set */
4498 NLA_PUT_U8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator);
4499
4500 if (request->alpha2[0] == '0' && request->alpha2[1] == '0')
4501 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
4502 NL80211_REGDOM_TYPE_WORLD);
4503 else if (request->alpha2[0] == '9' && request->alpha2[1] == '9')
4504 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
4505 NL80211_REGDOM_TYPE_CUSTOM_WORLD);
4506 else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
4507 request->intersect)
4508 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
4509 NL80211_REGDOM_TYPE_INTERSECTION);
4510 else {
4511 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
4512 NL80211_REGDOM_TYPE_COUNTRY);
4513 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2);
4514 }
4515
4516 if (wiphy_idx_valid(request->wiphy_idx))
4517 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx);
4518
4519 if (genlmsg_end(msg, hdr) < 0) {
4520 nlmsg_free(msg);
4521 return;
4522 }
4523
Johannes Bergbc43b282009-07-25 10:54:13 +02004524 rcu_read_lock();
Johannes Berg463d0182009-07-14 00:33:35 +02004525 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
Johannes Bergbc43b282009-07-25 10:54:13 +02004526 GFP_ATOMIC);
4527 rcu_read_unlock();
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04004528
4529 return;
4530
4531nla_put_failure:
4532 genlmsg_cancel(msg, hdr);
4533 nlmsg_free(msg);
4534}
4535
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004536static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
4537 struct net_device *netdev,
4538 const u8 *buf, size_t len,
Johannes Berge6d6e342009-07-01 21:26:47 +02004539 enum nl80211_commands cmd, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004540{
4541 struct sk_buff *msg;
4542 void *hdr;
4543
Johannes Berge6d6e342009-07-01 21:26:47 +02004544 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004545 if (!msg)
4546 return;
4547
4548 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
4549 if (!hdr) {
4550 nlmsg_free(msg);
4551 return;
4552 }
4553
4554 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
4555 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
4556 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
4557
4558 if (genlmsg_end(msg, hdr) < 0) {
4559 nlmsg_free(msg);
4560 return;
4561 }
4562
Johannes Berg463d0182009-07-14 00:33:35 +02004563 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4564 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004565 return;
4566
4567 nla_put_failure:
4568 genlmsg_cancel(msg, hdr);
4569 nlmsg_free(msg);
4570}
4571
4572void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02004573 struct net_device *netdev, const u8 *buf,
4574 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004575{
4576 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02004577 NL80211_CMD_AUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004578}
4579
4580void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
4581 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02004582 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004583{
Johannes Berge6d6e342009-07-01 21:26:47 +02004584 nl80211_send_mlme_event(rdev, netdev, buf, len,
4585 NL80211_CMD_ASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004586}
4587
Jouni Malinen53b46b82009-03-27 20:53:56 +02004588void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02004589 struct net_device *netdev, const u8 *buf,
4590 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004591{
4592 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02004593 NL80211_CMD_DEAUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004594}
4595
Jouni Malinen53b46b82009-03-27 20:53:56 +02004596void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
4597 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02004598 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004599{
4600 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02004601 NL80211_CMD_DISASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004602}
4603
Luis R. Rodriguez1b06bb42009-05-02 00:34:48 -04004604static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
4605 struct net_device *netdev, int cmd,
Johannes Berge6d6e342009-07-01 21:26:47 +02004606 const u8 *addr, gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03004607{
4608 struct sk_buff *msg;
4609 void *hdr;
4610
Johannes Berge6d6e342009-07-01 21:26:47 +02004611 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03004612 if (!msg)
4613 return;
4614
4615 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
4616 if (!hdr) {
4617 nlmsg_free(msg);
4618 return;
4619 }
4620
4621 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
4622 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
4623 NLA_PUT_FLAG(msg, NL80211_ATTR_TIMED_OUT);
4624 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
4625
4626 if (genlmsg_end(msg, hdr) < 0) {
4627 nlmsg_free(msg);
4628 return;
4629 }
4630
Johannes Berg463d0182009-07-14 00:33:35 +02004631 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4632 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03004633 return;
4634
4635 nla_put_failure:
4636 genlmsg_cancel(msg, hdr);
4637 nlmsg_free(msg);
4638}
4639
4640void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02004641 struct net_device *netdev, const u8 *addr,
4642 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03004643{
4644 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE,
Johannes Berge6d6e342009-07-01 21:26:47 +02004645 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03004646}
4647
4648void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02004649 struct net_device *netdev, const u8 *addr,
4650 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03004651{
Johannes Berge6d6e342009-07-01 21:26:47 +02004652 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE,
4653 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03004654}
4655
Samuel Ortizb23aa672009-07-01 21:26:54 +02004656void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
4657 struct net_device *netdev, const u8 *bssid,
4658 const u8 *req_ie, size_t req_ie_len,
4659 const u8 *resp_ie, size_t resp_ie_len,
4660 u16 status, gfp_t gfp)
4661{
4662 struct sk_buff *msg;
4663 void *hdr;
4664
4665 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
4666 if (!msg)
4667 return;
4668
4669 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT);
4670 if (!hdr) {
4671 nlmsg_free(msg);
4672 return;
4673 }
4674
4675 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
4676 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
4677 if (bssid)
4678 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
4679 NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status);
4680 if (req_ie)
4681 NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
4682 if (resp_ie)
4683 NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
4684
4685 if (genlmsg_end(msg, hdr) < 0) {
4686 nlmsg_free(msg);
4687 return;
4688 }
4689
Johannes Berg463d0182009-07-14 00:33:35 +02004690 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4691 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004692 return;
4693
4694 nla_put_failure:
4695 genlmsg_cancel(msg, hdr);
4696 nlmsg_free(msg);
4697
4698}
4699
4700void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
4701 struct net_device *netdev, const u8 *bssid,
4702 const u8 *req_ie, size_t req_ie_len,
4703 const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
4704{
4705 struct sk_buff *msg;
4706 void *hdr;
4707
4708 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
4709 if (!msg)
4710 return;
4711
4712 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM);
4713 if (!hdr) {
4714 nlmsg_free(msg);
4715 return;
4716 }
4717
4718 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
4719 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
4720 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
4721 if (req_ie)
4722 NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
4723 if (resp_ie)
4724 NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
4725
4726 if (genlmsg_end(msg, hdr) < 0) {
4727 nlmsg_free(msg);
4728 return;
4729 }
4730
Johannes Berg463d0182009-07-14 00:33:35 +02004731 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4732 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004733 return;
4734
4735 nla_put_failure:
4736 genlmsg_cancel(msg, hdr);
4737 nlmsg_free(msg);
4738
4739}
4740
4741void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
4742 struct net_device *netdev, u16 reason,
Johannes Berg667503d2009-07-07 03:56:11 +02004743 const u8 *ie, size_t ie_len, bool from_ap)
Samuel Ortizb23aa672009-07-01 21:26:54 +02004744{
4745 struct sk_buff *msg;
4746 void *hdr;
4747
Johannes Berg667503d2009-07-07 03:56:11 +02004748 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004749 if (!msg)
4750 return;
4751
4752 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT);
4753 if (!hdr) {
4754 nlmsg_free(msg);
4755 return;
4756 }
4757
4758 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
4759 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
4760 if (from_ap && reason)
4761 NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason);
4762 if (from_ap)
4763 NLA_PUT_FLAG(msg, NL80211_ATTR_DISCONNECTED_BY_AP);
4764 if (ie)
4765 NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie);
4766
4767 if (genlmsg_end(msg, hdr) < 0) {
4768 nlmsg_free(msg);
4769 return;
4770 }
4771
Johannes Berg463d0182009-07-14 00:33:35 +02004772 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4773 nl80211_mlme_mcgrp.id, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004774 return;
4775
4776 nla_put_failure:
4777 genlmsg_cancel(msg, hdr);
4778 nlmsg_free(msg);
4779
4780}
4781
Johannes Berg04a773a2009-04-19 21:24:32 +02004782void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
4783 struct net_device *netdev, const u8 *bssid,
4784 gfp_t gfp)
4785{
4786 struct sk_buff *msg;
4787 void *hdr;
4788
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07004789 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02004790 if (!msg)
4791 return;
4792
4793 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS);
4794 if (!hdr) {
4795 nlmsg_free(msg);
4796 return;
4797 }
4798
4799 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
4800 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
4801 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
4802
4803 if (genlmsg_end(msg, hdr) < 0) {
4804 nlmsg_free(msg);
4805 return;
4806 }
4807
Johannes Berg463d0182009-07-14 00:33:35 +02004808 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4809 nl80211_mlme_mcgrp.id, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02004810 return;
4811
4812 nla_put_failure:
4813 genlmsg_cancel(msg, hdr);
4814 nlmsg_free(msg);
4815}
4816
Jouni Malinena3b8b052009-03-27 21:59:49 +02004817void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
4818 struct net_device *netdev, const u8 *addr,
4819 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +02004820 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +02004821{
4822 struct sk_buff *msg;
4823 void *hdr;
4824
Johannes Berge6d6e342009-07-01 21:26:47 +02004825 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02004826 if (!msg)
4827 return;
4828
4829 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
4830 if (!hdr) {
4831 nlmsg_free(msg);
4832 return;
4833 }
4834
4835 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
4836 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
4837 if (addr)
4838 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
4839 NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, key_type);
4840 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id);
4841 if (tsc)
4842 NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc);
4843
4844 if (genlmsg_end(msg, hdr) < 0) {
4845 nlmsg_free(msg);
4846 return;
4847 }
4848
Johannes Berg463d0182009-07-14 00:33:35 +02004849 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
4850 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02004851 return;
4852
4853 nla_put_failure:
4854 genlmsg_cancel(msg, hdr);
4855 nlmsg_free(msg);
4856}
4857
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04004858void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
4859 struct ieee80211_channel *channel_before,
4860 struct ieee80211_channel *channel_after)
4861{
4862 struct sk_buff *msg;
4863 void *hdr;
4864 struct nlattr *nl_freq;
4865
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07004866 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04004867 if (!msg)
4868 return;
4869
4870 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
4871 if (!hdr) {
4872 nlmsg_free(msg);
4873 return;
4874 }
4875
4876 /*
4877 * Since we are applying the beacon hint to a wiphy we know its
4878 * wiphy_idx is valid
4879 */
4880 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy));
4881
4882 /* Before */
4883 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
4884 if (!nl_freq)
4885 goto nla_put_failure;
4886 if (nl80211_msg_put_channel(msg, channel_before))
4887 goto nla_put_failure;
4888 nla_nest_end(msg, nl_freq);
4889
4890 /* After */
4891 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
4892 if (!nl_freq)
4893 goto nla_put_failure;
4894 if (nl80211_msg_put_channel(msg, channel_after))
4895 goto nla_put_failure;
4896 nla_nest_end(msg, nl_freq);
4897
4898 if (genlmsg_end(msg, hdr) < 0) {
4899 nlmsg_free(msg);
4900 return;
4901 }
4902
Johannes Berg463d0182009-07-14 00:33:35 +02004903 rcu_read_lock();
4904 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
4905 GFP_ATOMIC);
4906 rcu_read_unlock();
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04004907
4908 return;
4909
4910nla_put_failure:
4911 genlmsg_cancel(msg, hdr);
4912 nlmsg_free(msg);
4913}
4914
Johannes Berg55682962007-09-20 13:09:35 -04004915/* initialisation/exit functions */
4916
4917int nl80211_init(void)
4918{
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00004919 int err;
Johannes Berg55682962007-09-20 13:09:35 -04004920
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00004921 err = genl_register_family_with_ops(&nl80211_fam,
4922 nl80211_ops, ARRAY_SIZE(nl80211_ops));
Johannes Berg55682962007-09-20 13:09:35 -04004923 if (err)
4924 return err;
4925
Johannes Berg55682962007-09-20 13:09:35 -04004926 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
4927 if (err)
4928 goto err_out;
4929
Johannes Berg2a519312009-02-10 21:25:55 +01004930 err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
4931 if (err)
4932 goto err_out;
4933
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04004934 err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
4935 if (err)
4936 goto err_out;
4937
Jouni Malinen6039f6d2009-03-19 13:39:21 +02004938 err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
4939 if (err)
4940 goto err_out;
4941
Johannes Bergaff89a92009-07-01 21:26:51 +02004942#ifdef CONFIG_NL80211_TESTMODE
4943 err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp);
4944 if (err)
4945 goto err_out;
4946#endif
4947
Johannes Berg55682962007-09-20 13:09:35 -04004948 return 0;
4949 err_out:
4950 genl_unregister_family(&nl80211_fam);
4951 return err;
4952}
4953
4954void nl80211_exit(void)
4955{
4956 genl_unregister_family(&nl80211_fam);
4957}