xref: /openbmc/linux/net/wireless/nl80211.c (revision 96de0e252cedffad61b3cb5e05662c591898e69a)
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 */
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,
29 };
30 
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)
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 */
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 },
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 */
67 static 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 
76 static 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 
93 static 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 
115 static 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 
142 static 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 
161 static 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 
179 static 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 
213 static 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 
244 static 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 
278 static 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 
312 static 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 
338 static 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 */
380 static struct genl_multicast_group nl80211_config_mcgrp = {
381 	.name = "config",
382 };
383 
384 /* notification functions */
385 
386 void 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 
404 int 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 
428 void nl80211_exit(void)
429 {
430 	genl_unregister_family(&nl80211_fam);
431 }
432