aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless/nl80211.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2007-09-20 13:09:35 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2007-10-10 19:52:14 -0400
commit556829657397b9b05baec6691ead4e22ee8d1567 (patch)
tree44242431553e5e22c0bceaab7a06d9d7bf0dd2f6 /net/wireless/nl80211.c
parent0800f170263d19b882e519441156c5f6ed190fc1 (diff)
[NL80211]: add netlink interface to cfg80211
Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r--net/wireless/nl80211.c431
1 files changed, 431 insertions, 0 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
new file mode 100644
index 000000000000..48b0d453e4e1
--- /dev/null
+++ b/net/wireless/nl80211.c
@@ -0,0 +1,431 @@
1/*
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 */
32static int get_drv_dev_by_info_ifindex(struct genl_info *info,
33 struct cfg80211_registered_device **drv,
34 struct net_device **dev)
35{
36 int ifindex;
37
38 if (!info->attrs[NL80211_ATTR_IFINDEX])
39 return -EINVAL;
40
41 ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
42 *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 },
64};
65
66/* message building helper */
67static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
68 int flags, u8 cmd)
69{
70 /* since there is no private header just add the generic one */
71 return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
72}
73
74/* netlink command implementations */
75
76static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
77 struct cfg80211_registered_device *dev)
78{
79 void *hdr;
80
81 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
82 if (!hdr)
83 return -1;
84
85 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->idx);
86 NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
87 return genlmsg_end(msg, hdr);
88
89 nla_put_failure:
90 return genlmsg_cancel(msg, hdr);
91}
92
93static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
94{
95 int idx = 0;
96 int start = cb->args[0];
97 struct cfg80211_registered_device *dev;
98
99 mutex_lock(&cfg80211_drv_mutex);
100 list_for_each_entry(dev, &cfg80211_drv_list, list) {
101 if (++idx < start)
102 continue;
103 if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
104 cb->nlh->nlmsg_seq, NLM_F_MULTI,
105 dev) < 0)
106 break;
107 }
108 mutex_unlock(&cfg80211_drv_mutex);
109
110 cb->args[0] = idx;
111
112 return skb->len;
113}
114
115static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
116{
117 struct sk_buff *msg;
118 struct cfg80211_registered_device *dev;
119
120 dev = cfg80211_get_dev_from_info(info);
121 if (IS_ERR(dev))
122 return PTR_ERR(dev);
123
124 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
125 if (!msg)
126 goto out_err;
127
128 if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0)
129 goto out_free;
130
131 cfg80211_put_dev(dev);
132
133 return genlmsg_unicast(msg, info->snd_pid);
134
135 out_free:
136 nlmsg_free(msg);
137 out_err:
138 cfg80211_put_dev(dev);
139 return -ENOBUFS;
140}
141
142static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
143{
144 struct cfg80211_registered_device *rdev;
145 int result;
146
147 if (!info->attrs[NL80211_ATTR_WIPHY_NAME])
148 return -EINVAL;
149
150 rdev = cfg80211_get_dev_from_info(info);
151 if (IS_ERR(rdev))
152 return PTR_ERR(rdev);
153
154 result = cfg80211_dev_rename(rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
155
156 cfg80211_put_dev(rdev);
157 return result;
158}
159
160
161static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
162 struct net_device *dev)
163{
164 void *hdr;
165
166 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
167 if (!hdr)
168 return -1;
169
170 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
171 NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
172 /* TODO: interface type */
173 return genlmsg_end(msg, hdr);
174
175 nla_put_failure:
176 return genlmsg_cancel(msg, hdr);
177}
178
179static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
180{
181 int wp_idx = 0;
182 int if_idx = 0;
183 int wp_start = cb->args[0];
184 int if_start = cb->args[1];
185 struct cfg80211_registered_device *dev;
186 struct wireless_dev *wdev;
187
188 mutex_lock(&cfg80211_drv_mutex);
189 list_for_each_entry(dev, &cfg80211_drv_list, list) {
190 if (++wp_idx < wp_start)
191 continue;
192 if_idx = 0;
193
194 mutex_lock(&dev->devlist_mtx);
195 list_for_each_entry(wdev, &dev->netdev_list, list) {
196 if (++if_idx < if_start)
197 continue;
198 if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
199 cb->nlh->nlmsg_seq, NLM_F_MULTI,
200 wdev->netdev) < 0)
201 break;
202 }
203 mutex_unlock(&dev->devlist_mtx);
204 }
205 mutex_unlock(&cfg80211_drv_mutex);
206
207 cb->args[0] = wp_idx;
208 cb->args[1] = if_idx;
209
210 return skb->len;
211}
212
213static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
214{
215 struct sk_buff *msg;
216 struct cfg80211_registered_device *dev;
217 struct net_device *netdev;
218 int err;
219
220 err = get_drv_dev_by_info_ifindex(info, &dev, &netdev);
221 if (err)
222 return err;
223
224 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
225 if (!msg)
226 goto out_err;
227
228 if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0, netdev) < 0)
229 goto out_free;
230
231 dev_put(netdev);
232 cfg80211_put_dev(dev);
233
234 return genlmsg_unicast(msg, info->snd_pid);
235
236 out_free:
237 nlmsg_free(msg);
238 out_err:
239 dev_put(netdev);
240 cfg80211_put_dev(dev);
241 return -ENOBUFS;
242}
243
244static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
245{
246 struct cfg80211_registered_device *drv;
247 int err, ifindex;
248 enum nl80211_iftype type;
249 struct net_device *dev;
250
251 if (info->attrs[NL80211_ATTR_IFTYPE]) {
252 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
253 if (type > NL80211_IFTYPE_MAX)
254 return -EINVAL;
255 } else
256 return -EINVAL;
257
258 err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
259 if (err)
260 return err;
261 ifindex = dev->ifindex;
262 dev_put(dev);
263
264 if (!drv->ops->change_virtual_intf) {
265 err = -EOPNOTSUPP;
266 goto unlock;
267 }
268
269 rtnl_lock();
270 err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex, type);
271 rtnl_unlock();
272
273 unlock:
274 cfg80211_put_dev(drv);
275 return err;
276}
277
278static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
279{
280 struct cfg80211_registered_device *drv;
281 int err;
282 enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
283
284 if (!info->attrs[NL80211_ATTR_IFNAME])
285 return -EINVAL;
286
287 if (info->attrs[NL80211_ATTR_IFTYPE]) {
288 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
289 if (type > NL80211_IFTYPE_MAX)
290 return -EINVAL;
291 }
292
293 drv = cfg80211_get_dev_from_info(info);
294 if (IS_ERR(drv))
295 return PTR_ERR(drv);
296
297 if (!drv->ops->add_virtual_intf) {
298 err = -EOPNOTSUPP;
299 goto unlock;
300 }
301
302 rtnl_lock();
303 err = drv->ops->add_virtual_intf(&drv->wiphy,
304 nla_data(info->attrs[NL80211_ATTR_IFNAME]), type);
305 rtnl_unlock();
306
307 unlock:
308 cfg80211_put_dev(drv);
309 return err;
310}
311
312static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
313{
314 struct cfg80211_registered_device *drv;
315 int ifindex, err;
316 struct net_device *dev;
317
318 err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
319 if (err)
320 return err;
321 ifindex = dev->ifindex;
322 dev_put(dev);
323
324 if (!drv->ops->del_virtual_intf) {
325 err = -EOPNOTSUPP;
326 goto out;
327 }
328
329 rtnl_lock();
330 err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex);
331 rtnl_unlock();
332
333 out:
334 cfg80211_put_dev(drv);
335 return err;
336}
337
338static struct genl_ops nl80211_ops[] = {
339 {
340 .cmd = NL80211_CMD_GET_WIPHY,
341 .doit = nl80211_get_wiphy,
342 .dumpit = nl80211_dump_wiphy,
343 .policy = nl80211_policy,
344 /* can be retrieved by unprivileged users */
345 },
346 {
347 .cmd = NL80211_CMD_SET_WIPHY,
348 .doit = nl80211_set_wiphy,
349 .policy = nl80211_policy,
350 .flags = GENL_ADMIN_PERM,
351 },
352 {
353 .cmd = NL80211_CMD_GET_INTERFACE,
354 .doit = nl80211_get_interface,
355 .dumpit = nl80211_dump_interface,
356 .policy = nl80211_policy,
357 /* can be retrieved by unprivileged users */
358 },
359 {
360 .cmd = NL80211_CMD_SET_INTERFACE,
361 .doit = nl80211_set_interface,
362 .policy = nl80211_policy,
363 .flags = GENL_ADMIN_PERM,
364 },
365 {
366 .cmd = NL80211_CMD_NEW_INTERFACE,
367 .doit = nl80211_new_interface,
368 .policy = nl80211_policy,
369 .flags = GENL_ADMIN_PERM,
370 },
371 {
372 .cmd = NL80211_CMD_DEL_INTERFACE,
373 .doit = nl80211_del_interface,
374 .policy = nl80211_policy,
375 .flags = GENL_ADMIN_PERM,
376 },
377};
378
379/* multicast groups */
380static struct genl_multicast_group nl80211_config_mcgrp = {
381 .name = "config",
382};
383
384/* notification functions */
385
386void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
387{
388 struct sk_buff *msg;
389
390 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
391 if (!msg)
392 return;
393
394 if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
395 nlmsg_free(msg);
396 return;
397 }
398
399 genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL);
400}
401
402/* initialisation/exit functions */
403
404int nl80211_init(void)
405{
406 int err, i;
407
408 err = genl_register_family(&nl80211_fam);
409 if (err)
410 return err;
411
412 for (i = 0; i < ARRAY_SIZE(nl80211_ops); i++) {
413 err = genl_register_ops(&nl80211_fam, &nl80211_ops[i]);
414 if (err)
415 goto err_out;
416 }
417
418 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
419 if (err)
420 goto err_out;
421
422 return 0;
423 err_out:
424 genl_unregister_family(&nl80211_fam);
425 return err;
426}
427
428void nl80211_exit(void)
429{
430 genl_unregister_family(&nl80211_fam);
431}