blob: 8808431bd58126a23e5a8fe899ebb79a8d050efc [file] [log] [blame]
Johannes Berg55682962007-09-20 13:09:35 -04001/*
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>
Johannes Berg55682962007-09-20 13:09:35 -040010#include <linux/list.h>
11#include <linux/if_ether.h>
12#include <linux/ieee80211.h>
13#include <linux/nl80211.h>
14#include <linux/rtnetlink.h>
15#include <linux/netlink.h>
Johannes Berg2a519312009-02-10 21:25:55 +010016#include <linux/etherdevice.h>
Johannes Berg55682962007-09-20 13:09:35 -040017#include <net/genetlink.h>
18#include <net/cfg80211.h>
19#include "core.h"
20#include "nl80211.h"
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -070021#include "reg.h"
Johannes Berg55682962007-09-20 13:09:35 -040022
23/* the netlink family */
24static struct genl_family nl80211_fam = {
25 .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
26 .name = "nl80211", /* have users key off the name instead */
27 .hdrsize = 0, /* no private header */
28 .version = 1, /* no particular meaning now */
29 .maxattr = NL80211_ATTR_MAX,
30};
31
32/* internal helper: get drv and dev */
Johannes Bergbba95fe2008-07-29 13:22:51 +020033static int get_drv_dev_by_info_ifindex(struct nlattr **attrs,
Johannes Berg55682962007-09-20 13:09:35 -040034 struct cfg80211_registered_device **drv,
35 struct net_device **dev)
36{
37 int ifindex;
38
Johannes Bergbba95fe2008-07-29 13:22:51 +020039 if (!attrs[NL80211_ATTR_IFINDEX])
Johannes Berg55682962007-09-20 13:09:35 -040040 return -EINVAL;
41
Johannes Bergbba95fe2008-07-29 13:22:51 +020042 ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
Johannes Berg55682962007-09-20 13:09:35 -040043 *dev = dev_get_by_index(&init_net, ifindex);
44 if (!*dev)
45 return -ENODEV;
46
47 *drv = cfg80211_get_dev_from_ifindex(ifindex);
48 if (IS_ERR(*drv)) {
49 dev_put(*dev);
50 return PTR_ERR(*drv);
51 }
52
53 return 0;
54}
55
56/* policy for the attributes */
57static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
58 [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
59 [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
60 .len = BUS_ID_SIZE-1 },
Jouni Malinen31888482008-10-30 16:59:24 +020061 [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
Jouni Malinen72bdcf32008-11-26 16:15:24 +020062 [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
Sujith094d05d2008-12-12 11:57:43 +053063 [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
Johannes Berg55682962007-09-20 13:09:35 -040064
65 [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
66 [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
67 [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
Johannes Berg41ade002007-12-19 02:03:29 +010068
69 [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN },
70
71 [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
72 .len = WLAN_MAX_KEY_LEN },
73 [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
74 [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
75 [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
Johannes Berged1b6cc2007-12-19 02:03:32 +010076
77 [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
78 [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
79 [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY,
80 .len = IEEE80211_MAX_DATA_LEN },
81 [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY,
82 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg5727ef12007-12-19 02:03:34 +010083 [NL80211_ATTR_STA_AID] = { .type = NLA_U16 },
84 [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED },
85 [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 },
86 [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY,
87 .len = NL80211_MAX_SUPP_RATES },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +010088 [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
Johannes Berg5727ef12007-12-19 02:03:34 +010089 [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
Johannes Berg0a9542e2008-10-15 11:54:04 +020090 [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +010091 [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
92 .len = IEEE80211_MAX_MESH_ID_LEN },
93 [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +030094
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -070095 [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 },
96 [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED },
97
Jouni Malinen9f1ba902008-08-07 20:07:01 +030098 [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
99 [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
100 [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
Jouni Malinen90c97a02008-10-30 16:59:22 +0200101 [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
102 .len = NL80211_MAX_SUPP_RATES },
Jouni Malinen36aedc902008-08-25 11:58:58 +0300103
colin@cozybit.com93da9cc2008-10-21 12:03:48 -0700104 [NL80211_ATTR_MESH_PARAMS] = { .type = NLA_NESTED },
105
Jouni Malinen36aedc902008-08-25 11:58:58 +0300106 [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY,
107 .len = NL80211_HT_CAPABILITY_LEN },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +0200108
109 [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
110 [NL80211_ATTR_IE] = { .type = NLA_BINARY,
111 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg2a519312009-02-10 21:25:55 +0100112 [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
113 [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
Jouni Malinen636a5d32009-03-19 13:39:22 +0200114
115 [NL80211_ATTR_SSID] = { .type = NLA_BINARY,
116 .len = IEEE80211_MAX_SSID_LEN },
117 [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
118 [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
Johannes Berg55682962007-09-20 13:09:35 -0400119};
120
121/* message building helper */
122static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
123 int flags, u8 cmd)
124{
125 /* since there is no private header just add the generic one */
126 return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
127}
128
129/* netlink command implementations */
130
131static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
132 struct cfg80211_registered_device *dev)
133{
134 void *hdr;
Johannes Bergee688b002008-01-24 19:38:39 +0100135 struct nlattr *nl_bands, *nl_band;
136 struct nlattr *nl_freqs, *nl_freq;
137 struct nlattr *nl_rates, *nl_rate;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700138 struct nlattr *nl_modes;
Johannes Berg8fdc6212009-03-14 09:34:01 +0100139 struct nlattr *nl_cmds;
Johannes Bergee688b002008-01-24 19:38:39 +0100140 enum ieee80211_band band;
141 struct ieee80211_channel *chan;
142 struct ieee80211_rate *rate;
143 int i;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700144 u16 ifmodes = dev->wiphy.interface_modes;
Johannes Berg55682962007-09-20 13:09:35 -0400145
146 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
147 if (!hdr)
148 return -1;
149
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -0500150 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400151 NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
Johannes Berg2a519312009-02-10 21:25:55 +0100152 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
153 dev->wiphy.max_scan_ssids);
Johannes Bergee688b002008-01-24 19:38:39 +0100154
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700155 nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES);
156 if (!nl_modes)
157 goto nla_put_failure;
158
159 i = 0;
160 while (ifmodes) {
161 if (ifmodes & 1)
162 NLA_PUT_FLAG(msg, i);
163 ifmodes >>= 1;
164 i++;
165 }
166
167 nla_nest_end(msg, nl_modes);
168
Johannes Bergee688b002008-01-24 19:38:39 +0100169 nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
170 if (!nl_bands)
171 goto nla_put_failure;
172
173 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
174 if (!dev->wiphy.bands[band])
175 continue;
176
177 nl_band = nla_nest_start(msg, band);
178 if (!nl_band)
179 goto nla_put_failure;
180
Johannes Bergd51626d2008-10-09 12:20:13 +0200181 /* add HT info */
182 if (dev->wiphy.bands[band]->ht_cap.ht_supported) {
183 NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET,
184 sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
185 &dev->wiphy.bands[band]->ht_cap.mcs);
186 NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA,
187 dev->wiphy.bands[band]->ht_cap.cap);
188 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
189 dev->wiphy.bands[band]->ht_cap.ampdu_factor);
190 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
191 dev->wiphy.bands[band]->ht_cap.ampdu_density);
192 }
193
Johannes Bergee688b002008-01-24 19:38:39 +0100194 /* add frequencies */
195 nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
196 if (!nl_freqs)
197 goto nla_put_failure;
198
199 for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
200 nl_freq = nla_nest_start(msg, i);
201 if (!nl_freq)
202 goto nla_put_failure;
203
204 chan = &dev->wiphy.bands[band]->channels[i];
205 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
206 chan->center_freq);
207
208 if (chan->flags & IEEE80211_CHAN_DISABLED)
209 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
210 if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
211 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
212 if (chan->flags & IEEE80211_CHAN_NO_IBSS)
213 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
214 if (chan->flags & IEEE80211_CHAN_RADAR)
215 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
216
Jouni Malinenbf8c1ac2008-11-22 22:00:31 +0200217 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
218 DBM_TO_MBM(chan->max_power));
Jouni Malinene2f367f262008-11-21 19:01:30 +0200219
Johannes Bergee688b002008-01-24 19:38:39 +0100220 nla_nest_end(msg, nl_freq);
221 }
222
223 nla_nest_end(msg, nl_freqs);
224
225 /* add bitrates */
226 nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
227 if (!nl_rates)
228 goto nla_put_failure;
229
230 for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
231 nl_rate = nla_nest_start(msg, i);
232 if (!nl_rate)
233 goto nla_put_failure;
234
235 rate = &dev->wiphy.bands[band]->bitrates[i];
236 NLA_PUT_U32(msg, NL80211_BITRATE_ATTR_RATE,
237 rate->bitrate);
238 if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
239 NLA_PUT_FLAG(msg,
240 NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE);
241
242 nla_nest_end(msg, nl_rate);
243 }
244
245 nla_nest_end(msg, nl_rates);
246
247 nla_nest_end(msg, nl_band);
248 }
249 nla_nest_end(msg, nl_bands);
250
Johannes Berg8fdc6212009-03-14 09:34:01 +0100251 nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
252 if (!nl_cmds)
253 goto nla_put_failure;
254
255 i = 0;
256#define CMD(op, n) \
257 do { \
258 if (dev->ops->op) { \
259 i++; \
260 NLA_PUT_U32(msg, i, NL80211_CMD_ ## n); \
261 } \
262 } while (0)
263
264 CMD(add_virtual_intf, NEW_INTERFACE);
265 CMD(change_virtual_intf, SET_INTERFACE);
266 CMD(add_key, NEW_KEY);
267 CMD(add_beacon, NEW_BEACON);
268 CMD(add_station, NEW_STATION);
269 CMD(add_mpath, NEW_MPATH);
270 CMD(set_mesh_params, SET_MESH_PARAMS);
271 CMD(change_bss, SET_BSS);
Jouni Malinen636a5d32009-03-19 13:39:22 +0200272 CMD(auth, AUTHENTICATE);
273 CMD(assoc, ASSOCIATE);
274 CMD(deauth, DEAUTHENTICATE);
275 CMD(disassoc, DISASSOCIATE);
Johannes Berg8fdc6212009-03-14 09:34:01 +0100276
277#undef CMD
278 nla_nest_end(msg, nl_cmds);
279
Johannes Berg55682962007-09-20 13:09:35 -0400280 return genlmsg_end(msg, hdr);
281
282 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700283 genlmsg_cancel(msg, hdr);
284 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400285}
286
287static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
288{
289 int idx = 0;
290 int start = cb->args[0];
291 struct cfg80211_registered_device *dev;
292
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500293 mutex_lock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400294 list_for_each_entry(dev, &cfg80211_drv_list, list) {
Julius Volzb4637272008-07-08 14:02:19 +0200295 if (++idx <= start)
Johannes Berg55682962007-09-20 13:09:35 -0400296 continue;
297 if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
298 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Julius Volzb4637272008-07-08 14:02:19 +0200299 dev) < 0) {
300 idx--;
Johannes Berg55682962007-09-20 13:09:35 -0400301 break;
Julius Volzb4637272008-07-08 14:02:19 +0200302 }
Johannes Berg55682962007-09-20 13:09:35 -0400303 }
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500304 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400305
306 cb->args[0] = idx;
307
308 return skb->len;
309}
310
311static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
312{
313 struct sk_buff *msg;
314 struct cfg80211_registered_device *dev;
315
316 dev = cfg80211_get_dev_from_info(info);
317 if (IS_ERR(dev))
318 return PTR_ERR(dev);
319
320 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
321 if (!msg)
322 goto out_err;
323
324 if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0)
325 goto out_free;
326
327 cfg80211_put_dev(dev);
328
329 return genlmsg_unicast(msg, info->snd_pid);
330
331 out_free:
332 nlmsg_free(msg);
333 out_err:
334 cfg80211_put_dev(dev);
335 return -ENOBUFS;
336}
337
Jouni Malinen31888482008-10-30 16:59:24 +0200338static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
339 [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
340 [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
341 [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
342 [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
343 [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
344};
345
346static int parse_txq_params(struct nlattr *tb[],
347 struct ieee80211_txq_params *txq_params)
348{
349 if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] ||
350 !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
351 !tb[NL80211_TXQ_ATTR_AIFS])
352 return -EINVAL;
353
354 txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]);
355 txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
356 txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
357 txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
358 txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
359
360 return 0;
361}
362
Johannes Berg55682962007-09-20 13:09:35 -0400363static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
364{
365 struct cfg80211_registered_device *rdev;
Jouni Malinen31888482008-10-30 16:59:24 +0200366 int result = 0, rem_txq_params = 0;
367 struct nlattr *nl_txq_params;
Johannes Berg55682962007-09-20 13:09:35 -0400368
369 rdev = cfg80211_get_dev_from_info(info);
370 if (IS_ERR(rdev))
371 return PTR_ERR(rdev);
372
Jouni Malinen31888482008-10-30 16:59:24 +0200373 if (info->attrs[NL80211_ATTR_WIPHY_NAME]) {
374 result = cfg80211_dev_rename(
375 rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
376 if (result)
377 goto bad_res;
378 }
Johannes Berg55682962007-09-20 13:09:35 -0400379
Jouni Malinen31888482008-10-30 16:59:24 +0200380 if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
381 struct ieee80211_txq_params txq_params;
382 struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
383
384 if (!rdev->ops->set_txq_params) {
385 result = -EOPNOTSUPP;
386 goto bad_res;
387 }
388
389 nla_for_each_nested(nl_txq_params,
390 info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
391 rem_txq_params) {
392 nla_parse(tb, NL80211_TXQ_ATTR_MAX,
393 nla_data(nl_txq_params),
394 nla_len(nl_txq_params),
395 txq_params_policy);
396 result = parse_txq_params(tb, &txq_params);
397 if (result)
398 goto bad_res;
399
400 result = rdev->ops->set_txq_params(&rdev->wiphy,
401 &txq_params);
402 if (result)
403 goto bad_res;
404 }
405 }
406
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200407 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Sujith094d05d2008-12-12 11:57:43 +0530408 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200409 struct ieee80211_channel *chan;
Johannes Berg306d6112008-12-08 12:39:04 +0100410 struct ieee80211_sta_ht_cap *ht_cap;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200411 u32 freq, sec_freq;
412
413 if (!rdev->ops->set_channel) {
414 result = -EOPNOTSUPP;
415 goto bad_res;
416 }
417
Johannes Berg306d6112008-12-08 12:39:04 +0100418 result = -EINVAL;
419
Sujith094d05d2008-12-12 11:57:43 +0530420 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
421 channel_type = nla_get_u32(info->attrs[
422 NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
423 if (channel_type != NL80211_CHAN_NO_HT &&
424 channel_type != NL80211_CHAN_HT20 &&
425 channel_type != NL80211_CHAN_HT40PLUS &&
426 channel_type != NL80211_CHAN_HT40MINUS)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200427 goto bad_res;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200428 }
429
430 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
431 chan = ieee80211_get_channel(&rdev->wiphy, freq);
Johannes Berg306d6112008-12-08 12:39:04 +0100432
433 /* Primary channel not allowed */
434 if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200435 goto bad_res;
Johannes Berg306d6112008-12-08 12:39:04 +0100436
Sujith094d05d2008-12-12 11:57:43 +0530437 if (channel_type == NL80211_CHAN_HT40MINUS)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200438 sec_freq = freq - 20;
Sujith094d05d2008-12-12 11:57:43 +0530439 else if (channel_type == NL80211_CHAN_HT40PLUS)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200440 sec_freq = freq + 20;
441 else
442 sec_freq = 0;
443
Johannes Berg306d6112008-12-08 12:39:04 +0100444 ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
445
446 /* no HT capabilities */
Sujith094d05d2008-12-12 11:57:43 +0530447 if (channel_type != NL80211_CHAN_NO_HT &&
Johannes Berg306d6112008-12-08 12:39:04 +0100448 !ht_cap->ht_supported)
449 goto bad_res;
450
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200451 if (sec_freq) {
452 struct ieee80211_channel *schan;
Johannes Berg306d6112008-12-08 12:39:04 +0100453
454 /* no 40 MHz capabilities */
455 if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
456 (ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200457 goto bad_res;
Johannes Berg306d6112008-12-08 12:39:04 +0100458
459 schan = ieee80211_get_channel(&rdev->wiphy, sec_freq);
460
461 /* Secondary channel not allowed */
462 if (!schan || schan->flags & IEEE80211_CHAN_DISABLED)
463 goto bad_res;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200464 }
465
466 result = rdev->ops->set_channel(&rdev->wiphy, chan,
Sujith094d05d2008-12-12 11:57:43 +0530467 channel_type);
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200468 if (result)
469 goto bad_res;
470 }
471
472
Johannes Berg306d6112008-12-08 12:39:04 +0100473 bad_res:
Johannes Berg55682962007-09-20 13:09:35 -0400474 cfg80211_put_dev(rdev);
475 return result;
476}
477
478
479static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
480 struct net_device *dev)
481{
482 void *hdr;
483
484 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
485 if (!hdr)
486 return -1;
487
488 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
489 NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
Johannes Berg60719ff2008-09-16 14:55:09 +0200490 NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype);
Johannes Berg55682962007-09-20 13:09:35 -0400491 return genlmsg_end(msg, hdr);
492
493 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700494 genlmsg_cancel(msg, hdr);
495 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400496}
497
498static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
499{
500 int wp_idx = 0;
501 int if_idx = 0;
502 int wp_start = cb->args[0];
503 int if_start = cb->args[1];
504 struct cfg80211_registered_device *dev;
505 struct wireless_dev *wdev;
506
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500507 mutex_lock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400508 list_for_each_entry(dev, &cfg80211_drv_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +0200509 if (wp_idx < wp_start) {
510 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400511 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200512 }
Johannes Berg55682962007-09-20 13:09:35 -0400513 if_idx = 0;
514
515 mutex_lock(&dev->devlist_mtx);
516 list_for_each_entry(wdev, &dev->netdev_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +0200517 if (if_idx < if_start) {
518 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400519 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200520 }
Johannes Berg55682962007-09-20 13:09:35 -0400521 if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
522 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Bergbba95fe2008-07-29 13:22:51 +0200523 wdev->netdev) < 0) {
524 mutex_unlock(&dev->devlist_mtx);
525 goto out;
526 }
527 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400528 }
529 mutex_unlock(&dev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +0200530
531 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400532 }
Johannes Bergbba95fe2008-07-29 13:22:51 +0200533 out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500534 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400535
536 cb->args[0] = wp_idx;
537 cb->args[1] = if_idx;
538
539 return skb->len;
540}
541
542static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
543{
544 struct sk_buff *msg;
545 struct cfg80211_registered_device *dev;
546 struct net_device *netdev;
547 int err;
548
Johannes Bergbba95fe2008-07-29 13:22:51 +0200549 err = get_drv_dev_by_info_ifindex(info->attrs, &dev, &netdev);
Johannes Berg55682962007-09-20 13:09:35 -0400550 if (err)
551 return err;
552
553 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
554 if (!msg)
555 goto out_err;
556
557 if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0, netdev) < 0)
558 goto out_free;
559
560 dev_put(netdev);
561 cfg80211_put_dev(dev);
562
563 return genlmsg_unicast(msg, info->snd_pid);
564
565 out_free:
566 nlmsg_free(msg);
567 out_err:
568 dev_put(netdev);
569 cfg80211_put_dev(dev);
570 return -ENOBUFS;
571}
572
Michael Wu66f7ac52008-01-31 19:48:22 +0100573static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
574 [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
575 [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
576 [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
577 [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
578 [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
579};
580
581static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
582{
583 struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
584 int flag;
585
586 *mntrflags = 0;
587
588 if (!nla)
589 return -EINVAL;
590
591 if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX,
592 nla, mntr_flags_policy))
593 return -EINVAL;
594
595 for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
596 if (flags[flag])
597 *mntrflags |= (1<<flag);
598
599 return 0;
600}
601
Johannes Berg55682962007-09-20 13:09:35 -0400602static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
603{
604 struct cfg80211_registered_device *drv;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100605 struct vif_params params;
Johannes Berg55682962007-09-20 13:09:35 -0400606 int err, ifindex;
607 enum nl80211_iftype type;
608 struct net_device *dev;
Johannes Berg92ffe052008-09-16 20:39:36 +0200609 u32 _flags, *flags = NULL;
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100610 bool change = false;
Johannes Berg55682962007-09-20 13:09:35 -0400611
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100612 memset(&params, 0, sizeof(params));
613
Johannes Berg3b858752009-03-12 09:55:09 +0100614 rtnl_lock();
615
Johannes Bergbba95fe2008-07-29 13:22:51 +0200616 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg55682962007-09-20 13:09:35 -0400617 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +0100618 goto unlock_rtnl;
619
Johannes Berg55682962007-09-20 13:09:35 -0400620 ifindex = dev->ifindex;
Johannes Berg723b0382008-09-16 20:22:09 +0200621 type = dev->ieee80211_ptr->iftype;
Johannes Berg55682962007-09-20 13:09:35 -0400622 dev_put(dev);
623
Johannes Berg723b0382008-09-16 20:22:09 +0200624 if (info->attrs[NL80211_ATTR_IFTYPE]) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100625 enum nl80211_iftype ntype;
626
627 ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
628 if (type != ntype)
629 change = true;
630 type = ntype;
631 if (type > NL80211_IFTYPE_MAX) {
632 err = -EINVAL;
Johannes Berg723b0382008-09-16 20:22:09 +0200633 goto unlock;
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100634 }
Johannes Berg723b0382008-09-16 20:22:09 +0200635 }
636
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700637 if (!drv->ops->change_virtual_intf ||
638 !(drv->wiphy.interface_modes & (1 << type))) {
Johannes Berg55682962007-09-20 13:09:35 -0400639 err = -EOPNOTSUPP;
640 goto unlock;
641 }
642
Johannes Berg92ffe052008-09-16 20:39:36 +0200643 if (info->attrs[NL80211_ATTR_MESH_ID]) {
644 if (type != NL80211_IFTYPE_MESH_POINT) {
645 err = -EINVAL;
646 goto unlock;
647 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100648 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
649 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100650 change = true;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100651 }
652
Johannes Berg92ffe052008-09-16 20:39:36 +0200653 if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
654 if (type != NL80211_IFTYPE_MONITOR) {
655 err = -EINVAL;
656 goto unlock;
657 }
658 err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
659 &_flags);
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100660 if (err)
661 goto unlock;
662
663 flags = &_flags;
664 change = true;
Johannes Berg92ffe052008-09-16 20:39:36 +0200665 }
Johannes Berg3b858752009-03-12 09:55:09 +0100666
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100667 if (change)
668 err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex,
669 type, flags, &params);
670 else
671 err = 0;
Johannes Berg60719ff2008-09-16 14:55:09 +0200672
673 dev = __dev_get_by_index(&init_net, ifindex);
674 WARN_ON(!dev || (!err && dev->ieee80211_ptr->iftype != type));
675
Johannes Berg55682962007-09-20 13:09:35 -0400676 unlock:
677 cfg80211_put_dev(drv);
Johannes Berg3b858752009-03-12 09:55:09 +0100678 unlock_rtnl:
679 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400680 return err;
681}
682
683static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
684{
685 struct cfg80211_registered_device *drv;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100686 struct vif_params params;
Johannes Berg55682962007-09-20 13:09:35 -0400687 int err;
688 enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
Michael Wu66f7ac52008-01-31 19:48:22 +0100689 u32 flags;
Johannes Berg55682962007-09-20 13:09:35 -0400690
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100691 memset(&params, 0, sizeof(params));
692
Johannes Berg55682962007-09-20 13:09:35 -0400693 if (!info->attrs[NL80211_ATTR_IFNAME])
694 return -EINVAL;
695
696 if (info->attrs[NL80211_ATTR_IFTYPE]) {
697 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
698 if (type > NL80211_IFTYPE_MAX)
699 return -EINVAL;
700 }
701
Johannes Berg3b858752009-03-12 09:55:09 +0100702 rtnl_lock();
703
Johannes Berg55682962007-09-20 13:09:35 -0400704 drv = cfg80211_get_dev_from_info(info);
Johannes Berg3b858752009-03-12 09:55:09 +0100705 if (IS_ERR(drv)) {
706 err = PTR_ERR(drv);
707 goto unlock_rtnl;
708 }
Johannes Berg55682962007-09-20 13:09:35 -0400709
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700710 if (!drv->ops->add_virtual_intf ||
711 !(drv->wiphy.interface_modes & (1 << type))) {
Johannes Berg55682962007-09-20 13:09:35 -0400712 err = -EOPNOTSUPP;
713 goto unlock;
714 }
715
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100716 if (type == NL80211_IFTYPE_MESH_POINT &&
717 info->attrs[NL80211_ATTR_MESH_ID]) {
718 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
719 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
720 }
721
Michael Wu66f7ac52008-01-31 19:48:22 +0100722 err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
723 info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
724 &flags);
Johannes Berg55682962007-09-20 13:09:35 -0400725 err = drv->ops->add_virtual_intf(&drv->wiphy,
Michael Wu66f7ac52008-01-31 19:48:22 +0100726 nla_data(info->attrs[NL80211_ATTR_IFNAME]),
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100727 type, err ? NULL : &flags, &params);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100728
Johannes Berg55682962007-09-20 13:09:35 -0400729 unlock:
730 cfg80211_put_dev(drv);
Johannes Berg3b858752009-03-12 09:55:09 +0100731 unlock_rtnl:
732 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400733 return err;
734}
735
736static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
737{
738 struct cfg80211_registered_device *drv;
739 int ifindex, err;
740 struct net_device *dev;
741
Johannes Berg3b858752009-03-12 09:55:09 +0100742 rtnl_lock();
743
Johannes Bergbba95fe2008-07-29 13:22:51 +0200744 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg55682962007-09-20 13:09:35 -0400745 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +0100746 goto unlock_rtnl;
Johannes Berg55682962007-09-20 13:09:35 -0400747 ifindex = dev->ifindex;
748 dev_put(dev);
749
750 if (!drv->ops->del_virtual_intf) {
751 err = -EOPNOTSUPP;
752 goto out;
753 }
754
Johannes Berg55682962007-09-20 13:09:35 -0400755 err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex);
Johannes Berg55682962007-09-20 13:09:35 -0400756
757 out:
758 cfg80211_put_dev(drv);
Johannes Berg3b858752009-03-12 09:55:09 +0100759 unlock_rtnl:
760 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400761 return err;
762}
763
Johannes Berg41ade002007-12-19 02:03:29 +0100764struct get_key_cookie {
765 struct sk_buff *msg;
766 int error;
767};
768
769static void get_key_callback(void *c, struct key_params *params)
770{
771 struct get_key_cookie *cookie = c;
772
773 if (params->key)
774 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA,
775 params->key_len, params->key);
776
777 if (params->seq)
778 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ,
779 params->seq_len, params->seq);
780
781 if (params->cipher)
782 NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
783 params->cipher);
784
785 return;
786 nla_put_failure:
787 cookie->error = 1;
788}
789
790static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
791{
792 struct cfg80211_registered_device *drv;
793 int err;
794 struct net_device *dev;
795 u8 key_idx = 0;
796 u8 *mac_addr = NULL;
797 struct get_key_cookie cookie = {
798 .error = 0,
799 };
800 void *hdr;
801 struct sk_buff *msg;
802
803 if (info->attrs[NL80211_ATTR_KEY_IDX])
804 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
805
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +0200806 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +0100807 return -EINVAL;
808
809 if (info->attrs[NL80211_ATTR_MAC])
810 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
811
Johannes Berg3b858752009-03-12 09:55:09 +0100812 rtnl_lock();
813
Johannes Bergbba95fe2008-07-29 13:22:51 +0200814 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +0100815 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +0100816 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +0100817
818 if (!drv->ops->get_key) {
819 err = -EOPNOTSUPP;
820 goto out;
821 }
822
823 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
824 if (!msg) {
825 err = -ENOMEM;
826 goto out;
827 }
828
829 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
830 NL80211_CMD_NEW_KEY);
831
832 if (IS_ERR(hdr)) {
833 err = PTR_ERR(hdr);
834 goto out;
835 }
836
837 cookie.msg = msg;
838
839 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
840 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
841 if (mac_addr)
842 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
843
Johannes Berg41ade002007-12-19 02:03:29 +0100844 err = drv->ops->get_key(&drv->wiphy, dev, key_idx, mac_addr,
845 &cookie, get_key_callback);
Johannes Berg41ade002007-12-19 02:03:29 +0100846
847 if (err)
848 goto out;
849
850 if (cookie.error)
851 goto nla_put_failure;
852
853 genlmsg_end(msg, hdr);
854 err = genlmsg_unicast(msg, info->snd_pid);
855 goto out;
856
857 nla_put_failure:
858 err = -ENOBUFS;
859 nlmsg_free(msg);
860 out:
861 cfg80211_put_dev(drv);
862 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +0100863 unlock_rtnl:
864 rtnl_unlock();
865
Johannes Berg41ade002007-12-19 02:03:29 +0100866 return err;
867}
868
869static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
870{
871 struct cfg80211_registered_device *drv;
872 int err;
873 struct net_device *dev;
874 u8 key_idx;
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +0200875 int (*func)(struct wiphy *wiphy, struct net_device *netdev,
876 u8 key_index);
Johannes Berg41ade002007-12-19 02:03:29 +0100877
878 if (!info->attrs[NL80211_ATTR_KEY_IDX])
879 return -EINVAL;
880
881 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
882
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +0200883 if (info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) {
884 if (key_idx < 4 || key_idx > 5)
885 return -EINVAL;
886 } else if (key_idx > 3)
Johannes Berg41ade002007-12-19 02:03:29 +0100887 return -EINVAL;
888
889 /* currently only support setting default key */
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +0200890 if (!info->attrs[NL80211_ATTR_KEY_DEFAULT] &&
891 !info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT])
Johannes Berg41ade002007-12-19 02:03:29 +0100892 return -EINVAL;
893
Johannes Berg3b858752009-03-12 09:55:09 +0100894 rtnl_lock();
895
Johannes Bergbba95fe2008-07-29 13:22:51 +0200896 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +0100897 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +0100898 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +0100899
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +0200900 if (info->attrs[NL80211_ATTR_KEY_DEFAULT])
901 func = drv->ops->set_default_key;
902 else
903 func = drv->ops->set_default_mgmt_key;
904
905 if (!func) {
Johannes Berg41ade002007-12-19 02:03:29 +0100906 err = -EOPNOTSUPP;
907 goto out;
908 }
909
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +0200910 err = func(&drv->wiphy, dev, key_idx);
Johannes Berg41ade002007-12-19 02:03:29 +0100911
912 out:
913 cfg80211_put_dev(drv);
914 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +0100915
916 unlock_rtnl:
917 rtnl_unlock();
918
Johannes Berg41ade002007-12-19 02:03:29 +0100919 return err;
920}
921
922static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
923{
924 struct cfg80211_registered_device *drv;
925 int err;
926 struct net_device *dev;
927 struct key_params params;
928 u8 key_idx = 0;
929 u8 *mac_addr = NULL;
930
931 memset(&params, 0, sizeof(params));
932
933 if (!info->attrs[NL80211_ATTR_KEY_CIPHER])
934 return -EINVAL;
935
936 if (info->attrs[NL80211_ATTR_KEY_DATA]) {
937 params.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
938 params.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
939 }
940
941 if (info->attrs[NL80211_ATTR_KEY_IDX])
942 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
943
944 params.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
945
946 if (info->attrs[NL80211_ATTR_MAC])
947 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
948
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +0200949 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +0100950 return -EINVAL;
951
952 /*
953 * Disallow pairwise keys with non-zero index unless it's WEP
954 * (because current deployments use pairwise WEP keys with
955 * non-zero indizes but 802.11i clearly specifies to use zero)
956 */
957 if (mac_addr && key_idx &&
958 params.cipher != WLAN_CIPHER_SUITE_WEP40 &&
959 params.cipher != WLAN_CIPHER_SUITE_WEP104)
960 return -EINVAL;
961
962 /* TODO: add definitions for the lengths to linux/ieee80211.h */
963 switch (params.cipher) {
964 case WLAN_CIPHER_SUITE_WEP40:
965 if (params.key_len != 5)
966 return -EINVAL;
967 break;
968 case WLAN_CIPHER_SUITE_TKIP:
969 if (params.key_len != 32)
970 return -EINVAL;
971 break;
972 case WLAN_CIPHER_SUITE_CCMP:
973 if (params.key_len != 16)
974 return -EINVAL;
975 break;
976 case WLAN_CIPHER_SUITE_WEP104:
977 if (params.key_len != 13)
978 return -EINVAL;
979 break;
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +0200980 case WLAN_CIPHER_SUITE_AES_CMAC:
981 if (params.key_len != 16)
982 return -EINVAL;
983 break;
Johannes Berg41ade002007-12-19 02:03:29 +0100984 default:
985 return -EINVAL;
986 }
987
Johannes Berg3b858752009-03-12 09:55:09 +0100988 rtnl_lock();
989
Johannes Bergbba95fe2008-07-29 13:22:51 +0200990 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +0100991 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +0100992 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +0100993
994 if (!drv->ops->add_key) {
995 err = -EOPNOTSUPP;
996 goto out;
997 }
998
Johannes Berg41ade002007-12-19 02:03:29 +0100999 err = drv->ops->add_key(&drv->wiphy, dev, key_idx, mac_addr, &params);
Johannes Berg41ade002007-12-19 02:03:29 +01001000
1001 out:
1002 cfg80211_put_dev(drv);
1003 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001004 unlock_rtnl:
1005 rtnl_unlock();
1006
Johannes Berg41ade002007-12-19 02:03:29 +01001007 return err;
1008}
1009
1010static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
1011{
1012 struct cfg80211_registered_device *drv;
1013 int err;
1014 struct net_device *dev;
1015 u8 key_idx = 0;
1016 u8 *mac_addr = NULL;
1017
1018 if (info->attrs[NL80211_ATTR_KEY_IDX])
1019 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1020
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001021 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +01001022 return -EINVAL;
1023
1024 if (info->attrs[NL80211_ATTR_MAC])
1025 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1026
Johannes Berg3b858752009-03-12 09:55:09 +01001027 rtnl_lock();
1028
Johannes Bergbba95fe2008-07-29 13:22:51 +02001029 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001030 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001031 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001032
1033 if (!drv->ops->del_key) {
1034 err = -EOPNOTSUPP;
1035 goto out;
1036 }
1037
Johannes Berg41ade002007-12-19 02:03:29 +01001038 err = drv->ops->del_key(&drv->wiphy, dev, key_idx, mac_addr);
Johannes Berg41ade002007-12-19 02:03:29 +01001039
1040 out:
1041 cfg80211_put_dev(drv);
1042 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001043
1044 unlock_rtnl:
1045 rtnl_unlock();
1046
Johannes Berg41ade002007-12-19 02:03:29 +01001047 return err;
1048}
1049
Johannes Berged1b6cc2007-12-19 02:03:32 +01001050static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
1051{
1052 int (*call)(struct wiphy *wiphy, struct net_device *dev,
1053 struct beacon_parameters *info);
1054 struct cfg80211_registered_device *drv;
1055 int err;
1056 struct net_device *dev;
1057 struct beacon_parameters params;
1058 int haveinfo = 0;
1059
Johannes Berg3b858752009-03-12 09:55:09 +01001060 rtnl_lock();
1061
Johannes Bergbba95fe2008-07-29 13:22:51 +02001062 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001063 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001064 goto unlock_rtnl;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001065
Jouni Malineneec60b02009-03-20 21:21:19 +02001066 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
1067 err = -EOPNOTSUPP;
1068 goto out;
1069 }
1070
Johannes Berged1b6cc2007-12-19 02:03:32 +01001071 switch (info->genlhdr->cmd) {
1072 case NL80211_CMD_NEW_BEACON:
1073 /* these are required for NEW_BEACON */
1074 if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
1075 !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
1076 !info->attrs[NL80211_ATTR_BEACON_HEAD]) {
1077 err = -EINVAL;
1078 goto out;
1079 }
1080
1081 call = drv->ops->add_beacon;
1082 break;
1083 case NL80211_CMD_SET_BEACON:
1084 call = drv->ops->set_beacon;
1085 break;
1086 default:
1087 WARN_ON(1);
1088 err = -EOPNOTSUPP;
1089 goto out;
1090 }
1091
1092 if (!call) {
1093 err = -EOPNOTSUPP;
1094 goto out;
1095 }
1096
1097 memset(&params, 0, sizeof(params));
1098
1099 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
1100 params.interval =
1101 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
1102 haveinfo = 1;
1103 }
1104
1105 if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) {
1106 params.dtim_period =
1107 nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
1108 haveinfo = 1;
1109 }
1110
1111 if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
1112 params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
1113 params.head_len =
1114 nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
1115 haveinfo = 1;
1116 }
1117
1118 if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
1119 params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
1120 params.tail_len =
1121 nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
1122 haveinfo = 1;
1123 }
1124
1125 if (!haveinfo) {
1126 err = -EINVAL;
1127 goto out;
1128 }
1129
Johannes Berged1b6cc2007-12-19 02:03:32 +01001130 err = call(&drv->wiphy, dev, &params);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001131
1132 out:
1133 cfg80211_put_dev(drv);
1134 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001135 unlock_rtnl:
1136 rtnl_unlock();
1137
Johannes Berged1b6cc2007-12-19 02:03:32 +01001138 return err;
1139}
1140
1141static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
1142{
1143 struct cfg80211_registered_device *drv;
1144 int err;
1145 struct net_device *dev;
1146
Johannes Berg3b858752009-03-12 09:55:09 +01001147 rtnl_lock();
1148
Johannes Bergbba95fe2008-07-29 13:22:51 +02001149 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001150 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001151 goto unlock_rtnl;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001152
1153 if (!drv->ops->del_beacon) {
1154 err = -EOPNOTSUPP;
1155 goto out;
1156 }
1157
Jouni Malineneec60b02009-03-20 21:21:19 +02001158 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
1159 err = -EOPNOTSUPP;
1160 goto out;
1161 }
Johannes Berged1b6cc2007-12-19 02:03:32 +01001162 err = drv->ops->del_beacon(&drv->wiphy, dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001163
1164 out:
1165 cfg80211_put_dev(drv);
1166 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001167 unlock_rtnl:
1168 rtnl_unlock();
1169
Johannes Berged1b6cc2007-12-19 02:03:32 +01001170 return err;
1171}
1172
Johannes Berg5727ef12007-12-19 02:03:34 +01001173static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
1174 [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
1175 [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
1176 [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
1177};
1178
1179static int parse_station_flags(struct nlattr *nla, u32 *staflags)
1180{
1181 struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
1182 int flag;
1183
1184 *staflags = 0;
1185
1186 if (!nla)
1187 return 0;
1188
1189 if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX,
1190 nla, sta_flags_policy))
1191 return -EINVAL;
1192
1193 *staflags = STATION_FLAG_CHANGED;
1194
1195 for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++)
1196 if (flags[flag])
1197 *staflags |= (1<<flag);
1198
1199 return 0;
1200}
1201
Henning Rogge420e7fa2008-12-11 22:04:19 +01001202static u16 nl80211_calculate_bitrate(struct rate_info *rate)
1203{
1204 int modulation, streams, bitrate;
1205
1206 if (!(rate->flags & RATE_INFO_FLAGS_MCS))
1207 return rate->legacy;
1208
1209 /* the formula below does only work for MCS values smaller than 32 */
1210 if (rate->mcs >= 32)
1211 return 0;
1212
1213 modulation = rate->mcs & 7;
1214 streams = (rate->mcs >> 3) + 1;
1215
1216 bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ?
1217 13500000 : 6500000;
1218
1219 if (modulation < 4)
1220 bitrate *= (modulation + 1);
1221 else if (modulation == 4)
1222 bitrate *= (modulation + 2);
1223 else
1224 bitrate *= (modulation + 3);
1225
1226 bitrate *= streams;
1227
1228 if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
1229 bitrate = (bitrate / 9) * 10;
1230
1231 /* do NOT round down here */
1232 return (bitrate + 50000) / 100000;
1233}
1234
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001235static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
1236 int flags, struct net_device *dev,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001237 u8 *mac_addr, struct station_info *sinfo)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001238{
1239 void *hdr;
Henning Rogge420e7fa2008-12-11 22:04:19 +01001240 struct nlattr *sinfoattr, *txrate;
1241 u16 bitrate;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001242
1243 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
1244 if (!hdr)
1245 return -1;
1246
1247 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1248 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1249
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001250 sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
1251 if (!sinfoattr)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001252 goto nla_put_failure;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001253 if (sinfo->filled & STATION_INFO_INACTIVE_TIME)
1254 NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME,
1255 sinfo->inactive_time);
1256 if (sinfo->filled & STATION_INFO_RX_BYTES)
1257 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES,
1258 sinfo->rx_bytes);
1259 if (sinfo->filled & STATION_INFO_TX_BYTES)
1260 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES,
1261 sinfo->tx_bytes);
1262 if (sinfo->filled & STATION_INFO_LLID)
1263 NLA_PUT_U16(msg, NL80211_STA_INFO_LLID,
1264 sinfo->llid);
1265 if (sinfo->filled & STATION_INFO_PLID)
1266 NLA_PUT_U16(msg, NL80211_STA_INFO_PLID,
1267 sinfo->plid);
1268 if (sinfo->filled & STATION_INFO_PLINK_STATE)
1269 NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
1270 sinfo->plink_state);
Henning Rogge420e7fa2008-12-11 22:04:19 +01001271 if (sinfo->filled & STATION_INFO_SIGNAL)
1272 NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL,
1273 sinfo->signal);
1274 if (sinfo->filled & STATION_INFO_TX_BITRATE) {
1275 txrate = nla_nest_start(msg, NL80211_STA_INFO_TX_BITRATE);
1276 if (!txrate)
1277 goto nla_put_failure;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001278
Henning Rogge420e7fa2008-12-11 22:04:19 +01001279 /* nl80211_calculate_bitrate will return 0 for mcs >= 32 */
1280 bitrate = nl80211_calculate_bitrate(&sinfo->txrate);
1281 if (bitrate > 0)
1282 NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
1283
1284 if (sinfo->txrate.flags & RATE_INFO_FLAGS_MCS)
1285 NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS,
1286 sinfo->txrate.mcs);
1287 if (sinfo->txrate.flags & RATE_INFO_FLAGS_40_MHZ_WIDTH)
1288 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH);
1289 if (sinfo->txrate.flags & RATE_INFO_FLAGS_SHORT_GI)
1290 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI);
1291
1292 nla_nest_end(msg, txrate);
1293 }
Jouni Malinen98c8a60a2009-02-17 13:24:57 +02001294 if (sinfo->filled & STATION_INFO_RX_PACKETS)
1295 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS,
1296 sinfo->rx_packets);
1297 if (sinfo->filled & STATION_INFO_TX_PACKETS)
1298 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS,
1299 sinfo->tx_packets);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001300 nla_nest_end(msg, sinfoattr);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001301
1302 return genlmsg_end(msg, hdr);
1303
1304 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001305 genlmsg_cancel(msg, hdr);
1306 return -EMSGSIZE;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001307}
1308
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001309static int nl80211_dump_station(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02001310 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001311{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001312 struct station_info sinfo;
1313 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001314 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001315 u8 mac_addr[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02001316 int ifidx = cb->args[0];
1317 int sta_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001318 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001319
Johannes Bergbba95fe2008-07-29 13:22:51 +02001320 if (!ifidx) {
1321 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
1322 nl80211_fam.attrbuf, nl80211_fam.maxattr,
1323 nl80211_policy);
1324 if (err)
1325 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001326
Johannes Bergbba95fe2008-07-29 13:22:51 +02001327 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
1328 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001329
Johannes Bergbba95fe2008-07-29 13:22:51 +02001330 ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
1331 if (!ifidx)
1332 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001333 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001334
Johannes Berg3b858752009-03-12 09:55:09 +01001335 rtnl_lock();
1336
1337 netdev = __dev_get_by_index(&init_net, ifidx);
1338 if (!netdev) {
1339 err = -ENODEV;
1340 goto out_rtnl;
1341 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001342
Johannes Bergbba95fe2008-07-29 13:22:51 +02001343 dev = cfg80211_get_dev_from_ifindex(ifidx);
1344 if (IS_ERR(dev)) {
1345 err = PTR_ERR(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001346 goto out_rtnl;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001347 }
1348
1349 if (!dev->ops->dump_station) {
Jouni Malineneec60b02009-03-20 21:21:19 +02001350 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001351 goto out_err;
1352 }
1353
Johannes Bergbba95fe2008-07-29 13:22:51 +02001354 while (1) {
1355 err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
1356 mac_addr, &sinfo);
1357 if (err == -ENOENT)
1358 break;
1359 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001360 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001361
1362 if (nl80211_send_station(skb,
1363 NETLINK_CB(cb->skb).pid,
1364 cb->nlh->nlmsg_seq, NLM_F_MULTI,
1365 netdev, mac_addr,
1366 &sinfo) < 0)
1367 goto out;
1368
1369 sta_idx++;
1370 }
1371
1372
1373 out:
1374 cb->args[1] = sta_idx;
1375 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001376 out_err:
1377 cfg80211_put_dev(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001378 out_rtnl:
1379 rtnl_unlock();
Johannes Bergbba95fe2008-07-29 13:22:51 +02001380
1381 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001382}
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001383
Johannes Berg5727ef12007-12-19 02:03:34 +01001384static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
1385{
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001386 struct cfg80211_registered_device *drv;
1387 int err;
1388 struct net_device *dev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001389 struct station_info sinfo;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001390 struct sk_buff *msg;
1391 u8 *mac_addr = NULL;
1392
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001393 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001394
1395 if (!info->attrs[NL80211_ATTR_MAC])
1396 return -EINVAL;
1397
1398 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1399
Johannes Berg3b858752009-03-12 09:55:09 +01001400 rtnl_lock();
1401
Johannes Bergbba95fe2008-07-29 13:22:51 +02001402 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001403 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001404 goto out_rtnl;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001405
1406 if (!drv->ops->get_station) {
1407 err = -EOPNOTSUPP;
1408 goto out;
1409 }
1410
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001411 err = drv->ops->get_station(&drv->wiphy, dev, mac_addr, &sinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001412 if (err)
1413 goto out;
1414
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001415 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1416 if (!msg)
1417 goto out;
1418
1419 if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001420 dev, mac_addr, &sinfo) < 0)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001421 goto out_free;
1422
1423 err = genlmsg_unicast(msg, info->snd_pid);
1424 goto out;
1425
1426 out_free:
1427 nlmsg_free(msg);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001428 out:
1429 cfg80211_put_dev(drv);
1430 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001431 out_rtnl:
1432 rtnl_unlock();
1433
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001434 return err;
Johannes Berg5727ef12007-12-19 02:03:34 +01001435}
1436
1437/*
1438 * Get vlan interface making sure it is on the right wiphy.
1439 */
1440static int get_vlan(struct nlattr *vlanattr,
1441 struct cfg80211_registered_device *rdev,
1442 struct net_device **vlan)
1443{
1444 *vlan = NULL;
1445
1446 if (vlanattr) {
1447 *vlan = dev_get_by_index(&init_net, nla_get_u32(vlanattr));
1448 if (!*vlan)
1449 return -ENODEV;
1450 if (!(*vlan)->ieee80211_ptr)
1451 return -EINVAL;
1452 if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy)
1453 return -EINVAL;
1454 }
1455 return 0;
1456}
1457
1458static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
1459{
1460 struct cfg80211_registered_device *drv;
1461 int err;
1462 struct net_device *dev;
1463 struct station_parameters params;
1464 u8 *mac_addr = NULL;
1465
1466 memset(&params, 0, sizeof(params));
1467
1468 params.listen_interval = -1;
1469
1470 if (info->attrs[NL80211_ATTR_STA_AID])
1471 return -EINVAL;
1472
1473 if (!info->attrs[NL80211_ATTR_MAC])
1474 return -EINVAL;
1475
1476 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1477
1478 if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
1479 params.supported_rates =
1480 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1481 params.supported_rates_len =
1482 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1483 }
1484
1485 if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
1486 params.listen_interval =
1487 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
1488
Jouni Malinen36aedc902008-08-25 11:58:58 +03001489 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
1490 params.ht_capa =
1491 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
1492
Johannes Berg5727ef12007-12-19 02:03:34 +01001493 if (parse_station_flags(info->attrs[NL80211_ATTR_STA_FLAGS],
1494 &params.station_flags))
1495 return -EINVAL;
1496
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001497 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
1498 params.plink_action =
1499 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
1500
Johannes Berg3b858752009-03-12 09:55:09 +01001501 rtnl_lock();
1502
Johannes Bergbba95fe2008-07-29 13:22:51 +02001503 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001504 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001505 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01001506
1507 err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, &params.vlan);
1508 if (err)
1509 goto out;
1510
1511 if (!drv->ops->change_station) {
1512 err = -EOPNOTSUPP;
1513 goto out;
1514 }
1515
Johannes Berg5727ef12007-12-19 02:03:34 +01001516 err = drv->ops->change_station(&drv->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01001517
1518 out:
1519 if (params.vlan)
1520 dev_put(params.vlan);
1521 cfg80211_put_dev(drv);
1522 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001523 out_rtnl:
1524 rtnl_unlock();
1525
Johannes Berg5727ef12007-12-19 02:03:34 +01001526 return err;
1527}
1528
1529static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
1530{
1531 struct cfg80211_registered_device *drv;
1532 int err;
1533 struct net_device *dev;
1534 struct station_parameters params;
1535 u8 *mac_addr = NULL;
1536
1537 memset(&params, 0, sizeof(params));
1538
1539 if (!info->attrs[NL80211_ATTR_MAC])
1540 return -EINVAL;
1541
1542 if (!info->attrs[NL80211_ATTR_STA_AID])
1543 return -EINVAL;
1544
1545 if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
1546 return -EINVAL;
1547
1548 if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
1549 return -EINVAL;
1550
1551 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1552 params.supported_rates =
1553 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1554 params.supported_rates_len =
1555 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1556 params.listen_interval =
1557 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
Johannes Berg16f2e852008-04-07 14:35:46 +02001558 params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
Jouni Malinen36aedc902008-08-25 11:58:58 +03001559 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
1560 params.ht_capa =
1561 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
Johannes Berg5727ef12007-12-19 02:03:34 +01001562
1563 if (parse_station_flags(info->attrs[NL80211_ATTR_STA_FLAGS],
1564 &params.station_flags))
1565 return -EINVAL;
1566
Johannes Berg3b858752009-03-12 09:55:09 +01001567 rtnl_lock();
1568
Johannes Bergbba95fe2008-07-29 13:22:51 +02001569 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001570 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001571 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01001572
1573 err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, &params.vlan);
1574 if (err)
1575 goto out;
1576
1577 if (!drv->ops->add_station) {
1578 err = -EOPNOTSUPP;
1579 goto out;
1580 }
1581
Jouni Malinen35a8efe2009-03-20 21:21:18 +02001582 if (!netif_running(dev)) {
1583 err = -ENETDOWN;
1584 goto out;
1585 }
1586
Johannes Berg5727ef12007-12-19 02:03:34 +01001587 err = drv->ops->add_station(&drv->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01001588
1589 out:
1590 if (params.vlan)
1591 dev_put(params.vlan);
1592 cfg80211_put_dev(drv);
1593 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001594 out_rtnl:
1595 rtnl_unlock();
1596
Johannes Berg5727ef12007-12-19 02:03:34 +01001597 return err;
1598}
1599
1600static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
1601{
1602 struct cfg80211_registered_device *drv;
1603 int err;
1604 struct net_device *dev;
1605 u8 *mac_addr = NULL;
1606
1607 if (info->attrs[NL80211_ATTR_MAC])
1608 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1609
Johannes Berg3b858752009-03-12 09:55:09 +01001610 rtnl_lock();
1611
Johannes Bergbba95fe2008-07-29 13:22:51 +02001612 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001613 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001614 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01001615
1616 if (!drv->ops->del_station) {
1617 err = -EOPNOTSUPP;
1618 goto out;
1619 }
1620
Johannes Berg5727ef12007-12-19 02:03:34 +01001621 err = drv->ops->del_station(&drv->wiphy, dev, mac_addr);
Johannes Berg5727ef12007-12-19 02:03:34 +01001622
1623 out:
1624 cfg80211_put_dev(drv);
1625 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001626 out_rtnl:
1627 rtnl_unlock();
1628
Johannes Berg5727ef12007-12-19 02:03:34 +01001629 return err;
1630}
1631
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001632static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq,
1633 int flags, struct net_device *dev,
1634 u8 *dst, u8 *next_hop,
1635 struct mpath_info *pinfo)
1636{
1637 void *hdr;
1638 struct nlattr *pinfoattr;
1639
1640 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
1641 if (!hdr)
1642 return -1;
1643
1644 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1645 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
1646 NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop);
1647
1648 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
1649 if (!pinfoattr)
1650 goto nla_put_failure;
1651 if (pinfo->filled & MPATH_INFO_FRAME_QLEN)
1652 NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
1653 pinfo->frame_qlen);
1654 if (pinfo->filled & MPATH_INFO_DSN)
1655 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DSN,
1656 pinfo->dsn);
1657 if (pinfo->filled & MPATH_INFO_METRIC)
1658 NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC,
1659 pinfo->metric);
1660 if (pinfo->filled & MPATH_INFO_EXPTIME)
1661 NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME,
1662 pinfo->exptime);
1663 if (pinfo->filled & MPATH_INFO_FLAGS)
1664 NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS,
1665 pinfo->flags);
1666 if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT)
1667 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
1668 pinfo->discovery_timeout);
1669 if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES)
1670 NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
1671 pinfo->discovery_retries);
1672
1673 nla_nest_end(msg, pinfoattr);
1674
1675 return genlmsg_end(msg, hdr);
1676
1677 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001678 genlmsg_cancel(msg, hdr);
1679 return -EMSGSIZE;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001680}
1681
1682static int nl80211_dump_mpath(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02001683 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001684{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001685 struct mpath_info pinfo;
1686 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001687 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001688 u8 dst[ETH_ALEN];
1689 u8 next_hop[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02001690 int ifidx = cb->args[0];
1691 int path_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001692 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001693
Johannes Bergbba95fe2008-07-29 13:22:51 +02001694 if (!ifidx) {
1695 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
1696 nl80211_fam.attrbuf, nl80211_fam.maxattr,
1697 nl80211_policy);
1698 if (err)
1699 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001700
Johannes Bergbba95fe2008-07-29 13:22:51 +02001701 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
1702 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001703
Johannes Bergbba95fe2008-07-29 13:22:51 +02001704 ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
1705 if (!ifidx)
1706 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001707 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001708
Johannes Berg3b858752009-03-12 09:55:09 +01001709 rtnl_lock();
1710
1711 netdev = __dev_get_by_index(&init_net, ifidx);
1712 if (!netdev) {
1713 err = -ENODEV;
1714 goto out_rtnl;
1715 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001716
Johannes Bergbba95fe2008-07-29 13:22:51 +02001717 dev = cfg80211_get_dev_from_ifindex(ifidx);
1718 if (IS_ERR(dev)) {
1719 err = PTR_ERR(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001720 goto out_rtnl;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001721 }
1722
1723 if (!dev->ops->dump_mpath) {
Jouni Malineneec60b02009-03-20 21:21:19 +02001724 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001725 goto out_err;
1726 }
1727
Jouni Malineneec60b02009-03-20 21:21:19 +02001728 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
1729 err = -EOPNOTSUPP;
1730 goto out;
1731 }
1732
Johannes Bergbba95fe2008-07-29 13:22:51 +02001733 while (1) {
1734 err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
1735 dst, next_hop, &pinfo);
1736 if (err == -ENOENT)
1737 break;
1738 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001739 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001740
1741 if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid,
1742 cb->nlh->nlmsg_seq, NLM_F_MULTI,
1743 netdev, dst, next_hop,
1744 &pinfo) < 0)
1745 goto out;
1746
1747 path_idx++;
1748 }
1749
1750
1751 out:
1752 cb->args[1] = path_idx;
1753 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001754 out_err:
1755 cfg80211_put_dev(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001756 out_rtnl:
1757 rtnl_unlock();
Johannes Bergbba95fe2008-07-29 13:22:51 +02001758
1759 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001760}
1761
1762static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
1763{
1764 struct cfg80211_registered_device *drv;
1765 int err;
1766 struct net_device *dev;
1767 struct mpath_info pinfo;
1768 struct sk_buff *msg;
1769 u8 *dst = NULL;
1770 u8 next_hop[ETH_ALEN];
1771
1772 memset(&pinfo, 0, sizeof(pinfo));
1773
1774 if (!info->attrs[NL80211_ATTR_MAC])
1775 return -EINVAL;
1776
1777 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
1778
Johannes Berg3b858752009-03-12 09:55:09 +01001779 rtnl_lock();
1780
Johannes Bergbba95fe2008-07-29 13:22:51 +02001781 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001782 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001783 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001784
1785 if (!drv->ops->get_mpath) {
1786 err = -EOPNOTSUPP;
1787 goto out;
1788 }
1789
Jouni Malineneec60b02009-03-20 21:21:19 +02001790 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
1791 err = -EOPNOTSUPP;
1792 goto out;
1793 }
1794
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001795 err = drv->ops->get_mpath(&drv->wiphy, dev, dst, next_hop, &pinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001796 if (err)
1797 goto out;
1798
1799 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1800 if (!msg)
1801 goto out;
1802
1803 if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0,
1804 dev, dst, next_hop, &pinfo) < 0)
1805 goto out_free;
1806
1807 err = genlmsg_unicast(msg, info->snd_pid);
1808 goto out;
1809
1810 out_free:
1811 nlmsg_free(msg);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001812 out:
1813 cfg80211_put_dev(drv);
1814 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001815 out_rtnl:
1816 rtnl_unlock();
1817
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001818 return err;
1819}
1820
1821static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
1822{
1823 struct cfg80211_registered_device *drv;
1824 int err;
1825 struct net_device *dev;
1826 u8 *dst = NULL;
1827 u8 *next_hop = NULL;
1828
1829 if (!info->attrs[NL80211_ATTR_MAC])
1830 return -EINVAL;
1831
1832 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
1833 return -EINVAL;
1834
1835 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
1836 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
1837
Johannes Berg3b858752009-03-12 09:55:09 +01001838 rtnl_lock();
1839
Johannes Bergbba95fe2008-07-29 13:22:51 +02001840 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001841 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001842 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001843
1844 if (!drv->ops->change_mpath) {
1845 err = -EOPNOTSUPP;
1846 goto out;
1847 }
1848
Jouni Malineneec60b02009-03-20 21:21:19 +02001849 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
1850 err = -EOPNOTSUPP;
1851 goto out;
1852 }
1853
Jouni Malinen35a8efe2009-03-20 21:21:18 +02001854 if (!netif_running(dev)) {
1855 err = -ENETDOWN;
1856 goto out;
1857 }
1858
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001859 err = drv->ops->change_mpath(&drv->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001860
1861 out:
1862 cfg80211_put_dev(drv);
1863 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001864 out_rtnl:
1865 rtnl_unlock();
1866
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001867 return err;
1868}
1869static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
1870{
1871 struct cfg80211_registered_device *drv;
1872 int err;
1873 struct net_device *dev;
1874 u8 *dst = NULL;
1875 u8 *next_hop = NULL;
1876
1877 if (!info->attrs[NL80211_ATTR_MAC])
1878 return -EINVAL;
1879
1880 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
1881 return -EINVAL;
1882
1883 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
1884 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
1885
Johannes Berg3b858752009-03-12 09:55:09 +01001886 rtnl_lock();
1887
Johannes Bergbba95fe2008-07-29 13:22:51 +02001888 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001889 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001890 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001891
1892 if (!drv->ops->add_mpath) {
1893 err = -EOPNOTSUPP;
1894 goto out;
1895 }
1896
Jouni Malineneec60b02009-03-20 21:21:19 +02001897 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
1898 err = -EOPNOTSUPP;
1899 goto out;
1900 }
1901
Jouni Malinen35a8efe2009-03-20 21:21:18 +02001902 if (!netif_running(dev)) {
1903 err = -ENETDOWN;
1904 goto out;
1905 }
1906
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001907 err = drv->ops->add_mpath(&drv->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001908
1909 out:
1910 cfg80211_put_dev(drv);
1911 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001912 out_rtnl:
1913 rtnl_unlock();
1914
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001915 return err;
1916}
1917
1918static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
1919{
1920 struct cfg80211_registered_device *drv;
1921 int err;
1922 struct net_device *dev;
1923 u8 *dst = NULL;
1924
1925 if (info->attrs[NL80211_ATTR_MAC])
1926 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
1927
Johannes Berg3b858752009-03-12 09:55:09 +01001928 rtnl_lock();
1929
Johannes Bergbba95fe2008-07-29 13:22:51 +02001930 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001931 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001932 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001933
1934 if (!drv->ops->del_mpath) {
1935 err = -EOPNOTSUPP;
1936 goto out;
1937 }
1938
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001939 err = drv->ops->del_mpath(&drv->wiphy, dev, dst);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001940
1941 out:
1942 cfg80211_put_dev(drv);
1943 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001944 out_rtnl:
1945 rtnl_unlock();
1946
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001947 return err;
1948}
1949
Jouni Malinen9f1ba902008-08-07 20:07:01 +03001950static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
1951{
1952 struct cfg80211_registered_device *drv;
1953 int err;
1954 struct net_device *dev;
1955 struct bss_parameters params;
1956
1957 memset(&params, 0, sizeof(params));
1958 /* default to not changing parameters */
1959 params.use_cts_prot = -1;
1960 params.use_short_preamble = -1;
1961 params.use_short_slot_time = -1;
1962
1963 if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
1964 params.use_cts_prot =
1965 nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
1966 if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
1967 params.use_short_preamble =
1968 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
1969 if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
1970 params.use_short_slot_time =
1971 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
Jouni Malinen90c97a02008-10-30 16:59:22 +02001972 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
1973 params.basic_rates =
1974 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
1975 params.basic_rates_len =
1976 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
1977 }
Jouni Malinen9f1ba902008-08-07 20:07:01 +03001978
Johannes Berg3b858752009-03-12 09:55:09 +01001979 rtnl_lock();
1980
Jouni Malinen9f1ba902008-08-07 20:07:01 +03001981 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
1982 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001983 goto out_rtnl;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03001984
1985 if (!drv->ops->change_bss) {
1986 err = -EOPNOTSUPP;
1987 goto out;
1988 }
1989
Jouni Malineneec60b02009-03-20 21:21:19 +02001990 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
1991 err = -EOPNOTSUPP;
1992 goto out;
1993 }
1994
Jouni Malinen9f1ba902008-08-07 20:07:01 +03001995 err = drv->ops->change_bss(&drv->wiphy, dev, &params);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03001996
1997 out:
1998 cfg80211_put_dev(drv);
1999 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002000 out_rtnl:
2001 rtnl_unlock();
2002
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002003 return err;
2004}
2005
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002006static const struct nla_policy
2007 reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
2008 [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
2009 [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
2010 [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
2011 [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
2012 [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
2013 [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
2014};
2015
2016static int parse_reg_rule(struct nlattr *tb[],
2017 struct ieee80211_reg_rule *reg_rule)
2018{
2019 struct ieee80211_freq_range *freq_range = &reg_rule->freq_range;
2020 struct ieee80211_power_rule *power_rule = &reg_rule->power_rule;
2021
2022 if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
2023 return -EINVAL;
2024 if (!tb[NL80211_ATTR_FREQ_RANGE_START])
2025 return -EINVAL;
2026 if (!tb[NL80211_ATTR_FREQ_RANGE_END])
2027 return -EINVAL;
2028 if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
2029 return -EINVAL;
2030 if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
2031 return -EINVAL;
2032
2033 reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
2034
2035 freq_range->start_freq_khz =
2036 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
2037 freq_range->end_freq_khz =
2038 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
2039 freq_range->max_bandwidth_khz =
2040 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
2041
2042 power_rule->max_eirp =
2043 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
2044
2045 if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
2046 power_rule->max_antenna_gain =
2047 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
2048
2049 return 0;
2050}
2051
2052static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
2053{
2054 int r;
2055 char *data = NULL;
2056
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002057 /*
2058 * You should only get this when cfg80211 hasn't yet initialized
2059 * completely when built-in to the kernel right between the time
2060 * window between nl80211_init() and regulatory_init(), if that is
2061 * even possible.
2062 */
2063 mutex_lock(&cfg80211_mutex);
2064 if (unlikely(!cfg80211_regdomain)) {
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002065 mutex_unlock(&cfg80211_mutex);
2066 return -EINPROGRESS;
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002067 }
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002068 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002069
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002070 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
2071 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002072
2073 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
2074
2075#ifdef CONFIG_WIRELESS_OLD_REGULATORY
2076 /* We ignore world regdom requests with the old regdom setup */
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002077 if (is_world_regdom(data))
2078 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002079#endif
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002080
2081 r = regulatory_hint_user(data);
2082
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002083 return r;
2084}
2085
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002086static int nl80211_get_mesh_params(struct sk_buff *skb,
2087 struct genl_info *info)
2088{
2089 struct cfg80211_registered_device *drv;
2090 struct mesh_config cur_params;
2091 int err;
2092 struct net_device *dev;
2093 void *hdr;
2094 struct nlattr *pinfoattr;
2095 struct sk_buff *msg;
2096
Johannes Berg3b858752009-03-12 09:55:09 +01002097 rtnl_lock();
2098
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002099 /* Look up our device */
2100 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2101 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002102 goto out_rtnl;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002103
Jouni Malinenf3f92582009-03-20 17:57:36 +02002104 if (!drv->ops->get_mesh_params) {
2105 err = -EOPNOTSUPP;
2106 goto out;
2107 }
2108
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002109 /* Get the mesh params */
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002110 err = drv->ops->get_mesh_params(&drv->wiphy, dev, &cur_params);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002111 if (err)
2112 goto out;
2113
2114 /* Draw up a netlink message to send back */
2115 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
2116 if (!msg) {
2117 err = -ENOBUFS;
2118 goto out;
2119 }
2120 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
2121 NL80211_CMD_GET_MESH_PARAMS);
2122 if (!hdr)
2123 goto nla_put_failure;
2124 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS);
2125 if (!pinfoattr)
2126 goto nla_put_failure;
2127 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2128 NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
2129 cur_params.dot11MeshRetryTimeout);
2130 NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
2131 cur_params.dot11MeshConfirmTimeout);
2132 NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
2133 cur_params.dot11MeshHoldingTimeout);
2134 NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
2135 cur_params.dot11MeshMaxPeerLinks);
2136 NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES,
2137 cur_params.dot11MeshMaxRetries);
2138 NLA_PUT_U8(msg, NL80211_MESHCONF_TTL,
2139 cur_params.dot11MeshTTL);
2140 NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
2141 cur_params.auto_open_plinks);
2142 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
2143 cur_params.dot11MeshHWMPmaxPREQretries);
2144 NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
2145 cur_params.path_refresh_time);
2146 NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
2147 cur_params.min_discovery_timeout);
2148 NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
2149 cur_params.dot11MeshHWMPactivePathTimeout);
2150 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
2151 cur_params.dot11MeshHWMPpreqMinInterval);
2152 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
2153 cur_params.dot11MeshHWMPnetDiameterTraversalTime);
2154 nla_nest_end(msg, pinfoattr);
2155 genlmsg_end(msg, hdr);
2156 err = genlmsg_unicast(msg, info->snd_pid);
2157 goto out;
2158
Johannes Berg3b858752009-03-12 09:55:09 +01002159 nla_put_failure:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002160 genlmsg_cancel(msg, hdr);
2161 err = -EMSGSIZE;
Johannes Berg3b858752009-03-12 09:55:09 +01002162 out:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002163 /* Cleanup */
2164 cfg80211_put_dev(drv);
2165 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002166 out_rtnl:
2167 rtnl_unlock();
2168
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002169 return err;
2170}
2171
2172#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \
2173do {\
2174 if (table[attr_num]) {\
2175 cfg.param = nla_fn(table[attr_num]); \
2176 mask |= (1 << (attr_num - 1)); \
2177 } \
2178} while (0);\
2179
2180static struct nla_policy
2181nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] __read_mostly = {
2182 [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
2183 [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
2184 [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
2185 [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
2186 [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
2187 [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
2188 [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
2189
2190 [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
2191 [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
2192 [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
2193 [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
2194 [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
2195 [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
2196};
2197
2198static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
2199{
2200 int err;
2201 u32 mask;
2202 struct cfg80211_registered_device *drv;
2203 struct net_device *dev;
2204 struct mesh_config cfg;
2205 struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
2206 struct nlattr *parent_attr;
2207
2208 parent_attr = info->attrs[NL80211_ATTR_MESH_PARAMS];
2209 if (!parent_attr)
2210 return -EINVAL;
2211 if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
2212 parent_attr, nl80211_meshconf_params_policy))
2213 return -EINVAL;
2214
Johannes Berg3b858752009-03-12 09:55:09 +01002215 rtnl_lock();
2216
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002217 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2218 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002219 goto out_rtnl;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002220
Jouni Malinenf3f92582009-03-20 17:57:36 +02002221 if (!drv->ops->set_mesh_params) {
2222 err = -EOPNOTSUPP;
2223 goto out;
2224 }
2225
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002226 /* This makes sure that there aren't more than 32 mesh config
2227 * parameters (otherwise our bitfield scheme would not work.) */
2228 BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
2229
2230 /* Fill in the params struct */
2231 mask = 0;
2232 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout,
2233 mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16);
2234 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout,
2235 mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16);
2236 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout,
2237 mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16);
2238 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks,
2239 mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16);
2240 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries,
2241 mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8);
2242 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL,
2243 mask, NL80211_MESHCONF_TTL, nla_get_u8);
2244 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
2245 mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8);
2246 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
2247 mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
2248 nla_get_u8);
2249 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time,
2250 mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32);
2251 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout,
2252 mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
2253 nla_get_u16);
2254 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
2255 mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
2256 nla_get_u32);
2257 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
2258 mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
2259 nla_get_u16);
2260 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
2261 dot11MeshHWMPnetDiameterTraversalTime,
2262 mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
2263 nla_get_u16);
2264
2265 /* Apply changes */
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002266 err = drv->ops->set_mesh_params(&drv->wiphy, dev, &cfg, mask);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002267
Jouni Malinenf3f92582009-03-20 17:57:36 +02002268 out:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002269 /* cleanup */
2270 cfg80211_put_dev(drv);
2271 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002272 out_rtnl:
2273 rtnl_unlock();
2274
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002275 return err;
2276}
2277
2278#undef FILL_IN_MESH_PARAM_IF_SET
2279
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002280static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
2281{
2282 struct sk_buff *msg;
2283 void *hdr = NULL;
2284 struct nlattr *nl_reg_rules;
2285 unsigned int i;
2286 int err = -EINVAL;
2287
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002288 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002289
2290 if (!cfg80211_regdomain)
2291 goto out;
2292
2293 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
2294 if (!msg) {
2295 err = -ENOBUFS;
2296 goto out;
2297 }
2298
2299 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
2300 NL80211_CMD_GET_REG);
2301 if (!hdr)
2302 goto nla_put_failure;
2303
2304 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2,
2305 cfg80211_regdomain->alpha2);
2306
2307 nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
2308 if (!nl_reg_rules)
2309 goto nla_put_failure;
2310
2311 for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) {
2312 struct nlattr *nl_reg_rule;
2313 const struct ieee80211_reg_rule *reg_rule;
2314 const struct ieee80211_freq_range *freq_range;
2315 const struct ieee80211_power_rule *power_rule;
2316
2317 reg_rule = &cfg80211_regdomain->reg_rules[i];
2318 freq_range = &reg_rule->freq_range;
2319 power_rule = &reg_rule->power_rule;
2320
2321 nl_reg_rule = nla_nest_start(msg, i);
2322 if (!nl_reg_rule)
2323 goto nla_put_failure;
2324
2325 NLA_PUT_U32(msg, NL80211_ATTR_REG_RULE_FLAGS,
2326 reg_rule->flags);
2327 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_START,
2328 freq_range->start_freq_khz);
2329 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_END,
2330 freq_range->end_freq_khz);
2331 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
2332 freq_range->max_bandwidth_khz);
2333 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
2334 power_rule->max_antenna_gain);
2335 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
2336 power_rule->max_eirp);
2337
2338 nla_nest_end(msg, nl_reg_rule);
2339 }
2340
2341 nla_nest_end(msg, nl_reg_rules);
2342
2343 genlmsg_end(msg, hdr);
2344 err = genlmsg_unicast(msg, info->snd_pid);
2345 goto out;
2346
2347nla_put_failure:
2348 genlmsg_cancel(msg, hdr);
2349 err = -EMSGSIZE;
2350out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002351 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002352 return err;
2353}
2354
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002355static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
2356{
2357 struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
2358 struct nlattr *nl_reg_rule;
2359 char *alpha2 = NULL;
2360 int rem_reg_rules = 0, r = 0;
2361 u32 num_rules = 0, rule_idx = 0, size_of_regd;
2362 struct ieee80211_regdomain *rd = NULL;
2363
2364 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
2365 return -EINVAL;
2366
2367 if (!info->attrs[NL80211_ATTR_REG_RULES])
2368 return -EINVAL;
2369
2370 alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
2371
2372 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
2373 rem_reg_rules) {
2374 num_rules++;
2375 if (num_rules > NL80211_MAX_SUPP_REG_RULES)
2376 goto bad_reg;
2377 }
2378
2379 if (!reg_is_valid_request(alpha2))
2380 return -EINVAL;
2381
2382 size_of_regd = sizeof(struct ieee80211_regdomain) +
2383 (num_rules * sizeof(struct ieee80211_reg_rule));
2384
2385 rd = kzalloc(size_of_regd, GFP_KERNEL);
2386 if (!rd)
2387 return -ENOMEM;
2388
2389 rd->n_reg_rules = num_rules;
2390 rd->alpha2[0] = alpha2[0];
2391 rd->alpha2[1] = alpha2[1];
2392
2393 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
2394 rem_reg_rules) {
2395 nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
2396 nla_data(nl_reg_rule), nla_len(nl_reg_rule),
2397 reg_rule_policy);
2398 r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
2399 if (r)
2400 goto bad_reg;
2401
2402 rule_idx++;
2403
2404 if (rule_idx > NL80211_MAX_SUPP_REG_RULES)
2405 goto bad_reg;
2406 }
2407
2408 BUG_ON(rule_idx != num_rules);
2409
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002410 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002411 r = set_regdom(rd);
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002412 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002413 return r;
2414
Johannes Bergd2372b32008-10-24 20:32:20 +02002415 bad_reg:
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002416 kfree(rd);
2417 return -EINVAL;
2418}
2419
Johannes Berg2a519312009-02-10 21:25:55 +01002420static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
2421{
2422 struct cfg80211_registered_device *drv;
2423 struct net_device *dev;
2424 struct cfg80211_scan_request *request;
2425 struct cfg80211_ssid *ssid;
2426 struct ieee80211_channel *channel;
2427 struct nlattr *attr;
2428 struct wiphy *wiphy;
2429 int err, tmp, n_ssids = 0, n_channels = 0, i;
2430 enum ieee80211_band band;
Jouni Malinen70692ad2009-02-16 19:39:13 +02002431 size_t ie_len;
Johannes Berg2a519312009-02-10 21:25:55 +01002432
Johannes Berg3b858752009-03-12 09:55:09 +01002433 rtnl_lock();
2434
Johannes Berg2a519312009-02-10 21:25:55 +01002435 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2436 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002437 goto out_rtnl;
Johannes Berg2a519312009-02-10 21:25:55 +01002438
2439 wiphy = &drv->wiphy;
2440
2441 if (!drv->ops->scan) {
2442 err = -EOPNOTSUPP;
2443 goto out;
2444 }
2445
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002446 if (!netif_running(dev)) {
2447 err = -ENETDOWN;
2448 goto out;
2449 }
2450
Johannes Berg2a519312009-02-10 21:25:55 +01002451 if (drv->scan_req) {
2452 err = -EBUSY;
Johannes Berg3b858752009-03-12 09:55:09 +01002453 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002454 }
2455
2456 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
2457 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp)
2458 n_channels++;
2459 if (!n_channels) {
2460 err = -EINVAL;
Johannes Berg3b858752009-03-12 09:55:09 +01002461 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002462 }
2463 } else {
2464 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
2465 if (wiphy->bands[band])
2466 n_channels += wiphy->bands[band]->n_channels;
2467 }
2468
2469 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
2470 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
2471 n_ssids++;
2472
2473 if (n_ssids > wiphy->max_scan_ssids) {
2474 err = -EINVAL;
Johannes Berg3b858752009-03-12 09:55:09 +01002475 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002476 }
2477
Jouni Malinen70692ad2009-02-16 19:39:13 +02002478 if (info->attrs[NL80211_ATTR_IE])
2479 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
2480 else
2481 ie_len = 0;
2482
Johannes Berg2a519312009-02-10 21:25:55 +01002483 request = kzalloc(sizeof(*request)
2484 + sizeof(*ssid) * n_ssids
Jouni Malinen70692ad2009-02-16 19:39:13 +02002485 + sizeof(channel) * n_channels
2486 + ie_len, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01002487 if (!request) {
2488 err = -ENOMEM;
Johannes Berg3b858752009-03-12 09:55:09 +01002489 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002490 }
2491
2492 request->channels = (void *)((char *)request + sizeof(*request));
2493 request->n_channels = n_channels;
2494 if (n_ssids)
2495 request->ssids = (void *)(request->channels + n_channels);
2496 request->n_ssids = n_ssids;
Jouni Malinen70692ad2009-02-16 19:39:13 +02002497 if (ie_len) {
2498 if (request->ssids)
2499 request->ie = (void *)(request->ssids + n_ssids);
2500 else
2501 request->ie = (void *)(request->channels + n_channels);
2502 }
Johannes Berg2a519312009-02-10 21:25:55 +01002503
2504 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
2505 /* user specified, bail out if channel not found */
2506 request->n_channels = n_channels;
2507 i = 0;
2508 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
2509 request->channels[i] = ieee80211_get_channel(wiphy, nla_get_u32(attr));
2510 if (!request->channels[i]) {
2511 err = -EINVAL;
2512 goto out_free;
2513 }
2514 i++;
2515 }
2516 } else {
2517 /* all channels */
2518 i = 0;
2519 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
2520 int j;
2521 if (!wiphy->bands[band])
2522 continue;
2523 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
2524 request->channels[i] = &wiphy->bands[band]->channels[j];
2525 i++;
2526 }
2527 }
2528 }
2529
2530 i = 0;
2531 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
2532 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
2533 if (request->ssids[i].ssid_len > IEEE80211_MAX_SSID_LEN) {
2534 err = -EINVAL;
2535 goto out_free;
2536 }
2537 memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
2538 request->ssids[i].ssid_len = nla_len(attr);
2539 i++;
2540 }
2541 }
2542
Jouni Malinen70692ad2009-02-16 19:39:13 +02002543 if (info->attrs[NL80211_ATTR_IE]) {
2544 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
2545 memcpy(request->ie, nla_data(info->attrs[NL80211_ATTR_IE]),
2546 request->ie_len);
2547 }
2548
Johannes Berg2a519312009-02-10 21:25:55 +01002549 request->ifidx = dev->ifindex;
2550 request->wiphy = &drv->wiphy;
2551
2552 drv->scan_req = request;
2553 err = drv->ops->scan(&drv->wiphy, dev, request);
2554
2555 out_free:
2556 if (err) {
2557 drv->scan_req = NULL;
2558 kfree(request);
2559 }
Johannes Berg2a519312009-02-10 21:25:55 +01002560 out:
2561 cfg80211_put_dev(drv);
2562 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002563 out_rtnl:
2564 rtnl_unlock();
2565
Johannes Berg2a519312009-02-10 21:25:55 +01002566 return err;
2567}
2568
2569static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
2570 struct cfg80211_registered_device *rdev,
2571 struct net_device *dev,
2572 struct cfg80211_bss *res)
2573{
2574 void *hdr;
2575 struct nlattr *bss;
2576
2577 hdr = nl80211hdr_put(msg, pid, seq, flags,
2578 NL80211_CMD_NEW_SCAN_RESULTS);
2579 if (!hdr)
2580 return -1;
2581
2582 NLA_PUT_U32(msg, NL80211_ATTR_SCAN_GENERATION,
2583 rdev->bss_generation);
2584 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2585
2586 bss = nla_nest_start(msg, NL80211_ATTR_BSS);
2587 if (!bss)
2588 goto nla_put_failure;
2589 if (!is_zero_ether_addr(res->bssid))
2590 NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid);
2591 if (res->information_elements && res->len_information_elements)
2592 NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS,
2593 res->len_information_elements,
2594 res->information_elements);
2595 if (res->tsf)
2596 NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf);
2597 if (res->beacon_interval)
2598 NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval);
2599 NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability);
2600 NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq);
2601
Johannes Berg77965c92009-02-18 18:45:06 +01002602 switch (rdev->wiphy.signal_type) {
Johannes Berg2a519312009-02-10 21:25:55 +01002603 case CFG80211_SIGNAL_TYPE_MBM:
2604 NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal);
2605 break;
2606 case CFG80211_SIGNAL_TYPE_UNSPEC:
2607 NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal);
2608 break;
2609 default:
2610 break;
2611 }
2612
2613 nla_nest_end(msg, bss);
2614
2615 return genlmsg_end(msg, hdr);
2616
2617 nla_put_failure:
2618 genlmsg_cancel(msg, hdr);
2619 return -EMSGSIZE;
2620}
2621
2622static int nl80211_dump_scan(struct sk_buff *skb,
2623 struct netlink_callback *cb)
2624{
2625 struct cfg80211_registered_device *dev;
2626 struct net_device *netdev;
2627 struct cfg80211_internal_bss *scan;
2628 int ifidx = cb->args[0];
2629 int start = cb->args[1], idx = 0;
2630 int err;
2631
2632 if (!ifidx) {
2633 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
2634 nl80211_fam.attrbuf, nl80211_fam.maxattr,
2635 nl80211_policy);
2636 if (err)
2637 return err;
2638
2639 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
2640 return -EINVAL;
2641
2642 ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
2643 if (!ifidx)
2644 return -EINVAL;
2645 cb->args[0] = ifidx;
2646 }
2647
2648 netdev = dev_get_by_index(&init_net, ifidx);
2649 if (!netdev)
2650 return -ENODEV;
2651
2652 dev = cfg80211_get_dev_from_ifindex(ifidx);
2653 if (IS_ERR(dev)) {
2654 err = PTR_ERR(dev);
2655 goto out_put_netdev;
2656 }
2657
2658 spin_lock_bh(&dev->bss_lock);
2659 cfg80211_bss_expire(dev);
2660
2661 list_for_each_entry(scan, &dev->bss_list, list) {
2662 if (++idx <= start)
2663 continue;
2664 if (nl80211_send_bss(skb,
2665 NETLINK_CB(cb->skb).pid,
2666 cb->nlh->nlmsg_seq, NLM_F_MULTI,
2667 dev, netdev, &scan->pub) < 0) {
2668 idx--;
2669 goto out;
2670 }
2671 }
2672
2673 out:
2674 spin_unlock_bh(&dev->bss_lock);
2675
2676 cb->args[1] = idx;
2677 err = skb->len;
2678 cfg80211_put_dev(dev);
2679 out_put_netdev:
2680 dev_put(netdev);
2681
2682 return err;
2683}
2684
Jouni Malinen255e7372009-03-20 21:21:17 +02002685static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
2686{
2687 return auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM ||
2688 auth_type == NL80211_AUTHTYPE_SHARED_KEY ||
2689 auth_type == NL80211_AUTHTYPE_FT ||
2690 auth_type == NL80211_AUTHTYPE_NETWORK_EAP;
2691}
2692
Jouni Malinen636a5d32009-03-19 13:39:22 +02002693static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
2694{
2695 struct cfg80211_registered_device *drv;
2696 struct net_device *dev;
2697 struct cfg80211_auth_request req;
2698 struct wiphy *wiphy;
2699 int err;
2700
2701 rtnl_lock();
2702
2703 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2704 if (err)
2705 goto unlock_rtnl;
2706
2707 if (!drv->ops->auth) {
2708 err = -EOPNOTSUPP;
2709 goto out;
2710 }
2711
Jouni Malineneec60b02009-03-20 21:21:19 +02002712 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
2713 err = -EOPNOTSUPP;
2714 goto out;
2715 }
2716
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002717 if (!netif_running(dev)) {
2718 err = -ENETDOWN;
2719 goto out;
2720 }
2721
Jouni Malinen636a5d32009-03-19 13:39:22 +02002722 if (!info->attrs[NL80211_ATTR_MAC]) {
2723 err = -EINVAL;
2724 goto out;
2725 }
2726
2727 wiphy = &drv->wiphy;
2728 memset(&req, 0, sizeof(req));
2729
2730 req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2731
2732 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
2733 req.chan = ieee80211_get_channel(
2734 wiphy,
2735 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
2736 if (!req.chan) {
2737 err = -EINVAL;
2738 goto out;
2739 }
2740 }
2741
2742 if (info->attrs[NL80211_ATTR_SSID]) {
2743 req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
2744 req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
2745 }
2746
2747 if (info->attrs[NL80211_ATTR_IE]) {
2748 req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
2749 req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
2750 }
2751
2752 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
2753 req.auth_type =
2754 nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
Jouni Malinen255e7372009-03-20 21:21:17 +02002755 if (!nl80211_valid_auth_type(req.auth_type)) {
2756 err = -EINVAL;
2757 goto out;
2758 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02002759 }
2760
2761 err = drv->ops->auth(&drv->wiphy, dev, &req);
2762
2763out:
2764 cfg80211_put_dev(drv);
2765 dev_put(dev);
2766unlock_rtnl:
2767 rtnl_unlock();
2768 return err;
2769}
2770
2771static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
2772{
2773 struct cfg80211_registered_device *drv;
2774 struct net_device *dev;
2775 struct cfg80211_assoc_request req;
2776 struct wiphy *wiphy;
2777 int err;
2778
2779 rtnl_lock();
2780
2781 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2782 if (err)
2783 goto unlock_rtnl;
2784
2785 if (!drv->ops->assoc) {
2786 err = -EOPNOTSUPP;
2787 goto out;
2788 }
2789
Jouni Malineneec60b02009-03-20 21:21:19 +02002790 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
2791 err = -EOPNOTSUPP;
2792 goto out;
2793 }
2794
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002795 if (!netif_running(dev)) {
2796 err = -ENETDOWN;
2797 goto out;
2798 }
2799
Jouni Malinen636a5d32009-03-19 13:39:22 +02002800 if (!info->attrs[NL80211_ATTR_MAC] ||
2801 !info->attrs[NL80211_ATTR_SSID]) {
2802 err = -EINVAL;
2803 goto out;
2804 }
2805
2806 wiphy = &drv->wiphy;
2807 memset(&req, 0, sizeof(req));
2808
2809 req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2810
2811 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
2812 req.chan = ieee80211_get_channel(
2813 wiphy,
2814 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
2815 if (!req.chan) {
2816 err = -EINVAL;
2817 goto out;
2818 }
2819 }
2820
Jouni Malinen636a5d32009-03-19 13:39:22 +02002821 req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
2822 req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
2823
2824 if (info->attrs[NL80211_ATTR_IE]) {
2825 req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
2826 req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
2827 }
2828
2829 err = drv->ops->assoc(&drv->wiphy, dev, &req);
2830
2831out:
2832 cfg80211_put_dev(drv);
2833 dev_put(dev);
2834unlock_rtnl:
2835 rtnl_unlock();
2836 return err;
2837}
2838
2839static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
2840{
2841 struct cfg80211_registered_device *drv;
2842 struct net_device *dev;
2843 struct cfg80211_deauth_request req;
2844 struct wiphy *wiphy;
2845 int err;
2846
2847 rtnl_lock();
2848
2849 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2850 if (err)
2851 goto unlock_rtnl;
2852
2853 if (!drv->ops->deauth) {
2854 err = -EOPNOTSUPP;
2855 goto out;
2856 }
2857
Jouni Malineneec60b02009-03-20 21:21:19 +02002858 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
2859 err = -EOPNOTSUPP;
2860 goto out;
2861 }
2862
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002863 if (!netif_running(dev)) {
2864 err = -ENETDOWN;
2865 goto out;
2866 }
2867
Jouni Malinen636a5d32009-03-19 13:39:22 +02002868 if (!info->attrs[NL80211_ATTR_MAC]) {
2869 err = -EINVAL;
2870 goto out;
2871 }
2872
2873 wiphy = &drv->wiphy;
2874 memset(&req, 0, sizeof(req));
2875
2876 req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2877
Jouni Malinen255e7372009-03-20 21:21:17 +02002878 if (info->attrs[NL80211_ATTR_REASON_CODE]) {
Jouni Malinen636a5d32009-03-19 13:39:22 +02002879 req.reason_code =
2880 nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
Jouni Malinen255e7372009-03-20 21:21:17 +02002881 if (req.reason_code == 0) {
2882 /* Reason Code 0 is reserved */
2883 err = -EINVAL;
2884 goto out;
2885 }
2886 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02002887
2888 if (info->attrs[NL80211_ATTR_IE]) {
2889 req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
2890 req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
2891 }
2892
2893 err = drv->ops->deauth(&drv->wiphy, dev, &req);
2894
2895out:
2896 cfg80211_put_dev(drv);
2897 dev_put(dev);
2898unlock_rtnl:
2899 rtnl_unlock();
2900 return err;
2901}
2902
2903static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
2904{
2905 struct cfg80211_registered_device *drv;
2906 struct net_device *dev;
2907 struct cfg80211_disassoc_request req;
2908 struct wiphy *wiphy;
2909 int err;
2910
2911 rtnl_lock();
2912
2913 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2914 if (err)
2915 goto unlock_rtnl;
2916
2917 if (!drv->ops->disassoc) {
2918 err = -EOPNOTSUPP;
2919 goto out;
2920 }
2921
Jouni Malineneec60b02009-03-20 21:21:19 +02002922 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
2923 err = -EOPNOTSUPP;
2924 goto out;
2925 }
2926
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002927 if (!netif_running(dev)) {
2928 err = -ENETDOWN;
2929 goto out;
2930 }
2931
Jouni Malinen636a5d32009-03-19 13:39:22 +02002932 if (!info->attrs[NL80211_ATTR_MAC]) {
2933 err = -EINVAL;
2934 goto out;
2935 }
2936
2937 wiphy = &drv->wiphy;
2938 memset(&req, 0, sizeof(req));
2939
2940 req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2941
Jouni Malinen255e7372009-03-20 21:21:17 +02002942 if (info->attrs[NL80211_ATTR_REASON_CODE]) {
Jouni Malinen636a5d32009-03-19 13:39:22 +02002943 req.reason_code =
2944 nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
Jouni Malinen255e7372009-03-20 21:21:17 +02002945 if (req.reason_code == 0) {
2946 /* Reason Code 0 is reserved */
2947 err = -EINVAL;
2948 goto out;
2949 }
2950 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02002951
2952 if (info->attrs[NL80211_ATTR_IE]) {
2953 req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
2954 req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
2955 }
2956
2957 err = drv->ops->disassoc(&drv->wiphy, dev, &req);
2958
2959out:
2960 cfg80211_put_dev(drv);
2961 dev_put(dev);
2962unlock_rtnl:
2963 rtnl_unlock();
2964 return err;
2965}
2966
Johannes Berg55682962007-09-20 13:09:35 -04002967static struct genl_ops nl80211_ops[] = {
2968 {
2969 .cmd = NL80211_CMD_GET_WIPHY,
2970 .doit = nl80211_get_wiphy,
2971 .dumpit = nl80211_dump_wiphy,
2972 .policy = nl80211_policy,
2973 /* can be retrieved by unprivileged users */
2974 },
2975 {
2976 .cmd = NL80211_CMD_SET_WIPHY,
2977 .doit = nl80211_set_wiphy,
2978 .policy = nl80211_policy,
2979 .flags = GENL_ADMIN_PERM,
2980 },
2981 {
2982 .cmd = NL80211_CMD_GET_INTERFACE,
2983 .doit = nl80211_get_interface,
2984 .dumpit = nl80211_dump_interface,
2985 .policy = nl80211_policy,
2986 /* can be retrieved by unprivileged users */
2987 },
2988 {
2989 .cmd = NL80211_CMD_SET_INTERFACE,
2990 .doit = nl80211_set_interface,
2991 .policy = nl80211_policy,
2992 .flags = GENL_ADMIN_PERM,
2993 },
2994 {
2995 .cmd = NL80211_CMD_NEW_INTERFACE,
2996 .doit = nl80211_new_interface,
2997 .policy = nl80211_policy,
2998 .flags = GENL_ADMIN_PERM,
2999 },
3000 {
3001 .cmd = NL80211_CMD_DEL_INTERFACE,
3002 .doit = nl80211_del_interface,
3003 .policy = nl80211_policy,
3004 .flags = GENL_ADMIN_PERM,
3005 },
Johannes Berg41ade002007-12-19 02:03:29 +01003006 {
3007 .cmd = NL80211_CMD_GET_KEY,
3008 .doit = nl80211_get_key,
3009 .policy = nl80211_policy,
3010 .flags = GENL_ADMIN_PERM,
3011 },
3012 {
3013 .cmd = NL80211_CMD_SET_KEY,
3014 .doit = nl80211_set_key,
3015 .policy = nl80211_policy,
3016 .flags = GENL_ADMIN_PERM,
3017 },
3018 {
3019 .cmd = NL80211_CMD_NEW_KEY,
3020 .doit = nl80211_new_key,
3021 .policy = nl80211_policy,
3022 .flags = GENL_ADMIN_PERM,
3023 },
3024 {
3025 .cmd = NL80211_CMD_DEL_KEY,
3026 .doit = nl80211_del_key,
3027 .policy = nl80211_policy,
3028 .flags = GENL_ADMIN_PERM,
3029 },
Johannes Berged1b6cc2007-12-19 02:03:32 +01003030 {
3031 .cmd = NL80211_CMD_SET_BEACON,
3032 .policy = nl80211_policy,
3033 .flags = GENL_ADMIN_PERM,
3034 .doit = nl80211_addset_beacon,
3035 },
3036 {
3037 .cmd = NL80211_CMD_NEW_BEACON,
3038 .policy = nl80211_policy,
3039 .flags = GENL_ADMIN_PERM,
3040 .doit = nl80211_addset_beacon,
3041 },
3042 {
3043 .cmd = NL80211_CMD_DEL_BEACON,
3044 .policy = nl80211_policy,
3045 .flags = GENL_ADMIN_PERM,
3046 .doit = nl80211_del_beacon,
3047 },
Johannes Berg5727ef12007-12-19 02:03:34 +01003048 {
3049 .cmd = NL80211_CMD_GET_STATION,
3050 .doit = nl80211_get_station,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003051 .dumpit = nl80211_dump_station,
Johannes Berg5727ef12007-12-19 02:03:34 +01003052 .policy = nl80211_policy,
Johannes Berg5727ef12007-12-19 02:03:34 +01003053 },
3054 {
3055 .cmd = NL80211_CMD_SET_STATION,
3056 .doit = nl80211_set_station,
3057 .policy = nl80211_policy,
3058 .flags = GENL_ADMIN_PERM,
3059 },
3060 {
3061 .cmd = NL80211_CMD_NEW_STATION,
3062 .doit = nl80211_new_station,
3063 .policy = nl80211_policy,
3064 .flags = GENL_ADMIN_PERM,
3065 },
3066 {
3067 .cmd = NL80211_CMD_DEL_STATION,
3068 .doit = nl80211_del_station,
3069 .policy = nl80211_policy,
3070 .flags = GENL_ADMIN_PERM,
3071 },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003072 {
3073 .cmd = NL80211_CMD_GET_MPATH,
3074 .doit = nl80211_get_mpath,
3075 .dumpit = nl80211_dump_mpath,
3076 .policy = nl80211_policy,
3077 .flags = GENL_ADMIN_PERM,
3078 },
3079 {
3080 .cmd = NL80211_CMD_SET_MPATH,
3081 .doit = nl80211_set_mpath,
3082 .policy = nl80211_policy,
3083 .flags = GENL_ADMIN_PERM,
3084 },
3085 {
3086 .cmd = NL80211_CMD_NEW_MPATH,
3087 .doit = nl80211_new_mpath,
3088 .policy = nl80211_policy,
3089 .flags = GENL_ADMIN_PERM,
3090 },
3091 {
3092 .cmd = NL80211_CMD_DEL_MPATH,
3093 .doit = nl80211_del_mpath,
3094 .policy = nl80211_policy,
3095 .flags = GENL_ADMIN_PERM,
3096 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003097 {
3098 .cmd = NL80211_CMD_SET_BSS,
3099 .doit = nl80211_set_bss,
3100 .policy = nl80211_policy,
3101 .flags = GENL_ADMIN_PERM,
3102 },
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003103 {
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003104 .cmd = NL80211_CMD_GET_REG,
3105 .doit = nl80211_get_reg,
3106 .policy = nl80211_policy,
3107 /* can be retrieved by unprivileged users */
3108 },
3109 {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003110 .cmd = NL80211_CMD_SET_REG,
3111 .doit = nl80211_set_reg,
3112 .policy = nl80211_policy,
3113 .flags = GENL_ADMIN_PERM,
3114 },
3115 {
3116 .cmd = NL80211_CMD_REQ_SET_REG,
3117 .doit = nl80211_req_set_reg,
3118 .policy = nl80211_policy,
3119 .flags = GENL_ADMIN_PERM,
3120 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003121 {
3122 .cmd = NL80211_CMD_GET_MESH_PARAMS,
3123 .doit = nl80211_get_mesh_params,
3124 .policy = nl80211_policy,
3125 /* can be retrieved by unprivileged users */
3126 },
3127 {
3128 .cmd = NL80211_CMD_SET_MESH_PARAMS,
3129 .doit = nl80211_set_mesh_params,
3130 .policy = nl80211_policy,
3131 .flags = GENL_ADMIN_PERM,
3132 },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +02003133 {
Johannes Berg2a519312009-02-10 21:25:55 +01003134 .cmd = NL80211_CMD_TRIGGER_SCAN,
3135 .doit = nl80211_trigger_scan,
3136 .policy = nl80211_policy,
3137 .flags = GENL_ADMIN_PERM,
3138 },
3139 {
3140 .cmd = NL80211_CMD_GET_SCAN,
3141 .policy = nl80211_policy,
3142 .dumpit = nl80211_dump_scan,
3143 },
Jouni Malinen636a5d32009-03-19 13:39:22 +02003144 {
3145 .cmd = NL80211_CMD_AUTHENTICATE,
3146 .doit = nl80211_authenticate,
3147 .policy = nl80211_policy,
3148 .flags = GENL_ADMIN_PERM,
3149 },
3150 {
3151 .cmd = NL80211_CMD_ASSOCIATE,
3152 .doit = nl80211_associate,
3153 .policy = nl80211_policy,
3154 .flags = GENL_ADMIN_PERM,
3155 },
3156 {
3157 .cmd = NL80211_CMD_DEAUTHENTICATE,
3158 .doit = nl80211_deauthenticate,
3159 .policy = nl80211_policy,
3160 .flags = GENL_ADMIN_PERM,
3161 },
3162 {
3163 .cmd = NL80211_CMD_DISASSOCIATE,
3164 .doit = nl80211_disassociate,
3165 .policy = nl80211_policy,
3166 .flags = GENL_ADMIN_PERM,
3167 },
Johannes Berg55682962007-09-20 13:09:35 -04003168};
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003169static struct genl_multicast_group nl80211_mlme_mcgrp = {
3170 .name = "mlme",
3171};
Johannes Berg55682962007-09-20 13:09:35 -04003172
3173/* multicast groups */
3174static struct genl_multicast_group nl80211_config_mcgrp = {
3175 .name = "config",
3176};
Johannes Berg2a519312009-02-10 21:25:55 +01003177static struct genl_multicast_group nl80211_scan_mcgrp = {
3178 .name = "scan",
3179};
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04003180static struct genl_multicast_group nl80211_regulatory_mcgrp = {
3181 .name = "regulatory",
3182};
Johannes Berg55682962007-09-20 13:09:35 -04003183
3184/* notification functions */
3185
3186void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
3187{
3188 struct sk_buff *msg;
3189
3190 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3191 if (!msg)
3192 return;
3193
3194 if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
3195 nlmsg_free(msg);
3196 return;
3197 }
3198
3199 genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL);
3200}
3201
Johannes Berg2a519312009-02-10 21:25:55 +01003202static int nl80211_send_scan_donemsg(struct sk_buff *msg,
3203 struct cfg80211_registered_device *rdev,
3204 struct net_device *netdev,
3205 u32 pid, u32 seq, int flags,
3206 u32 cmd)
3207{
3208 void *hdr;
3209
3210 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
3211 if (!hdr)
3212 return -1;
3213
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -05003214 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg2a519312009-02-10 21:25:55 +01003215 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
3216
3217 /* XXX: we should probably bounce back the request? */
3218
3219 return genlmsg_end(msg, hdr);
3220
3221 nla_put_failure:
3222 genlmsg_cancel(msg, hdr);
3223 return -EMSGSIZE;
3224}
3225
3226void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
3227 struct net_device *netdev)
3228{
3229 struct sk_buff *msg;
3230
3231 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3232 if (!msg)
3233 return;
3234
3235 if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0,
3236 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
3237 nlmsg_free(msg);
3238 return;
3239 }
3240
3241 genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL);
3242}
3243
3244void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
3245 struct net_device *netdev)
3246{
3247 struct sk_buff *msg;
3248
3249 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3250 if (!msg)
3251 return;
3252
3253 if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0,
3254 NL80211_CMD_SCAN_ABORTED) < 0) {
3255 nlmsg_free(msg);
3256 return;
3257 }
3258
3259 genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL);
3260}
3261
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04003262/*
3263 * This can happen on global regulatory changes or device specific settings
3264 * based on custom world regulatory domains.
3265 */
3266void nl80211_send_reg_change_event(struct regulatory_request *request)
3267{
3268 struct sk_buff *msg;
3269 void *hdr;
3270
3271 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3272 if (!msg)
3273 return;
3274
3275 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
3276 if (!hdr) {
3277 nlmsg_free(msg);
3278 return;
3279 }
3280
3281 /* Userspace can always count this one always being set */
3282 NLA_PUT_U8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator);
3283
3284 if (request->alpha2[0] == '0' && request->alpha2[1] == '0')
3285 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
3286 NL80211_REGDOM_TYPE_WORLD);
3287 else if (request->alpha2[0] == '9' && request->alpha2[1] == '9')
3288 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
3289 NL80211_REGDOM_TYPE_CUSTOM_WORLD);
3290 else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
3291 request->intersect)
3292 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
3293 NL80211_REGDOM_TYPE_INTERSECTION);
3294 else {
3295 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
3296 NL80211_REGDOM_TYPE_COUNTRY);
3297 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2);
3298 }
3299
3300 if (wiphy_idx_valid(request->wiphy_idx))
3301 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx);
3302
3303 if (genlmsg_end(msg, hdr) < 0) {
3304 nlmsg_free(msg);
3305 return;
3306 }
3307
3308 genlmsg_multicast(msg, 0, nl80211_regulatory_mcgrp.id, GFP_KERNEL);
3309
3310 return;
3311
3312nla_put_failure:
3313 genlmsg_cancel(msg, hdr);
3314 nlmsg_free(msg);
3315}
3316
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003317static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
3318 struct net_device *netdev,
3319 const u8 *buf, size_t len,
3320 enum nl80211_commands cmd)
3321{
3322 struct sk_buff *msg;
3323 void *hdr;
3324
3325 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3326 if (!msg)
3327 return;
3328
3329 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
3330 if (!hdr) {
3331 nlmsg_free(msg);
3332 return;
3333 }
3334
3335 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
3336 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
3337 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
3338
3339 if (genlmsg_end(msg, hdr) < 0) {
3340 nlmsg_free(msg);
3341 return;
3342 }
3343
3344 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_KERNEL);
3345 return;
3346
3347 nla_put_failure:
3348 genlmsg_cancel(msg, hdr);
3349 nlmsg_free(msg);
3350}
3351
3352void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
3353 struct net_device *netdev, const u8 *buf, size_t len)
3354{
3355 nl80211_send_mlme_event(rdev, netdev, buf, len,
3356 NL80211_CMD_AUTHENTICATE);
3357}
3358
3359void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
3360 struct net_device *netdev, const u8 *buf,
3361 size_t len)
3362{
3363 nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_ASSOCIATE);
3364}
3365
3366void nl80211_send_rx_deauth(struct cfg80211_registered_device *rdev,
3367 struct net_device *netdev, const u8 *buf,
3368 size_t len)
3369{
3370 nl80211_send_mlme_event(rdev, netdev, buf, len,
3371 NL80211_CMD_DEAUTHENTICATE);
3372}
3373
3374void nl80211_send_rx_disassoc(struct cfg80211_registered_device *rdev,
3375 struct net_device *netdev, const u8 *buf,
3376 size_t len)
3377{
3378 nl80211_send_mlme_event(rdev, netdev, buf, len,
3379 NL80211_CMD_DISASSOCIATE);
3380}
3381
Johannes Berg55682962007-09-20 13:09:35 -04003382/* initialisation/exit functions */
3383
3384int nl80211_init(void)
3385{
3386 int err, i;
3387
3388 err = genl_register_family(&nl80211_fam);
3389 if (err)
3390 return err;
3391
3392 for (i = 0; i < ARRAY_SIZE(nl80211_ops); i++) {
3393 err = genl_register_ops(&nl80211_fam, &nl80211_ops[i]);
3394 if (err)
3395 goto err_out;
3396 }
3397
3398 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
3399 if (err)
3400 goto err_out;
3401
Johannes Berg2a519312009-02-10 21:25:55 +01003402 err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
3403 if (err)
3404 goto err_out;
3405
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04003406 err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
3407 if (err)
3408 goto err_out;
3409
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003410 err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
3411 if (err)
3412 goto err_out;
3413
Johannes Berg55682962007-09-20 13:09:35 -04003414 return 0;
3415 err_out:
3416 genl_unregister_family(&nl80211_fam);
3417 return err;
3418}
3419
3420void nl80211_exit(void)
3421{
3422 genl_unregister_family(&nl80211_fam);
3423}