aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--include/linux/nl80211.h97
-rw-r--r--include/net/cfg80211.h11
-rw-r--r--include/net/iw_handler.h8
-rw-r--r--net/mac80211/ieee80211_cfg.c2
-rw-r--r--net/wireless/Kconfig17
-rw-r--r--net/wireless/Makefile1
-rw-r--r--net/wireless/core.c148
-rw-r--r--net/wireless/core.h32
-rw-r--r--net/wireless/nl80211.c431
-rw-r--r--net/wireless/nl80211.h24
10 files changed, 762 insertions, 9 deletions
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index 9a30ba2ca75e..538ee1dd3d0a 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -7,7 +7,97 @@
7 */ 7 */
8 8
9/** 9/**
10 * enum nl80211_commands - supported nl80211 commands
11 *
12 * @NL80211_CMD_UNSPEC: unspecified command to catch errors
13 *
14 * @NL80211_CMD_GET_WIPHY: request information about a wiphy or dump request
15 * to get a list of all present wiphys.
16 * @NL80211_CMD_SET_WIPHY: set wiphy name, needs %NL80211_ATTR_WIPHY and
17 * %NL80211_ATTR_WIPHY_NAME.
18 * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request
19 * or rename notification. Has attributes %NL80211_ATTR_WIPHY and
20 * %NL80211_ATTR_WIPHY_NAME.
21 * @NL80211_CMD_DEL_WIPHY: Wiphy deleted. Has attributes
22 * %NL80211_ATTR_WIPHY and %NL80211_ATTR_WIPHY_NAME.
23 *
24 * @NL80211_CMD_GET_INTERFACE: Request an interface's configuration;
25 * either a dump request on a %NL80211_ATTR_WIPHY or a specific get
26 * on an %NL80211_ATTR_IFINDEX is supported.
27 * @NL80211_CMD_SET_INTERFACE: Set type of a virtual interface, requires
28 * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_IFTYPE.
29 * @NL80211_CMD_NEW_INTERFACE: Newly created virtual interface or response
30 * to %NL80211_CMD_GET_INTERFACE. Has %NL80211_ATTR_IFINDEX,
31 * %NL80211_ATTR_WIPHY and %NL80211_ATTR_IFTYPE attributes. Can also
32 * be sent from userspace to request creation of a new virtual interface,
33 * then requires attributes %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFTYPE and
34 * %NL80211_ATTR_IFNAME.
35 * @NL80211_CMD_DEL_INTERFACE: Virtual interface was deleted, has attributes
36 * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_WIPHY. Can also be sent from
37 * userspace to request deletion of a virtual interface, then requires
38 * attribute %NL80211_ATTR_IFINDEX.
39 *
40 * @NL80211_CMD_MAX: highest used command number
41 * @__NL80211_CMD_AFTER_LAST: internal use
42 */
43enum nl80211_commands {
44/* don't change the order or add anything inbetween, this is ABI! */
45 NL80211_CMD_UNSPEC,
46
47 NL80211_CMD_GET_WIPHY, /* can dump */
48 NL80211_CMD_SET_WIPHY,
49 NL80211_CMD_NEW_WIPHY,
50 NL80211_CMD_DEL_WIPHY,
51
52 NL80211_CMD_GET_INTERFACE, /* can dump */
53 NL80211_CMD_SET_INTERFACE,
54 NL80211_CMD_NEW_INTERFACE,
55 NL80211_CMD_DEL_INTERFACE,
56
57 /* add commands here */
58
59 /* used to define NL80211_CMD_MAX below */
60 __NL80211_CMD_AFTER_LAST,
61 NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1
62};
63
64
65/**
66 * enum nl80211_attrs - nl80211 netlink attributes
67 *
68 * @NL80211_ATTR_UNSPEC: unspecified attribute to catch errors
69 *
70 * @NL80211_ATTR_WIPHY: index of wiphy to operate on, cf.
71 * /sys/class/ieee80211/<phyname>/index
72 * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming)
73 *
74 * @NL80211_ATTR_IFINDEX: network interface index of the device to operate on
75 * @NL80211_ATTR_IFNAME: network interface name
76 * @NL80211_ATTR_IFTYPE: type of virtual interface, see &enum nl80211_iftype
77 *
78 * @NL80211_ATTR_MAX: highest attribute number currently defined
79 * @__NL80211_ATTR_AFTER_LAST: internal use
80 */
81enum nl80211_attrs {
82/* don't change the order or add anything inbetween, this is ABI! */
83 NL80211_ATTR_UNSPEC,
84
85 NL80211_ATTR_WIPHY,
86 NL80211_ATTR_WIPHY_NAME,
87
88 NL80211_ATTR_IFINDEX,
89 NL80211_ATTR_IFNAME,
90 NL80211_ATTR_IFTYPE,
91
92 /* add attributes here, update the policy in nl80211.c */
93
94 __NL80211_ATTR_AFTER_LAST,
95 NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
96};
97
98/**
10 * enum nl80211_iftype - (virtual) interface types 99 * enum nl80211_iftype - (virtual) interface types
100 *
11 * @NL80211_IFTYPE_UNSPECIFIED: unspecified type, driver decides 101 * @NL80211_IFTYPE_UNSPECIFIED: unspecified type, driver decides
12 * @NL80211_IFTYPE_ADHOC: independent BSS member 102 * @NL80211_IFTYPE_ADHOC: independent BSS member
13 * @NL80211_IFTYPE_STATION: managed BSS member 103 * @NL80211_IFTYPE_STATION: managed BSS member
@@ -15,9 +105,10 @@
15 * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points 105 * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points
16 * @NL80211_IFTYPE_WDS: wireless distribution interface 106 * @NL80211_IFTYPE_WDS: wireless distribution interface
17 * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames 107 * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames
108 * @NL80211_IFTYPE_MAX: highest interface type number currently defined
18 * @__NL80211_IFTYPE_AFTER_LAST: internal use 109 * @__NL80211_IFTYPE_AFTER_LAST: internal use
19 * 110 *
20 * These values are used with the NL80211_ATTR_IFTYPE 111 * These values are used with the %NL80211_ATTR_IFTYPE
21 * to set the type of an interface. 112 * to set the type of an interface.
22 * 113 *
23 */ 114 */
@@ -31,8 +122,8 @@ enum nl80211_iftype {
31 NL80211_IFTYPE_MONITOR, 122 NL80211_IFTYPE_MONITOR,
32 123
33 /* keep last */ 124 /* keep last */
34 __NL80211_IFTYPE_AFTER_LAST 125 __NL80211_IFTYPE_AFTER_LAST,
126 NL80211_IFTYPE_MAX = __NL80211_IFTYPE_AFTER_LAST - 1
35}; 127};
36#define NL80211_IFTYPE_MAX (__NL80211_IFTYPE_AFTER_LAST - 1)
37 128
38#endif /* __LINUX_NL80211_H */ 129#endif /* __LINUX_NL80211_H */
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 7edaef6b29d6..d30960e1755c 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -3,15 +3,15 @@
3 3
4#include <linux/netlink.h> 4#include <linux/netlink.h>
5#include <linux/skbuff.h> 5#include <linux/skbuff.h>
6#include <linux/nl80211.h>
6#include <net/genetlink.h> 7#include <net/genetlink.h>
7 8
8/* 9/*
9 * 802.11 configuration in-kernel interface 10 * 802.11 configuration in-kernel interface
10 * 11 *
11 * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> 12 * Copyright 2006, 2007 Johannes Berg <johannes@sipsolutions.net>
12 */ 13 */
13 14
14
15/* Radiotap header iteration 15/* Radiotap header iteration
16 * implemented in net/wireless/radiotap.c 16 * implemented in net/wireless/radiotap.c
17 * docs in Documentation/networking/radiotap-headers.txt 17 * docs in Documentation/networking/radiotap-headers.txt
@@ -68,11 +68,16 @@ struct wiphy;
68 * @add_virtual_intf: create a new virtual interface with the given name 68 * @add_virtual_intf: create a new virtual interface with the given name
69 * 69 *
70 * @del_virtual_intf: remove the virtual interface determined by ifindex. 70 * @del_virtual_intf: remove the virtual interface determined by ifindex.
71 *
72 * @change_virtual_intf: change type of virtual interface
73 *
71 */ 74 */
72struct cfg80211_ops { 75struct cfg80211_ops {
73 int (*add_virtual_intf)(struct wiphy *wiphy, char *name, 76 int (*add_virtual_intf)(struct wiphy *wiphy, char *name,
74 unsigned int type); 77 enum nl80211_iftype type);
75 int (*del_virtual_intf)(struct wiphy *wiphy, int ifindex); 78 int (*del_virtual_intf)(struct wiphy *wiphy, int ifindex);
79 int (*change_virtual_intf)(struct wiphy *wiphy, int ifindex,
80 enum nl80211_iftype type);
76}; 81};
77 82
78#endif /* __NET_CFG80211_H */ 83#endif /* __NET_CFG80211_H */
diff --git a/include/net/iw_handler.h b/include/net/iw_handler.h
index f23d07ca7c59..369d50e08b99 100644
--- a/include/net/iw_handler.h
+++ b/include/net/iw_handler.h
@@ -431,7 +431,13 @@ struct iw_public_data {
431 * Those may be called only within the kernel. 431 * Those may be called only within the kernel.
432 */ 432 */
433 433
434/* functions that may be called by driver modules */ 434/* First : function strictly used inside the kernel */
435
436/* Handle /proc/net/wireless, called in net/code/dev.c */
437extern int dev_get_wireless_info(char * buffer, char **start, off_t offset,
438 int length);
439
440/* Second : functions that may be called by driver modules */
435 441
436/* Send a single event to user space */ 442/* Send a single event to user space */
437extern void wireless_send_event(struct net_device * dev, 443extern void wireless_send_event(struct net_device * dev,
diff --git a/net/mac80211/ieee80211_cfg.c b/net/mac80211/ieee80211_cfg.c
index b1c13bc9c3ca..d6fc55cc8ad4 100644
--- a/net/mac80211/ieee80211_cfg.c
+++ b/net/mac80211/ieee80211_cfg.c
@@ -14,7 +14,7 @@
14#include "ieee80211_cfg.h" 14#include "ieee80211_cfg.h"
15 15
16static int ieee80211_add_iface(struct wiphy *wiphy, char *name, 16static int ieee80211_add_iface(struct wiphy *wiphy, char *name,
17 unsigned int type) 17 enum nl80211_iftype type)
18{ 18{
19 struct ieee80211_local *local = wiphy_priv(wiphy); 19 struct ieee80211_local *local = wiphy_priv(wiphy);
20 int itype; 20 int itype;
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig
index a228d56a91b8..6291f13bba09 100644
--- a/net/wireless/Kconfig
+++ b/net/wireless/Kconfig
@@ -1,6 +1,19 @@
1config CFG80211 1config CFG80211
2 tristate "Improved wireless configuration API" 2 tristate "Improved wireless configuration API"
3 3
4config NL80211
5 bool "nl80211 new netlink interface support"
6 depends CFG80211
7 default y
8 ---help---
9 This option turns on the new netlink interface
10 (nl80211) support in cfg80211.
11
12 If =n, drivers using mac80211 will be configured via
13 wireless extension support provided by that subsystem.
14
15 If unsure, say Y.
16
4config WIRELESS_EXT 17config WIRELESS_EXT
5 bool "Wireless extensions" 18 bool "Wireless extensions"
6 default n 19 default n
@@ -10,7 +23,9 @@ config WIRELESS_EXT
10 23
11 Wireless extensions will be replaced by cfg80211 and 24 Wireless extensions will be replaced by cfg80211 and
12 will be required only by legacy drivers that implement 25 will be required only by legacy drivers that implement
13 wireless extension handlers. 26 wireless extension handlers. This option does not
27 affect the wireless-extension backward compatibility
28 code in cfg80211.
14 29
15 Say N (if you can) unless you know you need wireless 30 Say N (if you can) unless you know you need wireless
16 extensions for external modules. 31 extensions for external modules.
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
index 092116e390b6..65710a42e5a7 100644
--- a/net/wireless/Makefile
+++ b/net/wireless/Makefile
@@ -2,3 +2,4 @@ obj-$(CONFIG_WIRELESS_EXT) += wext.o
2obj-$(CONFIG_CFG80211) += cfg80211.o 2obj-$(CONFIG_CFG80211) += cfg80211.o
3 3
4cfg80211-y += core.o sysfs.o radiotap.o 4cfg80211-y += core.o sysfs.o radiotap.o
5cfg80211-$(CONFIG_NL80211) += nl80211.o
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 9771451eae21..febc33bc9c09 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -16,6 +16,7 @@
16#include <net/genetlink.h> 16#include <net/genetlink.h>
17#include <net/cfg80211.h> 17#include <net/cfg80211.h>
18#include <net/wireless.h> 18#include <net/wireless.h>
19#include "nl80211.h"
19#include "core.h" 20#include "core.h"
20#include "sysfs.h" 21#include "sysfs.h"
21 22
@@ -36,6 +37,146 @@ static int wiphy_counter;
36/* for debugfs */ 37/* for debugfs */
37static struct dentry *ieee80211_debugfs_dir; 38static struct dentry *ieee80211_debugfs_dir;
38 39
40/* requires cfg80211_drv_mutex to be held! */
41static struct cfg80211_registered_device *cfg80211_drv_by_wiphy(int wiphy)
42{
43 struct cfg80211_registered_device *result = NULL, *drv;
44
45 list_for_each_entry(drv, &cfg80211_drv_list, list) {
46 if (drv->idx == wiphy) {
47 result = drv;
48 break;
49 }
50 }
51
52 return result;
53}
54
55/* requires cfg80211_drv_mutex to be held! */
56static struct cfg80211_registered_device *
57__cfg80211_drv_from_info(struct genl_info *info)
58{
59 int ifindex;
60 struct cfg80211_registered_device *bywiphy = NULL, *byifidx = NULL;
61 struct net_device *dev;
62 int err = -EINVAL;
63
64 if (info->attrs[NL80211_ATTR_WIPHY]) {
65 bywiphy = cfg80211_drv_by_wiphy(
66 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY]));
67 err = -ENODEV;
68 }
69
70 if (info->attrs[NL80211_ATTR_IFINDEX]) {
71 ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
72 dev = dev_get_by_index(&init_net, ifindex);
73 if (dev) {
74 if (dev->ieee80211_ptr)
75 byifidx =
76 wiphy_to_dev(dev->ieee80211_ptr->wiphy);
77 dev_put(dev);
78 }
79 err = -ENODEV;
80 }
81
82 if (bywiphy && byifidx) {
83 if (bywiphy != byifidx)
84 return ERR_PTR(-EINVAL);
85 else
86 return bywiphy; /* == byifidx */
87 }
88 if (bywiphy)
89 return bywiphy;
90
91 if (byifidx)
92 return byifidx;
93
94 return ERR_PTR(err);
95}
96
97struct cfg80211_registered_device *
98cfg80211_get_dev_from_info(struct genl_info *info)
99{
100 struct cfg80211_registered_device *drv;
101
102 mutex_lock(&cfg80211_drv_mutex);
103 drv = __cfg80211_drv_from_info(info);
104
105 /* if it is not an error we grab the lock on
106 * it to assure it won't be going away while
107 * we operate on it */
108 if (!IS_ERR(drv))
109 mutex_lock(&drv->mtx);
110
111 mutex_unlock(&cfg80211_drv_mutex);
112
113 return drv;
114}
115
116struct cfg80211_registered_device *
117cfg80211_get_dev_from_ifindex(int ifindex)
118{
119 struct cfg80211_registered_device *drv = ERR_PTR(-ENODEV);
120 struct net_device *dev;
121
122 mutex_lock(&cfg80211_drv_mutex);
123 dev = dev_get_by_index(&init_net, ifindex);
124 if (!dev)
125 goto out;
126 if (dev->ieee80211_ptr) {
127 drv = wiphy_to_dev(dev->ieee80211_ptr->wiphy);
128 mutex_lock(&drv->mtx);
129 } else
130 drv = ERR_PTR(-ENODEV);
131 dev_put(dev);
132 out:
133 mutex_unlock(&cfg80211_drv_mutex);
134 return drv;
135}
136
137void cfg80211_put_dev(struct cfg80211_registered_device *drv)
138{
139 BUG_ON(IS_ERR(drv));
140 mutex_unlock(&drv->mtx);
141}
142
143int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
144 char *newname)
145{
146 int idx, taken = -1, result, digits;
147
148 /* prohibit calling the thing phy%d when %d is not its number */
149 sscanf(newname, PHY_NAME "%d%n", &idx, &taken);
150 if (taken == strlen(newname) && idx != rdev->idx) {
151 /* count number of places needed to print idx */
152 digits = 1;
153 while (idx /= 10)
154 digits++;
155 /*
156 * deny the name if it is phy<idx> where <idx> is printed
157 * without leading zeroes. taken == strlen(newname) here
158 */
159 if (taken == strlen(PHY_NAME) + digits)
160 return -EINVAL;
161 }
162
163 /* this will check for collisions */
164 result = device_rename(&rdev->wiphy.dev, newname);
165 if (result)
166 return result;
167
168 if (!debugfs_rename(rdev->wiphy.debugfsdir->d_parent,
169 rdev->wiphy.debugfsdir,
170 rdev->wiphy.debugfsdir->d_parent,
171 newname))
172 printk(KERN_ERR "cfg80211: failed to rename debugfs dir to %s!\n",
173 newname);
174
175 nl80211_notify_dev_rename(rdev);
176
177 return 0;
178}
179
39/* exported functions */ 180/* exported functions */
40 181
41struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv) 182struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv)
@@ -204,10 +345,16 @@ static int cfg80211_init(void)
204 if (err) 345 if (err)
205 goto out_fail_notifier; 346 goto out_fail_notifier;
206 347
348 err = nl80211_init();
349 if (err)
350 goto out_fail_nl80211;
351
207 ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL); 352 ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL);
208 353
209 return 0; 354 return 0;
210 355
356out_fail_nl80211:
357 unregister_netdevice_notifier(&cfg80211_netdev_notifier);
211out_fail_notifier: 358out_fail_notifier:
212 wiphy_sysfs_exit(); 359 wiphy_sysfs_exit();
213out_fail_sysfs: 360out_fail_sysfs:
@@ -218,6 +365,7 @@ subsys_initcall(cfg80211_init);
218static void cfg80211_exit(void) 365static void cfg80211_exit(void)
219{ 366{
220 debugfs_remove(ieee80211_debugfs_dir); 367 debugfs_remove(ieee80211_debugfs_dir);
368 nl80211_exit();
221 unregister_netdevice_notifier(&cfg80211_netdev_notifier); 369 unregister_netdevice_notifier(&cfg80211_netdev_notifier);
222 wiphy_sysfs_exit(); 370 wiphy_sysfs_exit();
223} 371}
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 158db1edb92a..eb0f846b40df 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -43,7 +43,39 @@ struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy)
43extern struct mutex cfg80211_drv_mutex; 43extern struct mutex cfg80211_drv_mutex;
44extern struct list_head cfg80211_drv_list; 44extern struct list_head cfg80211_drv_list;
45 45
46/*
47 * This function returns a pointer to the driver
48 * that the genl_info item that is passed refers to.
49 * If successful, it returns non-NULL and also locks
50 * the driver's mutex!
51 *
52 * This means that you need to call cfg80211_put_dev()
53 * before being allowed to acquire &cfg80211_drv_mutex!
54 *
55 * This is necessary because we need to lock the global
56 * mutex to get an item off the list safely, and then
57 * we lock the drv mutex so it doesn't go away under us.
58 *
59 * We don't want to keep cfg80211_drv_mutex locked
60 * for all the time in order to allow requests on
61 * other interfaces to go through at the same time.
62 *
63 * The result of this can be a PTR_ERR and hence must
64 * be checked with IS_ERR() for errors.
65 */
66extern struct cfg80211_registered_device *
67cfg80211_get_dev_from_info(struct genl_info *info);
68
69/* identical to cfg80211_get_dev_from_info but only operate on ifindex */
70extern struct cfg80211_registered_device *
71cfg80211_get_dev_from_ifindex(int ifindex);
72
73extern void cfg80211_put_dev(struct cfg80211_registered_device *drv);
74
46/* free object */ 75/* free object */
47extern void cfg80211_dev_free(struct cfg80211_registered_device *drv); 76extern void cfg80211_dev_free(struct cfg80211_registered_device *drv);
48 77
78extern int cfg80211_dev_rename(struct cfg80211_registered_device *drv,
79 char *newname);
80
49#endif /* __NET_WIRELESS_CORE_H */ 81#endif /* __NET_WIRELESS_CORE_H */
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}
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
new file mode 100644
index 000000000000..f3ea5c029aee
--- /dev/null
+++ b/net/wireless/nl80211.h
@@ -0,0 +1,24 @@
1#ifndef __NET_WIRELESS_NL80211_H
2#define __NET_WIRELESS_NL80211_H
3
4#include "core.h"
5
6#ifdef CONFIG_NL80211
7extern int nl80211_init(void);
8extern void nl80211_exit(void);
9extern void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev);
10#else
11static inline int nl80211_init(void)
12{
13 return 0;
14}
15static inline void nl80211_exit(void)
16{
17}
18static inline void nl80211_notify_dev_rename(
19 struct cfg80211_registered_device *rdev)
20{
21}
22#endif /* CONFIG_NL80211 */
23
24#endif /* __NET_WIRELESS_NL80211_H */