xref: /openbmc/linux/net/wireless/nl80211.c (revision 41ade00f)
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 	[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 },
72 };
73 
74 /* message building helper */
75 static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
76 				   int flags, u8 cmd)
77 {
78 	/* since there is no private header just add the generic one */
79 	return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
80 }
81 
82 /* netlink command implementations */
83 
84 static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
85 			      struct cfg80211_registered_device *dev)
86 {
87 	void *hdr;
88 
89 	hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
90 	if (!hdr)
91 		return -1;
92 
93 	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->idx);
94 	NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
95 	return genlmsg_end(msg, hdr);
96 
97  nla_put_failure:
98 	return genlmsg_cancel(msg, hdr);
99 }
100 
101 static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
102 {
103 	int idx = 0;
104 	int start = cb->args[0];
105 	struct cfg80211_registered_device *dev;
106 
107 	mutex_lock(&cfg80211_drv_mutex);
108 	list_for_each_entry(dev, &cfg80211_drv_list, list) {
109 		if (++idx < start)
110 			continue;
111 		if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
112 				       cb->nlh->nlmsg_seq, NLM_F_MULTI,
113 				       dev) < 0)
114 			break;
115 	}
116 	mutex_unlock(&cfg80211_drv_mutex);
117 
118 	cb->args[0] = idx;
119 
120 	return skb->len;
121 }
122 
123 static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
124 {
125 	struct sk_buff *msg;
126 	struct cfg80211_registered_device *dev;
127 
128 	dev = cfg80211_get_dev_from_info(info);
129 	if (IS_ERR(dev))
130 		return PTR_ERR(dev);
131 
132 	msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
133 	if (!msg)
134 		goto out_err;
135 
136 	if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0)
137 		goto out_free;
138 
139 	cfg80211_put_dev(dev);
140 
141 	return genlmsg_unicast(msg, info->snd_pid);
142 
143  out_free:
144 	nlmsg_free(msg);
145  out_err:
146 	cfg80211_put_dev(dev);
147 	return -ENOBUFS;
148 }
149 
150 static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
151 {
152 	struct cfg80211_registered_device *rdev;
153 	int result;
154 
155 	if (!info->attrs[NL80211_ATTR_WIPHY_NAME])
156 		return -EINVAL;
157 
158 	rdev = cfg80211_get_dev_from_info(info);
159 	if (IS_ERR(rdev))
160 		return PTR_ERR(rdev);
161 
162 	result = cfg80211_dev_rename(rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
163 
164 	cfg80211_put_dev(rdev);
165 	return result;
166 }
167 
168 
169 static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
170 			      struct net_device *dev)
171 {
172 	void *hdr;
173 
174 	hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
175 	if (!hdr)
176 		return -1;
177 
178 	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
179 	NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
180 	/* TODO: interface type */
181 	return genlmsg_end(msg, hdr);
182 
183  nla_put_failure:
184 	return genlmsg_cancel(msg, hdr);
185 }
186 
187 static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
188 {
189 	int wp_idx = 0;
190 	int if_idx = 0;
191 	int wp_start = cb->args[0];
192 	int if_start = cb->args[1];
193 	struct cfg80211_registered_device *dev;
194 	struct wireless_dev *wdev;
195 
196 	mutex_lock(&cfg80211_drv_mutex);
197 	list_for_each_entry(dev, &cfg80211_drv_list, list) {
198 		if (++wp_idx < wp_start)
199 			continue;
200 		if_idx = 0;
201 
202 		mutex_lock(&dev->devlist_mtx);
203 		list_for_each_entry(wdev, &dev->netdev_list, list) {
204 			if (++if_idx < if_start)
205 				continue;
206 			if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
207 					       cb->nlh->nlmsg_seq, NLM_F_MULTI,
208 					       wdev->netdev) < 0)
209 				break;
210 		}
211 		mutex_unlock(&dev->devlist_mtx);
212 	}
213 	mutex_unlock(&cfg80211_drv_mutex);
214 
215 	cb->args[0] = wp_idx;
216 	cb->args[1] = if_idx;
217 
218 	return skb->len;
219 }
220 
221 static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
222 {
223 	struct sk_buff *msg;
224 	struct cfg80211_registered_device *dev;
225 	struct net_device *netdev;
226 	int err;
227 
228 	err = get_drv_dev_by_info_ifindex(info, &dev, &netdev);
229 	if (err)
230 		return err;
231 
232 	msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
233 	if (!msg)
234 		goto out_err;
235 
236 	if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0, netdev) < 0)
237 		goto out_free;
238 
239 	dev_put(netdev);
240 	cfg80211_put_dev(dev);
241 
242 	return genlmsg_unicast(msg, info->snd_pid);
243 
244  out_free:
245 	nlmsg_free(msg);
246  out_err:
247 	dev_put(netdev);
248 	cfg80211_put_dev(dev);
249 	return -ENOBUFS;
250 }
251 
252 static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
253 {
254 	struct cfg80211_registered_device *drv;
255 	int err, ifindex;
256 	enum nl80211_iftype type;
257 	struct net_device *dev;
258 
259 	if (info->attrs[NL80211_ATTR_IFTYPE]) {
260 		type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
261 		if (type > NL80211_IFTYPE_MAX)
262 			return -EINVAL;
263 	} else
264 		return -EINVAL;
265 
266 	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
267 	if (err)
268 		return err;
269 	ifindex = dev->ifindex;
270 	dev_put(dev);
271 
272 	if (!drv->ops->change_virtual_intf) {
273 		err = -EOPNOTSUPP;
274 		goto unlock;
275 	}
276 
277 	rtnl_lock();
278 	err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex, type);
279 	rtnl_unlock();
280 
281  unlock:
282 	cfg80211_put_dev(drv);
283 	return err;
284 }
285 
286 static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
287 {
288 	struct cfg80211_registered_device *drv;
289 	int err;
290 	enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
291 
292 	if (!info->attrs[NL80211_ATTR_IFNAME])
293 		return -EINVAL;
294 
295 	if (info->attrs[NL80211_ATTR_IFTYPE]) {
296 		type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
297 		if (type > NL80211_IFTYPE_MAX)
298 			return -EINVAL;
299 	}
300 
301 	drv = cfg80211_get_dev_from_info(info);
302 	if (IS_ERR(drv))
303 		return PTR_ERR(drv);
304 
305 	if (!drv->ops->add_virtual_intf) {
306 		err = -EOPNOTSUPP;
307 		goto unlock;
308 	}
309 
310 	rtnl_lock();
311 	err = drv->ops->add_virtual_intf(&drv->wiphy,
312 		nla_data(info->attrs[NL80211_ATTR_IFNAME]), type);
313 	rtnl_unlock();
314 
315  unlock:
316 	cfg80211_put_dev(drv);
317 	return err;
318 }
319 
320 static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
321 {
322 	struct cfg80211_registered_device *drv;
323 	int ifindex, err;
324 	struct net_device *dev;
325 
326 	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
327 	if (err)
328 		return err;
329 	ifindex = dev->ifindex;
330 	dev_put(dev);
331 
332 	if (!drv->ops->del_virtual_intf) {
333 		err = -EOPNOTSUPP;
334 		goto out;
335 	}
336 
337 	rtnl_lock();
338 	err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex);
339 	rtnl_unlock();
340 
341  out:
342 	cfg80211_put_dev(drv);
343 	return err;
344 }
345 
346 struct get_key_cookie {
347 	struct sk_buff *msg;
348 	int error;
349 };
350 
351 static void get_key_callback(void *c, struct key_params *params)
352 {
353 	struct get_key_cookie *cookie = c;
354 
355 	if (params->key)
356 		NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA,
357 			params->key_len, params->key);
358 
359 	if (params->seq)
360 		NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ,
361 			params->seq_len, params->seq);
362 
363 	if (params->cipher)
364 		NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
365 			    params->cipher);
366 
367 	return;
368  nla_put_failure:
369 	cookie->error = 1;
370 }
371 
372 static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
373 {
374 	struct cfg80211_registered_device *drv;
375 	int err;
376 	struct net_device *dev;
377 	u8 key_idx = 0;
378 	u8 *mac_addr = NULL;
379 	struct get_key_cookie cookie = {
380 		.error = 0,
381 	};
382 	void *hdr;
383 	struct sk_buff *msg;
384 
385 	if (info->attrs[NL80211_ATTR_KEY_IDX])
386 		key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
387 
388 	if (key_idx > 3)
389 		return -EINVAL;
390 
391 	if (info->attrs[NL80211_ATTR_MAC])
392 		mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
393 
394 	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
395 	if (err)
396 		return err;
397 
398 	if (!drv->ops->get_key) {
399 		err = -EOPNOTSUPP;
400 		goto out;
401 	}
402 
403 	msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
404 	if (!msg) {
405 		err = -ENOMEM;
406 		goto out;
407 	}
408 
409 	hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
410 			     NL80211_CMD_NEW_KEY);
411 
412 	if (IS_ERR(hdr)) {
413 		err = PTR_ERR(hdr);
414 		goto out;
415 	}
416 
417 	cookie.msg = msg;
418 
419 	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
420 	NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
421 	if (mac_addr)
422 		NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
423 
424 	rtnl_lock();
425 	err = drv->ops->get_key(&drv->wiphy, dev, key_idx, mac_addr,
426 				&cookie, get_key_callback);
427 	rtnl_unlock();
428 
429 	if (err)
430 		goto out;
431 
432 	if (cookie.error)
433 		goto nla_put_failure;
434 
435 	genlmsg_end(msg, hdr);
436 	err = genlmsg_unicast(msg, info->snd_pid);
437 	goto out;
438 
439  nla_put_failure:
440 	err = -ENOBUFS;
441 	nlmsg_free(msg);
442  out:
443 	cfg80211_put_dev(drv);
444 	dev_put(dev);
445 	return err;
446 }
447 
448 static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
449 {
450 	struct cfg80211_registered_device *drv;
451 	int err;
452 	struct net_device *dev;
453 	u8 key_idx;
454 
455 	if (!info->attrs[NL80211_ATTR_KEY_IDX])
456 		return -EINVAL;
457 
458 	key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
459 
460 	if (key_idx > 3)
461 		return -EINVAL;
462 
463 	/* currently only support setting default key */
464 	if (!info->attrs[NL80211_ATTR_KEY_DEFAULT])
465 		return -EINVAL;
466 
467 	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
468 	if (err)
469 		return err;
470 
471 	if (!drv->ops->set_default_key) {
472 		err = -EOPNOTSUPP;
473 		goto out;
474 	}
475 
476 	rtnl_lock();
477 	err = drv->ops->set_default_key(&drv->wiphy, dev, key_idx);
478 	rtnl_unlock();
479 
480  out:
481 	cfg80211_put_dev(drv);
482 	dev_put(dev);
483 	return err;
484 }
485 
486 static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
487 {
488 	struct cfg80211_registered_device *drv;
489 	int err;
490 	struct net_device *dev;
491 	struct key_params params;
492 	u8 key_idx = 0;
493 	u8 *mac_addr = NULL;
494 
495 	memset(&params, 0, sizeof(params));
496 
497 	if (!info->attrs[NL80211_ATTR_KEY_CIPHER])
498 		return -EINVAL;
499 
500 	if (info->attrs[NL80211_ATTR_KEY_DATA]) {
501 		params.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
502 		params.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
503 	}
504 
505 	if (info->attrs[NL80211_ATTR_KEY_IDX])
506 		key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
507 
508 	params.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
509 
510 	if (info->attrs[NL80211_ATTR_MAC])
511 		mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
512 
513 	if (key_idx > 3)
514 		return -EINVAL;
515 
516 	/*
517 	 * Disallow pairwise keys with non-zero index unless it's WEP
518 	 * (because current deployments use pairwise WEP keys with
519 	 * non-zero indizes but 802.11i clearly specifies to use zero)
520 	 */
521 	if (mac_addr && key_idx &&
522 	    params.cipher != WLAN_CIPHER_SUITE_WEP40 &&
523 	    params.cipher != WLAN_CIPHER_SUITE_WEP104)
524 		return -EINVAL;
525 
526 	/* TODO: add definitions for the lengths to linux/ieee80211.h */
527 	switch (params.cipher) {
528 	case WLAN_CIPHER_SUITE_WEP40:
529 		if (params.key_len != 5)
530 			return -EINVAL;
531 		break;
532 	case WLAN_CIPHER_SUITE_TKIP:
533 		if (params.key_len != 32)
534 			return -EINVAL;
535 		break;
536 	case WLAN_CIPHER_SUITE_CCMP:
537 		if (params.key_len != 16)
538 			return -EINVAL;
539 		break;
540 	case WLAN_CIPHER_SUITE_WEP104:
541 		if (params.key_len != 13)
542 			return -EINVAL;
543 		break;
544 	default:
545 		return -EINVAL;
546 	}
547 
548 	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
549 	if (err)
550 		return err;
551 
552 	if (!drv->ops->add_key) {
553 		err = -EOPNOTSUPP;
554 		goto out;
555 	}
556 
557 	rtnl_lock();
558 	err = drv->ops->add_key(&drv->wiphy, dev, key_idx, mac_addr, &params);
559 	rtnl_unlock();
560 
561  out:
562 	cfg80211_put_dev(drv);
563 	dev_put(dev);
564 	return err;
565 }
566 
567 static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
568 {
569 	struct cfg80211_registered_device *drv;
570 	int err;
571 	struct net_device *dev;
572 	u8 key_idx = 0;
573 	u8 *mac_addr = NULL;
574 
575 	if (info->attrs[NL80211_ATTR_KEY_IDX])
576 		key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
577 
578 	if (key_idx > 3)
579 		return -EINVAL;
580 
581 	if (info->attrs[NL80211_ATTR_MAC])
582 		mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
583 
584 	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
585 	if (err)
586 		return err;
587 
588 	if (!drv->ops->del_key) {
589 		err = -EOPNOTSUPP;
590 		goto out;
591 	}
592 
593 	rtnl_lock();
594 	err = drv->ops->del_key(&drv->wiphy, dev, key_idx, mac_addr);
595 	rtnl_unlock();
596 
597  out:
598 	cfg80211_put_dev(drv);
599 	dev_put(dev);
600 	return err;
601 }
602 
603 static struct genl_ops nl80211_ops[] = {
604 	{
605 		.cmd = NL80211_CMD_GET_WIPHY,
606 		.doit = nl80211_get_wiphy,
607 		.dumpit = nl80211_dump_wiphy,
608 		.policy = nl80211_policy,
609 		/* can be retrieved by unprivileged users */
610 	},
611 	{
612 		.cmd = NL80211_CMD_SET_WIPHY,
613 		.doit = nl80211_set_wiphy,
614 		.policy = nl80211_policy,
615 		.flags = GENL_ADMIN_PERM,
616 	},
617 	{
618 		.cmd = NL80211_CMD_GET_INTERFACE,
619 		.doit = nl80211_get_interface,
620 		.dumpit = nl80211_dump_interface,
621 		.policy = nl80211_policy,
622 		/* can be retrieved by unprivileged users */
623 	},
624 	{
625 		.cmd = NL80211_CMD_SET_INTERFACE,
626 		.doit = nl80211_set_interface,
627 		.policy = nl80211_policy,
628 		.flags = GENL_ADMIN_PERM,
629 	},
630 	{
631 		.cmd = NL80211_CMD_NEW_INTERFACE,
632 		.doit = nl80211_new_interface,
633 		.policy = nl80211_policy,
634 		.flags = GENL_ADMIN_PERM,
635 	},
636 	{
637 		.cmd = NL80211_CMD_DEL_INTERFACE,
638 		.doit = nl80211_del_interface,
639 		.policy = nl80211_policy,
640 		.flags = GENL_ADMIN_PERM,
641 	},
642 	{
643 		.cmd = NL80211_CMD_GET_KEY,
644 		.doit = nl80211_get_key,
645 		.policy = nl80211_policy,
646 		.flags = GENL_ADMIN_PERM,
647 	},
648 	{
649 		.cmd = NL80211_CMD_SET_KEY,
650 		.doit = nl80211_set_key,
651 		.policy = nl80211_policy,
652 		.flags = GENL_ADMIN_PERM,
653 	},
654 	{
655 		.cmd = NL80211_CMD_NEW_KEY,
656 		.doit = nl80211_new_key,
657 		.policy = nl80211_policy,
658 		.flags = GENL_ADMIN_PERM,
659 	},
660 	{
661 		.cmd = NL80211_CMD_DEL_KEY,
662 		.doit = nl80211_del_key,
663 		.policy = nl80211_policy,
664 		.flags = GENL_ADMIN_PERM,
665 	},
666 };
667 
668 /* multicast groups */
669 static struct genl_multicast_group nl80211_config_mcgrp = {
670 	.name = "config",
671 };
672 
673 /* notification functions */
674 
675 void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
676 {
677 	struct sk_buff *msg;
678 
679 	msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
680 	if (!msg)
681 		return;
682 
683 	if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
684 		nlmsg_free(msg);
685 		return;
686 	}
687 
688 	genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL);
689 }
690 
691 /* initialisation/exit functions */
692 
693 int nl80211_init(void)
694 {
695 	int err, i;
696 
697 	err = genl_register_family(&nl80211_fam);
698 	if (err)
699 		return err;
700 
701 	for (i = 0; i < ARRAY_SIZE(nl80211_ops); i++) {
702 		err = genl_register_ops(&nl80211_fam, &nl80211_ops[i]);
703 		if (err)
704 			goto err_out;
705 	}
706 
707 	err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
708 	if (err)
709 		goto err_out;
710 
711 	return 0;
712  err_out:
713 	genl_unregister_family(&nl80211_fam);
714 	return err;
715 }
716 
717 void nl80211_exit(void)
718 {
719 	genl_unregister_family(&nl80211_fam);
720 }
721