blob: 47542ee01c57d4eba247b7a004b681037f7bd737 [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 },
Johannes Berg55682962007-09-20 13:09:35 -040094};
95
96/* message building helper */
97static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
98 int flags, u8 cmd)
99{
100 /* since there is no private header just add the generic one */
101 return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
102}
103
104/* netlink command implementations */
105
106static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
107 struct cfg80211_registered_device *dev)
108{
109 void *hdr;
Johannes Bergee688b002008-01-24 19:38:39 +0100110 struct nlattr *nl_bands, *nl_band;
111 struct nlattr *nl_freqs, *nl_freq;
112 struct nlattr *nl_rates, *nl_rate;
113 enum ieee80211_band band;
114 struct ieee80211_channel *chan;
115 struct ieee80211_rate *rate;
116 int i;
Johannes Berg55682962007-09-20 13:09:35 -0400117
118 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
119 if (!hdr)
120 return -1;
121
122 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->idx);
123 NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
Johannes Bergee688b002008-01-24 19:38:39 +0100124
125 nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
126 if (!nl_bands)
127 goto nla_put_failure;
128
129 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
130 if (!dev->wiphy.bands[band])
131 continue;
132
133 nl_band = nla_nest_start(msg, band);
134 if (!nl_band)
135 goto nla_put_failure;
136
137 /* add frequencies */
138 nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
139 if (!nl_freqs)
140 goto nla_put_failure;
141
142 for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
143 nl_freq = nla_nest_start(msg, i);
144 if (!nl_freq)
145 goto nla_put_failure;
146
147 chan = &dev->wiphy.bands[band]->channels[i];
148 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
149 chan->center_freq);
150
151 if (chan->flags & IEEE80211_CHAN_DISABLED)
152 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
153 if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
154 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
155 if (chan->flags & IEEE80211_CHAN_NO_IBSS)
156 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
157 if (chan->flags & IEEE80211_CHAN_RADAR)
158 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
159
160 nla_nest_end(msg, nl_freq);
161 }
162
163 nla_nest_end(msg, nl_freqs);
164
165 /* add bitrates */
166 nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
167 if (!nl_rates)
168 goto nla_put_failure;
169
170 for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
171 nl_rate = nla_nest_start(msg, i);
172 if (!nl_rate)
173 goto nla_put_failure;
174
175 rate = &dev->wiphy.bands[band]->bitrates[i];
176 NLA_PUT_U32(msg, NL80211_BITRATE_ATTR_RATE,
177 rate->bitrate);
178 if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
179 NLA_PUT_FLAG(msg,
180 NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE);
181
182 nla_nest_end(msg, nl_rate);
183 }
184
185 nla_nest_end(msg, nl_rates);
186
187 nla_nest_end(msg, nl_band);
188 }
189 nla_nest_end(msg, nl_bands);
190
Johannes Berg55682962007-09-20 13:09:35 -0400191 return genlmsg_end(msg, hdr);
192
193 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700194 genlmsg_cancel(msg, hdr);
195 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400196}
197
198static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
199{
200 int idx = 0;
201 int start = cb->args[0];
202 struct cfg80211_registered_device *dev;
203
204 mutex_lock(&cfg80211_drv_mutex);
205 list_for_each_entry(dev, &cfg80211_drv_list, list) {
Julius Volzb4637272008-07-08 14:02:19 +0200206 if (++idx <= start)
Johannes Berg55682962007-09-20 13:09:35 -0400207 continue;
208 if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
209 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Julius Volzb4637272008-07-08 14:02:19 +0200210 dev) < 0) {
211 idx--;
Johannes Berg55682962007-09-20 13:09:35 -0400212 break;
Julius Volzb4637272008-07-08 14:02:19 +0200213 }
Johannes Berg55682962007-09-20 13:09:35 -0400214 }
215 mutex_unlock(&cfg80211_drv_mutex);
216
217 cb->args[0] = idx;
218
219 return skb->len;
220}
221
222static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
223{
224 struct sk_buff *msg;
225 struct cfg80211_registered_device *dev;
226
227 dev = cfg80211_get_dev_from_info(info);
228 if (IS_ERR(dev))
229 return PTR_ERR(dev);
230
231 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
232 if (!msg)
233 goto out_err;
234
235 if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0)
236 goto out_free;
237
238 cfg80211_put_dev(dev);
239
240 return genlmsg_unicast(msg, info->snd_pid);
241
242 out_free:
243 nlmsg_free(msg);
244 out_err:
245 cfg80211_put_dev(dev);
246 return -ENOBUFS;
247}
248
249static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
250{
251 struct cfg80211_registered_device *rdev;
252 int result;
253
254 if (!info->attrs[NL80211_ATTR_WIPHY_NAME])
255 return -EINVAL;
256
257 rdev = cfg80211_get_dev_from_info(info);
258 if (IS_ERR(rdev))
259 return PTR_ERR(rdev);
260
261 result = cfg80211_dev_rename(rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
262
263 cfg80211_put_dev(rdev);
264 return result;
265}
266
267
268static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
269 struct net_device *dev)
270{
271 void *hdr;
272
273 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
274 if (!hdr)
275 return -1;
276
277 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
278 NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
279 /* TODO: interface type */
280 return genlmsg_end(msg, hdr);
281
282 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700283 genlmsg_cancel(msg, hdr);
284 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400285}
286
287static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
288{
289 int wp_idx = 0;
290 int if_idx = 0;
291 int wp_start = cb->args[0];
292 int if_start = cb->args[1];
293 struct cfg80211_registered_device *dev;
294 struct wireless_dev *wdev;
295
296 mutex_lock(&cfg80211_drv_mutex);
297 list_for_each_entry(dev, &cfg80211_drv_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +0200298 if (wp_idx < wp_start) {
299 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400300 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200301 }
Johannes Berg55682962007-09-20 13:09:35 -0400302 if_idx = 0;
303
304 mutex_lock(&dev->devlist_mtx);
305 list_for_each_entry(wdev, &dev->netdev_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +0200306 if (if_idx < if_start) {
307 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400308 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200309 }
Johannes Berg55682962007-09-20 13:09:35 -0400310 if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
311 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Bergbba95fe2008-07-29 13:22:51 +0200312 wdev->netdev) < 0) {
313 mutex_unlock(&dev->devlist_mtx);
314 goto out;
315 }
316 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400317 }
318 mutex_unlock(&dev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +0200319
320 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400321 }
Johannes Bergbba95fe2008-07-29 13:22:51 +0200322 out:
Johannes Berg55682962007-09-20 13:09:35 -0400323 mutex_unlock(&cfg80211_drv_mutex);
324
325 cb->args[0] = wp_idx;
326 cb->args[1] = if_idx;
327
328 return skb->len;
329}
330
331static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
332{
333 struct sk_buff *msg;
334 struct cfg80211_registered_device *dev;
335 struct net_device *netdev;
336 int err;
337
Johannes Bergbba95fe2008-07-29 13:22:51 +0200338 err = get_drv_dev_by_info_ifindex(info->attrs, &dev, &netdev);
Johannes Berg55682962007-09-20 13:09:35 -0400339 if (err)
340 return err;
341
342 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
343 if (!msg)
344 goto out_err;
345
346 if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0, netdev) < 0)
347 goto out_free;
348
349 dev_put(netdev);
350 cfg80211_put_dev(dev);
351
352 return genlmsg_unicast(msg, info->snd_pid);
353
354 out_free:
355 nlmsg_free(msg);
356 out_err:
357 dev_put(netdev);
358 cfg80211_put_dev(dev);
359 return -ENOBUFS;
360}
361
Michael Wu66f7ac52008-01-31 19:48:22 +0100362static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
363 [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
364 [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
365 [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
366 [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
367 [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
368};
369
370static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
371{
372 struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
373 int flag;
374
375 *mntrflags = 0;
376
377 if (!nla)
378 return -EINVAL;
379
380 if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX,
381 nla, mntr_flags_policy))
382 return -EINVAL;
383
384 for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
385 if (flags[flag])
386 *mntrflags |= (1<<flag);
387
388 return 0;
389}
390
Johannes Berg55682962007-09-20 13:09:35 -0400391static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
392{
393 struct cfg80211_registered_device *drv;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100394 struct vif_params params;
Johannes Berg55682962007-09-20 13:09:35 -0400395 int err, ifindex;
396 enum nl80211_iftype type;
397 struct net_device *dev;
Michael Wu66f7ac52008-01-31 19:48:22 +0100398 u32 flags;
Johannes Berg55682962007-09-20 13:09:35 -0400399
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100400 memset(&params, 0, sizeof(params));
401
Johannes Berg55682962007-09-20 13:09:35 -0400402 if (info->attrs[NL80211_ATTR_IFTYPE]) {
403 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
404 if (type > NL80211_IFTYPE_MAX)
405 return -EINVAL;
406 } else
407 return -EINVAL;
408
Johannes Bergbba95fe2008-07-29 13:22:51 +0200409 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg55682962007-09-20 13:09:35 -0400410 if (err)
411 return err;
412 ifindex = dev->ifindex;
413 dev_put(dev);
414
415 if (!drv->ops->change_virtual_intf) {
416 err = -EOPNOTSUPP;
417 goto unlock;
418 }
419
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100420 if (type == NL80211_IFTYPE_MESH_POINT &&
421 info->attrs[NL80211_ATTR_MESH_ID]) {
422 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
423 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
424 }
425
Johannes Berg55682962007-09-20 13:09:35 -0400426 rtnl_lock();
Michael Wu66f7ac52008-01-31 19:48:22 +0100427 err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
428 info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
429 &flags);
430 err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100431 type, err ? NULL : &flags, &params);
Johannes Berg55682962007-09-20 13:09:35 -0400432 rtnl_unlock();
433
434 unlock:
435 cfg80211_put_dev(drv);
436 return err;
437}
438
439static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
440{
441 struct cfg80211_registered_device *drv;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100442 struct vif_params params;
Johannes Berg55682962007-09-20 13:09:35 -0400443 int err;
444 enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
Michael Wu66f7ac52008-01-31 19:48:22 +0100445 u32 flags;
Johannes Berg55682962007-09-20 13:09:35 -0400446
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100447 memset(&params, 0, sizeof(params));
448
Johannes Berg55682962007-09-20 13:09:35 -0400449 if (!info->attrs[NL80211_ATTR_IFNAME])
450 return -EINVAL;
451
452 if (info->attrs[NL80211_ATTR_IFTYPE]) {
453 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
454 if (type > NL80211_IFTYPE_MAX)
455 return -EINVAL;
456 }
457
458 drv = cfg80211_get_dev_from_info(info);
459 if (IS_ERR(drv))
460 return PTR_ERR(drv);
461
462 if (!drv->ops->add_virtual_intf) {
463 err = -EOPNOTSUPP;
464 goto unlock;
465 }
466
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100467 if (type == NL80211_IFTYPE_MESH_POINT &&
468 info->attrs[NL80211_ATTR_MESH_ID]) {
469 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
470 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
471 }
472
Johannes Berg55682962007-09-20 13:09:35 -0400473 rtnl_lock();
Michael Wu66f7ac52008-01-31 19:48:22 +0100474 err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
475 info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
476 &flags);
Johannes Berg55682962007-09-20 13:09:35 -0400477 err = drv->ops->add_virtual_intf(&drv->wiphy,
Michael Wu66f7ac52008-01-31 19:48:22 +0100478 nla_data(info->attrs[NL80211_ATTR_IFNAME]),
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100479 type, err ? NULL : &flags, &params);
Johannes Berg55682962007-09-20 13:09:35 -0400480 rtnl_unlock();
481
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100482
Johannes Berg55682962007-09-20 13:09:35 -0400483 unlock:
484 cfg80211_put_dev(drv);
485 return err;
486}
487
488static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
489{
490 struct cfg80211_registered_device *drv;
491 int ifindex, err;
492 struct net_device *dev;
493
Johannes Bergbba95fe2008-07-29 13:22:51 +0200494 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg55682962007-09-20 13:09:35 -0400495 if (err)
496 return err;
497 ifindex = dev->ifindex;
498 dev_put(dev);
499
500 if (!drv->ops->del_virtual_intf) {
501 err = -EOPNOTSUPP;
502 goto out;
503 }
504
505 rtnl_lock();
506 err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex);
507 rtnl_unlock();
508
509 out:
510 cfg80211_put_dev(drv);
511 return err;
512}
513
Johannes Berg41ade002007-12-19 02:03:29 +0100514struct get_key_cookie {
515 struct sk_buff *msg;
516 int error;
517};
518
519static void get_key_callback(void *c, struct key_params *params)
520{
521 struct get_key_cookie *cookie = c;
522
523 if (params->key)
524 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA,
525 params->key_len, params->key);
526
527 if (params->seq)
528 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ,
529 params->seq_len, params->seq);
530
531 if (params->cipher)
532 NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
533 params->cipher);
534
535 return;
536 nla_put_failure:
537 cookie->error = 1;
538}
539
540static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
541{
542 struct cfg80211_registered_device *drv;
543 int err;
544 struct net_device *dev;
545 u8 key_idx = 0;
546 u8 *mac_addr = NULL;
547 struct get_key_cookie cookie = {
548 .error = 0,
549 };
550 void *hdr;
551 struct sk_buff *msg;
552
553 if (info->attrs[NL80211_ATTR_KEY_IDX])
554 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
555
556 if (key_idx > 3)
557 return -EINVAL;
558
559 if (info->attrs[NL80211_ATTR_MAC])
560 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
561
Johannes Bergbba95fe2008-07-29 13:22:51 +0200562 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +0100563 if (err)
564 return err;
565
566 if (!drv->ops->get_key) {
567 err = -EOPNOTSUPP;
568 goto out;
569 }
570
571 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
572 if (!msg) {
573 err = -ENOMEM;
574 goto out;
575 }
576
577 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
578 NL80211_CMD_NEW_KEY);
579
580 if (IS_ERR(hdr)) {
581 err = PTR_ERR(hdr);
582 goto out;
583 }
584
585 cookie.msg = msg;
586
587 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
588 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
589 if (mac_addr)
590 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
591
592 rtnl_lock();
593 err = drv->ops->get_key(&drv->wiphy, dev, key_idx, mac_addr,
594 &cookie, get_key_callback);
595 rtnl_unlock();
596
597 if (err)
598 goto out;
599
600 if (cookie.error)
601 goto nla_put_failure;
602
603 genlmsg_end(msg, hdr);
604 err = genlmsg_unicast(msg, info->snd_pid);
605 goto out;
606
607 nla_put_failure:
608 err = -ENOBUFS;
609 nlmsg_free(msg);
610 out:
611 cfg80211_put_dev(drv);
612 dev_put(dev);
613 return err;
614}
615
616static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
617{
618 struct cfg80211_registered_device *drv;
619 int err;
620 struct net_device *dev;
621 u8 key_idx;
622
623 if (!info->attrs[NL80211_ATTR_KEY_IDX])
624 return -EINVAL;
625
626 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
627
628 if (key_idx > 3)
629 return -EINVAL;
630
631 /* currently only support setting default key */
632 if (!info->attrs[NL80211_ATTR_KEY_DEFAULT])
633 return -EINVAL;
634
Johannes Bergbba95fe2008-07-29 13:22:51 +0200635 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +0100636 if (err)
637 return err;
638
639 if (!drv->ops->set_default_key) {
640 err = -EOPNOTSUPP;
641 goto out;
642 }
643
644 rtnl_lock();
645 err = drv->ops->set_default_key(&drv->wiphy, dev, key_idx);
646 rtnl_unlock();
647
648 out:
649 cfg80211_put_dev(drv);
650 dev_put(dev);
651 return err;
652}
653
654static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
655{
656 struct cfg80211_registered_device *drv;
657 int err;
658 struct net_device *dev;
659 struct key_params params;
660 u8 key_idx = 0;
661 u8 *mac_addr = NULL;
662
663 memset(&params, 0, sizeof(params));
664
665 if (!info->attrs[NL80211_ATTR_KEY_CIPHER])
666 return -EINVAL;
667
668 if (info->attrs[NL80211_ATTR_KEY_DATA]) {
669 params.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
670 params.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
671 }
672
673 if (info->attrs[NL80211_ATTR_KEY_IDX])
674 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
675
676 params.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
677
678 if (info->attrs[NL80211_ATTR_MAC])
679 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
680
681 if (key_idx > 3)
682 return -EINVAL;
683
684 /*
685 * Disallow pairwise keys with non-zero index unless it's WEP
686 * (because current deployments use pairwise WEP keys with
687 * non-zero indizes but 802.11i clearly specifies to use zero)
688 */
689 if (mac_addr && key_idx &&
690 params.cipher != WLAN_CIPHER_SUITE_WEP40 &&
691 params.cipher != WLAN_CIPHER_SUITE_WEP104)
692 return -EINVAL;
693
694 /* TODO: add definitions for the lengths to linux/ieee80211.h */
695 switch (params.cipher) {
696 case WLAN_CIPHER_SUITE_WEP40:
697 if (params.key_len != 5)
698 return -EINVAL;
699 break;
700 case WLAN_CIPHER_SUITE_TKIP:
701 if (params.key_len != 32)
702 return -EINVAL;
703 break;
704 case WLAN_CIPHER_SUITE_CCMP:
705 if (params.key_len != 16)
706 return -EINVAL;
707 break;
708 case WLAN_CIPHER_SUITE_WEP104:
709 if (params.key_len != 13)
710 return -EINVAL;
711 break;
712 default:
713 return -EINVAL;
714 }
715
Johannes Bergbba95fe2008-07-29 13:22:51 +0200716 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +0100717 if (err)
718 return err;
719
720 if (!drv->ops->add_key) {
721 err = -EOPNOTSUPP;
722 goto out;
723 }
724
725 rtnl_lock();
726 err = drv->ops->add_key(&drv->wiphy, dev, key_idx, mac_addr, &params);
727 rtnl_unlock();
728
729 out:
730 cfg80211_put_dev(drv);
731 dev_put(dev);
732 return err;
733}
734
735static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
736{
737 struct cfg80211_registered_device *drv;
738 int err;
739 struct net_device *dev;
740 u8 key_idx = 0;
741 u8 *mac_addr = NULL;
742
743 if (info->attrs[NL80211_ATTR_KEY_IDX])
744 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
745
746 if (key_idx > 3)
747 return -EINVAL;
748
749 if (info->attrs[NL80211_ATTR_MAC])
750 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
751
Johannes Bergbba95fe2008-07-29 13:22:51 +0200752 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +0100753 if (err)
754 return err;
755
756 if (!drv->ops->del_key) {
757 err = -EOPNOTSUPP;
758 goto out;
759 }
760
761 rtnl_lock();
762 err = drv->ops->del_key(&drv->wiphy, dev, key_idx, mac_addr);
763 rtnl_unlock();
764
765 out:
766 cfg80211_put_dev(drv);
767 dev_put(dev);
768 return err;
769}
770
Johannes Berged1b6cc2007-12-19 02:03:32 +0100771static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
772{
773 int (*call)(struct wiphy *wiphy, struct net_device *dev,
774 struct beacon_parameters *info);
775 struct cfg80211_registered_device *drv;
776 int err;
777 struct net_device *dev;
778 struct beacon_parameters params;
779 int haveinfo = 0;
780
Johannes Bergbba95fe2008-07-29 13:22:51 +0200781 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +0100782 if (err)
783 return err;
784
785 switch (info->genlhdr->cmd) {
786 case NL80211_CMD_NEW_BEACON:
787 /* these are required for NEW_BEACON */
788 if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
789 !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
790 !info->attrs[NL80211_ATTR_BEACON_HEAD]) {
791 err = -EINVAL;
792 goto out;
793 }
794
795 call = drv->ops->add_beacon;
796 break;
797 case NL80211_CMD_SET_BEACON:
798 call = drv->ops->set_beacon;
799 break;
800 default:
801 WARN_ON(1);
802 err = -EOPNOTSUPP;
803 goto out;
804 }
805
806 if (!call) {
807 err = -EOPNOTSUPP;
808 goto out;
809 }
810
811 memset(&params, 0, sizeof(params));
812
813 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
814 params.interval =
815 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
816 haveinfo = 1;
817 }
818
819 if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) {
820 params.dtim_period =
821 nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
822 haveinfo = 1;
823 }
824
825 if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
826 params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
827 params.head_len =
828 nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
829 haveinfo = 1;
830 }
831
832 if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
833 params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
834 params.tail_len =
835 nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
836 haveinfo = 1;
837 }
838
839 if (!haveinfo) {
840 err = -EINVAL;
841 goto out;
842 }
843
844 rtnl_lock();
845 err = call(&drv->wiphy, dev, &params);
846 rtnl_unlock();
847
848 out:
849 cfg80211_put_dev(drv);
850 dev_put(dev);
851 return err;
852}
853
854static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
855{
856 struct cfg80211_registered_device *drv;
857 int err;
858 struct net_device *dev;
859
Johannes Bergbba95fe2008-07-29 13:22:51 +0200860 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +0100861 if (err)
862 return err;
863
864 if (!drv->ops->del_beacon) {
865 err = -EOPNOTSUPP;
866 goto out;
867 }
868
869 rtnl_lock();
870 err = drv->ops->del_beacon(&drv->wiphy, dev);
871 rtnl_unlock();
872
873 out:
874 cfg80211_put_dev(drv);
875 dev_put(dev);
876 return err;
877}
878
Johannes Berg5727ef12007-12-19 02:03:34 +0100879static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
880 [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
881 [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
882 [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
883};
884
885static int parse_station_flags(struct nlattr *nla, u32 *staflags)
886{
887 struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
888 int flag;
889
890 *staflags = 0;
891
892 if (!nla)
893 return 0;
894
895 if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX,
896 nla, sta_flags_policy))
897 return -EINVAL;
898
899 *staflags = STATION_FLAG_CHANGED;
900
901 for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++)
902 if (flags[flag])
903 *staflags |= (1<<flag);
904
905 return 0;
906}
907
Johannes Bergfd5b74d2007-12-19 02:03:36 +0100908static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
909 int flags, struct net_device *dev,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100910 u8 *mac_addr, struct station_info *sinfo)
Johannes Bergfd5b74d2007-12-19 02:03:36 +0100911{
912 void *hdr;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100913 struct nlattr *sinfoattr;
Johannes Bergfd5b74d2007-12-19 02:03:36 +0100914
915 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
916 if (!hdr)
917 return -1;
918
919 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
920 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
921
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100922 sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
923 if (!sinfoattr)
Johannes Bergfd5b74d2007-12-19 02:03:36 +0100924 goto nla_put_failure;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100925 if (sinfo->filled & STATION_INFO_INACTIVE_TIME)
926 NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME,
927 sinfo->inactive_time);
928 if (sinfo->filled & STATION_INFO_RX_BYTES)
929 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES,
930 sinfo->rx_bytes);
931 if (sinfo->filled & STATION_INFO_TX_BYTES)
932 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES,
933 sinfo->tx_bytes);
934 if (sinfo->filled & STATION_INFO_LLID)
935 NLA_PUT_U16(msg, NL80211_STA_INFO_LLID,
936 sinfo->llid);
937 if (sinfo->filled & STATION_INFO_PLID)
938 NLA_PUT_U16(msg, NL80211_STA_INFO_PLID,
939 sinfo->plid);
940 if (sinfo->filled & STATION_INFO_PLINK_STATE)
941 NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
942 sinfo->plink_state);
Johannes Bergfd5b74d2007-12-19 02:03:36 +0100943
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100944 nla_nest_end(msg, sinfoattr);
Johannes Bergfd5b74d2007-12-19 02:03:36 +0100945
946 return genlmsg_end(msg, hdr);
947
948 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700949 genlmsg_cancel(msg, hdr);
950 return -EMSGSIZE;
Johannes Bergfd5b74d2007-12-19 02:03:36 +0100951}
952
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100953static int nl80211_dump_station(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +0200954 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100955{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100956 struct station_info sinfo;
957 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200958 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100959 u8 mac_addr[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +0200960 int ifidx = cb->args[0];
961 int sta_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100962 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100963
Johannes Bergbba95fe2008-07-29 13:22:51 +0200964 if (!ifidx) {
965 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
966 nl80211_fam.attrbuf, nl80211_fam.maxattr,
967 nl80211_policy);
968 if (err)
969 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100970
Johannes Bergbba95fe2008-07-29 13:22:51 +0200971 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
972 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100973
Johannes Bergbba95fe2008-07-29 13:22:51 +0200974 ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
975 if (!ifidx)
976 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100977 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100978
Johannes Bergbba95fe2008-07-29 13:22:51 +0200979 netdev = dev_get_by_index(&init_net, ifidx);
980 if (!netdev)
981 return -ENODEV;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100982
Johannes Bergbba95fe2008-07-29 13:22:51 +0200983 dev = cfg80211_get_dev_from_ifindex(ifidx);
984 if (IS_ERR(dev)) {
985 err = PTR_ERR(dev);
986 goto out_put_netdev;
987 }
988
989 if (!dev->ops->dump_station) {
990 err = -ENOSYS;
991 goto out_err;
992 }
993
994 rtnl_lock();
995
996 while (1) {
997 err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
998 mac_addr, &sinfo);
999 if (err == -ENOENT)
1000 break;
1001 if (err)
1002 goto out_err_rtnl;
1003
1004 if (nl80211_send_station(skb,
1005 NETLINK_CB(cb->skb).pid,
1006 cb->nlh->nlmsg_seq, NLM_F_MULTI,
1007 netdev, mac_addr,
1008 &sinfo) < 0)
1009 goto out;
1010
1011 sta_idx++;
1012 }
1013
1014
1015 out:
1016 cb->args[1] = sta_idx;
1017 err = skb->len;
1018 out_err_rtnl:
1019 rtnl_unlock();
1020 out_err:
1021 cfg80211_put_dev(dev);
1022 out_put_netdev:
1023 dev_put(netdev);
1024
1025 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001026}
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001027
Johannes Berg5727ef12007-12-19 02:03:34 +01001028static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
1029{
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001030 struct cfg80211_registered_device *drv;
1031 int err;
1032 struct net_device *dev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001033 struct station_info sinfo;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001034 struct sk_buff *msg;
1035 u8 *mac_addr = NULL;
1036
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001037 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001038
1039 if (!info->attrs[NL80211_ATTR_MAC])
1040 return -EINVAL;
1041
1042 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1043
Johannes Bergbba95fe2008-07-29 13:22:51 +02001044 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001045 if (err)
1046 return err;
1047
1048 if (!drv->ops->get_station) {
1049 err = -EOPNOTSUPP;
1050 goto out;
1051 }
1052
1053 rtnl_lock();
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001054 err = drv->ops->get_station(&drv->wiphy, dev, mac_addr, &sinfo);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001055 rtnl_unlock();
1056
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001057 if (err)
1058 goto out;
1059
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001060 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1061 if (!msg)
1062 goto out;
1063
1064 if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001065 dev, mac_addr, &sinfo) < 0)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001066 goto out_free;
1067
1068 err = genlmsg_unicast(msg, info->snd_pid);
1069 goto out;
1070
1071 out_free:
1072 nlmsg_free(msg);
1073
1074 out:
1075 cfg80211_put_dev(drv);
1076 dev_put(dev);
1077 return err;
Johannes Berg5727ef12007-12-19 02:03:34 +01001078}
1079
1080/*
1081 * Get vlan interface making sure it is on the right wiphy.
1082 */
1083static int get_vlan(struct nlattr *vlanattr,
1084 struct cfg80211_registered_device *rdev,
1085 struct net_device **vlan)
1086{
1087 *vlan = NULL;
1088
1089 if (vlanattr) {
1090 *vlan = dev_get_by_index(&init_net, nla_get_u32(vlanattr));
1091 if (!*vlan)
1092 return -ENODEV;
1093 if (!(*vlan)->ieee80211_ptr)
1094 return -EINVAL;
1095 if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy)
1096 return -EINVAL;
1097 }
1098 return 0;
1099}
1100
1101static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
1102{
1103 struct cfg80211_registered_device *drv;
1104 int err;
1105 struct net_device *dev;
1106 struct station_parameters params;
1107 u8 *mac_addr = NULL;
1108
1109 memset(&params, 0, sizeof(params));
1110
1111 params.listen_interval = -1;
1112
1113 if (info->attrs[NL80211_ATTR_STA_AID])
1114 return -EINVAL;
1115
1116 if (!info->attrs[NL80211_ATTR_MAC])
1117 return -EINVAL;
1118
1119 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1120
1121 if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
1122 params.supported_rates =
1123 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1124 params.supported_rates_len =
1125 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1126 }
1127
1128 if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
1129 params.listen_interval =
1130 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
1131
1132 if (parse_station_flags(info->attrs[NL80211_ATTR_STA_FLAGS],
1133 &params.station_flags))
1134 return -EINVAL;
1135
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001136 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
1137 params.plink_action =
1138 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
1139
Johannes Bergbba95fe2008-07-29 13:22:51 +02001140 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001141 if (err)
1142 return err;
1143
1144 err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, &params.vlan);
1145 if (err)
1146 goto out;
1147
1148 if (!drv->ops->change_station) {
1149 err = -EOPNOTSUPP;
1150 goto out;
1151 }
1152
1153 rtnl_lock();
1154 err = drv->ops->change_station(&drv->wiphy, dev, mac_addr, &params);
1155 rtnl_unlock();
1156
1157 out:
1158 if (params.vlan)
1159 dev_put(params.vlan);
1160 cfg80211_put_dev(drv);
1161 dev_put(dev);
1162 return err;
1163}
1164
1165static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
1166{
1167 struct cfg80211_registered_device *drv;
1168 int err;
1169 struct net_device *dev;
1170 struct station_parameters params;
1171 u8 *mac_addr = NULL;
1172
1173 memset(&params, 0, sizeof(params));
1174
1175 if (!info->attrs[NL80211_ATTR_MAC])
1176 return -EINVAL;
1177
1178 if (!info->attrs[NL80211_ATTR_STA_AID])
1179 return -EINVAL;
1180
1181 if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
1182 return -EINVAL;
1183
1184 if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
1185 return -EINVAL;
1186
1187 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1188 params.supported_rates =
1189 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1190 params.supported_rates_len =
1191 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1192 params.listen_interval =
1193 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
Johannes Berg16f2e852008-04-07 14:35:46 +02001194 params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
Johannes Berg5727ef12007-12-19 02:03:34 +01001195
1196 if (parse_station_flags(info->attrs[NL80211_ATTR_STA_FLAGS],
1197 &params.station_flags))
1198 return -EINVAL;
1199
Johannes Bergbba95fe2008-07-29 13:22:51 +02001200 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001201 if (err)
1202 return err;
1203
1204 err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, &params.vlan);
1205 if (err)
1206 goto out;
1207
1208 if (!drv->ops->add_station) {
1209 err = -EOPNOTSUPP;
1210 goto out;
1211 }
1212
1213 rtnl_lock();
1214 err = drv->ops->add_station(&drv->wiphy, dev, mac_addr, &params);
1215 rtnl_unlock();
1216
1217 out:
1218 if (params.vlan)
1219 dev_put(params.vlan);
1220 cfg80211_put_dev(drv);
1221 dev_put(dev);
1222 return err;
1223}
1224
1225static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
1226{
1227 struct cfg80211_registered_device *drv;
1228 int err;
1229 struct net_device *dev;
1230 u8 *mac_addr = NULL;
1231
1232 if (info->attrs[NL80211_ATTR_MAC])
1233 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1234
Johannes Bergbba95fe2008-07-29 13:22:51 +02001235 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001236 if (err)
1237 return err;
1238
1239 if (!drv->ops->del_station) {
1240 err = -EOPNOTSUPP;
1241 goto out;
1242 }
1243
1244 rtnl_lock();
1245 err = drv->ops->del_station(&drv->wiphy, dev, mac_addr);
1246 rtnl_unlock();
1247
1248 out:
1249 cfg80211_put_dev(drv);
1250 dev_put(dev);
1251 return err;
1252}
1253
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001254static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq,
1255 int flags, struct net_device *dev,
1256 u8 *dst, u8 *next_hop,
1257 struct mpath_info *pinfo)
1258{
1259 void *hdr;
1260 struct nlattr *pinfoattr;
1261
1262 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
1263 if (!hdr)
1264 return -1;
1265
1266 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1267 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
1268 NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop);
1269
1270 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
1271 if (!pinfoattr)
1272 goto nla_put_failure;
1273 if (pinfo->filled & MPATH_INFO_FRAME_QLEN)
1274 NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
1275 pinfo->frame_qlen);
1276 if (pinfo->filled & MPATH_INFO_DSN)
1277 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DSN,
1278 pinfo->dsn);
1279 if (pinfo->filled & MPATH_INFO_METRIC)
1280 NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC,
1281 pinfo->metric);
1282 if (pinfo->filled & MPATH_INFO_EXPTIME)
1283 NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME,
1284 pinfo->exptime);
1285 if (pinfo->filled & MPATH_INFO_FLAGS)
1286 NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS,
1287 pinfo->flags);
1288 if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT)
1289 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
1290 pinfo->discovery_timeout);
1291 if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES)
1292 NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
1293 pinfo->discovery_retries);
1294
1295 nla_nest_end(msg, pinfoattr);
1296
1297 return genlmsg_end(msg, hdr);
1298
1299 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001300 genlmsg_cancel(msg, hdr);
1301 return -EMSGSIZE;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001302}
1303
1304static int nl80211_dump_mpath(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02001305 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001306{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001307 struct mpath_info pinfo;
1308 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001309 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001310 u8 dst[ETH_ALEN];
1311 u8 next_hop[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02001312 int ifidx = cb->args[0];
1313 int path_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001314 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001315
Johannes Bergbba95fe2008-07-29 13:22:51 +02001316 if (!ifidx) {
1317 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
1318 nl80211_fam.attrbuf, nl80211_fam.maxattr,
1319 nl80211_policy);
1320 if (err)
1321 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001322
Johannes Bergbba95fe2008-07-29 13:22:51 +02001323 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
1324 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001325
Johannes Bergbba95fe2008-07-29 13:22:51 +02001326 ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
1327 if (!ifidx)
1328 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001329 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001330
Johannes Bergbba95fe2008-07-29 13:22:51 +02001331 netdev = dev_get_by_index(&init_net, ifidx);
1332 if (!netdev)
1333 return -ENODEV;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001334
Johannes Bergbba95fe2008-07-29 13:22:51 +02001335 dev = cfg80211_get_dev_from_ifindex(ifidx);
1336 if (IS_ERR(dev)) {
1337 err = PTR_ERR(dev);
1338 goto out_put_netdev;
1339 }
1340
1341 if (!dev->ops->dump_mpath) {
1342 err = -ENOSYS;
1343 goto out_err;
1344 }
1345
1346 rtnl_lock();
1347
1348 while (1) {
1349 err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
1350 dst, next_hop, &pinfo);
1351 if (err == -ENOENT)
1352 break;
1353 if (err)
1354 goto out_err_rtnl;
1355
1356 if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid,
1357 cb->nlh->nlmsg_seq, NLM_F_MULTI,
1358 netdev, dst, next_hop,
1359 &pinfo) < 0)
1360 goto out;
1361
1362 path_idx++;
1363 }
1364
1365
1366 out:
1367 cb->args[1] = path_idx;
1368 err = skb->len;
1369 out_err_rtnl:
1370 rtnl_unlock();
1371 out_err:
1372 cfg80211_put_dev(dev);
1373 out_put_netdev:
1374 dev_put(netdev);
1375
1376 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001377}
1378
1379static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
1380{
1381 struct cfg80211_registered_device *drv;
1382 int err;
1383 struct net_device *dev;
1384 struct mpath_info pinfo;
1385 struct sk_buff *msg;
1386 u8 *dst = NULL;
1387 u8 next_hop[ETH_ALEN];
1388
1389 memset(&pinfo, 0, sizeof(pinfo));
1390
1391 if (!info->attrs[NL80211_ATTR_MAC])
1392 return -EINVAL;
1393
1394 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
1395
Johannes Bergbba95fe2008-07-29 13:22:51 +02001396 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001397 if (err)
1398 return err;
1399
1400 if (!drv->ops->get_mpath) {
1401 err = -EOPNOTSUPP;
1402 goto out;
1403 }
1404
1405 rtnl_lock();
1406 err = drv->ops->get_mpath(&drv->wiphy, dev, dst, next_hop, &pinfo);
1407 rtnl_unlock();
1408
1409 if (err)
1410 goto out;
1411
1412 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1413 if (!msg)
1414 goto out;
1415
1416 if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0,
1417 dev, dst, next_hop, &pinfo) < 0)
1418 goto out_free;
1419
1420 err = genlmsg_unicast(msg, info->snd_pid);
1421 goto out;
1422
1423 out_free:
1424 nlmsg_free(msg);
1425
1426 out:
1427 cfg80211_put_dev(drv);
1428 dev_put(dev);
1429 return err;
1430}
1431
1432static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
1433{
1434 struct cfg80211_registered_device *drv;
1435 int err;
1436 struct net_device *dev;
1437 u8 *dst = NULL;
1438 u8 *next_hop = NULL;
1439
1440 if (!info->attrs[NL80211_ATTR_MAC])
1441 return -EINVAL;
1442
1443 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
1444 return -EINVAL;
1445
1446 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
1447 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
1448
Johannes Bergbba95fe2008-07-29 13:22:51 +02001449 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001450 if (err)
1451 return err;
1452
1453 if (!drv->ops->change_mpath) {
1454 err = -EOPNOTSUPP;
1455 goto out;
1456 }
1457
1458 rtnl_lock();
1459 err = drv->ops->change_mpath(&drv->wiphy, dev, dst, next_hop);
1460 rtnl_unlock();
1461
1462 out:
1463 cfg80211_put_dev(drv);
1464 dev_put(dev);
1465 return err;
1466}
1467static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
1468{
1469 struct cfg80211_registered_device *drv;
1470 int err;
1471 struct net_device *dev;
1472 u8 *dst = NULL;
1473 u8 *next_hop = NULL;
1474
1475 if (!info->attrs[NL80211_ATTR_MAC])
1476 return -EINVAL;
1477
1478 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
1479 return -EINVAL;
1480
1481 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
1482 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
1483
Johannes Bergbba95fe2008-07-29 13:22:51 +02001484 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001485 if (err)
1486 return err;
1487
1488 if (!drv->ops->add_mpath) {
1489 err = -EOPNOTSUPP;
1490 goto out;
1491 }
1492
1493 rtnl_lock();
1494 err = drv->ops->add_mpath(&drv->wiphy, dev, dst, next_hop);
1495 rtnl_unlock();
1496
1497 out:
1498 cfg80211_put_dev(drv);
1499 dev_put(dev);
1500 return err;
1501}
1502
1503static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
1504{
1505 struct cfg80211_registered_device *drv;
1506 int err;
1507 struct net_device *dev;
1508 u8 *dst = NULL;
1509
1510 if (info->attrs[NL80211_ATTR_MAC])
1511 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
1512
Johannes Bergbba95fe2008-07-29 13:22:51 +02001513 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001514 if (err)
1515 return err;
1516
1517 if (!drv->ops->del_mpath) {
1518 err = -EOPNOTSUPP;
1519 goto out;
1520 }
1521
1522 rtnl_lock();
1523 err = drv->ops->del_mpath(&drv->wiphy, dev, dst);
1524 rtnl_unlock();
1525
1526 out:
1527 cfg80211_put_dev(drv);
1528 dev_put(dev);
1529 return err;
1530}
1531
Jouni Malinen9f1ba902008-08-07 20:07:01 +03001532static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
1533{
1534 struct cfg80211_registered_device *drv;
1535 int err;
1536 struct net_device *dev;
1537 struct bss_parameters params;
1538
1539 memset(&params, 0, sizeof(params));
1540 /* default to not changing parameters */
1541 params.use_cts_prot = -1;
1542 params.use_short_preamble = -1;
1543 params.use_short_slot_time = -1;
1544
1545 if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
1546 params.use_cts_prot =
1547 nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
1548 if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
1549 params.use_short_preamble =
1550 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
1551 if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
1552 params.use_short_slot_time =
1553 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
1554
1555 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
1556 if (err)
1557 return err;
1558
1559 if (!drv->ops->change_bss) {
1560 err = -EOPNOTSUPP;
1561 goto out;
1562 }
1563
1564 rtnl_lock();
1565 err = drv->ops->change_bss(&drv->wiphy, dev, &params);
1566 rtnl_unlock();
1567
1568 out:
1569 cfg80211_put_dev(drv);
1570 dev_put(dev);
1571 return err;
1572}
1573
Johannes Berg55682962007-09-20 13:09:35 -04001574static struct genl_ops nl80211_ops[] = {
1575 {
1576 .cmd = NL80211_CMD_GET_WIPHY,
1577 .doit = nl80211_get_wiphy,
1578 .dumpit = nl80211_dump_wiphy,
1579 .policy = nl80211_policy,
1580 /* can be retrieved by unprivileged users */
1581 },
1582 {
1583 .cmd = NL80211_CMD_SET_WIPHY,
1584 .doit = nl80211_set_wiphy,
1585 .policy = nl80211_policy,
1586 .flags = GENL_ADMIN_PERM,
1587 },
1588 {
1589 .cmd = NL80211_CMD_GET_INTERFACE,
1590 .doit = nl80211_get_interface,
1591 .dumpit = nl80211_dump_interface,
1592 .policy = nl80211_policy,
1593 /* can be retrieved by unprivileged users */
1594 },
1595 {
1596 .cmd = NL80211_CMD_SET_INTERFACE,
1597 .doit = nl80211_set_interface,
1598 .policy = nl80211_policy,
1599 .flags = GENL_ADMIN_PERM,
1600 },
1601 {
1602 .cmd = NL80211_CMD_NEW_INTERFACE,
1603 .doit = nl80211_new_interface,
1604 .policy = nl80211_policy,
1605 .flags = GENL_ADMIN_PERM,
1606 },
1607 {
1608 .cmd = NL80211_CMD_DEL_INTERFACE,
1609 .doit = nl80211_del_interface,
1610 .policy = nl80211_policy,
1611 .flags = GENL_ADMIN_PERM,
1612 },
Johannes Berg41ade002007-12-19 02:03:29 +01001613 {
1614 .cmd = NL80211_CMD_GET_KEY,
1615 .doit = nl80211_get_key,
1616 .policy = nl80211_policy,
1617 .flags = GENL_ADMIN_PERM,
1618 },
1619 {
1620 .cmd = NL80211_CMD_SET_KEY,
1621 .doit = nl80211_set_key,
1622 .policy = nl80211_policy,
1623 .flags = GENL_ADMIN_PERM,
1624 },
1625 {
1626 .cmd = NL80211_CMD_NEW_KEY,
1627 .doit = nl80211_new_key,
1628 .policy = nl80211_policy,
1629 .flags = GENL_ADMIN_PERM,
1630 },
1631 {
1632 .cmd = NL80211_CMD_DEL_KEY,
1633 .doit = nl80211_del_key,
1634 .policy = nl80211_policy,
1635 .flags = GENL_ADMIN_PERM,
1636 },
Johannes Berged1b6cc2007-12-19 02:03:32 +01001637 {
1638 .cmd = NL80211_CMD_SET_BEACON,
1639 .policy = nl80211_policy,
1640 .flags = GENL_ADMIN_PERM,
1641 .doit = nl80211_addset_beacon,
1642 },
1643 {
1644 .cmd = NL80211_CMD_NEW_BEACON,
1645 .policy = nl80211_policy,
1646 .flags = GENL_ADMIN_PERM,
1647 .doit = nl80211_addset_beacon,
1648 },
1649 {
1650 .cmd = NL80211_CMD_DEL_BEACON,
1651 .policy = nl80211_policy,
1652 .flags = GENL_ADMIN_PERM,
1653 .doit = nl80211_del_beacon,
1654 },
Johannes Berg5727ef12007-12-19 02:03:34 +01001655 {
1656 .cmd = NL80211_CMD_GET_STATION,
1657 .doit = nl80211_get_station,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001658 .dumpit = nl80211_dump_station,
Johannes Berg5727ef12007-12-19 02:03:34 +01001659 .policy = nl80211_policy,
1660 .flags = GENL_ADMIN_PERM,
1661 },
1662 {
1663 .cmd = NL80211_CMD_SET_STATION,
1664 .doit = nl80211_set_station,
1665 .policy = nl80211_policy,
1666 .flags = GENL_ADMIN_PERM,
1667 },
1668 {
1669 .cmd = NL80211_CMD_NEW_STATION,
1670 .doit = nl80211_new_station,
1671 .policy = nl80211_policy,
1672 .flags = GENL_ADMIN_PERM,
1673 },
1674 {
1675 .cmd = NL80211_CMD_DEL_STATION,
1676 .doit = nl80211_del_station,
1677 .policy = nl80211_policy,
1678 .flags = GENL_ADMIN_PERM,
1679 },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001680 {
1681 .cmd = NL80211_CMD_GET_MPATH,
1682 .doit = nl80211_get_mpath,
1683 .dumpit = nl80211_dump_mpath,
1684 .policy = nl80211_policy,
1685 .flags = GENL_ADMIN_PERM,
1686 },
1687 {
1688 .cmd = NL80211_CMD_SET_MPATH,
1689 .doit = nl80211_set_mpath,
1690 .policy = nl80211_policy,
1691 .flags = GENL_ADMIN_PERM,
1692 },
1693 {
1694 .cmd = NL80211_CMD_NEW_MPATH,
1695 .doit = nl80211_new_mpath,
1696 .policy = nl80211_policy,
1697 .flags = GENL_ADMIN_PERM,
1698 },
1699 {
1700 .cmd = NL80211_CMD_DEL_MPATH,
1701 .doit = nl80211_del_mpath,
1702 .policy = nl80211_policy,
1703 .flags = GENL_ADMIN_PERM,
1704 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +03001705 {
1706 .cmd = NL80211_CMD_SET_BSS,
1707 .doit = nl80211_set_bss,
1708 .policy = nl80211_policy,
1709 .flags = GENL_ADMIN_PERM,
1710 },
Johannes Berg55682962007-09-20 13:09:35 -04001711};
1712
1713/* multicast groups */
1714static struct genl_multicast_group nl80211_config_mcgrp = {
1715 .name = "config",
1716};
1717
1718/* notification functions */
1719
1720void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
1721{
1722 struct sk_buff *msg;
1723
1724 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1725 if (!msg)
1726 return;
1727
1728 if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
1729 nlmsg_free(msg);
1730 return;
1731 }
1732
1733 genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL);
1734}
1735
1736/* initialisation/exit functions */
1737
1738int nl80211_init(void)
1739{
1740 int err, i;
1741
1742 err = genl_register_family(&nl80211_fam);
1743 if (err)
1744 return err;
1745
1746 for (i = 0; i < ARRAY_SIZE(nl80211_ops); i++) {
1747 err = genl_register_ops(&nl80211_fam, &nl80211_ops[i]);
1748 if (err)
1749 goto err_out;
1750 }
1751
1752 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
1753 if (err)
1754 goto err_out;
1755
1756 return 0;
1757 err_out:
1758 genl_unregister_family(&nl80211_fam);
1759 return err;
1760}
1761
1762void nl80211_exit(void)
1763{
1764 genl_unregister_family(&nl80211_fam);
1765}