sysctl: simplify the pty sysctl logic
[linux-2.6/kmemtrace.git] / net / wireless / nl80211.c
blob48b0d453e4e161cb243130f295cfd0573bd9874d
1 /*
2 * This is the new netlink-based wireless configuration interface.
4 * Copyright 2006, 2007 Johannes Berg <johannes@sipsolutions.net>
5 */
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"
22 /* the netlink family */
23 static 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,
31 /* internal helper: get drv and dev */
32 static int get_drv_dev_by_info_ifindex(struct genl_info *info,
33 struct cfg80211_registered_device **drv,
34 struct net_device **dev)
36 int ifindex;
38 if (!info->attrs[NL80211_ATTR_IFINDEX])
39 return -EINVAL;
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;
46 *drv = cfg80211_get_dev_from_ifindex(ifindex);
47 if (IS_ERR(*drv)) {
48 dev_put(*dev);
49 return PTR_ERR(*drv);
52 return 0;
55 /* policy for the attributes */
56 static 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 },
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 },
66 /* message building helper */
67 static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
68 int flags, u8 cmd)
70 /* since there is no private header just add the generic one */
71 return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
74 /* netlink command implementations */
76 static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
77 struct cfg80211_registered_device *dev)
79 void *hdr;
81 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
82 if (!hdr)
83 return -1;
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);
89 nla_put_failure:
90 return genlmsg_cancel(msg, hdr);
93 static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
95 int idx = 0;
96 int start = cb->args[0];
97 struct cfg80211_registered_device *dev;
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;
108 mutex_unlock(&cfg80211_drv_mutex);
110 cb->args[0] = idx;
112 return skb->len;
115 static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
117 struct sk_buff *msg;
118 struct cfg80211_registered_device *dev;
120 dev = cfg80211_get_dev_from_info(info);
121 if (IS_ERR(dev))
122 return PTR_ERR(dev);
124 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
125 if (!msg)
126 goto out_err;
128 if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0)
129 goto out_free;
131 cfg80211_put_dev(dev);
133 return genlmsg_unicast(msg, info->snd_pid);
135 out_free:
136 nlmsg_free(msg);
137 out_err:
138 cfg80211_put_dev(dev);
139 return -ENOBUFS;
142 static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
144 struct cfg80211_registered_device *rdev;
145 int result;
147 if (!info->attrs[NL80211_ATTR_WIPHY_NAME])
148 return -EINVAL;
150 rdev = cfg80211_get_dev_from_info(info);
151 if (IS_ERR(rdev))
152 return PTR_ERR(rdev);
154 result = cfg80211_dev_rename(rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
156 cfg80211_put_dev(rdev);
157 return result;
161 static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
162 struct net_device *dev)
164 void *hdr;
166 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
167 if (!hdr)
168 return -1;
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);
175 nla_put_failure:
176 return genlmsg_cancel(msg, hdr);
179 static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
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;
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;
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;
203 mutex_unlock(&dev->devlist_mtx);
205 mutex_unlock(&cfg80211_drv_mutex);
207 cb->args[0] = wp_idx;
208 cb->args[1] = if_idx;
210 return skb->len;
213 static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
215 struct sk_buff *msg;
216 struct cfg80211_registered_device *dev;
217 struct net_device *netdev;
218 int err;
220 err = get_drv_dev_by_info_ifindex(info, &dev, &netdev);
221 if (err)
222 return err;
224 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
225 if (!msg)
226 goto out_err;
228 if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0, netdev) < 0)
229 goto out_free;
231 dev_put(netdev);
232 cfg80211_put_dev(dev);
234 return genlmsg_unicast(msg, info->snd_pid);
236 out_free:
237 nlmsg_free(msg);
238 out_err:
239 dev_put(netdev);
240 cfg80211_put_dev(dev);
241 return -ENOBUFS;
244 static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
246 struct cfg80211_registered_device *drv;
247 int err, ifindex;
248 enum nl80211_iftype type;
249 struct net_device *dev;
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;
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);
264 if (!drv->ops->change_virtual_intf) {
265 err = -EOPNOTSUPP;
266 goto unlock;
269 rtnl_lock();
270 err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex, type);
271 rtnl_unlock();
273 unlock:
274 cfg80211_put_dev(drv);
275 return err;
278 static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
280 struct cfg80211_registered_device *drv;
281 int err;
282 enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
284 if (!info->attrs[NL80211_ATTR_IFNAME])
285 return -EINVAL;
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;
293 drv = cfg80211_get_dev_from_info(info);
294 if (IS_ERR(drv))
295 return PTR_ERR(drv);
297 if (!drv->ops->add_virtual_intf) {
298 err = -EOPNOTSUPP;
299 goto unlock;
302 rtnl_lock();
303 err = drv->ops->add_virtual_intf(&drv->wiphy,
304 nla_data(info->attrs[NL80211_ATTR_IFNAME]), type);
305 rtnl_unlock();
307 unlock:
308 cfg80211_put_dev(drv);
309 return err;
312 static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
314 struct cfg80211_registered_device *drv;
315 int ifindex, err;
316 struct net_device *dev;
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);
324 if (!drv->ops->del_virtual_intf) {
325 err = -EOPNOTSUPP;
326 goto out;
329 rtnl_lock();
330 err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex);
331 rtnl_unlock();
333 out:
334 cfg80211_put_dev(drv);
335 return err;
338 static struct genl_ops nl80211_ops[] = {
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 */
347 .cmd = NL80211_CMD_SET_WIPHY,
348 .doit = nl80211_set_wiphy,
349 .policy = nl80211_policy,
350 .flags = GENL_ADMIN_PERM,
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 */
360 .cmd = NL80211_CMD_SET_INTERFACE,
361 .doit = nl80211_set_interface,
362 .policy = nl80211_policy,
363 .flags = GENL_ADMIN_PERM,
366 .cmd = NL80211_CMD_NEW_INTERFACE,
367 .doit = nl80211_new_interface,
368 .policy = nl80211_policy,
369 .flags = GENL_ADMIN_PERM,
372 .cmd = NL80211_CMD_DEL_INTERFACE,
373 .doit = nl80211_del_interface,
374 .policy = nl80211_policy,
375 .flags = GENL_ADMIN_PERM,
379 /* multicast groups */
380 static struct genl_multicast_group nl80211_config_mcgrp = {
381 .name = "config",
384 /* notification functions */
386 void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
388 struct sk_buff *msg;
390 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
391 if (!msg)
392 return;
394 if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
395 nlmsg_free(msg);
396 return;
399 genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL);
402 /* initialisation/exit functions */
404 int nl80211_init(void)
406 int err, i;
408 err = genl_register_family(&nl80211_fam);
409 if (err)
410 return err;
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;
418 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
419 if (err)
420 goto err_out;
422 return 0;
423 err_out:
424 genl_unregister_family(&nl80211_fam);
425 return err;
428 void nl80211_exit(void)
430 genl_unregister_family(&nl80211_fam);