blob: 77880ba8b619c806195142baf78d603e93d5fcfe [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>
10#include <linux/mutex.h>
11#include <linux/list.h>
12#include <linux/if_ether.h>
13#include <linux/ieee80211.h>
14#include <linux/nl80211.h>
15#include <linux/rtnetlink.h>
16#include <linux/netlink.h>
17#include <net/genetlink.h>
18#include <net/cfg80211.h>
19#include "core.h"
20#include "nl80211.h"
21
22/* the netlink family */
23static struct genl_family nl80211_fam = {
24 .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
25 .name = "nl80211", /* have users key off the name instead */
26 .hdrsize = 0, /* no private header */
27 .version = 1, /* no particular meaning now */
28 .maxattr = NL80211_ATTR_MAX,
29};
30
31/* internal helper: get drv and dev */
Johannes Bergbba95fe2008-07-29 13:22:51 +020032static int get_drv_dev_by_info_ifindex(struct nlattr **attrs,
Johannes Berg55682962007-09-20 13:09:35 -040033 struct cfg80211_registered_device **drv,
34 struct net_device **dev)
35{
36 int ifindex;
37
Johannes Bergbba95fe2008-07-29 13:22:51 +020038 if (!attrs[NL80211_ATTR_IFINDEX])
Johannes Berg55682962007-09-20 13:09:35 -040039 return -EINVAL;
40
Johannes Bergbba95fe2008-07-29 13:22:51 +020041 ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
Johannes Berg55682962007-09-20 13:09:35 -040042 *dev = dev_get_by_index(&init_net, ifindex);
43 if (!*dev)
44 return -ENODEV;
45
46 *drv = cfg80211_get_dev_from_ifindex(ifindex);
47 if (IS_ERR(*drv)) {
48 dev_put(*dev);
49 return PTR_ERR(*drv);
50 }
51
52 return 0;
53}
54
55/* policy for the attributes */
56static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
57 [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
58 [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
59 .len = BUS_ID_SIZE-1 },
60
61 [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
62 [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
63 [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
Johannes Berg41ade002007-12-19 02:03:29 +010064
65 [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN },
66
67 [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
68 .len = WLAN_MAX_KEY_LEN },
69 [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
70 [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
71 [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
Johannes Berged1b6cc2007-12-19 02:03:32 +010072
73 [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
74 [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
75 [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY,
76 .len = IEEE80211_MAX_DATA_LEN },
77 [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY,
78 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg5727ef12007-12-19 02:03:34 +010079 [NL80211_ATTR_STA_AID] = { .type = NLA_U16 },
80 [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED },
81 [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 },
82 [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY,
83 .len = NL80211_MAX_SUPP_RATES },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +010084 [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
Johannes Berg5727ef12007-12-19 02:03:34 +010085 [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
Michael Wu66f7ac52008-01-31 19:48:22 +010086 [NL80211_ATTR_MNTR_FLAGS] = { .type = NLA_NESTED },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +010087 [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
88 .len = IEEE80211_MAX_MESH_ID_LEN },
89 [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +030090
91 [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
92 [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
93 [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
Jouni Malinen36aedc92008-08-25 11:58:58 +030094
95 [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY,
96 .len = NL80211_HT_CAPABILITY_LEN },
Johannes Berg55682962007-09-20 13:09:35 -040097};
98
99/* message building helper */
100static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
101 int flags, u8 cmd)
102{
103 /* since there is no private header just add the generic one */
104 return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
105}
106
107/* netlink command implementations */
108
109static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
110 struct cfg80211_registered_device *dev)
111{
112 void *hdr;
Johannes Bergee688b002008-01-24 19:38:39 +0100113 struct nlattr *nl_bands, *nl_band;
114 struct nlattr *nl_freqs, *nl_freq;
115 struct nlattr *nl_rates, *nl_rate;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700116 struct nlattr *nl_modes;
Johannes Bergee688b002008-01-24 19:38:39 +0100117 enum ieee80211_band band;
118 struct ieee80211_channel *chan;
119 struct ieee80211_rate *rate;
120 int i;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700121 u16 ifmodes = dev->wiphy.interface_modes;
Johannes Berg55682962007-09-20 13:09:35 -0400122
123 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
124 if (!hdr)
125 return -1;
126
127 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->idx);
128 NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
Johannes Bergee688b002008-01-24 19:38:39 +0100129
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700130 nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES);
131 if (!nl_modes)
132 goto nla_put_failure;
133
134 i = 0;
135 while (ifmodes) {
136 if (ifmodes & 1)
137 NLA_PUT_FLAG(msg, i);
138 ifmodes >>= 1;
139 i++;
140 }
141
142 nla_nest_end(msg, nl_modes);
143
Johannes Bergee688b002008-01-24 19:38:39 +0100144 nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
145 if (!nl_bands)
146 goto nla_put_failure;
147
148 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
149 if (!dev->wiphy.bands[band])
150 continue;
151
152 nl_band = nla_nest_start(msg, band);
153 if (!nl_band)
154 goto nla_put_failure;
155
156 /* add frequencies */
157 nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
158 if (!nl_freqs)
159 goto nla_put_failure;
160
161 for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
162 nl_freq = nla_nest_start(msg, i);
163 if (!nl_freq)
164 goto nla_put_failure;
165
166 chan = &dev->wiphy.bands[band]->channels[i];
167 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
168 chan->center_freq);
169
170 if (chan->flags & IEEE80211_CHAN_DISABLED)
171 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
172 if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
173 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
174 if (chan->flags & IEEE80211_CHAN_NO_IBSS)
175 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
176 if (chan->flags & IEEE80211_CHAN_RADAR)
177 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
178
179 nla_nest_end(msg, nl_freq);
180 }
181
182 nla_nest_end(msg, nl_freqs);
183
184 /* add bitrates */
185 nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
186 if (!nl_rates)
187 goto nla_put_failure;
188
189 for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
190 nl_rate = nla_nest_start(msg, i);
191 if (!nl_rate)
192 goto nla_put_failure;
193
194 rate = &dev->wiphy.bands[band]->bitrates[i];
195 NLA_PUT_U32(msg, NL80211_BITRATE_ATTR_RATE,
196 rate->bitrate);
197 if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
198 NLA_PUT_FLAG(msg,
199 NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE);
200
201 nla_nest_end(msg, nl_rate);
202 }
203
204 nla_nest_end(msg, nl_rates);
205
206 nla_nest_end(msg, nl_band);
207 }
208 nla_nest_end(msg, nl_bands);
209
Johannes Berg55682962007-09-20 13:09:35 -0400210 return genlmsg_end(msg, hdr);
211
212 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700213 genlmsg_cancel(msg, hdr);
214 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400215}
216
217static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
218{
219 int idx = 0;
220 int start = cb->args[0];
221 struct cfg80211_registered_device *dev;
222
223 mutex_lock(&cfg80211_drv_mutex);
224 list_for_each_entry(dev, &cfg80211_drv_list, list) {
Julius Volzb4637272008-07-08 14:02:19 +0200225 if (++idx <= start)
Johannes Berg55682962007-09-20 13:09:35 -0400226 continue;
227 if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
228 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Julius Volzb4637272008-07-08 14:02:19 +0200229 dev) < 0) {
230 idx--;
Johannes Berg55682962007-09-20 13:09:35 -0400231 break;
Julius Volzb4637272008-07-08 14:02:19 +0200232 }
Johannes Berg55682962007-09-20 13:09:35 -0400233 }
234 mutex_unlock(&cfg80211_drv_mutex);
235
236 cb->args[0] = idx;
237
238 return skb->len;
239}
240
241static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
242{
243 struct sk_buff *msg;
244 struct cfg80211_registered_device *dev;
245
246 dev = cfg80211_get_dev_from_info(info);
247 if (IS_ERR(dev))
248 return PTR_ERR(dev);
249
250 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
251 if (!msg)
252 goto out_err;
253
254 if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0)
255 goto out_free;
256
257 cfg80211_put_dev(dev);
258
259 return genlmsg_unicast(msg, info->snd_pid);
260
261 out_free:
262 nlmsg_free(msg);
263 out_err:
264 cfg80211_put_dev(dev);
265 return -ENOBUFS;
266}
267
268static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
269{
270 struct cfg80211_registered_device *rdev;
271 int result;
272
273 if (!info->attrs[NL80211_ATTR_WIPHY_NAME])
274 return -EINVAL;
275
276 rdev = cfg80211_get_dev_from_info(info);
277 if (IS_ERR(rdev))
278 return PTR_ERR(rdev);
279
280 result = cfg80211_dev_rename(rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
281
282 cfg80211_put_dev(rdev);
283 return result;
284}
285
286
287static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
288 struct net_device *dev)
289{
290 void *hdr;
291
292 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
293 if (!hdr)
294 return -1;
295
296 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
297 NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
298 /* TODO: interface type */
299 return genlmsg_end(msg, hdr);
300
301 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700302 genlmsg_cancel(msg, hdr);
303 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400304}
305
306static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
307{
308 int wp_idx = 0;
309 int if_idx = 0;
310 int wp_start = cb->args[0];
311 int if_start = cb->args[1];
312 struct cfg80211_registered_device *dev;
313 struct wireless_dev *wdev;
314
315 mutex_lock(&cfg80211_drv_mutex);
316 list_for_each_entry(dev, &cfg80211_drv_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +0200317 if (wp_idx < wp_start) {
318 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400319 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200320 }
Johannes Berg55682962007-09-20 13:09:35 -0400321 if_idx = 0;
322
323 mutex_lock(&dev->devlist_mtx);
324 list_for_each_entry(wdev, &dev->netdev_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +0200325 if (if_idx < if_start) {
326 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400327 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200328 }
Johannes Berg55682962007-09-20 13:09:35 -0400329 if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
330 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Bergbba95fe2008-07-29 13:22:51 +0200331 wdev->netdev) < 0) {
332 mutex_unlock(&dev->devlist_mtx);
333 goto out;
334 }
335 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400336 }
337 mutex_unlock(&dev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +0200338
339 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400340 }
Johannes Bergbba95fe2008-07-29 13:22:51 +0200341 out:
Johannes Berg55682962007-09-20 13:09:35 -0400342 mutex_unlock(&cfg80211_drv_mutex);
343
344 cb->args[0] = wp_idx;
345 cb->args[1] = if_idx;
346
347 return skb->len;
348}
349
350static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
351{
352 struct sk_buff *msg;
353 struct cfg80211_registered_device *dev;
354 struct net_device *netdev;
355 int err;
356
Johannes Bergbba95fe2008-07-29 13:22:51 +0200357 err = get_drv_dev_by_info_ifindex(info->attrs, &dev, &netdev);
Johannes Berg55682962007-09-20 13:09:35 -0400358 if (err)
359 return err;
360
361 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
362 if (!msg)
363 goto out_err;
364
365 if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0, netdev) < 0)
366 goto out_free;
367
368 dev_put(netdev);
369 cfg80211_put_dev(dev);
370
371 return genlmsg_unicast(msg, info->snd_pid);
372
373 out_free:
374 nlmsg_free(msg);
375 out_err:
376 dev_put(netdev);
377 cfg80211_put_dev(dev);
378 return -ENOBUFS;
379}
380
Michael Wu66f7ac52008-01-31 19:48:22 +0100381static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
382 [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
383 [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
384 [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
385 [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
386 [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
387};
388
389static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
390{
391 struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
392 int flag;
393
394 *mntrflags = 0;
395
396 if (!nla)
397 return -EINVAL;
398
399 if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX,
400 nla, mntr_flags_policy))
401 return -EINVAL;
402
403 for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
404 if (flags[flag])
405 *mntrflags |= (1<<flag);
406
407 return 0;
408}
409
Johannes Berg55682962007-09-20 13:09:35 -0400410static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
411{
412 struct cfg80211_registered_device *drv;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100413 struct vif_params params;
Johannes Berg55682962007-09-20 13:09:35 -0400414 int err, ifindex;
415 enum nl80211_iftype type;
416 struct net_device *dev;
Michael Wu66f7ac52008-01-31 19:48:22 +0100417 u32 flags;
Johannes Berg55682962007-09-20 13:09:35 -0400418
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100419 memset(&params, 0, sizeof(params));
420
Johannes Berg55682962007-09-20 13:09:35 -0400421 if (info->attrs[NL80211_ATTR_IFTYPE]) {
422 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
423 if (type > NL80211_IFTYPE_MAX)
424 return -EINVAL;
425 } else
426 return -EINVAL;
427
Johannes Bergbba95fe2008-07-29 13:22:51 +0200428 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg55682962007-09-20 13:09:35 -0400429 if (err)
430 return err;
431 ifindex = dev->ifindex;
432 dev_put(dev);
433
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700434 if (!drv->ops->change_virtual_intf ||
435 !(drv->wiphy.interface_modes & (1 << type))) {
Johannes Berg55682962007-09-20 13:09:35 -0400436 err = -EOPNOTSUPP;
437 goto unlock;
438 }
439
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100440 if (type == NL80211_IFTYPE_MESH_POINT &&
441 info->attrs[NL80211_ATTR_MESH_ID]) {
442 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
443 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
444 }
445
Johannes Berg55682962007-09-20 13:09:35 -0400446 rtnl_lock();
Michael Wu66f7ac52008-01-31 19:48:22 +0100447 err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
448 info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
449 &flags);
450 err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100451 type, err ? NULL : &flags, &params);
Johannes Berg55682962007-09-20 13:09:35 -0400452 rtnl_unlock();
453
454 unlock:
455 cfg80211_put_dev(drv);
456 return err;
457}
458
459static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
460{
461 struct cfg80211_registered_device *drv;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100462 struct vif_params params;
Johannes Berg55682962007-09-20 13:09:35 -0400463 int err;
464 enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
Michael Wu66f7ac52008-01-31 19:48:22 +0100465 u32 flags;
Johannes Berg55682962007-09-20 13:09:35 -0400466
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100467 memset(&params, 0, sizeof(params));
468
Johannes Berg55682962007-09-20 13:09:35 -0400469 if (!info->attrs[NL80211_ATTR_IFNAME])
470 return -EINVAL;
471
472 if (info->attrs[NL80211_ATTR_IFTYPE]) {
473 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
474 if (type > NL80211_IFTYPE_MAX)
475 return -EINVAL;
476 }
477
478 drv = cfg80211_get_dev_from_info(info);
479 if (IS_ERR(drv))
480 return PTR_ERR(drv);
481
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700482 if (!drv->ops->add_virtual_intf ||
483 !(drv->wiphy.interface_modes & (1 << type))) {
Johannes Berg55682962007-09-20 13:09:35 -0400484 err = -EOPNOTSUPP;
485 goto unlock;
486 }
487
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100488 if (type == NL80211_IFTYPE_MESH_POINT &&
489 info->attrs[NL80211_ATTR_MESH_ID]) {
490 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
491 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
492 }
493
Johannes Berg55682962007-09-20 13:09:35 -0400494 rtnl_lock();
Michael Wu66f7ac52008-01-31 19:48:22 +0100495 err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
496 info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
497 &flags);
Johannes Berg55682962007-09-20 13:09:35 -0400498 err = drv->ops->add_virtual_intf(&drv->wiphy,
Michael Wu66f7ac52008-01-31 19:48:22 +0100499 nla_data(info->attrs[NL80211_ATTR_IFNAME]),
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100500 type, err ? NULL : &flags, &params);
Johannes Berg55682962007-09-20 13:09:35 -0400501 rtnl_unlock();
502
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100503
Johannes Berg55682962007-09-20 13:09:35 -0400504 unlock:
505 cfg80211_put_dev(drv);
506 return err;
507}
508
509static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
510{
511 struct cfg80211_registered_device *drv;
512 int ifindex, err;
513 struct net_device *dev;
514
Johannes Bergbba95fe2008-07-29 13:22:51 +0200515 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg55682962007-09-20 13:09:35 -0400516 if (err)
517 return err;
518 ifindex = dev->ifindex;
519 dev_put(dev);
520
521 if (!drv->ops->del_virtual_intf) {
522 err = -EOPNOTSUPP;
523 goto out;
524 }
525
526 rtnl_lock();
527 err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex);
528 rtnl_unlock();
529
530 out:
531 cfg80211_put_dev(drv);
532 return err;
533}
534
Johannes Berg41ade002007-12-19 02:03:29 +0100535struct get_key_cookie {
536 struct sk_buff *msg;
537 int error;
538};
539
540static void get_key_callback(void *c, struct key_params *params)
541{
542 struct get_key_cookie *cookie = c;
543
544 if (params->key)
545 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA,
546 params->key_len, params->key);
547
548 if (params->seq)
549 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ,
550 params->seq_len, params->seq);
551
552 if (params->cipher)
553 NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
554 params->cipher);
555
556 return;
557 nla_put_failure:
558 cookie->error = 1;
559}
560
561static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
562{
563 struct cfg80211_registered_device *drv;
564 int err;
565 struct net_device *dev;
566 u8 key_idx = 0;
567 u8 *mac_addr = NULL;
568 struct get_key_cookie cookie = {
569 .error = 0,
570 };
571 void *hdr;
572 struct sk_buff *msg;
573
574 if (info->attrs[NL80211_ATTR_KEY_IDX])
575 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
576
577 if (key_idx > 3)
578 return -EINVAL;
579
580 if (info->attrs[NL80211_ATTR_MAC])
581 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
582
Johannes Bergbba95fe2008-07-29 13:22:51 +0200583 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +0100584 if (err)
585 return err;
586
587 if (!drv->ops->get_key) {
588 err = -EOPNOTSUPP;
589 goto out;
590 }
591
592 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
593 if (!msg) {
594 err = -ENOMEM;
595 goto out;
596 }
597
598 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
599 NL80211_CMD_NEW_KEY);
600
601 if (IS_ERR(hdr)) {
602 err = PTR_ERR(hdr);
603 goto out;
604 }
605
606 cookie.msg = msg;
607
608 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
609 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
610 if (mac_addr)
611 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
612
613 rtnl_lock();
614 err = drv->ops->get_key(&drv->wiphy, dev, key_idx, mac_addr,
615 &cookie, get_key_callback);
616 rtnl_unlock();
617
618 if (err)
619 goto out;
620
621 if (cookie.error)
622 goto nla_put_failure;
623
624 genlmsg_end(msg, hdr);
625 err = genlmsg_unicast(msg, info->snd_pid);
626 goto out;
627
628 nla_put_failure:
629 err = -ENOBUFS;
630 nlmsg_free(msg);
631 out:
632 cfg80211_put_dev(drv);
633 dev_put(dev);
634 return err;
635}
636
637static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
638{
639 struct cfg80211_registered_device *drv;
640 int err;
641 struct net_device *dev;
642 u8 key_idx;
643
644 if (!info->attrs[NL80211_ATTR_KEY_IDX])
645 return -EINVAL;
646
647 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
648
649 if (key_idx > 3)
650 return -EINVAL;
651
652 /* currently only support setting default key */
653 if (!info->attrs[NL80211_ATTR_KEY_DEFAULT])
654 return -EINVAL;
655
Johannes Bergbba95fe2008-07-29 13:22:51 +0200656 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +0100657 if (err)
658 return err;
659
660 if (!drv->ops->set_default_key) {
661 err = -EOPNOTSUPP;
662 goto out;
663 }
664
665 rtnl_lock();
666 err = drv->ops->set_default_key(&drv->wiphy, dev, key_idx);
667 rtnl_unlock();
668
669 out:
670 cfg80211_put_dev(drv);
671 dev_put(dev);
672 return err;
673}
674
675static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
676{
677 struct cfg80211_registered_device *drv;
678 int err;
679 struct net_device *dev;
680 struct key_params params;
681 u8 key_idx = 0;
682 u8 *mac_addr = NULL;
683
684 memset(&params, 0, sizeof(params));
685
686 if (!info->attrs[NL80211_ATTR_KEY_CIPHER])
687 return -EINVAL;
688
689 if (info->attrs[NL80211_ATTR_KEY_DATA]) {
690 params.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
691 params.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
692 }
693
694 if (info->attrs[NL80211_ATTR_KEY_IDX])
695 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
696
697 params.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
698
699 if (info->attrs[NL80211_ATTR_MAC])
700 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
701
702 if (key_idx > 3)
703 return -EINVAL;
704
705 /*
706 * Disallow pairwise keys with non-zero index unless it's WEP
707 * (because current deployments use pairwise WEP keys with
708 * non-zero indizes but 802.11i clearly specifies to use zero)
709 */
710 if (mac_addr && key_idx &&
711 params.cipher != WLAN_CIPHER_SUITE_WEP40 &&
712 params.cipher != WLAN_CIPHER_SUITE_WEP104)
713 return -EINVAL;
714
715 /* TODO: add definitions for the lengths to linux/ieee80211.h */
716 switch (params.cipher) {
717 case WLAN_CIPHER_SUITE_WEP40:
718 if (params.key_len != 5)
719 return -EINVAL;
720 break;
721 case WLAN_CIPHER_SUITE_TKIP:
722 if (params.key_len != 32)
723 return -EINVAL;
724 break;
725 case WLAN_CIPHER_SUITE_CCMP:
726 if (params.key_len != 16)
727 return -EINVAL;
728 break;
729 case WLAN_CIPHER_SUITE_WEP104:
730 if (params.key_len != 13)
731 return -EINVAL;
732 break;
733 default:
734 return -EINVAL;
735 }
736
Johannes Bergbba95fe2008-07-29 13:22:51 +0200737 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +0100738 if (err)
739 return err;
740
741 if (!drv->ops->add_key) {
742 err = -EOPNOTSUPP;
743 goto out;
744 }
745
746 rtnl_lock();
747 err = drv->ops->add_key(&drv->wiphy, dev, key_idx, mac_addr, &params);
748 rtnl_unlock();
749
750 out:
751 cfg80211_put_dev(drv);
752 dev_put(dev);
753 return err;
754}
755
756static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
757{
758 struct cfg80211_registered_device *drv;
759 int err;
760 struct net_device *dev;
761 u8 key_idx = 0;
762 u8 *mac_addr = NULL;
763
764 if (info->attrs[NL80211_ATTR_KEY_IDX])
765 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
766
767 if (key_idx > 3)
768 return -EINVAL;
769
770 if (info->attrs[NL80211_ATTR_MAC])
771 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
772
Johannes Bergbba95fe2008-07-29 13:22:51 +0200773 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +0100774 if (err)
775 return err;
776
777 if (!drv->ops->del_key) {
778 err = -EOPNOTSUPP;
779 goto out;
780 }
781
782 rtnl_lock();
783 err = drv->ops->del_key(&drv->wiphy, dev, key_idx, mac_addr);
784 rtnl_unlock();
785
786 out:
787 cfg80211_put_dev(drv);
788 dev_put(dev);
789 return err;
790}
791
Johannes Berged1b6cc2007-12-19 02:03:32 +0100792static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
793{
794 int (*call)(struct wiphy *wiphy, struct net_device *dev,
795 struct beacon_parameters *info);
796 struct cfg80211_registered_device *drv;
797 int err;
798 struct net_device *dev;
799 struct beacon_parameters params;
800 int haveinfo = 0;
801
Johannes Bergbba95fe2008-07-29 13:22:51 +0200802 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +0100803 if (err)
804 return err;
805
806 switch (info->genlhdr->cmd) {
807 case NL80211_CMD_NEW_BEACON:
808 /* these are required for NEW_BEACON */
809 if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
810 !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
811 !info->attrs[NL80211_ATTR_BEACON_HEAD]) {
812 err = -EINVAL;
813 goto out;
814 }
815
816 call = drv->ops->add_beacon;
817 break;
818 case NL80211_CMD_SET_BEACON:
819 call = drv->ops->set_beacon;
820 break;
821 default:
822 WARN_ON(1);
823 err = -EOPNOTSUPP;
824 goto out;
825 }
826
827 if (!call) {
828 err = -EOPNOTSUPP;
829 goto out;
830 }
831
832 memset(&params, 0, sizeof(params));
833
834 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
835 params.interval =
836 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
837 haveinfo = 1;
838 }
839
840 if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) {
841 params.dtim_period =
842 nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
843 haveinfo = 1;
844 }
845
846 if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
847 params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
848 params.head_len =
849 nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
850 haveinfo = 1;
851 }
852
853 if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
854 params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
855 params.tail_len =
856 nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
857 haveinfo = 1;
858 }
859
860 if (!haveinfo) {
861 err = -EINVAL;
862 goto out;
863 }
864
865 rtnl_lock();
866 err = call(&drv->wiphy, dev, &params);
867 rtnl_unlock();
868
869 out:
870 cfg80211_put_dev(drv);
871 dev_put(dev);
872 return err;
873}
874
875static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
876{
877 struct cfg80211_registered_device *drv;
878 int err;
879 struct net_device *dev;
880
Johannes Bergbba95fe2008-07-29 13:22:51 +0200881 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +0100882 if (err)
883 return err;
884
885 if (!drv->ops->del_beacon) {
886 err = -EOPNOTSUPP;
887 goto out;
888 }
889
890 rtnl_lock();
891 err = drv->ops->del_beacon(&drv->wiphy, dev);
892 rtnl_unlock();
893
894 out:
895 cfg80211_put_dev(drv);
896 dev_put(dev);
897 return err;
898}
899
Johannes Berg5727ef12007-12-19 02:03:34 +0100900static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
901 [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
902 [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
903 [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
904};
905
906static int parse_station_flags(struct nlattr *nla, u32 *staflags)
907{
908 struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
909 int flag;
910
911 *staflags = 0;
912
913 if (!nla)
914 return 0;
915
916 if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX,
917 nla, sta_flags_policy))
918 return -EINVAL;
919
920 *staflags = STATION_FLAG_CHANGED;
921
922 for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++)
923 if (flags[flag])
924 *staflags |= (1<<flag);
925
926 return 0;
927}
928
Johannes Bergfd5b74d2007-12-19 02:03:36 +0100929static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
930 int flags, struct net_device *dev,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100931 u8 *mac_addr, struct station_info *sinfo)
Johannes Bergfd5b74d2007-12-19 02:03:36 +0100932{
933 void *hdr;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100934 struct nlattr *sinfoattr;
Johannes Bergfd5b74d2007-12-19 02:03:36 +0100935
936 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
937 if (!hdr)
938 return -1;
939
940 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
941 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
942
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100943 sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
944 if (!sinfoattr)
Johannes Bergfd5b74d2007-12-19 02:03:36 +0100945 goto nla_put_failure;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100946 if (sinfo->filled & STATION_INFO_INACTIVE_TIME)
947 NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME,
948 sinfo->inactive_time);
949 if (sinfo->filled & STATION_INFO_RX_BYTES)
950 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES,
951 sinfo->rx_bytes);
952 if (sinfo->filled & STATION_INFO_TX_BYTES)
953 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES,
954 sinfo->tx_bytes);
955 if (sinfo->filled & STATION_INFO_LLID)
956 NLA_PUT_U16(msg, NL80211_STA_INFO_LLID,
957 sinfo->llid);
958 if (sinfo->filled & STATION_INFO_PLID)
959 NLA_PUT_U16(msg, NL80211_STA_INFO_PLID,
960 sinfo->plid);
961 if (sinfo->filled & STATION_INFO_PLINK_STATE)
962 NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
963 sinfo->plink_state);
Johannes Bergfd5b74d2007-12-19 02:03:36 +0100964
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100965 nla_nest_end(msg, sinfoattr);
Johannes Bergfd5b74d2007-12-19 02:03:36 +0100966
967 return genlmsg_end(msg, hdr);
968
969 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700970 genlmsg_cancel(msg, hdr);
971 return -EMSGSIZE;
Johannes Bergfd5b74d2007-12-19 02:03:36 +0100972}
973
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100974static int nl80211_dump_station(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +0200975 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100976{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100977 struct station_info sinfo;
978 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200979 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100980 u8 mac_addr[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +0200981 int ifidx = cb->args[0];
982 int sta_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100983 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100984
Johannes Bergbba95fe2008-07-29 13:22:51 +0200985 if (!ifidx) {
986 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
987 nl80211_fam.attrbuf, nl80211_fam.maxattr,
988 nl80211_policy);
989 if (err)
990 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100991
Johannes Bergbba95fe2008-07-29 13:22:51 +0200992 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
993 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100994
Johannes Bergbba95fe2008-07-29 13:22:51 +0200995 ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
996 if (!ifidx)
997 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100998 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100999
Johannes Bergbba95fe2008-07-29 13:22:51 +02001000 netdev = dev_get_by_index(&init_net, ifidx);
1001 if (!netdev)
1002 return -ENODEV;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001003
Johannes Bergbba95fe2008-07-29 13:22:51 +02001004 dev = cfg80211_get_dev_from_ifindex(ifidx);
1005 if (IS_ERR(dev)) {
1006 err = PTR_ERR(dev);
1007 goto out_put_netdev;
1008 }
1009
1010 if (!dev->ops->dump_station) {
1011 err = -ENOSYS;
1012 goto out_err;
1013 }
1014
1015 rtnl_lock();
1016
1017 while (1) {
1018 err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
1019 mac_addr, &sinfo);
1020 if (err == -ENOENT)
1021 break;
1022 if (err)
1023 goto out_err_rtnl;
1024
1025 if (nl80211_send_station(skb,
1026 NETLINK_CB(cb->skb).pid,
1027 cb->nlh->nlmsg_seq, NLM_F_MULTI,
1028 netdev, mac_addr,
1029 &sinfo) < 0)
1030 goto out;
1031
1032 sta_idx++;
1033 }
1034
1035
1036 out:
1037 cb->args[1] = sta_idx;
1038 err = skb->len;
1039 out_err_rtnl:
1040 rtnl_unlock();
1041 out_err:
1042 cfg80211_put_dev(dev);
1043 out_put_netdev:
1044 dev_put(netdev);
1045
1046 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001047}
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001048
Johannes Berg5727ef12007-12-19 02:03:34 +01001049static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
1050{
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001051 struct cfg80211_registered_device *drv;
1052 int err;
1053 struct net_device *dev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001054 struct station_info sinfo;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001055 struct sk_buff *msg;
1056 u8 *mac_addr = NULL;
1057
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001058 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001059
1060 if (!info->attrs[NL80211_ATTR_MAC])
1061 return -EINVAL;
1062
1063 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1064
Johannes Bergbba95fe2008-07-29 13:22:51 +02001065 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001066 if (err)
1067 return err;
1068
1069 if (!drv->ops->get_station) {
1070 err = -EOPNOTSUPP;
1071 goto out;
1072 }
1073
1074 rtnl_lock();
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001075 err = drv->ops->get_station(&drv->wiphy, dev, mac_addr, &sinfo);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001076 rtnl_unlock();
1077
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001078 if (err)
1079 goto out;
1080
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001081 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1082 if (!msg)
1083 goto out;
1084
1085 if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001086 dev, mac_addr, &sinfo) < 0)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001087 goto out_free;
1088
1089 err = genlmsg_unicast(msg, info->snd_pid);
1090 goto out;
1091
1092 out_free:
1093 nlmsg_free(msg);
1094
1095 out:
1096 cfg80211_put_dev(drv);
1097 dev_put(dev);
1098 return err;
Johannes Berg5727ef12007-12-19 02:03:34 +01001099}
1100
1101/*
1102 * Get vlan interface making sure it is on the right wiphy.
1103 */
1104static int get_vlan(struct nlattr *vlanattr,
1105 struct cfg80211_registered_device *rdev,
1106 struct net_device **vlan)
1107{
1108 *vlan = NULL;
1109
1110 if (vlanattr) {
1111 *vlan = dev_get_by_index(&init_net, nla_get_u32(vlanattr));
1112 if (!*vlan)
1113 return -ENODEV;
1114 if (!(*vlan)->ieee80211_ptr)
1115 return -EINVAL;
1116 if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy)
1117 return -EINVAL;
1118 }
1119 return 0;
1120}
1121
1122static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
1123{
1124 struct cfg80211_registered_device *drv;
1125 int err;
1126 struct net_device *dev;
1127 struct station_parameters params;
1128 u8 *mac_addr = NULL;
1129
1130 memset(&params, 0, sizeof(params));
1131
1132 params.listen_interval = -1;
1133
1134 if (info->attrs[NL80211_ATTR_STA_AID])
1135 return -EINVAL;
1136
1137 if (!info->attrs[NL80211_ATTR_MAC])
1138 return -EINVAL;
1139
1140 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1141
1142 if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
1143 params.supported_rates =
1144 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1145 params.supported_rates_len =
1146 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1147 }
1148
1149 if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
1150 params.listen_interval =
1151 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
1152
Jouni Malinen36aedc92008-08-25 11:58:58 +03001153 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
1154 params.ht_capa =
1155 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
1156
Johannes Berg5727ef12007-12-19 02:03:34 +01001157 if (parse_station_flags(info->attrs[NL80211_ATTR_STA_FLAGS],
1158 &params.station_flags))
1159 return -EINVAL;
1160
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001161 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
1162 params.plink_action =
1163 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
1164
Johannes Bergbba95fe2008-07-29 13:22:51 +02001165 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001166 if (err)
1167 return err;
1168
1169 err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, &params.vlan);
1170 if (err)
1171 goto out;
1172
1173 if (!drv->ops->change_station) {
1174 err = -EOPNOTSUPP;
1175 goto out;
1176 }
1177
1178 rtnl_lock();
1179 err = drv->ops->change_station(&drv->wiphy, dev, mac_addr, &params);
1180 rtnl_unlock();
1181
1182 out:
1183 if (params.vlan)
1184 dev_put(params.vlan);
1185 cfg80211_put_dev(drv);
1186 dev_put(dev);
1187 return err;
1188}
1189
1190static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
1191{
1192 struct cfg80211_registered_device *drv;
1193 int err;
1194 struct net_device *dev;
1195 struct station_parameters params;
1196 u8 *mac_addr = NULL;
1197
1198 memset(&params, 0, sizeof(params));
1199
1200 if (!info->attrs[NL80211_ATTR_MAC])
1201 return -EINVAL;
1202
1203 if (!info->attrs[NL80211_ATTR_STA_AID])
1204 return -EINVAL;
1205
1206 if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
1207 return -EINVAL;
1208
1209 if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
1210 return -EINVAL;
1211
1212 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1213 params.supported_rates =
1214 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1215 params.supported_rates_len =
1216 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1217 params.listen_interval =
1218 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
Johannes Berg16f2e852008-04-07 14:35:46 +02001219 params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
Jouni Malinen36aedc92008-08-25 11:58:58 +03001220 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
1221 params.ht_capa =
1222 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
Johannes Berg5727ef12007-12-19 02:03:34 +01001223
1224 if (parse_station_flags(info->attrs[NL80211_ATTR_STA_FLAGS],
1225 &params.station_flags))
1226 return -EINVAL;
1227
Johannes Bergbba95fe2008-07-29 13:22:51 +02001228 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001229 if (err)
1230 return err;
1231
1232 err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, &params.vlan);
1233 if (err)
1234 goto out;
1235
1236 if (!drv->ops->add_station) {
1237 err = -EOPNOTSUPP;
1238 goto out;
1239 }
1240
1241 rtnl_lock();
1242 err = drv->ops->add_station(&drv->wiphy, dev, mac_addr, &params);
1243 rtnl_unlock();
1244
1245 out:
1246 if (params.vlan)
1247 dev_put(params.vlan);
1248 cfg80211_put_dev(drv);
1249 dev_put(dev);
1250 return err;
1251}
1252
1253static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
1254{
1255 struct cfg80211_registered_device *drv;
1256 int err;
1257 struct net_device *dev;
1258 u8 *mac_addr = NULL;
1259
1260 if (info->attrs[NL80211_ATTR_MAC])
1261 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1262
Johannes Bergbba95fe2008-07-29 13:22:51 +02001263 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001264 if (err)
1265 return err;
1266
1267 if (!drv->ops->del_station) {
1268 err = -EOPNOTSUPP;
1269 goto out;
1270 }
1271
1272 rtnl_lock();
1273 err = drv->ops->del_station(&drv->wiphy, dev, mac_addr);
1274 rtnl_unlock();
1275
1276 out:
1277 cfg80211_put_dev(drv);
1278 dev_put(dev);
1279 return err;
1280}
1281
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001282static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq,
1283 int flags, struct net_device *dev,
1284 u8 *dst, u8 *next_hop,
1285 struct mpath_info *pinfo)
1286{
1287 void *hdr;
1288 struct nlattr *pinfoattr;
1289
1290 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
1291 if (!hdr)
1292 return -1;
1293
1294 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1295 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
1296 NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop);
1297
1298 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
1299 if (!pinfoattr)
1300 goto nla_put_failure;
1301 if (pinfo->filled & MPATH_INFO_FRAME_QLEN)
1302 NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
1303 pinfo->frame_qlen);
1304 if (pinfo->filled & MPATH_INFO_DSN)
1305 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DSN,
1306 pinfo->dsn);
1307 if (pinfo->filled & MPATH_INFO_METRIC)
1308 NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC,
1309 pinfo->metric);
1310 if (pinfo->filled & MPATH_INFO_EXPTIME)
1311 NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME,
1312 pinfo->exptime);
1313 if (pinfo->filled & MPATH_INFO_FLAGS)
1314 NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS,
1315 pinfo->flags);
1316 if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT)
1317 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
1318 pinfo->discovery_timeout);
1319 if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES)
1320 NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
1321 pinfo->discovery_retries);
1322
1323 nla_nest_end(msg, pinfoattr);
1324
1325 return genlmsg_end(msg, hdr);
1326
1327 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001328 genlmsg_cancel(msg, hdr);
1329 return -EMSGSIZE;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001330}
1331
1332static int nl80211_dump_mpath(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02001333 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001334{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001335 struct mpath_info pinfo;
1336 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001337 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001338 u8 dst[ETH_ALEN];
1339 u8 next_hop[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02001340 int ifidx = cb->args[0];
1341 int path_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001342 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001343
Johannes Bergbba95fe2008-07-29 13:22:51 +02001344 if (!ifidx) {
1345 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
1346 nl80211_fam.attrbuf, nl80211_fam.maxattr,
1347 nl80211_policy);
1348 if (err)
1349 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001350
Johannes Bergbba95fe2008-07-29 13:22:51 +02001351 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
1352 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001353
Johannes Bergbba95fe2008-07-29 13:22:51 +02001354 ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
1355 if (!ifidx)
1356 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001357 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001358
Johannes Bergbba95fe2008-07-29 13:22:51 +02001359 netdev = dev_get_by_index(&init_net, ifidx);
1360 if (!netdev)
1361 return -ENODEV;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001362
Johannes Bergbba95fe2008-07-29 13:22:51 +02001363 dev = cfg80211_get_dev_from_ifindex(ifidx);
1364 if (IS_ERR(dev)) {
1365 err = PTR_ERR(dev);
1366 goto out_put_netdev;
1367 }
1368
1369 if (!dev->ops->dump_mpath) {
1370 err = -ENOSYS;
1371 goto out_err;
1372 }
1373
1374 rtnl_lock();
1375
1376 while (1) {
1377 err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
1378 dst, next_hop, &pinfo);
1379 if (err == -ENOENT)
1380 break;
1381 if (err)
1382 goto out_err_rtnl;
1383
1384 if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid,
1385 cb->nlh->nlmsg_seq, NLM_F_MULTI,
1386 netdev, dst, next_hop,
1387 &pinfo) < 0)
1388 goto out;
1389
1390 path_idx++;
1391 }
1392
1393
1394 out:
1395 cb->args[1] = path_idx;
1396 err = skb->len;
1397 out_err_rtnl:
1398 rtnl_unlock();
1399 out_err:
1400 cfg80211_put_dev(dev);
1401 out_put_netdev:
1402 dev_put(netdev);
1403
1404 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001405}
1406
1407static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
1408{
1409 struct cfg80211_registered_device *drv;
1410 int err;
1411 struct net_device *dev;
1412 struct mpath_info pinfo;
1413 struct sk_buff *msg;
1414 u8 *dst = NULL;
1415 u8 next_hop[ETH_ALEN];
1416
1417 memset(&pinfo, 0, sizeof(pinfo));
1418
1419 if (!info->attrs[NL80211_ATTR_MAC])
1420 return -EINVAL;
1421
1422 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
1423
Johannes Bergbba95fe2008-07-29 13:22:51 +02001424 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001425 if (err)
1426 return err;
1427
1428 if (!drv->ops->get_mpath) {
1429 err = -EOPNOTSUPP;
1430 goto out;
1431 }
1432
1433 rtnl_lock();
1434 err = drv->ops->get_mpath(&drv->wiphy, dev, dst, next_hop, &pinfo);
1435 rtnl_unlock();
1436
1437 if (err)
1438 goto out;
1439
1440 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1441 if (!msg)
1442 goto out;
1443
1444 if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0,
1445 dev, dst, next_hop, &pinfo) < 0)
1446 goto out_free;
1447
1448 err = genlmsg_unicast(msg, info->snd_pid);
1449 goto out;
1450
1451 out_free:
1452 nlmsg_free(msg);
1453
1454 out:
1455 cfg80211_put_dev(drv);
1456 dev_put(dev);
1457 return err;
1458}
1459
1460static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
1461{
1462 struct cfg80211_registered_device *drv;
1463 int err;
1464 struct net_device *dev;
1465 u8 *dst = NULL;
1466 u8 *next_hop = NULL;
1467
1468 if (!info->attrs[NL80211_ATTR_MAC])
1469 return -EINVAL;
1470
1471 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
1472 return -EINVAL;
1473
1474 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
1475 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
1476
Johannes Bergbba95fe2008-07-29 13:22:51 +02001477 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001478 if (err)
1479 return err;
1480
1481 if (!drv->ops->change_mpath) {
1482 err = -EOPNOTSUPP;
1483 goto out;
1484 }
1485
1486 rtnl_lock();
1487 err = drv->ops->change_mpath(&drv->wiphy, dev, dst, next_hop);
1488 rtnl_unlock();
1489
1490 out:
1491 cfg80211_put_dev(drv);
1492 dev_put(dev);
1493 return err;
1494}
1495static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
1496{
1497 struct cfg80211_registered_device *drv;
1498 int err;
1499 struct net_device *dev;
1500 u8 *dst = NULL;
1501 u8 *next_hop = NULL;
1502
1503 if (!info->attrs[NL80211_ATTR_MAC])
1504 return -EINVAL;
1505
1506 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
1507 return -EINVAL;
1508
1509 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
1510 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
1511
Johannes Bergbba95fe2008-07-29 13:22:51 +02001512 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001513 if (err)
1514 return err;
1515
1516 if (!drv->ops->add_mpath) {
1517 err = -EOPNOTSUPP;
1518 goto out;
1519 }
1520
1521 rtnl_lock();
1522 err = drv->ops->add_mpath(&drv->wiphy, dev, dst, next_hop);
1523 rtnl_unlock();
1524
1525 out:
1526 cfg80211_put_dev(drv);
1527 dev_put(dev);
1528 return err;
1529}
1530
1531static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
1532{
1533 struct cfg80211_registered_device *drv;
1534 int err;
1535 struct net_device *dev;
1536 u8 *dst = NULL;
1537
1538 if (info->attrs[NL80211_ATTR_MAC])
1539 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
1540
Johannes Bergbba95fe2008-07-29 13:22:51 +02001541 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001542 if (err)
1543 return err;
1544
1545 if (!drv->ops->del_mpath) {
1546 err = -EOPNOTSUPP;
1547 goto out;
1548 }
1549
1550 rtnl_lock();
1551 err = drv->ops->del_mpath(&drv->wiphy, dev, dst);
1552 rtnl_unlock();
1553
1554 out:
1555 cfg80211_put_dev(drv);
1556 dev_put(dev);
1557 return err;
1558}
1559
Jouni Malinen9f1ba902008-08-07 20:07:01 +03001560static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
1561{
1562 struct cfg80211_registered_device *drv;
1563 int err;
1564 struct net_device *dev;
1565 struct bss_parameters params;
1566
1567 memset(&params, 0, sizeof(params));
1568 /* default to not changing parameters */
1569 params.use_cts_prot = -1;
1570 params.use_short_preamble = -1;
1571 params.use_short_slot_time = -1;
1572
1573 if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
1574 params.use_cts_prot =
1575 nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
1576 if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
1577 params.use_short_preamble =
1578 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
1579 if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
1580 params.use_short_slot_time =
1581 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
1582
1583 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
1584 if (err)
1585 return err;
1586
1587 if (!drv->ops->change_bss) {
1588 err = -EOPNOTSUPP;
1589 goto out;
1590 }
1591
1592 rtnl_lock();
1593 err = drv->ops->change_bss(&drv->wiphy, dev, &params);
1594 rtnl_unlock();
1595
1596 out:
1597 cfg80211_put_dev(drv);
1598 dev_put(dev);
1599 return err;
1600}
1601
Johannes Berg55682962007-09-20 13:09:35 -04001602static struct genl_ops nl80211_ops[] = {
1603 {
1604 .cmd = NL80211_CMD_GET_WIPHY,
1605 .doit = nl80211_get_wiphy,
1606 .dumpit = nl80211_dump_wiphy,
1607 .policy = nl80211_policy,
1608 /* can be retrieved by unprivileged users */
1609 },
1610 {
1611 .cmd = NL80211_CMD_SET_WIPHY,
1612 .doit = nl80211_set_wiphy,
1613 .policy = nl80211_policy,
1614 .flags = GENL_ADMIN_PERM,
1615 },
1616 {
1617 .cmd = NL80211_CMD_GET_INTERFACE,
1618 .doit = nl80211_get_interface,
1619 .dumpit = nl80211_dump_interface,
1620 .policy = nl80211_policy,
1621 /* can be retrieved by unprivileged users */
1622 },
1623 {
1624 .cmd = NL80211_CMD_SET_INTERFACE,
1625 .doit = nl80211_set_interface,
1626 .policy = nl80211_policy,
1627 .flags = GENL_ADMIN_PERM,
1628 },
1629 {
1630 .cmd = NL80211_CMD_NEW_INTERFACE,
1631 .doit = nl80211_new_interface,
1632 .policy = nl80211_policy,
1633 .flags = GENL_ADMIN_PERM,
1634 },
1635 {
1636 .cmd = NL80211_CMD_DEL_INTERFACE,
1637 .doit = nl80211_del_interface,
1638 .policy = nl80211_policy,
1639 .flags = GENL_ADMIN_PERM,
1640 },
Johannes Berg41ade002007-12-19 02:03:29 +01001641 {
1642 .cmd = NL80211_CMD_GET_KEY,
1643 .doit = nl80211_get_key,
1644 .policy = nl80211_policy,
1645 .flags = GENL_ADMIN_PERM,
1646 },
1647 {
1648 .cmd = NL80211_CMD_SET_KEY,
1649 .doit = nl80211_set_key,
1650 .policy = nl80211_policy,
1651 .flags = GENL_ADMIN_PERM,
1652 },
1653 {
1654 .cmd = NL80211_CMD_NEW_KEY,
1655 .doit = nl80211_new_key,
1656 .policy = nl80211_policy,
1657 .flags = GENL_ADMIN_PERM,
1658 },
1659 {
1660 .cmd = NL80211_CMD_DEL_KEY,
1661 .doit = nl80211_del_key,
1662 .policy = nl80211_policy,
1663 .flags = GENL_ADMIN_PERM,
1664 },
Johannes Berged1b6cc2007-12-19 02:03:32 +01001665 {
1666 .cmd = NL80211_CMD_SET_BEACON,
1667 .policy = nl80211_policy,
1668 .flags = GENL_ADMIN_PERM,
1669 .doit = nl80211_addset_beacon,
1670 },
1671 {
1672 .cmd = NL80211_CMD_NEW_BEACON,
1673 .policy = nl80211_policy,
1674 .flags = GENL_ADMIN_PERM,
1675 .doit = nl80211_addset_beacon,
1676 },
1677 {
1678 .cmd = NL80211_CMD_DEL_BEACON,
1679 .policy = nl80211_policy,
1680 .flags = GENL_ADMIN_PERM,
1681 .doit = nl80211_del_beacon,
1682 },
Johannes Berg5727ef12007-12-19 02:03:34 +01001683 {
1684 .cmd = NL80211_CMD_GET_STATION,
1685 .doit = nl80211_get_station,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001686 .dumpit = nl80211_dump_station,
Johannes Berg5727ef12007-12-19 02:03:34 +01001687 .policy = nl80211_policy,
1688 .flags = GENL_ADMIN_PERM,
1689 },
1690 {
1691 .cmd = NL80211_CMD_SET_STATION,
1692 .doit = nl80211_set_station,
1693 .policy = nl80211_policy,
1694 .flags = GENL_ADMIN_PERM,
1695 },
1696 {
1697 .cmd = NL80211_CMD_NEW_STATION,
1698 .doit = nl80211_new_station,
1699 .policy = nl80211_policy,
1700 .flags = GENL_ADMIN_PERM,
1701 },
1702 {
1703 .cmd = NL80211_CMD_DEL_STATION,
1704 .doit = nl80211_del_station,
1705 .policy = nl80211_policy,
1706 .flags = GENL_ADMIN_PERM,
1707 },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001708 {
1709 .cmd = NL80211_CMD_GET_MPATH,
1710 .doit = nl80211_get_mpath,
1711 .dumpit = nl80211_dump_mpath,
1712 .policy = nl80211_policy,
1713 .flags = GENL_ADMIN_PERM,
1714 },
1715 {
1716 .cmd = NL80211_CMD_SET_MPATH,
1717 .doit = nl80211_set_mpath,
1718 .policy = nl80211_policy,
1719 .flags = GENL_ADMIN_PERM,
1720 },
1721 {
1722 .cmd = NL80211_CMD_NEW_MPATH,
1723 .doit = nl80211_new_mpath,
1724 .policy = nl80211_policy,
1725 .flags = GENL_ADMIN_PERM,
1726 },
1727 {
1728 .cmd = NL80211_CMD_DEL_MPATH,
1729 .doit = nl80211_del_mpath,
1730 .policy = nl80211_policy,
1731 .flags = GENL_ADMIN_PERM,
1732 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +03001733 {
1734 .cmd = NL80211_CMD_SET_BSS,
1735 .doit = nl80211_set_bss,
1736 .policy = nl80211_policy,
1737 .flags = GENL_ADMIN_PERM,
1738 },
Johannes Berg55682962007-09-20 13:09:35 -04001739};
1740
1741/* multicast groups */
1742static struct genl_multicast_group nl80211_config_mcgrp = {
1743 .name = "config",
1744};
1745
1746/* notification functions */
1747
1748void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
1749{
1750 struct sk_buff *msg;
1751
1752 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1753 if (!msg)
1754 return;
1755
1756 if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
1757 nlmsg_free(msg);
1758 return;
1759 }
1760
1761 genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL);
1762}
1763
1764/* initialisation/exit functions */
1765
1766int nl80211_init(void)
1767{
1768 int err, i;
1769
1770 err = genl_register_family(&nl80211_fam);
1771 if (err)
1772 return err;
1773
1774 for (i = 0; i < ARRAY_SIZE(nl80211_ops); i++) {
1775 err = genl_register_ops(&nl80211_fam, &nl80211_ops[i]);
1776 if (err)
1777 goto err_out;
1778 }
1779
1780 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
1781 if (err)
1782 goto err_out;
1783
1784 return 0;
1785 err_out:
1786 genl_unregister_family(&nl80211_fam);
1787 return err;
1788}
1789
1790void nl80211_exit(void)
1791{
1792 genl_unregister_family(&nl80211_fam);
1793}