blob: d2cfde659e76a409744aea69ce3812915e380b58 [file] [log] [blame]
Johannes Berg55682962007-09-20 13:09:35 -04001/*
2 * This is the new netlink-based wireless configuration interface.
3 *
4 * Copyright 2006, 2007 Johannes Berg <johannes@sipsolutions.net>
5 */
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 Berg55682962007-09-20 13:09:35 -040017#include <net/genetlink.h>
18#include <net/cfg80211.h>
19#include "core.h"
20#include "nl80211.h"
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -070021#include "reg.h"
Johannes Berg55682962007-09-20 13:09:35 -040022
23/* the netlink family */
24static struct genl_family nl80211_fam = {
25 .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
26 .name = "nl80211", /* have users key off the name instead */
27 .hdrsize = 0, /* no private header */
28 .version = 1, /* no particular meaning now */
29 .maxattr = NL80211_ATTR_MAX,
30};
31
32/* internal helper: get drv and dev */
Johannes Bergbba95fe2008-07-29 13:22:51 +020033static int get_drv_dev_by_info_ifindex(struct nlattr **attrs,
Johannes Berg55682962007-09-20 13:09:35 -040034 struct cfg80211_registered_device **drv,
35 struct net_device **dev)
36{
37 int ifindex;
38
Johannes Bergbba95fe2008-07-29 13:22:51 +020039 if (!attrs[NL80211_ATTR_IFINDEX])
Johannes Berg55682962007-09-20 13:09:35 -040040 return -EINVAL;
41
Johannes Bergbba95fe2008-07-29 13:22:51 +020042 ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
Johannes Berg55682962007-09-20 13:09:35 -040043 *dev = dev_get_by_index(&init_net, ifindex);
44 if (!*dev)
45 return -ENODEV;
46
47 *drv = cfg80211_get_dev_from_ifindex(ifindex);
48 if (IS_ERR(*drv)) {
49 dev_put(*dev);
50 return PTR_ERR(*drv);
51 }
52
53 return 0;
54}
55
56/* policy for the attributes */
57static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
58 [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
59 [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
60 .len = BUS_ID_SIZE-1 },
Jouni Malinen31888482008-10-30 16:59:24 +020061 [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
Jouni Malinen72bdcf32008-11-26 16:15:24 +020062 [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
Sujith094d05d2008-12-12 11:57:43 +053063 [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
Johannes Berg55682962007-09-20 13:09:35 -040064
65 [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
66 [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
67 [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
Johannes Berg41ade002007-12-19 02:03:29 +010068
69 [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN },
70
71 [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
72 .len = WLAN_MAX_KEY_LEN },
73 [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
74 [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
75 [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
Johannes Berged1b6cc2007-12-19 02:03:32 +010076
77 [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
78 [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
79 [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY,
80 .len = IEEE80211_MAX_DATA_LEN },
81 [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY,
82 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg5727ef12007-12-19 02:03:34 +010083 [NL80211_ATTR_STA_AID] = { .type = NLA_U16 },
84 [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED },
85 [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 },
86 [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY,
87 .len = NL80211_MAX_SUPP_RATES },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +010088 [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
Johannes Berg5727ef12007-12-19 02:03:34 +010089 [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
Johannes Berg0a9542e2008-10-15 11:54:04 +020090 [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +010091 [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
92 .len = IEEE80211_MAX_MESH_ID_LEN },
93 [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +030094
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -070095 [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 },
96 [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED },
97
Jouni Malinen9f1ba902008-08-07 20:07:01 +030098 [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
99 [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
100 [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
Jouni Malinen90c97a02008-10-30 16:59:22 +0200101 [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
102 .len = NL80211_MAX_SUPP_RATES },
Jouni Malinen36aedc902008-08-25 11:58:58 +0300103
colin@cozybit.com93da9cc2008-10-21 12:03:48 -0700104 [NL80211_ATTR_MESH_PARAMS] = { .type = NLA_NESTED },
105
Jouni Malinen36aedc902008-08-25 11:58:58 +0300106 [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY,
107 .len = NL80211_HT_CAPABILITY_LEN },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +0200108
109 [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
110 [NL80211_ATTR_IE] = { .type = NLA_BINARY,
111 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg2a519312009-02-10 21:25:55 +0100112 [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
113 [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
Jouni Malinen636a5d32009-03-19 13:39:22 +0200114
115 [NL80211_ATTR_SSID] = { .type = NLA_BINARY,
116 .len = IEEE80211_MAX_SSID_LEN },
117 [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
118 [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
Johannes Berg55682962007-09-20 13:09:35 -0400119};
120
Johannes Bergf4a11bb2009-03-27 12:40:28 +0100121/* IE validation */
122static bool is_valid_ie_attr(const struct nlattr *attr)
123{
124 const u8 *pos;
125 int len;
126
127 if (!attr)
128 return true;
129
130 pos = nla_data(attr);
131 len = nla_len(attr);
132
133 while (len) {
134 u8 elemlen;
135
136 if (len < 2)
137 return false;
138 len -= 2;
139
140 elemlen = pos[1];
141 if (elemlen > len)
142 return false;
143
144 len -= elemlen;
145 pos += 2 + elemlen;
146 }
147
148 return true;
149}
150
Johannes Berg55682962007-09-20 13:09:35 -0400151/* message building helper */
152static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
153 int flags, u8 cmd)
154{
155 /* since there is no private header just add the generic one */
156 return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
157}
158
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400159static int nl80211_msg_put_channel(struct sk_buff *msg,
160 struct ieee80211_channel *chan)
161{
162 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
163 chan->center_freq);
164
165 if (chan->flags & IEEE80211_CHAN_DISABLED)
166 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
167 if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
168 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
169 if (chan->flags & IEEE80211_CHAN_NO_IBSS)
170 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
171 if (chan->flags & IEEE80211_CHAN_RADAR)
172 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
173
174 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
175 DBM_TO_MBM(chan->max_power));
176
177 return 0;
178
179 nla_put_failure:
180 return -ENOBUFS;
181}
182
Johannes Berg55682962007-09-20 13:09:35 -0400183/* netlink command implementations */
184
185static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
186 struct cfg80211_registered_device *dev)
187{
188 void *hdr;
Johannes Bergee688b002008-01-24 19:38:39 +0100189 struct nlattr *nl_bands, *nl_band;
190 struct nlattr *nl_freqs, *nl_freq;
191 struct nlattr *nl_rates, *nl_rate;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700192 struct nlattr *nl_modes;
Johannes Berg8fdc6212009-03-14 09:34:01 +0100193 struct nlattr *nl_cmds;
Johannes Bergee688b002008-01-24 19:38:39 +0100194 enum ieee80211_band band;
195 struct ieee80211_channel *chan;
196 struct ieee80211_rate *rate;
197 int i;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700198 u16 ifmodes = dev->wiphy.interface_modes;
Johannes Berg55682962007-09-20 13:09:35 -0400199
200 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
201 if (!hdr)
202 return -1;
203
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -0500204 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400205 NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
Johannes Berg2a519312009-02-10 21:25:55 +0100206 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
207 dev->wiphy.max_scan_ssids);
Johannes Berg18a83652009-03-31 12:12:05 +0200208 NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
209 dev->wiphy.max_scan_ie_len);
Johannes Bergee688b002008-01-24 19:38:39 +0100210
Johannes Berg25e47c12009-04-02 20:14:06 +0200211 NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
212 sizeof(u32) * dev->wiphy.n_cipher_suites,
213 dev->wiphy.cipher_suites);
214
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700215 nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES);
216 if (!nl_modes)
217 goto nla_put_failure;
218
219 i = 0;
220 while (ifmodes) {
221 if (ifmodes & 1)
222 NLA_PUT_FLAG(msg, i);
223 ifmodes >>= 1;
224 i++;
225 }
226
227 nla_nest_end(msg, nl_modes);
228
Johannes Bergee688b002008-01-24 19:38:39 +0100229 nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
230 if (!nl_bands)
231 goto nla_put_failure;
232
233 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
234 if (!dev->wiphy.bands[band])
235 continue;
236
237 nl_band = nla_nest_start(msg, band);
238 if (!nl_band)
239 goto nla_put_failure;
240
Johannes Bergd51626d2008-10-09 12:20:13 +0200241 /* add HT info */
242 if (dev->wiphy.bands[band]->ht_cap.ht_supported) {
243 NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET,
244 sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
245 &dev->wiphy.bands[band]->ht_cap.mcs);
246 NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA,
247 dev->wiphy.bands[band]->ht_cap.cap);
248 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
249 dev->wiphy.bands[band]->ht_cap.ampdu_factor);
250 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
251 dev->wiphy.bands[band]->ht_cap.ampdu_density);
252 }
253
Johannes Bergee688b002008-01-24 19:38:39 +0100254 /* add frequencies */
255 nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
256 if (!nl_freqs)
257 goto nla_put_failure;
258
259 for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
260 nl_freq = nla_nest_start(msg, i);
261 if (!nl_freq)
262 goto nla_put_failure;
263
264 chan = &dev->wiphy.bands[band]->channels[i];
Johannes Bergee688b002008-01-24 19:38:39 +0100265
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400266 if (nl80211_msg_put_channel(msg, chan))
267 goto nla_put_failure;
Jouni Malinene2f367f262008-11-21 19:01:30 +0200268
Johannes Bergee688b002008-01-24 19:38:39 +0100269 nla_nest_end(msg, nl_freq);
270 }
271
272 nla_nest_end(msg, nl_freqs);
273
274 /* add bitrates */
275 nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
276 if (!nl_rates)
277 goto nla_put_failure;
278
279 for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
280 nl_rate = nla_nest_start(msg, i);
281 if (!nl_rate)
282 goto nla_put_failure;
283
284 rate = &dev->wiphy.bands[band]->bitrates[i];
285 NLA_PUT_U32(msg, NL80211_BITRATE_ATTR_RATE,
286 rate->bitrate);
287 if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
288 NLA_PUT_FLAG(msg,
289 NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE);
290
291 nla_nest_end(msg, nl_rate);
292 }
293
294 nla_nest_end(msg, nl_rates);
295
296 nla_nest_end(msg, nl_band);
297 }
298 nla_nest_end(msg, nl_bands);
299
Johannes Berg8fdc6212009-03-14 09:34:01 +0100300 nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
301 if (!nl_cmds)
302 goto nla_put_failure;
303
304 i = 0;
305#define CMD(op, n) \
306 do { \
307 if (dev->ops->op) { \
308 i++; \
309 NLA_PUT_U32(msg, i, NL80211_CMD_ ## n); \
310 } \
311 } while (0)
312
313 CMD(add_virtual_intf, NEW_INTERFACE);
314 CMD(change_virtual_intf, SET_INTERFACE);
315 CMD(add_key, NEW_KEY);
316 CMD(add_beacon, NEW_BEACON);
317 CMD(add_station, NEW_STATION);
318 CMD(add_mpath, NEW_MPATH);
319 CMD(set_mesh_params, SET_MESH_PARAMS);
320 CMD(change_bss, SET_BSS);
Jouni Malinen636a5d32009-03-19 13:39:22 +0200321 CMD(auth, AUTHENTICATE);
322 CMD(assoc, ASSOCIATE);
323 CMD(deauth, DEAUTHENTICATE);
324 CMD(disassoc, DISASSOCIATE);
Johannes Berg8fdc6212009-03-14 09:34:01 +0100325
326#undef CMD
327 nla_nest_end(msg, nl_cmds);
328
Johannes Berg55682962007-09-20 13:09:35 -0400329 return genlmsg_end(msg, hdr);
330
331 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700332 genlmsg_cancel(msg, hdr);
333 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400334}
335
336static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
337{
338 int idx = 0;
339 int start = cb->args[0];
340 struct cfg80211_registered_device *dev;
341
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500342 mutex_lock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400343 list_for_each_entry(dev, &cfg80211_drv_list, list) {
Julius Volzb4637272008-07-08 14:02:19 +0200344 if (++idx <= start)
Johannes Berg55682962007-09-20 13:09:35 -0400345 continue;
346 if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
347 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Julius Volzb4637272008-07-08 14:02:19 +0200348 dev) < 0) {
349 idx--;
Johannes Berg55682962007-09-20 13:09:35 -0400350 break;
Julius Volzb4637272008-07-08 14:02:19 +0200351 }
Johannes Berg55682962007-09-20 13:09:35 -0400352 }
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500353 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400354
355 cb->args[0] = idx;
356
357 return skb->len;
358}
359
360static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
361{
362 struct sk_buff *msg;
363 struct cfg80211_registered_device *dev;
364
365 dev = cfg80211_get_dev_from_info(info);
366 if (IS_ERR(dev))
367 return PTR_ERR(dev);
368
369 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
370 if (!msg)
371 goto out_err;
372
373 if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0)
374 goto out_free;
375
376 cfg80211_put_dev(dev);
377
378 return genlmsg_unicast(msg, info->snd_pid);
379
380 out_free:
381 nlmsg_free(msg);
382 out_err:
383 cfg80211_put_dev(dev);
384 return -ENOBUFS;
385}
386
Jouni Malinen31888482008-10-30 16:59:24 +0200387static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
388 [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
389 [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
390 [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
391 [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
392 [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
393};
394
395static int parse_txq_params(struct nlattr *tb[],
396 struct ieee80211_txq_params *txq_params)
397{
398 if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] ||
399 !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
400 !tb[NL80211_TXQ_ATTR_AIFS])
401 return -EINVAL;
402
403 txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]);
404 txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
405 txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
406 txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
407 txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
408
409 return 0;
410}
411
Johannes Berg55682962007-09-20 13:09:35 -0400412static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
413{
414 struct cfg80211_registered_device *rdev;
Jouni Malinen31888482008-10-30 16:59:24 +0200415 int result = 0, rem_txq_params = 0;
416 struct nlattr *nl_txq_params;
Johannes Berg55682962007-09-20 13:09:35 -0400417
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100418 rtnl_lock();
Johannes Berg55682962007-09-20 13:09:35 -0400419
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100420 mutex_lock(&cfg80211_mutex);
421
422 rdev = __cfg80211_drv_from_info(info);
423 if (IS_ERR(rdev)) {
424 result = PTR_ERR(rdev);
425 goto unlock;
426 }
427
428 mutex_lock(&rdev->mtx);
429
430 if (info->attrs[NL80211_ATTR_WIPHY_NAME])
Jouni Malinen31888482008-10-30 16:59:24 +0200431 result = cfg80211_dev_rename(
432 rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100433
434 mutex_unlock(&cfg80211_mutex);
435
436 if (result)
437 goto bad_res;
Johannes Berg55682962007-09-20 13:09:35 -0400438
Jouni Malinen31888482008-10-30 16:59:24 +0200439 if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
440 struct ieee80211_txq_params txq_params;
441 struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
442
443 if (!rdev->ops->set_txq_params) {
444 result = -EOPNOTSUPP;
445 goto bad_res;
446 }
447
448 nla_for_each_nested(nl_txq_params,
449 info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
450 rem_txq_params) {
451 nla_parse(tb, NL80211_TXQ_ATTR_MAX,
452 nla_data(nl_txq_params),
453 nla_len(nl_txq_params),
454 txq_params_policy);
455 result = parse_txq_params(tb, &txq_params);
456 if (result)
457 goto bad_res;
458
459 result = rdev->ops->set_txq_params(&rdev->wiphy,
460 &txq_params);
461 if (result)
462 goto bad_res;
463 }
464 }
465
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200466 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Sujith094d05d2008-12-12 11:57:43 +0530467 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200468 struct ieee80211_channel *chan;
Johannes Berg306d6112008-12-08 12:39:04 +0100469 struct ieee80211_sta_ht_cap *ht_cap;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200470 u32 freq, sec_freq;
471
472 if (!rdev->ops->set_channel) {
473 result = -EOPNOTSUPP;
474 goto bad_res;
475 }
476
Johannes Berg306d6112008-12-08 12:39:04 +0100477 result = -EINVAL;
478
Sujith094d05d2008-12-12 11:57:43 +0530479 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
480 channel_type = nla_get_u32(info->attrs[
481 NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
482 if (channel_type != NL80211_CHAN_NO_HT &&
483 channel_type != NL80211_CHAN_HT20 &&
484 channel_type != NL80211_CHAN_HT40PLUS &&
485 channel_type != NL80211_CHAN_HT40MINUS)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200486 goto bad_res;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200487 }
488
489 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
490 chan = ieee80211_get_channel(&rdev->wiphy, freq);
Johannes Berg306d6112008-12-08 12:39:04 +0100491
492 /* Primary channel not allowed */
493 if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200494 goto bad_res;
Johannes Berg306d6112008-12-08 12:39:04 +0100495
Sujith094d05d2008-12-12 11:57:43 +0530496 if (channel_type == NL80211_CHAN_HT40MINUS)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200497 sec_freq = freq - 20;
Sujith094d05d2008-12-12 11:57:43 +0530498 else if (channel_type == NL80211_CHAN_HT40PLUS)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200499 sec_freq = freq + 20;
500 else
501 sec_freq = 0;
502
Johannes Berg306d6112008-12-08 12:39:04 +0100503 ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
504
505 /* no HT capabilities */
Sujith094d05d2008-12-12 11:57:43 +0530506 if (channel_type != NL80211_CHAN_NO_HT &&
Johannes Berg306d6112008-12-08 12:39:04 +0100507 !ht_cap->ht_supported)
508 goto bad_res;
509
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200510 if (sec_freq) {
511 struct ieee80211_channel *schan;
Johannes Berg306d6112008-12-08 12:39:04 +0100512
513 /* no 40 MHz capabilities */
514 if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
515 (ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200516 goto bad_res;
Johannes Berg306d6112008-12-08 12:39:04 +0100517
518 schan = ieee80211_get_channel(&rdev->wiphy, sec_freq);
519
520 /* Secondary channel not allowed */
521 if (!schan || schan->flags & IEEE80211_CHAN_DISABLED)
522 goto bad_res;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200523 }
524
525 result = rdev->ops->set_channel(&rdev->wiphy, chan,
Sujith094d05d2008-12-12 11:57:43 +0530526 channel_type);
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200527 if (result)
528 goto bad_res;
529 }
530
531
Johannes Berg306d6112008-12-08 12:39:04 +0100532 bad_res:
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100533 mutex_unlock(&rdev->mtx);
534 unlock:
535 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400536 return result;
537}
538
539
540static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
Johannes Bergd7264052009-04-19 16:23:20 +0200541 struct cfg80211_registered_device *rdev,
Johannes Berg55682962007-09-20 13:09:35 -0400542 struct net_device *dev)
543{
544 void *hdr;
545
546 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
547 if (!hdr)
548 return -1;
549
550 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
Johannes Bergd7264052009-04-19 16:23:20 +0200551 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400552 NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
Johannes Berg60719ff2008-09-16 14:55:09 +0200553 NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype);
Johannes Berg55682962007-09-20 13:09:35 -0400554 return genlmsg_end(msg, hdr);
555
556 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700557 genlmsg_cancel(msg, hdr);
558 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400559}
560
561static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
562{
563 int wp_idx = 0;
564 int if_idx = 0;
565 int wp_start = cb->args[0];
566 int if_start = cb->args[1];
567 struct cfg80211_registered_device *dev;
568 struct wireless_dev *wdev;
569
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500570 mutex_lock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400571 list_for_each_entry(dev, &cfg80211_drv_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +0200572 if (wp_idx < wp_start) {
573 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400574 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200575 }
Johannes Berg55682962007-09-20 13:09:35 -0400576 if_idx = 0;
577
578 mutex_lock(&dev->devlist_mtx);
579 list_for_each_entry(wdev, &dev->netdev_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +0200580 if (if_idx < if_start) {
581 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400582 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200583 }
Johannes Berg55682962007-09-20 13:09:35 -0400584 if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
585 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Bergd7264052009-04-19 16:23:20 +0200586 dev, wdev->netdev) < 0) {
Johannes Bergbba95fe2008-07-29 13:22:51 +0200587 mutex_unlock(&dev->devlist_mtx);
588 goto out;
589 }
590 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400591 }
592 mutex_unlock(&dev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +0200593
594 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400595 }
Johannes Bergbba95fe2008-07-29 13:22:51 +0200596 out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500597 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400598
599 cb->args[0] = wp_idx;
600 cb->args[1] = if_idx;
601
602 return skb->len;
603}
604
605static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
606{
607 struct sk_buff *msg;
608 struct cfg80211_registered_device *dev;
609 struct net_device *netdev;
610 int err;
611
Johannes Bergbba95fe2008-07-29 13:22:51 +0200612 err = get_drv_dev_by_info_ifindex(info->attrs, &dev, &netdev);
Johannes Berg55682962007-09-20 13:09:35 -0400613 if (err)
614 return err;
615
616 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
617 if (!msg)
618 goto out_err;
619
Johannes Bergd7264052009-04-19 16:23:20 +0200620 if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0,
621 dev, netdev) < 0)
Johannes Berg55682962007-09-20 13:09:35 -0400622 goto out_free;
623
624 dev_put(netdev);
625 cfg80211_put_dev(dev);
626
627 return genlmsg_unicast(msg, info->snd_pid);
628
629 out_free:
630 nlmsg_free(msg);
631 out_err:
632 dev_put(netdev);
633 cfg80211_put_dev(dev);
634 return -ENOBUFS;
635}
636
Michael Wu66f7ac52008-01-31 19:48:22 +0100637static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
638 [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
639 [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
640 [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
641 [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
642 [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
643};
644
645static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
646{
647 struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
648 int flag;
649
650 *mntrflags = 0;
651
652 if (!nla)
653 return -EINVAL;
654
655 if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX,
656 nla, mntr_flags_policy))
657 return -EINVAL;
658
659 for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
660 if (flags[flag])
661 *mntrflags |= (1<<flag);
662
663 return 0;
664}
665
Johannes Berg55682962007-09-20 13:09:35 -0400666static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
667{
668 struct cfg80211_registered_device *drv;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100669 struct vif_params params;
Johannes Berg55682962007-09-20 13:09:35 -0400670 int err, ifindex;
671 enum nl80211_iftype type;
672 struct net_device *dev;
Johannes Berg92ffe052008-09-16 20:39:36 +0200673 u32 _flags, *flags = NULL;
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100674 bool change = false;
Johannes Berg55682962007-09-20 13:09:35 -0400675
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100676 memset(&params, 0, sizeof(params));
677
Johannes Berg3b858752009-03-12 09:55:09 +0100678 rtnl_lock();
679
Johannes Bergbba95fe2008-07-29 13:22:51 +0200680 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg55682962007-09-20 13:09:35 -0400681 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +0100682 goto unlock_rtnl;
683
Johannes Berg55682962007-09-20 13:09:35 -0400684 ifindex = dev->ifindex;
Johannes Berg723b0382008-09-16 20:22:09 +0200685 type = dev->ieee80211_ptr->iftype;
Johannes Berg55682962007-09-20 13:09:35 -0400686 dev_put(dev);
687
Johannes Berg723b0382008-09-16 20:22:09 +0200688 if (info->attrs[NL80211_ATTR_IFTYPE]) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100689 enum nl80211_iftype ntype;
690
691 ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
692 if (type != ntype)
693 change = true;
694 type = ntype;
695 if (type > NL80211_IFTYPE_MAX) {
696 err = -EINVAL;
Johannes Berg723b0382008-09-16 20:22:09 +0200697 goto unlock;
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100698 }
Johannes Berg723b0382008-09-16 20:22:09 +0200699 }
700
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700701 if (!drv->ops->change_virtual_intf ||
702 !(drv->wiphy.interface_modes & (1 << type))) {
Johannes Berg55682962007-09-20 13:09:35 -0400703 err = -EOPNOTSUPP;
704 goto unlock;
705 }
706
Johannes Berg92ffe052008-09-16 20:39:36 +0200707 if (info->attrs[NL80211_ATTR_MESH_ID]) {
708 if (type != NL80211_IFTYPE_MESH_POINT) {
709 err = -EINVAL;
710 goto unlock;
711 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100712 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
713 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100714 change = true;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100715 }
716
Johannes Berg92ffe052008-09-16 20:39:36 +0200717 if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
718 if (type != NL80211_IFTYPE_MONITOR) {
719 err = -EINVAL;
720 goto unlock;
721 }
722 err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
723 &_flags);
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100724 if (err)
725 goto unlock;
726
727 flags = &_flags;
728 change = true;
Johannes Berg92ffe052008-09-16 20:39:36 +0200729 }
Johannes Berg3b858752009-03-12 09:55:09 +0100730
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100731 if (change)
732 err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex,
733 type, flags, &params);
734 else
735 err = 0;
Johannes Berg60719ff2008-09-16 14:55:09 +0200736
737 dev = __dev_get_by_index(&init_net, ifindex);
738 WARN_ON(!dev || (!err && dev->ieee80211_ptr->iftype != type));
739
Johannes Berg55682962007-09-20 13:09:35 -0400740 unlock:
741 cfg80211_put_dev(drv);
Johannes Berg3b858752009-03-12 09:55:09 +0100742 unlock_rtnl:
743 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400744 return err;
745}
746
747static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
748{
749 struct cfg80211_registered_device *drv;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100750 struct vif_params params;
Johannes Berg55682962007-09-20 13:09:35 -0400751 int err;
752 enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
Michael Wu66f7ac52008-01-31 19:48:22 +0100753 u32 flags;
Johannes Berg55682962007-09-20 13:09:35 -0400754
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100755 memset(&params, 0, sizeof(params));
756
Johannes Berg55682962007-09-20 13:09:35 -0400757 if (!info->attrs[NL80211_ATTR_IFNAME])
758 return -EINVAL;
759
760 if (info->attrs[NL80211_ATTR_IFTYPE]) {
761 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
762 if (type > NL80211_IFTYPE_MAX)
763 return -EINVAL;
764 }
765
Johannes Berg3b858752009-03-12 09:55:09 +0100766 rtnl_lock();
767
Johannes Berg55682962007-09-20 13:09:35 -0400768 drv = cfg80211_get_dev_from_info(info);
Johannes Berg3b858752009-03-12 09:55:09 +0100769 if (IS_ERR(drv)) {
770 err = PTR_ERR(drv);
771 goto unlock_rtnl;
772 }
Johannes Berg55682962007-09-20 13:09:35 -0400773
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700774 if (!drv->ops->add_virtual_intf ||
775 !(drv->wiphy.interface_modes & (1 << type))) {
Johannes Berg55682962007-09-20 13:09:35 -0400776 err = -EOPNOTSUPP;
777 goto unlock;
778 }
779
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100780 if (type == NL80211_IFTYPE_MESH_POINT &&
781 info->attrs[NL80211_ATTR_MESH_ID]) {
782 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
783 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
784 }
785
Michael Wu66f7ac52008-01-31 19:48:22 +0100786 err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
787 info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
788 &flags);
Johannes Berg55682962007-09-20 13:09:35 -0400789 err = drv->ops->add_virtual_intf(&drv->wiphy,
Michael Wu66f7ac52008-01-31 19:48:22 +0100790 nla_data(info->attrs[NL80211_ATTR_IFNAME]),
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100791 type, err ? NULL : &flags, &params);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100792
Johannes Berg55682962007-09-20 13:09:35 -0400793 unlock:
794 cfg80211_put_dev(drv);
Johannes Berg3b858752009-03-12 09:55:09 +0100795 unlock_rtnl:
796 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400797 return err;
798}
799
800static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
801{
802 struct cfg80211_registered_device *drv;
803 int ifindex, err;
804 struct net_device *dev;
805
Johannes Berg3b858752009-03-12 09:55:09 +0100806 rtnl_lock();
807
Johannes Bergbba95fe2008-07-29 13:22:51 +0200808 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg55682962007-09-20 13:09:35 -0400809 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +0100810 goto unlock_rtnl;
Johannes Berg55682962007-09-20 13:09:35 -0400811 ifindex = dev->ifindex;
812 dev_put(dev);
813
814 if (!drv->ops->del_virtual_intf) {
815 err = -EOPNOTSUPP;
816 goto out;
817 }
818
Johannes Berg55682962007-09-20 13:09:35 -0400819 err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex);
Johannes Berg55682962007-09-20 13:09:35 -0400820
821 out:
822 cfg80211_put_dev(drv);
Johannes Berg3b858752009-03-12 09:55:09 +0100823 unlock_rtnl:
824 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400825 return err;
826}
827
Johannes Berg41ade002007-12-19 02:03:29 +0100828struct get_key_cookie {
829 struct sk_buff *msg;
830 int error;
831};
832
833static void get_key_callback(void *c, struct key_params *params)
834{
835 struct get_key_cookie *cookie = c;
836
837 if (params->key)
838 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA,
839 params->key_len, params->key);
840
841 if (params->seq)
842 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ,
843 params->seq_len, params->seq);
844
845 if (params->cipher)
846 NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
847 params->cipher);
848
849 return;
850 nla_put_failure:
851 cookie->error = 1;
852}
853
854static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
855{
856 struct cfg80211_registered_device *drv;
857 int err;
858 struct net_device *dev;
859 u8 key_idx = 0;
860 u8 *mac_addr = NULL;
861 struct get_key_cookie cookie = {
862 .error = 0,
863 };
864 void *hdr;
865 struct sk_buff *msg;
866
867 if (info->attrs[NL80211_ATTR_KEY_IDX])
868 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
869
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +0200870 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +0100871 return -EINVAL;
872
873 if (info->attrs[NL80211_ATTR_MAC])
874 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
875
Johannes Berg3b858752009-03-12 09:55:09 +0100876 rtnl_lock();
877
Johannes Bergbba95fe2008-07-29 13:22:51 +0200878 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +0100879 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +0100880 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +0100881
882 if (!drv->ops->get_key) {
883 err = -EOPNOTSUPP;
884 goto out;
885 }
886
887 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
888 if (!msg) {
889 err = -ENOMEM;
890 goto out;
891 }
892
893 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
894 NL80211_CMD_NEW_KEY);
895
896 if (IS_ERR(hdr)) {
897 err = PTR_ERR(hdr);
898 goto out;
899 }
900
901 cookie.msg = msg;
902
903 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
904 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
905 if (mac_addr)
906 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
907
Johannes Berg41ade002007-12-19 02:03:29 +0100908 err = drv->ops->get_key(&drv->wiphy, dev, key_idx, mac_addr,
909 &cookie, get_key_callback);
Johannes Berg41ade002007-12-19 02:03:29 +0100910
911 if (err)
912 goto out;
913
914 if (cookie.error)
915 goto nla_put_failure;
916
917 genlmsg_end(msg, hdr);
918 err = genlmsg_unicast(msg, info->snd_pid);
919 goto out;
920
921 nla_put_failure:
922 err = -ENOBUFS;
923 nlmsg_free(msg);
924 out:
925 cfg80211_put_dev(drv);
926 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +0100927 unlock_rtnl:
928 rtnl_unlock();
929
Johannes Berg41ade002007-12-19 02:03:29 +0100930 return err;
931}
932
933static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
934{
935 struct cfg80211_registered_device *drv;
936 int err;
937 struct net_device *dev;
938 u8 key_idx;
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +0200939 int (*func)(struct wiphy *wiphy, struct net_device *netdev,
940 u8 key_index);
Johannes Berg41ade002007-12-19 02:03:29 +0100941
942 if (!info->attrs[NL80211_ATTR_KEY_IDX])
943 return -EINVAL;
944
945 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
946
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +0200947 if (info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) {
948 if (key_idx < 4 || key_idx > 5)
949 return -EINVAL;
950 } else if (key_idx > 3)
Johannes Berg41ade002007-12-19 02:03:29 +0100951 return -EINVAL;
952
953 /* currently only support setting default key */
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +0200954 if (!info->attrs[NL80211_ATTR_KEY_DEFAULT] &&
955 !info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT])
Johannes Berg41ade002007-12-19 02:03:29 +0100956 return -EINVAL;
957
Johannes Berg3b858752009-03-12 09:55:09 +0100958 rtnl_lock();
959
Johannes Bergbba95fe2008-07-29 13:22:51 +0200960 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +0100961 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +0100962 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +0100963
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +0200964 if (info->attrs[NL80211_ATTR_KEY_DEFAULT])
965 func = drv->ops->set_default_key;
966 else
967 func = drv->ops->set_default_mgmt_key;
968
969 if (!func) {
Johannes Berg41ade002007-12-19 02:03:29 +0100970 err = -EOPNOTSUPP;
971 goto out;
972 }
973
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +0200974 err = func(&drv->wiphy, dev, key_idx);
Johannes Berg41ade002007-12-19 02:03:29 +0100975
976 out:
977 cfg80211_put_dev(drv);
978 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +0100979
980 unlock_rtnl:
981 rtnl_unlock();
982
Johannes Berg41ade002007-12-19 02:03:29 +0100983 return err;
984}
985
986static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
987{
988 struct cfg80211_registered_device *drv;
Johannes Berg25e47c12009-04-02 20:14:06 +0200989 int err, i;
Johannes Berg41ade002007-12-19 02:03:29 +0100990 struct net_device *dev;
991 struct key_params params;
992 u8 key_idx = 0;
993 u8 *mac_addr = NULL;
994
995 memset(&params, 0, sizeof(params));
996
997 if (!info->attrs[NL80211_ATTR_KEY_CIPHER])
998 return -EINVAL;
999
1000 if (info->attrs[NL80211_ATTR_KEY_DATA]) {
1001 params.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
1002 params.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
1003 }
1004
1005 if (info->attrs[NL80211_ATTR_KEY_IDX])
1006 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1007
1008 params.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
1009
1010 if (info->attrs[NL80211_ATTR_MAC])
1011 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1012
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001013 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +01001014 return -EINVAL;
1015
1016 /*
1017 * Disallow pairwise keys with non-zero index unless it's WEP
1018 * (because current deployments use pairwise WEP keys with
1019 * non-zero indizes but 802.11i clearly specifies to use zero)
1020 */
1021 if (mac_addr && key_idx &&
1022 params.cipher != WLAN_CIPHER_SUITE_WEP40 &&
1023 params.cipher != WLAN_CIPHER_SUITE_WEP104)
1024 return -EINVAL;
1025
1026 /* TODO: add definitions for the lengths to linux/ieee80211.h */
1027 switch (params.cipher) {
1028 case WLAN_CIPHER_SUITE_WEP40:
1029 if (params.key_len != 5)
1030 return -EINVAL;
1031 break;
1032 case WLAN_CIPHER_SUITE_TKIP:
1033 if (params.key_len != 32)
1034 return -EINVAL;
1035 break;
1036 case WLAN_CIPHER_SUITE_CCMP:
1037 if (params.key_len != 16)
1038 return -EINVAL;
1039 break;
1040 case WLAN_CIPHER_SUITE_WEP104:
1041 if (params.key_len != 13)
1042 return -EINVAL;
1043 break;
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001044 case WLAN_CIPHER_SUITE_AES_CMAC:
1045 if (params.key_len != 16)
1046 return -EINVAL;
1047 break;
Johannes Berg41ade002007-12-19 02:03:29 +01001048 default:
1049 return -EINVAL;
1050 }
1051
Johannes Berg3b858752009-03-12 09:55:09 +01001052 rtnl_lock();
1053
Johannes Bergbba95fe2008-07-29 13:22:51 +02001054 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001055 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001056 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001057
Johannes Berg25e47c12009-04-02 20:14:06 +02001058 for (i = 0; i < drv->wiphy.n_cipher_suites; i++)
1059 if (params.cipher == drv->wiphy.cipher_suites[i])
1060 break;
1061 if (i == drv->wiphy.n_cipher_suites) {
1062 err = -EINVAL;
1063 goto out;
1064 }
1065
Johannes Berg41ade002007-12-19 02:03:29 +01001066 if (!drv->ops->add_key) {
1067 err = -EOPNOTSUPP;
1068 goto out;
1069 }
1070
Johannes Berg41ade002007-12-19 02:03:29 +01001071 err = drv->ops->add_key(&drv->wiphy, dev, key_idx, mac_addr, &params);
Johannes Berg41ade002007-12-19 02:03:29 +01001072
1073 out:
1074 cfg80211_put_dev(drv);
1075 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001076 unlock_rtnl:
1077 rtnl_unlock();
1078
Johannes Berg41ade002007-12-19 02:03:29 +01001079 return err;
1080}
1081
1082static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
1083{
1084 struct cfg80211_registered_device *drv;
1085 int err;
1086 struct net_device *dev;
1087 u8 key_idx = 0;
1088 u8 *mac_addr = NULL;
1089
1090 if (info->attrs[NL80211_ATTR_KEY_IDX])
1091 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1092
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001093 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +01001094 return -EINVAL;
1095
1096 if (info->attrs[NL80211_ATTR_MAC])
1097 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1098
Johannes Berg3b858752009-03-12 09:55:09 +01001099 rtnl_lock();
1100
Johannes Bergbba95fe2008-07-29 13:22:51 +02001101 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001102 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001103 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001104
1105 if (!drv->ops->del_key) {
1106 err = -EOPNOTSUPP;
1107 goto out;
1108 }
1109
Johannes Berg41ade002007-12-19 02:03:29 +01001110 err = drv->ops->del_key(&drv->wiphy, dev, key_idx, mac_addr);
Johannes Berg41ade002007-12-19 02:03:29 +01001111
1112 out:
1113 cfg80211_put_dev(drv);
1114 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001115
1116 unlock_rtnl:
1117 rtnl_unlock();
1118
Johannes Berg41ade002007-12-19 02:03:29 +01001119 return err;
1120}
1121
Johannes Berged1b6cc2007-12-19 02:03:32 +01001122static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
1123{
1124 int (*call)(struct wiphy *wiphy, struct net_device *dev,
1125 struct beacon_parameters *info);
1126 struct cfg80211_registered_device *drv;
1127 int err;
1128 struct net_device *dev;
1129 struct beacon_parameters params;
1130 int haveinfo = 0;
1131
Johannes Bergf4a11bb2009-03-27 12:40:28 +01001132 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]))
1133 return -EINVAL;
1134
Johannes Berg3b858752009-03-12 09:55:09 +01001135 rtnl_lock();
1136
Johannes Bergbba95fe2008-07-29 13:22:51 +02001137 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001138 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001139 goto unlock_rtnl;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001140
Jouni Malineneec60b02009-03-20 21:21:19 +02001141 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
1142 err = -EOPNOTSUPP;
1143 goto out;
1144 }
1145
Johannes Berged1b6cc2007-12-19 02:03:32 +01001146 switch (info->genlhdr->cmd) {
1147 case NL80211_CMD_NEW_BEACON:
1148 /* these are required for NEW_BEACON */
1149 if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
1150 !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
1151 !info->attrs[NL80211_ATTR_BEACON_HEAD]) {
1152 err = -EINVAL;
1153 goto out;
1154 }
1155
1156 call = drv->ops->add_beacon;
1157 break;
1158 case NL80211_CMD_SET_BEACON:
1159 call = drv->ops->set_beacon;
1160 break;
1161 default:
1162 WARN_ON(1);
1163 err = -EOPNOTSUPP;
1164 goto out;
1165 }
1166
1167 if (!call) {
1168 err = -EOPNOTSUPP;
1169 goto out;
1170 }
1171
1172 memset(&params, 0, sizeof(params));
1173
1174 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
1175 params.interval =
1176 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
1177 haveinfo = 1;
1178 }
1179
1180 if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) {
1181 params.dtim_period =
1182 nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
1183 haveinfo = 1;
1184 }
1185
1186 if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
1187 params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
1188 params.head_len =
1189 nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
1190 haveinfo = 1;
1191 }
1192
1193 if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
1194 params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
1195 params.tail_len =
1196 nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
1197 haveinfo = 1;
1198 }
1199
1200 if (!haveinfo) {
1201 err = -EINVAL;
1202 goto out;
1203 }
1204
Johannes Berged1b6cc2007-12-19 02:03:32 +01001205 err = call(&drv->wiphy, dev, &params);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001206
1207 out:
1208 cfg80211_put_dev(drv);
1209 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001210 unlock_rtnl:
1211 rtnl_unlock();
1212
Johannes Berged1b6cc2007-12-19 02:03:32 +01001213 return err;
1214}
1215
1216static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
1217{
1218 struct cfg80211_registered_device *drv;
1219 int err;
1220 struct net_device *dev;
1221
Johannes Berg3b858752009-03-12 09:55:09 +01001222 rtnl_lock();
1223
Johannes Bergbba95fe2008-07-29 13:22:51 +02001224 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001225 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001226 goto unlock_rtnl;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001227
1228 if (!drv->ops->del_beacon) {
1229 err = -EOPNOTSUPP;
1230 goto out;
1231 }
1232
Jouni Malineneec60b02009-03-20 21:21:19 +02001233 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
1234 err = -EOPNOTSUPP;
1235 goto out;
1236 }
Johannes Berged1b6cc2007-12-19 02:03:32 +01001237 err = drv->ops->del_beacon(&drv->wiphy, dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001238
1239 out:
1240 cfg80211_put_dev(drv);
1241 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001242 unlock_rtnl:
1243 rtnl_unlock();
1244
Johannes Berged1b6cc2007-12-19 02:03:32 +01001245 return err;
1246}
1247
Johannes Berg5727ef12007-12-19 02:03:34 +01001248static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
1249 [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
1250 [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
1251 [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
1252};
1253
1254static int parse_station_flags(struct nlattr *nla, u32 *staflags)
1255{
1256 struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
1257 int flag;
1258
1259 *staflags = 0;
1260
1261 if (!nla)
1262 return 0;
1263
1264 if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX,
1265 nla, sta_flags_policy))
1266 return -EINVAL;
1267
1268 *staflags = STATION_FLAG_CHANGED;
1269
1270 for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++)
1271 if (flags[flag])
1272 *staflags |= (1<<flag);
1273
1274 return 0;
1275}
1276
Henning Rogge420e7fa2008-12-11 22:04:19 +01001277static u16 nl80211_calculate_bitrate(struct rate_info *rate)
1278{
1279 int modulation, streams, bitrate;
1280
1281 if (!(rate->flags & RATE_INFO_FLAGS_MCS))
1282 return rate->legacy;
1283
1284 /* the formula below does only work for MCS values smaller than 32 */
1285 if (rate->mcs >= 32)
1286 return 0;
1287
1288 modulation = rate->mcs & 7;
1289 streams = (rate->mcs >> 3) + 1;
1290
1291 bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ?
1292 13500000 : 6500000;
1293
1294 if (modulation < 4)
1295 bitrate *= (modulation + 1);
1296 else if (modulation == 4)
1297 bitrate *= (modulation + 2);
1298 else
1299 bitrate *= (modulation + 3);
1300
1301 bitrate *= streams;
1302
1303 if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
1304 bitrate = (bitrate / 9) * 10;
1305
1306 /* do NOT round down here */
1307 return (bitrate + 50000) / 100000;
1308}
1309
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001310static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
1311 int flags, struct net_device *dev,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001312 u8 *mac_addr, struct station_info *sinfo)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001313{
1314 void *hdr;
Henning Rogge420e7fa2008-12-11 22:04:19 +01001315 struct nlattr *sinfoattr, *txrate;
1316 u16 bitrate;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001317
1318 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
1319 if (!hdr)
1320 return -1;
1321
1322 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1323 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1324
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001325 sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
1326 if (!sinfoattr)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001327 goto nla_put_failure;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001328 if (sinfo->filled & STATION_INFO_INACTIVE_TIME)
1329 NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME,
1330 sinfo->inactive_time);
1331 if (sinfo->filled & STATION_INFO_RX_BYTES)
1332 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES,
1333 sinfo->rx_bytes);
1334 if (sinfo->filled & STATION_INFO_TX_BYTES)
1335 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES,
1336 sinfo->tx_bytes);
1337 if (sinfo->filled & STATION_INFO_LLID)
1338 NLA_PUT_U16(msg, NL80211_STA_INFO_LLID,
1339 sinfo->llid);
1340 if (sinfo->filled & STATION_INFO_PLID)
1341 NLA_PUT_U16(msg, NL80211_STA_INFO_PLID,
1342 sinfo->plid);
1343 if (sinfo->filled & STATION_INFO_PLINK_STATE)
1344 NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
1345 sinfo->plink_state);
Henning Rogge420e7fa2008-12-11 22:04:19 +01001346 if (sinfo->filled & STATION_INFO_SIGNAL)
1347 NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL,
1348 sinfo->signal);
1349 if (sinfo->filled & STATION_INFO_TX_BITRATE) {
1350 txrate = nla_nest_start(msg, NL80211_STA_INFO_TX_BITRATE);
1351 if (!txrate)
1352 goto nla_put_failure;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001353
Henning Rogge420e7fa2008-12-11 22:04:19 +01001354 /* nl80211_calculate_bitrate will return 0 for mcs >= 32 */
1355 bitrate = nl80211_calculate_bitrate(&sinfo->txrate);
1356 if (bitrate > 0)
1357 NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
1358
1359 if (sinfo->txrate.flags & RATE_INFO_FLAGS_MCS)
1360 NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS,
1361 sinfo->txrate.mcs);
1362 if (sinfo->txrate.flags & RATE_INFO_FLAGS_40_MHZ_WIDTH)
1363 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH);
1364 if (sinfo->txrate.flags & RATE_INFO_FLAGS_SHORT_GI)
1365 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI);
1366
1367 nla_nest_end(msg, txrate);
1368 }
Jouni Malinen98c8a60a2009-02-17 13:24:57 +02001369 if (sinfo->filled & STATION_INFO_RX_PACKETS)
1370 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS,
1371 sinfo->rx_packets);
1372 if (sinfo->filled & STATION_INFO_TX_PACKETS)
1373 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS,
1374 sinfo->tx_packets);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001375 nla_nest_end(msg, sinfoattr);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001376
1377 return genlmsg_end(msg, hdr);
1378
1379 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001380 genlmsg_cancel(msg, hdr);
1381 return -EMSGSIZE;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001382}
1383
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001384static int nl80211_dump_station(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02001385 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001386{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001387 struct station_info sinfo;
1388 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001389 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001390 u8 mac_addr[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02001391 int ifidx = cb->args[0];
1392 int sta_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001393 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001394
Johannes Bergbba95fe2008-07-29 13:22:51 +02001395 if (!ifidx) {
1396 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
1397 nl80211_fam.attrbuf, nl80211_fam.maxattr,
1398 nl80211_policy);
1399 if (err)
1400 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001401
Johannes Bergbba95fe2008-07-29 13:22:51 +02001402 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
1403 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001404
Johannes Bergbba95fe2008-07-29 13:22:51 +02001405 ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
1406 if (!ifidx)
1407 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001408 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001409
Johannes Berg3b858752009-03-12 09:55:09 +01001410 rtnl_lock();
1411
1412 netdev = __dev_get_by_index(&init_net, ifidx);
1413 if (!netdev) {
1414 err = -ENODEV;
1415 goto out_rtnl;
1416 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001417
Johannes Bergbba95fe2008-07-29 13:22:51 +02001418 dev = cfg80211_get_dev_from_ifindex(ifidx);
1419 if (IS_ERR(dev)) {
1420 err = PTR_ERR(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001421 goto out_rtnl;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001422 }
1423
1424 if (!dev->ops->dump_station) {
Jouni Malineneec60b02009-03-20 21:21:19 +02001425 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001426 goto out_err;
1427 }
1428
Johannes Bergbba95fe2008-07-29 13:22:51 +02001429 while (1) {
1430 err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
1431 mac_addr, &sinfo);
1432 if (err == -ENOENT)
1433 break;
1434 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001435 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001436
1437 if (nl80211_send_station(skb,
1438 NETLINK_CB(cb->skb).pid,
1439 cb->nlh->nlmsg_seq, NLM_F_MULTI,
1440 netdev, mac_addr,
1441 &sinfo) < 0)
1442 goto out;
1443
1444 sta_idx++;
1445 }
1446
1447
1448 out:
1449 cb->args[1] = sta_idx;
1450 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001451 out_err:
1452 cfg80211_put_dev(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001453 out_rtnl:
1454 rtnl_unlock();
Johannes Bergbba95fe2008-07-29 13:22:51 +02001455
1456 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001457}
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001458
Johannes Berg5727ef12007-12-19 02:03:34 +01001459static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
1460{
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001461 struct cfg80211_registered_device *drv;
1462 int err;
1463 struct net_device *dev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001464 struct station_info sinfo;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001465 struct sk_buff *msg;
1466 u8 *mac_addr = NULL;
1467
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001468 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001469
1470 if (!info->attrs[NL80211_ATTR_MAC])
1471 return -EINVAL;
1472
1473 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1474
Johannes Berg3b858752009-03-12 09:55:09 +01001475 rtnl_lock();
1476
Johannes Bergbba95fe2008-07-29 13:22:51 +02001477 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001478 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001479 goto out_rtnl;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001480
1481 if (!drv->ops->get_station) {
1482 err = -EOPNOTSUPP;
1483 goto out;
1484 }
1485
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001486 err = drv->ops->get_station(&drv->wiphy, dev, mac_addr, &sinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001487 if (err)
1488 goto out;
1489
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001490 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1491 if (!msg)
1492 goto out;
1493
1494 if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001495 dev, mac_addr, &sinfo) < 0)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001496 goto out_free;
1497
1498 err = genlmsg_unicast(msg, info->snd_pid);
1499 goto out;
1500
1501 out_free:
1502 nlmsg_free(msg);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001503 out:
1504 cfg80211_put_dev(drv);
1505 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001506 out_rtnl:
1507 rtnl_unlock();
1508
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001509 return err;
Johannes Berg5727ef12007-12-19 02:03:34 +01001510}
1511
1512/*
1513 * Get vlan interface making sure it is on the right wiphy.
1514 */
1515static int get_vlan(struct nlattr *vlanattr,
1516 struct cfg80211_registered_device *rdev,
1517 struct net_device **vlan)
1518{
1519 *vlan = NULL;
1520
1521 if (vlanattr) {
1522 *vlan = dev_get_by_index(&init_net, nla_get_u32(vlanattr));
1523 if (!*vlan)
1524 return -ENODEV;
1525 if (!(*vlan)->ieee80211_ptr)
1526 return -EINVAL;
1527 if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy)
1528 return -EINVAL;
1529 }
1530 return 0;
1531}
1532
1533static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
1534{
1535 struct cfg80211_registered_device *drv;
1536 int err;
1537 struct net_device *dev;
1538 struct station_parameters params;
1539 u8 *mac_addr = NULL;
1540
1541 memset(&params, 0, sizeof(params));
1542
1543 params.listen_interval = -1;
1544
1545 if (info->attrs[NL80211_ATTR_STA_AID])
1546 return -EINVAL;
1547
1548 if (!info->attrs[NL80211_ATTR_MAC])
1549 return -EINVAL;
1550
1551 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1552
1553 if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
1554 params.supported_rates =
1555 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1556 params.supported_rates_len =
1557 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1558 }
1559
1560 if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
1561 params.listen_interval =
1562 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
1563
Jouni Malinen36aedc902008-08-25 11:58:58 +03001564 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
1565 params.ht_capa =
1566 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
1567
Johannes Berg5727ef12007-12-19 02:03:34 +01001568 if (parse_station_flags(info->attrs[NL80211_ATTR_STA_FLAGS],
1569 &params.station_flags))
1570 return -EINVAL;
1571
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001572 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
1573 params.plink_action =
1574 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
1575
Johannes Berg3b858752009-03-12 09:55:09 +01001576 rtnl_lock();
1577
Johannes Bergbba95fe2008-07-29 13:22:51 +02001578 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001579 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001580 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01001581
1582 err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, &params.vlan);
1583 if (err)
1584 goto out;
1585
1586 if (!drv->ops->change_station) {
1587 err = -EOPNOTSUPP;
1588 goto out;
1589 }
1590
Johannes Berg5727ef12007-12-19 02:03:34 +01001591 err = drv->ops->change_station(&drv->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01001592
1593 out:
1594 if (params.vlan)
1595 dev_put(params.vlan);
1596 cfg80211_put_dev(drv);
1597 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001598 out_rtnl:
1599 rtnl_unlock();
1600
Johannes Berg5727ef12007-12-19 02:03:34 +01001601 return err;
1602}
1603
1604static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
1605{
1606 struct cfg80211_registered_device *drv;
1607 int err;
1608 struct net_device *dev;
1609 struct station_parameters params;
1610 u8 *mac_addr = NULL;
1611
1612 memset(&params, 0, sizeof(params));
1613
1614 if (!info->attrs[NL80211_ATTR_MAC])
1615 return -EINVAL;
1616
1617 if (!info->attrs[NL80211_ATTR_STA_AID])
1618 return -EINVAL;
1619
1620 if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
1621 return -EINVAL;
1622
1623 if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
1624 return -EINVAL;
1625
1626 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1627 params.supported_rates =
1628 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1629 params.supported_rates_len =
1630 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1631 params.listen_interval =
1632 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
Johannes Berg16f2e852008-04-07 14:35:46 +02001633 params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
Jouni Malinen36aedc902008-08-25 11:58:58 +03001634 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
1635 params.ht_capa =
1636 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
Johannes Berg5727ef12007-12-19 02:03:34 +01001637
1638 if (parse_station_flags(info->attrs[NL80211_ATTR_STA_FLAGS],
1639 &params.station_flags))
1640 return -EINVAL;
1641
Johannes Berg3b858752009-03-12 09:55:09 +01001642 rtnl_lock();
1643
Johannes Bergbba95fe2008-07-29 13:22:51 +02001644 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001645 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001646 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01001647
1648 err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, &params.vlan);
1649 if (err)
1650 goto out;
1651
1652 if (!drv->ops->add_station) {
1653 err = -EOPNOTSUPP;
1654 goto out;
1655 }
1656
Jouni Malinen35a8efe2009-03-20 21:21:18 +02001657 if (!netif_running(dev)) {
1658 err = -ENETDOWN;
1659 goto out;
1660 }
1661
Johannes Berg5727ef12007-12-19 02:03:34 +01001662 err = drv->ops->add_station(&drv->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01001663
1664 out:
1665 if (params.vlan)
1666 dev_put(params.vlan);
1667 cfg80211_put_dev(drv);
1668 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001669 out_rtnl:
1670 rtnl_unlock();
1671
Johannes Berg5727ef12007-12-19 02:03:34 +01001672 return err;
1673}
1674
1675static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
1676{
1677 struct cfg80211_registered_device *drv;
1678 int err;
1679 struct net_device *dev;
1680 u8 *mac_addr = NULL;
1681
1682 if (info->attrs[NL80211_ATTR_MAC])
1683 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1684
Johannes Berg3b858752009-03-12 09:55:09 +01001685 rtnl_lock();
1686
Johannes Bergbba95fe2008-07-29 13:22:51 +02001687 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001688 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001689 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01001690
1691 if (!drv->ops->del_station) {
1692 err = -EOPNOTSUPP;
1693 goto out;
1694 }
1695
Johannes Berg5727ef12007-12-19 02:03:34 +01001696 err = drv->ops->del_station(&drv->wiphy, dev, mac_addr);
Johannes Berg5727ef12007-12-19 02:03:34 +01001697
1698 out:
1699 cfg80211_put_dev(drv);
1700 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001701 out_rtnl:
1702 rtnl_unlock();
1703
Johannes Berg5727ef12007-12-19 02:03:34 +01001704 return err;
1705}
1706
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001707static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq,
1708 int flags, struct net_device *dev,
1709 u8 *dst, u8 *next_hop,
1710 struct mpath_info *pinfo)
1711{
1712 void *hdr;
1713 struct nlattr *pinfoattr;
1714
1715 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
1716 if (!hdr)
1717 return -1;
1718
1719 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1720 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
1721 NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop);
1722
1723 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
1724 if (!pinfoattr)
1725 goto nla_put_failure;
1726 if (pinfo->filled & MPATH_INFO_FRAME_QLEN)
1727 NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
1728 pinfo->frame_qlen);
1729 if (pinfo->filled & MPATH_INFO_DSN)
1730 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DSN,
1731 pinfo->dsn);
1732 if (pinfo->filled & MPATH_INFO_METRIC)
1733 NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC,
1734 pinfo->metric);
1735 if (pinfo->filled & MPATH_INFO_EXPTIME)
1736 NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME,
1737 pinfo->exptime);
1738 if (pinfo->filled & MPATH_INFO_FLAGS)
1739 NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS,
1740 pinfo->flags);
1741 if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT)
1742 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
1743 pinfo->discovery_timeout);
1744 if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES)
1745 NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
1746 pinfo->discovery_retries);
1747
1748 nla_nest_end(msg, pinfoattr);
1749
1750 return genlmsg_end(msg, hdr);
1751
1752 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001753 genlmsg_cancel(msg, hdr);
1754 return -EMSGSIZE;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001755}
1756
1757static int nl80211_dump_mpath(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02001758 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001759{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001760 struct mpath_info pinfo;
1761 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001762 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001763 u8 dst[ETH_ALEN];
1764 u8 next_hop[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02001765 int ifidx = cb->args[0];
1766 int path_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001767 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001768
Johannes Bergbba95fe2008-07-29 13:22:51 +02001769 if (!ifidx) {
1770 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
1771 nl80211_fam.attrbuf, nl80211_fam.maxattr,
1772 nl80211_policy);
1773 if (err)
1774 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001775
Johannes Bergbba95fe2008-07-29 13:22:51 +02001776 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
1777 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001778
Johannes Bergbba95fe2008-07-29 13:22:51 +02001779 ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
1780 if (!ifidx)
1781 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001782 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001783
Johannes Berg3b858752009-03-12 09:55:09 +01001784 rtnl_lock();
1785
1786 netdev = __dev_get_by_index(&init_net, ifidx);
1787 if (!netdev) {
1788 err = -ENODEV;
1789 goto out_rtnl;
1790 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001791
Johannes Bergbba95fe2008-07-29 13:22:51 +02001792 dev = cfg80211_get_dev_from_ifindex(ifidx);
1793 if (IS_ERR(dev)) {
1794 err = PTR_ERR(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001795 goto out_rtnl;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001796 }
1797
1798 if (!dev->ops->dump_mpath) {
Jouni Malineneec60b02009-03-20 21:21:19 +02001799 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001800 goto out_err;
1801 }
1802
Jouni Malineneec60b02009-03-20 21:21:19 +02001803 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
1804 err = -EOPNOTSUPP;
1805 goto out;
1806 }
1807
Johannes Bergbba95fe2008-07-29 13:22:51 +02001808 while (1) {
1809 err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
1810 dst, next_hop, &pinfo);
1811 if (err == -ENOENT)
1812 break;
1813 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001814 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001815
1816 if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid,
1817 cb->nlh->nlmsg_seq, NLM_F_MULTI,
1818 netdev, dst, next_hop,
1819 &pinfo) < 0)
1820 goto out;
1821
1822 path_idx++;
1823 }
1824
1825
1826 out:
1827 cb->args[1] = path_idx;
1828 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001829 out_err:
1830 cfg80211_put_dev(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001831 out_rtnl:
1832 rtnl_unlock();
Johannes Bergbba95fe2008-07-29 13:22:51 +02001833
1834 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001835}
1836
1837static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
1838{
1839 struct cfg80211_registered_device *drv;
1840 int err;
1841 struct net_device *dev;
1842 struct mpath_info pinfo;
1843 struct sk_buff *msg;
1844 u8 *dst = NULL;
1845 u8 next_hop[ETH_ALEN];
1846
1847 memset(&pinfo, 0, sizeof(pinfo));
1848
1849 if (!info->attrs[NL80211_ATTR_MAC])
1850 return -EINVAL;
1851
1852 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
1853
Johannes Berg3b858752009-03-12 09:55:09 +01001854 rtnl_lock();
1855
Johannes Bergbba95fe2008-07-29 13:22:51 +02001856 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001857 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001858 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001859
1860 if (!drv->ops->get_mpath) {
1861 err = -EOPNOTSUPP;
1862 goto out;
1863 }
1864
Jouni Malineneec60b02009-03-20 21:21:19 +02001865 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
1866 err = -EOPNOTSUPP;
1867 goto out;
1868 }
1869
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001870 err = drv->ops->get_mpath(&drv->wiphy, dev, dst, next_hop, &pinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001871 if (err)
1872 goto out;
1873
1874 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1875 if (!msg)
1876 goto out;
1877
1878 if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0,
1879 dev, dst, next_hop, &pinfo) < 0)
1880 goto out_free;
1881
1882 err = genlmsg_unicast(msg, info->snd_pid);
1883 goto out;
1884
1885 out_free:
1886 nlmsg_free(msg);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001887 out:
1888 cfg80211_put_dev(drv);
1889 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001890 out_rtnl:
1891 rtnl_unlock();
1892
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001893 return err;
1894}
1895
1896static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
1897{
1898 struct cfg80211_registered_device *drv;
1899 int err;
1900 struct net_device *dev;
1901 u8 *dst = NULL;
1902 u8 *next_hop = NULL;
1903
1904 if (!info->attrs[NL80211_ATTR_MAC])
1905 return -EINVAL;
1906
1907 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
1908 return -EINVAL;
1909
1910 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
1911 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
1912
Johannes Berg3b858752009-03-12 09:55:09 +01001913 rtnl_lock();
1914
Johannes Bergbba95fe2008-07-29 13:22:51 +02001915 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001916 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001917 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001918
1919 if (!drv->ops->change_mpath) {
1920 err = -EOPNOTSUPP;
1921 goto out;
1922 }
1923
Jouni Malineneec60b02009-03-20 21:21:19 +02001924 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
1925 err = -EOPNOTSUPP;
1926 goto out;
1927 }
1928
Jouni Malinen35a8efe2009-03-20 21:21:18 +02001929 if (!netif_running(dev)) {
1930 err = -ENETDOWN;
1931 goto out;
1932 }
1933
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001934 err = drv->ops->change_mpath(&drv->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001935
1936 out:
1937 cfg80211_put_dev(drv);
1938 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001939 out_rtnl:
1940 rtnl_unlock();
1941
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001942 return err;
1943}
1944static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
1945{
1946 struct cfg80211_registered_device *drv;
1947 int err;
1948 struct net_device *dev;
1949 u8 *dst = NULL;
1950 u8 *next_hop = NULL;
1951
1952 if (!info->attrs[NL80211_ATTR_MAC])
1953 return -EINVAL;
1954
1955 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
1956 return -EINVAL;
1957
1958 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
1959 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
1960
Johannes Berg3b858752009-03-12 09:55:09 +01001961 rtnl_lock();
1962
Johannes Bergbba95fe2008-07-29 13:22:51 +02001963 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001964 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001965 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001966
1967 if (!drv->ops->add_mpath) {
1968 err = -EOPNOTSUPP;
1969 goto out;
1970 }
1971
Jouni Malineneec60b02009-03-20 21:21:19 +02001972 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
1973 err = -EOPNOTSUPP;
1974 goto out;
1975 }
1976
Jouni Malinen35a8efe2009-03-20 21:21:18 +02001977 if (!netif_running(dev)) {
1978 err = -ENETDOWN;
1979 goto out;
1980 }
1981
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001982 err = drv->ops->add_mpath(&drv->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001983
1984 out:
1985 cfg80211_put_dev(drv);
1986 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001987 out_rtnl:
1988 rtnl_unlock();
1989
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001990 return err;
1991}
1992
1993static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
1994{
1995 struct cfg80211_registered_device *drv;
1996 int err;
1997 struct net_device *dev;
1998 u8 *dst = NULL;
1999
2000 if (info->attrs[NL80211_ATTR_MAC])
2001 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2002
Johannes Berg3b858752009-03-12 09:55:09 +01002003 rtnl_lock();
2004
Johannes Bergbba95fe2008-07-29 13:22:51 +02002005 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002006 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002007 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002008
2009 if (!drv->ops->del_mpath) {
2010 err = -EOPNOTSUPP;
2011 goto out;
2012 }
2013
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002014 err = drv->ops->del_mpath(&drv->wiphy, dev, dst);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002015
2016 out:
2017 cfg80211_put_dev(drv);
2018 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002019 out_rtnl:
2020 rtnl_unlock();
2021
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002022 return err;
2023}
2024
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002025static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
2026{
2027 struct cfg80211_registered_device *drv;
2028 int err;
2029 struct net_device *dev;
2030 struct bss_parameters params;
2031
2032 memset(&params, 0, sizeof(params));
2033 /* default to not changing parameters */
2034 params.use_cts_prot = -1;
2035 params.use_short_preamble = -1;
2036 params.use_short_slot_time = -1;
2037
2038 if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
2039 params.use_cts_prot =
2040 nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
2041 if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
2042 params.use_short_preamble =
2043 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
2044 if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
2045 params.use_short_slot_time =
2046 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
Jouni Malinen90c97a02008-10-30 16:59:22 +02002047 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
2048 params.basic_rates =
2049 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2050 params.basic_rates_len =
2051 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2052 }
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002053
Johannes Berg3b858752009-03-12 09:55:09 +01002054 rtnl_lock();
2055
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002056 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2057 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002058 goto out_rtnl;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002059
2060 if (!drv->ops->change_bss) {
2061 err = -EOPNOTSUPP;
2062 goto out;
2063 }
2064
Jouni Malineneec60b02009-03-20 21:21:19 +02002065 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
2066 err = -EOPNOTSUPP;
2067 goto out;
2068 }
2069
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002070 err = drv->ops->change_bss(&drv->wiphy, dev, &params);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002071
2072 out:
2073 cfg80211_put_dev(drv);
2074 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002075 out_rtnl:
2076 rtnl_unlock();
2077
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002078 return err;
2079}
2080
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002081static const struct nla_policy
2082 reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
2083 [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
2084 [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
2085 [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
2086 [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
2087 [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
2088 [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
2089};
2090
2091static int parse_reg_rule(struct nlattr *tb[],
2092 struct ieee80211_reg_rule *reg_rule)
2093{
2094 struct ieee80211_freq_range *freq_range = &reg_rule->freq_range;
2095 struct ieee80211_power_rule *power_rule = &reg_rule->power_rule;
2096
2097 if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
2098 return -EINVAL;
2099 if (!tb[NL80211_ATTR_FREQ_RANGE_START])
2100 return -EINVAL;
2101 if (!tb[NL80211_ATTR_FREQ_RANGE_END])
2102 return -EINVAL;
2103 if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
2104 return -EINVAL;
2105 if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
2106 return -EINVAL;
2107
2108 reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
2109
2110 freq_range->start_freq_khz =
2111 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
2112 freq_range->end_freq_khz =
2113 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
2114 freq_range->max_bandwidth_khz =
2115 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
2116
2117 power_rule->max_eirp =
2118 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
2119
2120 if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
2121 power_rule->max_antenna_gain =
2122 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
2123
2124 return 0;
2125}
2126
2127static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
2128{
2129 int r;
2130 char *data = NULL;
2131
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002132 /*
2133 * You should only get this when cfg80211 hasn't yet initialized
2134 * completely when built-in to the kernel right between the time
2135 * window between nl80211_init() and regulatory_init(), if that is
2136 * even possible.
2137 */
2138 mutex_lock(&cfg80211_mutex);
2139 if (unlikely(!cfg80211_regdomain)) {
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002140 mutex_unlock(&cfg80211_mutex);
2141 return -EINPROGRESS;
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002142 }
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002143 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002144
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002145 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
2146 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002147
2148 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
2149
2150#ifdef CONFIG_WIRELESS_OLD_REGULATORY
2151 /* We ignore world regdom requests with the old regdom setup */
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002152 if (is_world_regdom(data))
2153 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002154#endif
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002155
2156 r = regulatory_hint_user(data);
2157
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002158 return r;
2159}
2160
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002161static int nl80211_get_mesh_params(struct sk_buff *skb,
2162 struct genl_info *info)
2163{
2164 struct cfg80211_registered_device *drv;
2165 struct mesh_config cur_params;
2166 int err;
2167 struct net_device *dev;
2168 void *hdr;
2169 struct nlattr *pinfoattr;
2170 struct sk_buff *msg;
2171
Johannes Berg3b858752009-03-12 09:55:09 +01002172 rtnl_lock();
2173
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002174 /* Look up our device */
2175 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2176 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002177 goto out_rtnl;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002178
Jouni Malinenf3f92582009-03-20 17:57:36 +02002179 if (!drv->ops->get_mesh_params) {
2180 err = -EOPNOTSUPP;
2181 goto out;
2182 }
2183
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002184 /* Get the mesh params */
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002185 err = drv->ops->get_mesh_params(&drv->wiphy, dev, &cur_params);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002186 if (err)
2187 goto out;
2188
2189 /* Draw up a netlink message to send back */
2190 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
2191 if (!msg) {
2192 err = -ENOBUFS;
2193 goto out;
2194 }
2195 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
2196 NL80211_CMD_GET_MESH_PARAMS);
2197 if (!hdr)
2198 goto nla_put_failure;
2199 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS);
2200 if (!pinfoattr)
2201 goto nla_put_failure;
2202 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2203 NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
2204 cur_params.dot11MeshRetryTimeout);
2205 NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
2206 cur_params.dot11MeshConfirmTimeout);
2207 NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
2208 cur_params.dot11MeshHoldingTimeout);
2209 NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
2210 cur_params.dot11MeshMaxPeerLinks);
2211 NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES,
2212 cur_params.dot11MeshMaxRetries);
2213 NLA_PUT_U8(msg, NL80211_MESHCONF_TTL,
2214 cur_params.dot11MeshTTL);
2215 NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
2216 cur_params.auto_open_plinks);
2217 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
2218 cur_params.dot11MeshHWMPmaxPREQretries);
2219 NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
2220 cur_params.path_refresh_time);
2221 NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
2222 cur_params.min_discovery_timeout);
2223 NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
2224 cur_params.dot11MeshHWMPactivePathTimeout);
2225 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
2226 cur_params.dot11MeshHWMPpreqMinInterval);
2227 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
2228 cur_params.dot11MeshHWMPnetDiameterTraversalTime);
2229 nla_nest_end(msg, pinfoattr);
2230 genlmsg_end(msg, hdr);
2231 err = genlmsg_unicast(msg, info->snd_pid);
2232 goto out;
2233
Johannes Berg3b858752009-03-12 09:55:09 +01002234 nla_put_failure:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002235 genlmsg_cancel(msg, hdr);
2236 err = -EMSGSIZE;
Johannes Berg3b858752009-03-12 09:55:09 +01002237 out:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002238 /* Cleanup */
2239 cfg80211_put_dev(drv);
2240 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002241 out_rtnl:
2242 rtnl_unlock();
2243
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002244 return err;
2245}
2246
2247#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \
2248do {\
2249 if (table[attr_num]) {\
2250 cfg.param = nla_fn(table[attr_num]); \
2251 mask |= (1 << (attr_num - 1)); \
2252 } \
2253} while (0);\
2254
2255static struct nla_policy
2256nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] __read_mostly = {
2257 [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
2258 [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
2259 [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
2260 [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
2261 [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
2262 [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
2263 [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
2264
2265 [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
2266 [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
2267 [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
2268 [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
2269 [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
2270 [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
2271};
2272
2273static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
2274{
2275 int err;
2276 u32 mask;
2277 struct cfg80211_registered_device *drv;
2278 struct net_device *dev;
2279 struct mesh_config cfg;
2280 struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
2281 struct nlattr *parent_attr;
2282
2283 parent_attr = info->attrs[NL80211_ATTR_MESH_PARAMS];
2284 if (!parent_attr)
2285 return -EINVAL;
2286 if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
2287 parent_attr, nl80211_meshconf_params_policy))
2288 return -EINVAL;
2289
Johannes Berg3b858752009-03-12 09:55:09 +01002290 rtnl_lock();
2291
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002292 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2293 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002294 goto out_rtnl;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002295
Jouni Malinenf3f92582009-03-20 17:57:36 +02002296 if (!drv->ops->set_mesh_params) {
2297 err = -EOPNOTSUPP;
2298 goto out;
2299 }
2300
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002301 /* This makes sure that there aren't more than 32 mesh config
2302 * parameters (otherwise our bitfield scheme would not work.) */
2303 BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
2304
2305 /* Fill in the params struct */
2306 mask = 0;
2307 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout,
2308 mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16);
2309 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout,
2310 mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16);
2311 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout,
2312 mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16);
2313 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks,
2314 mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16);
2315 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries,
2316 mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8);
2317 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL,
2318 mask, NL80211_MESHCONF_TTL, nla_get_u8);
2319 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
2320 mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8);
2321 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
2322 mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
2323 nla_get_u8);
2324 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time,
2325 mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32);
2326 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout,
2327 mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
2328 nla_get_u16);
2329 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
2330 mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
2331 nla_get_u32);
2332 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
2333 mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
2334 nla_get_u16);
2335 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
2336 dot11MeshHWMPnetDiameterTraversalTime,
2337 mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
2338 nla_get_u16);
2339
2340 /* Apply changes */
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002341 err = drv->ops->set_mesh_params(&drv->wiphy, dev, &cfg, mask);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002342
Jouni Malinenf3f92582009-03-20 17:57:36 +02002343 out:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002344 /* cleanup */
2345 cfg80211_put_dev(drv);
2346 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002347 out_rtnl:
2348 rtnl_unlock();
2349
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002350 return err;
2351}
2352
2353#undef FILL_IN_MESH_PARAM_IF_SET
2354
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002355static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
2356{
2357 struct sk_buff *msg;
2358 void *hdr = NULL;
2359 struct nlattr *nl_reg_rules;
2360 unsigned int i;
2361 int err = -EINVAL;
2362
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002363 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002364
2365 if (!cfg80211_regdomain)
2366 goto out;
2367
2368 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
2369 if (!msg) {
2370 err = -ENOBUFS;
2371 goto out;
2372 }
2373
2374 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
2375 NL80211_CMD_GET_REG);
2376 if (!hdr)
2377 goto nla_put_failure;
2378
2379 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2,
2380 cfg80211_regdomain->alpha2);
2381
2382 nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
2383 if (!nl_reg_rules)
2384 goto nla_put_failure;
2385
2386 for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) {
2387 struct nlattr *nl_reg_rule;
2388 const struct ieee80211_reg_rule *reg_rule;
2389 const struct ieee80211_freq_range *freq_range;
2390 const struct ieee80211_power_rule *power_rule;
2391
2392 reg_rule = &cfg80211_regdomain->reg_rules[i];
2393 freq_range = &reg_rule->freq_range;
2394 power_rule = &reg_rule->power_rule;
2395
2396 nl_reg_rule = nla_nest_start(msg, i);
2397 if (!nl_reg_rule)
2398 goto nla_put_failure;
2399
2400 NLA_PUT_U32(msg, NL80211_ATTR_REG_RULE_FLAGS,
2401 reg_rule->flags);
2402 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_START,
2403 freq_range->start_freq_khz);
2404 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_END,
2405 freq_range->end_freq_khz);
2406 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
2407 freq_range->max_bandwidth_khz);
2408 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
2409 power_rule->max_antenna_gain);
2410 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
2411 power_rule->max_eirp);
2412
2413 nla_nest_end(msg, nl_reg_rule);
2414 }
2415
2416 nla_nest_end(msg, nl_reg_rules);
2417
2418 genlmsg_end(msg, hdr);
2419 err = genlmsg_unicast(msg, info->snd_pid);
2420 goto out;
2421
2422nla_put_failure:
2423 genlmsg_cancel(msg, hdr);
2424 err = -EMSGSIZE;
2425out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002426 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002427 return err;
2428}
2429
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002430static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
2431{
2432 struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
2433 struct nlattr *nl_reg_rule;
2434 char *alpha2 = NULL;
2435 int rem_reg_rules = 0, r = 0;
2436 u32 num_rules = 0, rule_idx = 0, size_of_regd;
2437 struct ieee80211_regdomain *rd = NULL;
2438
2439 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
2440 return -EINVAL;
2441
2442 if (!info->attrs[NL80211_ATTR_REG_RULES])
2443 return -EINVAL;
2444
2445 alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
2446
2447 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
2448 rem_reg_rules) {
2449 num_rules++;
2450 if (num_rules > NL80211_MAX_SUPP_REG_RULES)
2451 goto bad_reg;
2452 }
2453
2454 if (!reg_is_valid_request(alpha2))
2455 return -EINVAL;
2456
2457 size_of_regd = sizeof(struct ieee80211_regdomain) +
2458 (num_rules * sizeof(struct ieee80211_reg_rule));
2459
2460 rd = kzalloc(size_of_regd, GFP_KERNEL);
2461 if (!rd)
2462 return -ENOMEM;
2463
2464 rd->n_reg_rules = num_rules;
2465 rd->alpha2[0] = alpha2[0];
2466 rd->alpha2[1] = alpha2[1];
2467
2468 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
2469 rem_reg_rules) {
2470 nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
2471 nla_data(nl_reg_rule), nla_len(nl_reg_rule),
2472 reg_rule_policy);
2473 r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
2474 if (r)
2475 goto bad_reg;
2476
2477 rule_idx++;
2478
2479 if (rule_idx > NL80211_MAX_SUPP_REG_RULES)
2480 goto bad_reg;
2481 }
2482
2483 BUG_ON(rule_idx != num_rules);
2484
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002485 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002486 r = set_regdom(rd);
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002487 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002488 return r;
2489
Johannes Bergd2372b32008-10-24 20:32:20 +02002490 bad_reg:
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002491 kfree(rd);
2492 return -EINVAL;
2493}
2494
Johannes Berg2a519312009-02-10 21:25:55 +01002495static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
2496{
2497 struct cfg80211_registered_device *drv;
2498 struct net_device *dev;
2499 struct cfg80211_scan_request *request;
2500 struct cfg80211_ssid *ssid;
2501 struct ieee80211_channel *channel;
2502 struct nlattr *attr;
2503 struct wiphy *wiphy;
2504 int err, tmp, n_ssids = 0, n_channels = 0, i;
2505 enum ieee80211_band band;
Jouni Malinen70692ad2009-02-16 19:39:13 +02002506 size_t ie_len;
Johannes Berg2a519312009-02-10 21:25:55 +01002507
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002508 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
2509 return -EINVAL;
2510
Johannes Berg3b858752009-03-12 09:55:09 +01002511 rtnl_lock();
2512
Johannes Berg2a519312009-02-10 21:25:55 +01002513 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2514 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002515 goto out_rtnl;
Johannes Berg2a519312009-02-10 21:25:55 +01002516
2517 wiphy = &drv->wiphy;
2518
2519 if (!drv->ops->scan) {
2520 err = -EOPNOTSUPP;
2521 goto out;
2522 }
2523
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002524 if (!netif_running(dev)) {
2525 err = -ENETDOWN;
2526 goto out;
2527 }
2528
Johannes Berg2a519312009-02-10 21:25:55 +01002529 if (drv->scan_req) {
2530 err = -EBUSY;
Johannes Berg3b858752009-03-12 09:55:09 +01002531 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002532 }
2533
2534 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
2535 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp)
2536 n_channels++;
2537 if (!n_channels) {
2538 err = -EINVAL;
Johannes Berg3b858752009-03-12 09:55:09 +01002539 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002540 }
2541 } else {
2542 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
2543 if (wiphy->bands[band])
2544 n_channels += wiphy->bands[band]->n_channels;
2545 }
2546
2547 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
2548 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
2549 n_ssids++;
2550
2551 if (n_ssids > wiphy->max_scan_ssids) {
2552 err = -EINVAL;
Johannes Berg3b858752009-03-12 09:55:09 +01002553 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002554 }
2555
Jouni Malinen70692ad2009-02-16 19:39:13 +02002556 if (info->attrs[NL80211_ATTR_IE])
2557 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
2558 else
2559 ie_len = 0;
2560
Johannes Berg18a83652009-03-31 12:12:05 +02002561 if (ie_len > wiphy->max_scan_ie_len) {
2562 err = -EINVAL;
2563 goto out;
2564 }
2565
Johannes Berg2a519312009-02-10 21:25:55 +01002566 request = kzalloc(sizeof(*request)
2567 + sizeof(*ssid) * n_ssids
Jouni Malinen70692ad2009-02-16 19:39:13 +02002568 + sizeof(channel) * n_channels
2569 + ie_len, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01002570 if (!request) {
2571 err = -ENOMEM;
Johannes Berg3b858752009-03-12 09:55:09 +01002572 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002573 }
2574
2575 request->channels = (void *)((char *)request + sizeof(*request));
2576 request->n_channels = n_channels;
2577 if (n_ssids)
2578 request->ssids = (void *)(request->channels + n_channels);
2579 request->n_ssids = n_ssids;
Jouni Malinen70692ad2009-02-16 19:39:13 +02002580 if (ie_len) {
2581 if (request->ssids)
2582 request->ie = (void *)(request->ssids + n_ssids);
2583 else
2584 request->ie = (void *)(request->channels + n_channels);
2585 }
Johannes Berg2a519312009-02-10 21:25:55 +01002586
2587 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
2588 /* user specified, bail out if channel not found */
2589 request->n_channels = n_channels;
2590 i = 0;
2591 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
2592 request->channels[i] = ieee80211_get_channel(wiphy, nla_get_u32(attr));
2593 if (!request->channels[i]) {
2594 err = -EINVAL;
2595 goto out_free;
2596 }
2597 i++;
2598 }
2599 } else {
2600 /* all channels */
2601 i = 0;
2602 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
2603 int j;
2604 if (!wiphy->bands[band])
2605 continue;
2606 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
2607 request->channels[i] = &wiphy->bands[band]->channels[j];
2608 i++;
2609 }
2610 }
2611 }
2612
2613 i = 0;
2614 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
2615 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
2616 if (request->ssids[i].ssid_len > IEEE80211_MAX_SSID_LEN) {
2617 err = -EINVAL;
2618 goto out_free;
2619 }
2620 memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
2621 request->ssids[i].ssid_len = nla_len(attr);
2622 i++;
2623 }
2624 }
2625
Jouni Malinen70692ad2009-02-16 19:39:13 +02002626 if (info->attrs[NL80211_ATTR_IE]) {
2627 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Johannes Bergde95a542009-04-01 11:58:36 +02002628 memcpy((void *)request->ie,
2629 nla_data(info->attrs[NL80211_ATTR_IE]),
Jouni Malinen70692ad2009-02-16 19:39:13 +02002630 request->ie_len);
2631 }
2632
Johannes Berg2a519312009-02-10 21:25:55 +01002633 request->ifidx = dev->ifindex;
2634 request->wiphy = &drv->wiphy;
2635
2636 drv->scan_req = request;
2637 err = drv->ops->scan(&drv->wiphy, dev, request);
2638
2639 out_free:
2640 if (err) {
2641 drv->scan_req = NULL;
2642 kfree(request);
2643 }
Johannes Berg2a519312009-02-10 21:25:55 +01002644 out:
2645 cfg80211_put_dev(drv);
2646 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002647 out_rtnl:
2648 rtnl_unlock();
2649
Johannes Berg2a519312009-02-10 21:25:55 +01002650 return err;
2651}
2652
2653static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
2654 struct cfg80211_registered_device *rdev,
2655 struct net_device *dev,
2656 struct cfg80211_bss *res)
2657{
2658 void *hdr;
2659 struct nlattr *bss;
2660
2661 hdr = nl80211hdr_put(msg, pid, seq, flags,
2662 NL80211_CMD_NEW_SCAN_RESULTS);
2663 if (!hdr)
2664 return -1;
2665
2666 NLA_PUT_U32(msg, NL80211_ATTR_SCAN_GENERATION,
2667 rdev->bss_generation);
2668 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2669
2670 bss = nla_nest_start(msg, NL80211_ATTR_BSS);
2671 if (!bss)
2672 goto nla_put_failure;
2673 if (!is_zero_ether_addr(res->bssid))
2674 NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid);
2675 if (res->information_elements && res->len_information_elements)
2676 NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS,
2677 res->len_information_elements,
2678 res->information_elements);
2679 if (res->tsf)
2680 NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf);
2681 if (res->beacon_interval)
2682 NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval);
2683 NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability);
2684 NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq);
2685
Johannes Berg77965c92009-02-18 18:45:06 +01002686 switch (rdev->wiphy.signal_type) {
Johannes Berg2a519312009-02-10 21:25:55 +01002687 case CFG80211_SIGNAL_TYPE_MBM:
2688 NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal);
2689 break;
2690 case CFG80211_SIGNAL_TYPE_UNSPEC:
2691 NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal);
2692 break;
2693 default:
2694 break;
2695 }
2696
2697 nla_nest_end(msg, bss);
2698
2699 return genlmsg_end(msg, hdr);
2700
2701 nla_put_failure:
2702 genlmsg_cancel(msg, hdr);
2703 return -EMSGSIZE;
2704}
2705
2706static int nl80211_dump_scan(struct sk_buff *skb,
2707 struct netlink_callback *cb)
2708{
2709 struct cfg80211_registered_device *dev;
2710 struct net_device *netdev;
2711 struct cfg80211_internal_bss *scan;
2712 int ifidx = cb->args[0];
2713 int start = cb->args[1], idx = 0;
2714 int err;
2715
2716 if (!ifidx) {
2717 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
2718 nl80211_fam.attrbuf, nl80211_fam.maxattr,
2719 nl80211_policy);
2720 if (err)
2721 return err;
2722
2723 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
2724 return -EINVAL;
2725
2726 ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
2727 if (!ifidx)
2728 return -EINVAL;
2729 cb->args[0] = ifidx;
2730 }
2731
2732 netdev = dev_get_by_index(&init_net, ifidx);
2733 if (!netdev)
2734 return -ENODEV;
2735
2736 dev = cfg80211_get_dev_from_ifindex(ifidx);
2737 if (IS_ERR(dev)) {
2738 err = PTR_ERR(dev);
2739 goto out_put_netdev;
2740 }
2741
2742 spin_lock_bh(&dev->bss_lock);
2743 cfg80211_bss_expire(dev);
2744
2745 list_for_each_entry(scan, &dev->bss_list, list) {
2746 if (++idx <= start)
2747 continue;
2748 if (nl80211_send_bss(skb,
2749 NETLINK_CB(cb->skb).pid,
2750 cb->nlh->nlmsg_seq, NLM_F_MULTI,
2751 dev, netdev, &scan->pub) < 0) {
2752 idx--;
2753 goto out;
2754 }
2755 }
2756
2757 out:
2758 spin_unlock_bh(&dev->bss_lock);
2759
2760 cb->args[1] = idx;
2761 err = skb->len;
2762 cfg80211_put_dev(dev);
2763 out_put_netdev:
2764 dev_put(netdev);
2765
2766 return err;
2767}
2768
Jouni Malinen255e7372009-03-20 21:21:17 +02002769static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
2770{
2771 return auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM ||
2772 auth_type == NL80211_AUTHTYPE_SHARED_KEY ||
2773 auth_type == NL80211_AUTHTYPE_FT ||
2774 auth_type == NL80211_AUTHTYPE_NETWORK_EAP;
2775}
2776
Jouni Malinen636a5d32009-03-19 13:39:22 +02002777static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
2778{
2779 struct cfg80211_registered_device *drv;
2780 struct net_device *dev;
2781 struct cfg80211_auth_request req;
2782 struct wiphy *wiphy;
2783 int err;
2784
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002785 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
2786 return -EINVAL;
2787
2788 if (!info->attrs[NL80211_ATTR_MAC])
2789 return -EINVAL;
2790
Jouni Malinen17780922009-03-27 20:52:47 +02002791 if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
2792 return -EINVAL;
2793
Jouni Malinen636a5d32009-03-19 13:39:22 +02002794 rtnl_lock();
2795
2796 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2797 if (err)
2798 goto unlock_rtnl;
2799
2800 if (!drv->ops->auth) {
2801 err = -EOPNOTSUPP;
2802 goto out;
2803 }
2804
Jouni Malineneec60b02009-03-20 21:21:19 +02002805 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
2806 err = -EOPNOTSUPP;
2807 goto out;
2808 }
2809
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002810 if (!netif_running(dev)) {
2811 err = -ENETDOWN;
2812 goto out;
2813 }
2814
Jouni Malinen636a5d32009-03-19 13:39:22 +02002815 wiphy = &drv->wiphy;
2816 memset(&req, 0, sizeof(req));
2817
2818 req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2819
2820 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
2821 req.chan = ieee80211_get_channel(
2822 wiphy,
2823 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
2824 if (!req.chan) {
2825 err = -EINVAL;
2826 goto out;
2827 }
2828 }
2829
2830 if (info->attrs[NL80211_ATTR_SSID]) {
2831 req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
2832 req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
2833 }
2834
2835 if (info->attrs[NL80211_ATTR_IE]) {
2836 req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
2837 req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
2838 }
2839
Jouni Malinen17780922009-03-27 20:52:47 +02002840 req.auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
2841 if (!nl80211_valid_auth_type(req.auth_type)) {
2842 err = -EINVAL;
2843 goto out;
Jouni Malinen636a5d32009-03-19 13:39:22 +02002844 }
2845
2846 err = drv->ops->auth(&drv->wiphy, dev, &req);
2847
2848out:
2849 cfg80211_put_dev(drv);
2850 dev_put(dev);
2851unlock_rtnl:
2852 rtnl_unlock();
2853 return err;
2854}
2855
2856static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
2857{
2858 struct cfg80211_registered_device *drv;
2859 struct net_device *dev;
2860 struct cfg80211_assoc_request req;
2861 struct wiphy *wiphy;
2862 int err;
2863
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002864 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
2865 return -EINVAL;
2866
2867 if (!info->attrs[NL80211_ATTR_MAC] ||
2868 !info->attrs[NL80211_ATTR_SSID])
2869 return -EINVAL;
2870
Jouni Malinen636a5d32009-03-19 13:39:22 +02002871 rtnl_lock();
2872
2873 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2874 if (err)
2875 goto unlock_rtnl;
2876
2877 if (!drv->ops->assoc) {
2878 err = -EOPNOTSUPP;
2879 goto out;
2880 }
2881
Jouni Malineneec60b02009-03-20 21:21:19 +02002882 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
2883 err = -EOPNOTSUPP;
2884 goto out;
2885 }
2886
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002887 if (!netif_running(dev)) {
2888 err = -ENETDOWN;
2889 goto out;
2890 }
2891
Jouni Malinen636a5d32009-03-19 13:39:22 +02002892 wiphy = &drv->wiphy;
2893 memset(&req, 0, sizeof(req));
2894
2895 req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2896
2897 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
2898 req.chan = ieee80211_get_channel(
2899 wiphy,
2900 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
2901 if (!req.chan) {
2902 err = -EINVAL;
2903 goto out;
2904 }
2905 }
2906
Jouni Malinen636a5d32009-03-19 13:39:22 +02002907 req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
2908 req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
2909
2910 if (info->attrs[NL80211_ATTR_IE]) {
2911 req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
2912 req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
2913 }
2914
2915 err = drv->ops->assoc(&drv->wiphy, dev, &req);
2916
2917out:
2918 cfg80211_put_dev(drv);
2919 dev_put(dev);
2920unlock_rtnl:
2921 rtnl_unlock();
2922 return err;
2923}
2924
2925static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
2926{
2927 struct cfg80211_registered_device *drv;
2928 struct net_device *dev;
2929 struct cfg80211_deauth_request req;
2930 struct wiphy *wiphy;
2931 int err;
2932
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002933 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
2934 return -EINVAL;
2935
2936 if (!info->attrs[NL80211_ATTR_MAC])
2937 return -EINVAL;
2938
2939 if (!info->attrs[NL80211_ATTR_REASON_CODE])
2940 return -EINVAL;
2941
Jouni Malinen636a5d32009-03-19 13:39:22 +02002942 rtnl_lock();
2943
2944 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2945 if (err)
2946 goto unlock_rtnl;
2947
2948 if (!drv->ops->deauth) {
2949 err = -EOPNOTSUPP;
2950 goto out;
2951 }
2952
Jouni Malineneec60b02009-03-20 21:21:19 +02002953 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
2954 err = -EOPNOTSUPP;
2955 goto out;
2956 }
2957
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002958 if (!netif_running(dev)) {
2959 err = -ENETDOWN;
2960 goto out;
2961 }
2962
Jouni Malinen636a5d32009-03-19 13:39:22 +02002963 wiphy = &drv->wiphy;
2964 memset(&req, 0, sizeof(req));
2965
2966 req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2967
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002968 req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
2969 if (req.reason_code == 0) {
2970 /* Reason Code 0 is reserved */
2971 err = -EINVAL;
2972 goto out;
Jouni Malinen255e7372009-03-20 21:21:17 +02002973 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02002974
2975 if (info->attrs[NL80211_ATTR_IE]) {
2976 req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
2977 req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
2978 }
2979
2980 err = drv->ops->deauth(&drv->wiphy, dev, &req);
2981
2982out:
2983 cfg80211_put_dev(drv);
2984 dev_put(dev);
2985unlock_rtnl:
2986 rtnl_unlock();
2987 return err;
2988}
2989
2990static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
2991{
2992 struct cfg80211_registered_device *drv;
2993 struct net_device *dev;
2994 struct cfg80211_disassoc_request req;
2995 struct wiphy *wiphy;
2996 int err;
2997
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002998 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
2999 return -EINVAL;
3000
3001 if (!info->attrs[NL80211_ATTR_MAC])
3002 return -EINVAL;
3003
3004 if (!info->attrs[NL80211_ATTR_REASON_CODE])
3005 return -EINVAL;
3006
Jouni Malinen636a5d32009-03-19 13:39:22 +02003007 rtnl_lock();
3008
3009 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
3010 if (err)
3011 goto unlock_rtnl;
3012
3013 if (!drv->ops->disassoc) {
3014 err = -EOPNOTSUPP;
3015 goto out;
3016 }
3017
Jouni Malineneec60b02009-03-20 21:21:19 +02003018 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
3019 err = -EOPNOTSUPP;
3020 goto out;
3021 }
3022
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003023 if (!netif_running(dev)) {
3024 err = -ENETDOWN;
3025 goto out;
3026 }
3027
Jouni Malinen636a5d32009-03-19 13:39:22 +02003028 wiphy = &drv->wiphy;
3029 memset(&req, 0, sizeof(req));
3030
3031 req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
3032
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003033 req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
3034 if (req.reason_code == 0) {
3035 /* Reason Code 0 is reserved */
3036 err = -EINVAL;
3037 goto out;
Jouni Malinen255e7372009-03-20 21:21:17 +02003038 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02003039
3040 if (info->attrs[NL80211_ATTR_IE]) {
3041 req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3042 req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3043 }
3044
3045 err = drv->ops->disassoc(&drv->wiphy, dev, &req);
3046
3047out:
3048 cfg80211_put_dev(drv);
3049 dev_put(dev);
3050unlock_rtnl:
3051 rtnl_unlock();
3052 return err;
3053}
3054
Johannes Berg55682962007-09-20 13:09:35 -04003055static struct genl_ops nl80211_ops[] = {
3056 {
3057 .cmd = NL80211_CMD_GET_WIPHY,
3058 .doit = nl80211_get_wiphy,
3059 .dumpit = nl80211_dump_wiphy,
3060 .policy = nl80211_policy,
3061 /* can be retrieved by unprivileged users */
3062 },
3063 {
3064 .cmd = NL80211_CMD_SET_WIPHY,
3065 .doit = nl80211_set_wiphy,
3066 .policy = nl80211_policy,
3067 .flags = GENL_ADMIN_PERM,
3068 },
3069 {
3070 .cmd = NL80211_CMD_GET_INTERFACE,
3071 .doit = nl80211_get_interface,
3072 .dumpit = nl80211_dump_interface,
3073 .policy = nl80211_policy,
3074 /* can be retrieved by unprivileged users */
3075 },
3076 {
3077 .cmd = NL80211_CMD_SET_INTERFACE,
3078 .doit = nl80211_set_interface,
3079 .policy = nl80211_policy,
3080 .flags = GENL_ADMIN_PERM,
3081 },
3082 {
3083 .cmd = NL80211_CMD_NEW_INTERFACE,
3084 .doit = nl80211_new_interface,
3085 .policy = nl80211_policy,
3086 .flags = GENL_ADMIN_PERM,
3087 },
3088 {
3089 .cmd = NL80211_CMD_DEL_INTERFACE,
3090 .doit = nl80211_del_interface,
3091 .policy = nl80211_policy,
3092 .flags = GENL_ADMIN_PERM,
3093 },
Johannes Berg41ade002007-12-19 02:03:29 +01003094 {
3095 .cmd = NL80211_CMD_GET_KEY,
3096 .doit = nl80211_get_key,
3097 .policy = nl80211_policy,
3098 .flags = GENL_ADMIN_PERM,
3099 },
3100 {
3101 .cmd = NL80211_CMD_SET_KEY,
3102 .doit = nl80211_set_key,
3103 .policy = nl80211_policy,
3104 .flags = GENL_ADMIN_PERM,
3105 },
3106 {
3107 .cmd = NL80211_CMD_NEW_KEY,
3108 .doit = nl80211_new_key,
3109 .policy = nl80211_policy,
3110 .flags = GENL_ADMIN_PERM,
3111 },
3112 {
3113 .cmd = NL80211_CMD_DEL_KEY,
3114 .doit = nl80211_del_key,
3115 .policy = nl80211_policy,
3116 .flags = GENL_ADMIN_PERM,
3117 },
Johannes Berged1b6cc2007-12-19 02:03:32 +01003118 {
3119 .cmd = NL80211_CMD_SET_BEACON,
3120 .policy = nl80211_policy,
3121 .flags = GENL_ADMIN_PERM,
3122 .doit = nl80211_addset_beacon,
3123 },
3124 {
3125 .cmd = NL80211_CMD_NEW_BEACON,
3126 .policy = nl80211_policy,
3127 .flags = GENL_ADMIN_PERM,
3128 .doit = nl80211_addset_beacon,
3129 },
3130 {
3131 .cmd = NL80211_CMD_DEL_BEACON,
3132 .policy = nl80211_policy,
3133 .flags = GENL_ADMIN_PERM,
3134 .doit = nl80211_del_beacon,
3135 },
Johannes Berg5727ef12007-12-19 02:03:34 +01003136 {
3137 .cmd = NL80211_CMD_GET_STATION,
3138 .doit = nl80211_get_station,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003139 .dumpit = nl80211_dump_station,
Johannes Berg5727ef12007-12-19 02:03:34 +01003140 .policy = nl80211_policy,
Johannes Berg5727ef12007-12-19 02:03:34 +01003141 },
3142 {
3143 .cmd = NL80211_CMD_SET_STATION,
3144 .doit = nl80211_set_station,
3145 .policy = nl80211_policy,
3146 .flags = GENL_ADMIN_PERM,
3147 },
3148 {
3149 .cmd = NL80211_CMD_NEW_STATION,
3150 .doit = nl80211_new_station,
3151 .policy = nl80211_policy,
3152 .flags = GENL_ADMIN_PERM,
3153 },
3154 {
3155 .cmd = NL80211_CMD_DEL_STATION,
3156 .doit = nl80211_del_station,
3157 .policy = nl80211_policy,
3158 .flags = GENL_ADMIN_PERM,
3159 },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003160 {
3161 .cmd = NL80211_CMD_GET_MPATH,
3162 .doit = nl80211_get_mpath,
3163 .dumpit = nl80211_dump_mpath,
3164 .policy = nl80211_policy,
3165 .flags = GENL_ADMIN_PERM,
3166 },
3167 {
3168 .cmd = NL80211_CMD_SET_MPATH,
3169 .doit = nl80211_set_mpath,
3170 .policy = nl80211_policy,
3171 .flags = GENL_ADMIN_PERM,
3172 },
3173 {
3174 .cmd = NL80211_CMD_NEW_MPATH,
3175 .doit = nl80211_new_mpath,
3176 .policy = nl80211_policy,
3177 .flags = GENL_ADMIN_PERM,
3178 },
3179 {
3180 .cmd = NL80211_CMD_DEL_MPATH,
3181 .doit = nl80211_del_mpath,
3182 .policy = nl80211_policy,
3183 .flags = GENL_ADMIN_PERM,
3184 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003185 {
3186 .cmd = NL80211_CMD_SET_BSS,
3187 .doit = nl80211_set_bss,
3188 .policy = nl80211_policy,
3189 .flags = GENL_ADMIN_PERM,
3190 },
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003191 {
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003192 .cmd = NL80211_CMD_GET_REG,
3193 .doit = nl80211_get_reg,
3194 .policy = nl80211_policy,
3195 /* can be retrieved by unprivileged users */
3196 },
3197 {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003198 .cmd = NL80211_CMD_SET_REG,
3199 .doit = nl80211_set_reg,
3200 .policy = nl80211_policy,
3201 .flags = GENL_ADMIN_PERM,
3202 },
3203 {
3204 .cmd = NL80211_CMD_REQ_SET_REG,
3205 .doit = nl80211_req_set_reg,
3206 .policy = nl80211_policy,
3207 .flags = GENL_ADMIN_PERM,
3208 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003209 {
3210 .cmd = NL80211_CMD_GET_MESH_PARAMS,
3211 .doit = nl80211_get_mesh_params,
3212 .policy = nl80211_policy,
3213 /* can be retrieved by unprivileged users */
3214 },
3215 {
3216 .cmd = NL80211_CMD_SET_MESH_PARAMS,
3217 .doit = nl80211_set_mesh_params,
3218 .policy = nl80211_policy,
3219 .flags = GENL_ADMIN_PERM,
3220 },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +02003221 {
Johannes Berg2a519312009-02-10 21:25:55 +01003222 .cmd = NL80211_CMD_TRIGGER_SCAN,
3223 .doit = nl80211_trigger_scan,
3224 .policy = nl80211_policy,
3225 .flags = GENL_ADMIN_PERM,
3226 },
3227 {
3228 .cmd = NL80211_CMD_GET_SCAN,
3229 .policy = nl80211_policy,
3230 .dumpit = nl80211_dump_scan,
3231 },
Jouni Malinen636a5d32009-03-19 13:39:22 +02003232 {
3233 .cmd = NL80211_CMD_AUTHENTICATE,
3234 .doit = nl80211_authenticate,
3235 .policy = nl80211_policy,
3236 .flags = GENL_ADMIN_PERM,
3237 },
3238 {
3239 .cmd = NL80211_CMD_ASSOCIATE,
3240 .doit = nl80211_associate,
3241 .policy = nl80211_policy,
3242 .flags = GENL_ADMIN_PERM,
3243 },
3244 {
3245 .cmd = NL80211_CMD_DEAUTHENTICATE,
3246 .doit = nl80211_deauthenticate,
3247 .policy = nl80211_policy,
3248 .flags = GENL_ADMIN_PERM,
3249 },
3250 {
3251 .cmd = NL80211_CMD_DISASSOCIATE,
3252 .doit = nl80211_disassociate,
3253 .policy = nl80211_policy,
3254 .flags = GENL_ADMIN_PERM,
3255 },
Johannes Berg55682962007-09-20 13:09:35 -04003256};
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003257static struct genl_multicast_group nl80211_mlme_mcgrp = {
3258 .name = "mlme",
3259};
Johannes Berg55682962007-09-20 13:09:35 -04003260
3261/* multicast groups */
3262static struct genl_multicast_group nl80211_config_mcgrp = {
3263 .name = "config",
3264};
Johannes Berg2a519312009-02-10 21:25:55 +01003265static struct genl_multicast_group nl80211_scan_mcgrp = {
3266 .name = "scan",
3267};
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04003268static struct genl_multicast_group nl80211_regulatory_mcgrp = {
3269 .name = "regulatory",
3270};
Johannes Berg55682962007-09-20 13:09:35 -04003271
3272/* notification functions */
3273
3274void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
3275{
3276 struct sk_buff *msg;
3277
3278 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3279 if (!msg)
3280 return;
3281
3282 if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
3283 nlmsg_free(msg);
3284 return;
3285 }
3286
3287 genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL);
3288}
3289
Johannes Berg2a519312009-02-10 21:25:55 +01003290static int nl80211_send_scan_donemsg(struct sk_buff *msg,
3291 struct cfg80211_registered_device *rdev,
3292 struct net_device *netdev,
3293 u32 pid, u32 seq, int flags,
3294 u32 cmd)
3295{
3296 void *hdr;
3297
3298 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
3299 if (!hdr)
3300 return -1;
3301
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -05003302 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg2a519312009-02-10 21:25:55 +01003303 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
3304
3305 /* XXX: we should probably bounce back the request? */
3306
3307 return genlmsg_end(msg, hdr);
3308
3309 nla_put_failure:
3310 genlmsg_cancel(msg, hdr);
3311 return -EMSGSIZE;
3312}
3313
3314void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
3315 struct net_device *netdev)
3316{
3317 struct sk_buff *msg;
3318
3319 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3320 if (!msg)
3321 return;
3322
3323 if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0,
3324 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
3325 nlmsg_free(msg);
3326 return;
3327 }
3328
3329 genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL);
3330}
3331
3332void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
3333 struct net_device *netdev)
3334{
3335 struct sk_buff *msg;
3336
3337 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3338 if (!msg)
3339 return;
3340
3341 if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0,
3342 NL80211_CMD_SCAN_ABORTED) < 0) {
3343 nlmsg_free(msg);
3344 return;
3345 }
3346
3347 genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL);
3348}
3349
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04003350/*
3351 * This can happen on global regulatory changes or device specific settings
3352 * based on custom world regulatory domains.
3353 */
3354void nl80211_send_reg_change_event(struct regulatory_request *request)
3355{
3356 struct sk_buff *msg;
3357 void *hdr;
3358
3359 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3360 if (!msg)
3361 return;
3362
3363 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
3364 if (!hdr) {
3365 nlmsg_free(msg);
3366 return;
3367 }
3368
3369 /* Userspace can always count this one always being set */
3370 NLA_PUT_U8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator);
3371
3372 if (request->alpha2[0] == '0' && request->alpha2[1] == '0')
3373 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
3374 NL80211_REGDOM_TYPE_WORLD);
3375 else if (request->alpha2[0] == '9' && request->alpha2[1] == '9')
3376 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
3377 NL80211_REGDOM_TYPE_CUSTOM_WORLD);
3378 else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
3379 request->intersect)
3380 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
3381 NL80211_REGDOM_TYPE_INTERSECTION);
3382 else {
3383 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
3384 NL80211_REGDOM_TYPE_COUNTRY);
3385 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2);
3386 }
3387
3388 if (wiphy_idx_valid(request->wiphy_idx))
3389 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx);
3390
3391 if (genlmsg_end(msg, hdr) < 0) {
3392 nlmsg_free(msg);
3393 return;
3394 }
3395
3396 genlmsg_multicast(msg, 0, nl80211_regulatory_mcgrp.id, GFP_KERNEL);
3397
3398 return;
3399
3400nla_put_failure:
3401 genlmsg_cancel(msg, hdr);
3402 nlmsg_free(msg);
3403}
3404
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003405static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
3406 struct net_device *netdev,
3407 const u8 *buf, size_t len,
3408 enum nl80211_commands cmd)
3409{
3410 struct sk_buff *msg;
3411 void *hdr;
3412
Jouni Malinend91c01c2009-04-18 21:53:15 +03003413 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003414 if (!msg)
3415 return;
3416
3417 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
3418 if (!hdr) {
3419 nlmsg_free(msg);
3420 return;
3421 }
3422
3423 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
3424 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
3425 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
3426
3427 if (genlmsg_end(msg, hdr) < 0) {
3428 nlmsg_free(msg);
3429 return;
3430 }
3431
Jouni Malinend91c01c2009-04-18 21:53:15 +03003432 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_ATOMIC);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003433 return;
3434
3435 nla_put_failure:
3436 genlmsg_cancel(msg, hdr);
3437 nlmsg_free(msg);
3438}
3439
3440void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
3441 struct net_device *netdev, const u8 *buf, size_t len)
3442{
3443 nl80211_send_mlme_event(rdev, netdev, buf, len,
3444 NL80211_CMD_AUTHENTICATE);
3445}
3446
3447void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
3448 struct net_device *netdev, const u8 *buf,
3449 size_t len)
3450{
3451 nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_ASSOCIATE);
3452}
3453
Jouni Malinen53b46b82009-03-27 20:53:56 +02003454void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
3455 struct net_device *netdev, const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003456{
3457 nl80211_send_mlme_event(rdev, netdev, buf, len,
3458 NL80211_CMD_DEAUTHENTICATE);
3459}
3460
Jouni Malinen53b46b82009-03-27 20:53:56 +02003461void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
3462 struct net_device *netdev, const u8 *buf,
3463 size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003464{
3465 nl80211_send_mlme_event(rdev, netdev, buf, len,
3466 NL80211_CMD_DISASSOCIATE);
3467}
3468
Jouni Malinena3b8b052009-03-27 21:59:49 +02003469void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
3470 struct net_device *netdev, const u8 *addr,
3471 enum nl80211_key_type key_type, int key_id,
3472 const u8 *tsc)
3473{
3474 struct sk_buff *msg;
3475 void *hdr;
3476
3477 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3478 if (!msg)
3479 return;
3480
3481 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
3482 if (!hdr) {
3483 nlmsg_free(msg);
3484 return;
3485 }
3486
3487 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
3488 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
3489 if (addr)
3490 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
3491 NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, key_type);
3492 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id);
3493 if (tsc)
3494 NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc);
3495
3496 if (genlmsg_end(msg, hdr) < 0) {
3497 nlmsg_free(msg);
3498 return;
3499 }
3500
3501 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_KERNEL);
3502 return;
3503
3504 nla_put_failure:
3505 genlmsg_cancel(msg, hdr);
3506 nlmsg_free(msg);
3507}
3508
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04003509void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
3510 struct ieee80211_channel *channel_before,
3511 struct ieee80211_channel *channel_after)
3512{
3513 struct sk_buff *msg;
3514 void *hdr;
3515 struct nlattr *nl_freq;
3516
3517 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
3518 if (!msg)
3519 return;
3520
3521 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
3522 if (!hdr) {
3523 nlmsg_free(msg);
3524 return;
3525 }
3526
3527 /*
3528 * Since we are applying the beacon hint to a wiphy we know its
3529 * wiphy_idx is valid
3530 */
3531 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy));
3532
3533 /* Before */
3534 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
3535 if (!nl_freq)
3536 goto nla_put_failure;
3537 if (nl80211_msg_put_channel(msg, channel_before))
3538 goto nla_put_failure;
3539 nla_nest_end(msg, nl_freq);
3540
3541 /* After */
3542 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
3543 if (!nl_freq)
3544 goto nla_put_failure;
3545 if (nl80211_msg_put_channel(msg, channel_after))
3546 goto nla_put_failure;
3547 nla_nest_end(msg, nl_freq);
3548
3549 if (genlmsg_end(msg, hdr) < 0) {
3550 nlmsg_free(msg);
3551 return;
3552 }
3553
3554 genlmsg_multicast(msg, 0, nl80211_regulatory_mcgrp.id, GFP_ATOMIC);
3555
3556 return;
3557
3558nla_put_failure:
3559 genlmsg_cancel(msg, hdr);
3560 nlmsg_free(msg);
3561}
3562
Johannes Berg55682962007-09-20 13:09:35 -04003563/* initialisation/exit functions */
3564
3565int nl80211_init(void)
3566{
3567 int err, i;
3568
3569 err = genl_register_family(&nl80211_fam);
3570 if (err)
3571 return err;
3572
3573 for (i = 0; i < ARRAY_SIZE(nl80211_ops); i++) {
3574 err = genl_register_ops(&nl80211_fam, &nl80211_ops[i]);
3575 if (err)
3576 goto err_out;
3577 }
3578
3579 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
3580 if (err)
3581 goto err_out;
3582
Johannes Berg2a519312009-02-10 21:25:55 +01003583 err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
3584 if (err)
3585 goto err_out;
3586
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04003587 err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
3588 if (err)
3589 goto err_out;
3590
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003591 err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
3592 if (err)
3593 goto err_out;
3594
Johannes Berg55682962007-09-20 13:09:35 -04003595 return 0;
3596 err_out:
3597 genl_unregister_family(&nl80211_fam);
3598 return err;
3599}
3600
3601void nl80211_exit(void)
3602{
3603 genl_unregister_family(&nl80211_fam);
3604}