diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2007-09-20 13:09:35 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-10-10 19:52:14 -0400 |
commit | 556829657397b9b05baec6691ead4e22ee8d1567 (patch) | |
tree | 44242431553e5e22c0bceaab7a06d9d7bf0dd2f6 /net/wireless | |
parent | 0800f170263d19b882e519441156c5f6ed190fc1 (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')
-rw-r--r-- | net/wireless/Kconfig | 17 | ||||
-rw-r--r-- | net/wireless/Makefile | 1 | ||||
-rw-r--r-- | net/wireless/core.c | 148 | ||||
-rw-r--r-- | net/wireless/core.h | 32 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 431 | ||||
-rw-r--r-- | net/wireless/nl80211.h | 24 |
6 files changed, 652 insertions, 1 deletions
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 @@ | |||
1 | config CFG80211 | 1 | config CFG80211 |
2 | tristate "Improved wireless configuration API" | 2 | tristate "Improved wireless configuration API" |
3 | 3 | ||
4 | config 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 | |||
4 | config WIRELESS_EXT | 17 | config 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 | |||
2 | obj-$(CONFIG_CFG80211) += cfg80211.o | 2 | obj-$(CONFIG_CFG80211) += cfg80211.o |
3 | 3 | ||
4 | cfg80211-y += core.o sysfs.o radiotap.o | 4 | cfg80211-y += core.o sysfs.o radiotap.o |
5 | cfg80211-$(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 */ |
37 | static struct dentry *ieee80211_debugfs_dir; | 38 | static struct dentry *ieee80211_debugfs_dir; |
38 | 39 | ||
40 | /* requires cfg80211_drv_mutex to be held! */ | ||
41 | static 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! */ | ||
56 | static 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 | |||
97 | struct cfg80211_registered_device * | ||
98 | cfg80211_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 | |||
116 | struct cfg80211_registered_device * | ||
117 | cfg80211_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 | |||
137 | void cfg80211_put_dev(struct cfg80211_registered_device *drv) | ||
138 | { | ||
139 | BUG_ON(IS_ERR(drv)); | ||
140 | mutex_unlock(&drv->mtx); | ||
141 | } | ||
142 | |||
143 | int 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 | ||
41 | struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv) | 182 | struct 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 | ||
356 | out_fail_nl80211: | ||
357 | unregister_netdevice_notifier(&cfg80211_netdev_notifier); | ||
211 | out_fail_notifier: | 358 | out_fail_notifier: |
212 | wiphy_sysfs_exit(); | 359 | wiphy_sysfs_exit(); |
213 | out_fail_sysfs: | 360 | out_fail_sysfs: |
@@ -218,6 +365,7 @@ subsys_initcall(cfg80211_init); | |||
218 | static void cfg80211_exit(void) | 365 | static 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) | |||
43 | extern struct mutex cfg80211_drv_mutex; | 43 | extern struct mutex cfg80211_drv_mutex; |
44 | extern struct list_head cfg80211_drv_list; | 44 | extern 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 | */ | ||
66 | extern struct cfg80211_registered_device * | ||
67 | cfg80211_get_dev_from_info(struct genl_info *info); | ||
68 | |||
69 | /* identical to cfg80211_get_dev_from_info but only operate on ifindex */ | ||
70 | extern struct cfg80211_registered_device * | ||
71 | cfg80211_get_dev_from_ifindex(int ifindex); | ||
72 | |||
73 | extern void cfg80211_put_dev(struct cfg80211_registered_device *drv); | ||
74 | |||
46 | /* free object */ | 75 | /* free object */ |
47 | extern void cfg80211_dev_free(struct cfg80211_registered_device *drv); | 76 | extern void cfg80211_dev_free(struct cfg80211_registered_device *drv); |
48 | 77 | ||
78 | extern 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 */ | ||
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 | } | ||
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 | ||
7 | extern int nl80211_init(void); | ||
8 | extern void nl80211_exit(void); | ||
9 | extern void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev); | ||
10 | #else | ||
11 | static inline int nl80211_init(void) | ||
12 | { | ||
13 | return 0; | ||
14 | } | ||
15 | static inline void nl80211_exit(void) | ||
16 | { | ||
17 | } | ||
18 | static 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 */ | ||