blob: 16f86356ac97c7b07edd1d435bc1024cde853afe [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 Malinen36aedc92008-08-25 11:58:58 +0300103
colin@cozybit.com93da9cc2008-10-21 12:03:48 -0700104 [NL80211_ATTR_MESH_PARAMS] = { .type = NLA_NESTED },
105
Jouni Malinen36aedc92008-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 Berg04a773a2009-04-19 21:24:32 +0200119 [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
Johannes Berg55682962007-09-20 13:09:35 -0400120};
121
Johannes Bergf4a11bb2009-03-27 12:40:28 +0100122/* IE validation */
123static bool is_valid_ie_attr(const struct nlattr *attr)
124{
125 const u8 *pos;
126 int len;
127
128 if (!attr)
129 return true;
130
131 pos = nla_data(attr);
132 len = nla_len(attr);
133
134 while (len) {
135 u8 elemlen;
136
137 if (len < 2)
138 return false;
139 len -= 2;
140
141 elemlen = pos[1];
142 if (elemlen > len)
143 return false;
144
145 len -= elemlen;
146 pos += 2 + elemlen;
147 }
148
149 return true;
150}
151
Johannes Berg55682962007-09-20 13:09:35 -0400152/* message building helper */
153static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
154 int flags, u8 cmd)
155{
156 /* since there is no private header just add the generic one */
157 return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
158}
159
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400160static int nl80211_msg_put_channel(struct sk_buff *msg,
161 struct ieee80211_channel *chan)
162{
163 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
164 chan->center_freq);
165
166 if (chan->flags & IEEE80211_CHAN_DISABLED)
167 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
168 if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
169 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
170 if (chan->flags & IEEE80211_CHAN_NO_IBSS)
171 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
172 if (chan->flags & IEEE80211_CHAN_RADAR)
173 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
174
175 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
176 DBM_TO_MBM(chan->max_power));
177
178 return 0;
179
180 nla_put_failure:
181 return -ENOBUFS;
182}
183
Johannes Berg55682962007-09-20 13:09:35 -0400184/* netlink command implementations */
185
186static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
187 struct cfg80211_registered_device *dev)
188{
189 void *hdr;
Johannes Bergee688b002008-01-24 19:38:39 +0100190 struct nlattr *nl_bands, *nl_band;
191 struct nlattr *nl_freqs, *nl_freq;
192 struct nlattr *nl_rates, *nl_rate;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700193 struct nlattr *nl_modes;
Johannes Berg8fdc6212009-03-14 09:34:01 +0100194 struct nlattr *nl_cmds;
Johannes Bergee688b002008-01-24 19:38:39 +0100195 enum ieee80211_band band;
196 struct ieee80211_channel *chan;
197 struct ieee80211_rate *rate;
198 int i;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700199 u16 ifmodes = dev->wiphy.interface_modes;
Johannes Berg55682962007-09-20 13:09:35 -0400200
201 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
202 if (!hdr)
203 return -1;
204
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -0500205 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400206 NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
Johannes Berg2a519312009-02-10 21:25:55 +0100207 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
208 dev->wiphy.max_scan_ssids);
Johannes Berg18a83652009-03-31 12:12:05 +0200209 NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
210 dev->wiphy.max_scan_ie_len);
Johannes Bergee688b002008-01-24 19:38:39 +0100211
Johannes Berg25e47c12009-04-02 20:14:06 +0200212 NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
213 sizeof(u32) * dev->wiphy.n_cipher_suites,
214 dev->wiphy.cipher_suites);
215
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700216 nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES);
217 if (!nl_modes)
218 goto nla_put_failure;
219
220 i = 0;
221 while (ifmodes) {
222 if (ifmodes & 1)
223 NLA_PUT_FLAG(msg, i);
224 ifmodes >>= 1;
225 i++;
226 }
227
228 nla_nest_end(msg, nl_modes);
229
Johannes Bergee688b002008-01-24 19:38:39 +0100230 nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
231 if (!nl_bands)
232 goto nla_put_failure;
233
234 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
235 if (!dev->wiphy.bands[band])
236 continue;
237
238 nl_band = nla_nest_start(msg, band);
239 if (!nl_band)
240 goto nla_put_failure;
241
Johannes Bergd51626d2008-10-09 12:20:13 +0200242 /* add HT info */
243 if (dev->wiphy.bands[band]->ht_cap.ht_supported) {
244 NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET,
245 sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
246 &dev->wiphy.bands[band]->ht_cap.mcs);
247 NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA,
248 dev->wiphy.bands[band]->ht_cap.cap);
249 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
250 dev->wiphy.bands[band]->ht_cap.ampdu_factor);
251 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
252 dev->wiphy.bands[band]->ht_cap.ampdu_density);
253 }
254
Johannes Bergee688b002008-01-24 19:38:39 +0100255 /* add frequencies */
256 nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
257 if (!nl_freqs)
258 goto nla_put_failure;
259
260 for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
261 nl_freq = nla_nest_start(msg, i);
262 if (!nl_freq)
263 goto nla_put_failure;
264
265 chan = &dev->wiphy.bands[band]->channels[i];
Johannes Bergee688b002008-01-24 19:38:39 +0100266
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400267 if (nl80211_msg_put_channel(msg, chan))
268 goto nla_put_failure;
Jouni Malinene2f367f262008-11-21 19:01:30 +0200269
Johannes Bergee688b002008-01-24 19:38:39 +0100270 nla_nest_end(msg, nl_freq);
271 }
272
273 nla_nest_end(msg, nl_freqs);
274
275 /* add bitrates */
276 nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
277 if (!nl_rates)
278 goto nla_put_failure;
279
280 for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
281 nl_rate = nla_nest_start(msg, i);
282 if (!nl_rate)
283 goto nla_put_failure;
284
285 rate = &dev->wiphy.bands[band]->bitrates[i];
286 NLA_PUT_U32(msg, NL80211_BITRATE_ATTR_RATE,
287 rate->bitrate);
288 if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
289 NLA_PUT_FLAG(msg,
290 NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE);
291
292 nla_nest_end(msg, nl_rate);
293 }
294
295 nla_nest_end(msg, nl_rates);
296
297 nla_nest_end(msg, nl_band);
298 }
299 nla_nest_end(msg, nl_bands);
300
Johannes Berg8fdc6212009-03-14 09:34:01 +0100301 nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
302 if (!nl_cmds)
303 goto nla_put_failure;
304
305 i = 0;
306#define CMD(op, n) \
307 do { \
308 if (dev->ops->op) { \
309 i++; \
310 NLA_PUT_U32(msg, i, NL80211_CMD_ ## n); \
311 } \
312 } while (0)
313
314 CMD(add_virtual_intf, NEW_INTERFACE);
315 CMD(change_virtual_intf, SET_INTERFACE);
316 CMD(add_key, NEW_KEY);
317 CMD(add_beacon, NEW_BEACON);
318 CMD(add_station, NEW_STATION);
319 CMD(add_mpath, NEW_MPATH);
320 CMD(set_mesh_params, SET_MESH_PARAMS);
321 CMD(change_bss, SET_BSS);
Jouni Malinen636a5d32009-03-19 13:39:22 +0200322 CMD(auth, AUTHENTICATE);
323 CMD(assoc, ASSOCIATE);
324 CMD(deauth, DEAUTHENTICATE);
325 CMD(disassoc, DISASSOCIATE);
Johannes Berg04a773a2009-04-19 21:24:32 +0200326 CMD(join_ibss, JOIN_IBSS);
Johannes Berg8fdc6212009-03-14 09:34:01 +0100327
328#undef CMD
329 nla_nest_end(msg, nl_cmds);
330
Johannes Berg55682962007-09-20 13:09:35 -0400331 return genlmsg_end(msg, hdr);
332
333 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700334 genlmsg_cancel(msg, hdr);
335 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400336}
337
338static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
339{
340 int idx = 0;
341 int start = cb->args[0];
342 struct cfg80211_registered_device *dev;
343
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500344 mutex_lock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400345 list_for_each_entry(dev, &cfg80211_drv_list, list) {
Julius Volzb4637272008-07-08 14:02:19 +0200346 if (++idx <= start)
Johannes Berg55682962007-09-20 13:09:35 -0400347 continue;
348 if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
349 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Julius Volzb4637272008-07-08 14:02:19 +0200350 dev) < 0) {
351 idx--;
Johannes Berg55682962007-09-20 13:09:35 -0400352 break;
Julius Volzb4637272008-07-08 14:02:19 +0200353 }
Johannes Berg55682962007-09-20 13:09:35 -0400354 }
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500355 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400356
357 cb->args[0] = idx;
358
359 return skb->len;
360}
361
362static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
363{
364 struct sk_buff *msg;
365 struct cfg80211_registered_device *dev;
366
367 dev = cfg80211_get_dev_from_info(info);
368 if (IS_ERR(dev))
369 return PTR_ERR(dev);
370
371 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
372 if (!msg)
373 goto out_err;
374
375 if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0)
376 goto out_free;
377
378 cfg80211_put_dev(dev);
379
380 return genlmsg_unicast(msg, info->snd_pid);
381
382 out_free:
383 nlmsg_free(msg);
384 out_err:
385 cfg80211_put_dev(dev);
386 return -ENOBUFS;
387}
388
Jouni Malinen31888482008-10-30 16:59:24 +0200389static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
390 [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
391 [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
392 [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
393 [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
394 [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
395};
396
397static int parse_txq_params(struct nlattr *tb[],
398 struct ieee80211_txq_params *txq_params)
399{
400 if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] ||
401 !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
402 !tb[NL80211_TXQ_ATTR_AIFS])
403 return -EINVAL;
404
405 txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]);
406 txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
407 txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
408 txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
409 txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
410
411 return 0;
412}
413
Johannes Berg55682962007-09-20 13:09:35 -0400414static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
415{
416 struct cfg80211_registered_device *rdev;
Jouni Malinen31888482008-10-30 16:59:24 +0200417 int result = 0, rem_txq_params = 0;
418 struct nlattr *nl_txq_params;
Johannes Berg55682962007-09-20 13:09:35 -0400419
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100420 rtnl_lock();
Johannes Berg55682962007-09-20 13:09:35 -0400421
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100422 mutex_lock(&cfg80211_mutex);
423
424 rdev = __cfg80211_drv_from_info(info);
425 if (IS_ERR(rdev)) {
426 result = PTR_ERR(rdev);
427 goto unlock;
428 }
429
430 mutex_lock(&rdev->mtx);
431
432 if (info->attrs[NL80211_ATTR_WIPHY_NAME])
Jouni Malinen31888482008-10-30 16:59:24 +0200433 result = cfg80211_dev_rename(
434 rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100435
436 mutex_unlock(&cfg80211_mutex);
437
438 if (result)
439 goto bad_res;
Johannes Berg55682962007-09-20 13:09:35 -0400440
Jouni Malinen31888482008-10-30 16:59:24 +0200441 if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
442 struct ieee80211_txq_params txq_params;
443 struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
444
445 if (!rdev->ops->set_txq_params) {
446 result = -EOPNOTSUPP;
447 goto bad_res;
448 }
449
450 nla_for_each_nested(nl_txq_params,
451 info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
452 rem_txq_params) {
453 nla_parse(tb, NL80211_TXQ_ATTR_MAX,
454 nla_data(nl_txq_params),
455 nla_len(nl_txq_params),
456 txq_params_policy);
457 result = parse_txq_params(tb, &txq_params);
458 if (result)
459 goto bad_res;
460
461 result = rdev->ops->set_txq_params(&rdev->wiphy,
462 &txq_params);
463 if (result)
464 goto bad_res;
465 }
466 }
467
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200468 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Sujith094d05d2008-12-12 11:57:43 +0530469 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200470 struct ieee80211_channel *chan;
Johannes Berg306d6112008-12-08 12:39:04 +0100471 struct ieee80211_sta_ht_cap *ht_cap;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200472 u32 freq, sec_freq;
473
474 if (!rdev->ops->set_channel) {
475 result = -EOPNOTSUPP;
476 goto bad_res;
477 }
478
Johannes Berg306d6112008-12-08 12:39:04 +0100479 result = -EINVAL;
480
Sujith094d05d2008-12-12 11:57:43 +0530481 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
482 channel_type = nla_get_u32(info->attrs[
483 NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
484 if (channel_type != NL80211_CHAN_NO_HT &&
485 channel_type != NL80211_CHAN_HT20 &&
486 channel_type != NL80211_CHAN_HT40PLUS &&
487 channel_type != NL80211_CHAN_HT40MINUS)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200488 goto bad_res;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200489 }
490
491 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
492 chan = ieee80211_get_channel(&rdev->wiphy, freq);
Johannes Berg306d6112008-12-08 12:39:04 +0100493
494 /* Primary channel not allowed */
495 if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200496 goto bad_res;
Johannes Berg306d6112008-12-08 12:39:04 +0100497
Sujith094d05d2008-12-12 11:57:43 +0530498 if (channel_type == NL80211_CHAN_HT40MINUS)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200499 sec_freq = freq - 20;
Sujith094d05d2008-12-12 11:57:43 +0530500 else if (channel_type == NL80211_CHAN_HT40PLUS)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200501 sec_freq = freq + 20;
502 else
503 sec_freq = 0;
504
Johannes Berg306d6112008-12-08 12:39:04 +0100505 ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
506
507 /* no HT capabilities */
Sujith094d05d2008-12-12 11:57:43 +0530508 if (channel_type != NL80211_CHAN_NO_HT &&
Johannes Berg306d6112008-12-08 12:39:04 +0100509 !ht_cap->ht_supported)
510 goto bad_res;
511
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200512 if (sec_freq) {
513 struct ieee80211_channel *schan;
Johannes Berg306d6112008-12-08 12:39:04 +0100514
515 /* no 40 MHz capabilities */
516 if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
517 (ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200518 goto bad_res;
Johannes Berg306d6112008-12-08 12:39:04 +0100519
520 schan = ieee80211_get_channel(&rdev->wiphy, sec_freq);
521
522 /* Secondary channel not allowed */
523 if (!schan || schan->flags & IEEE80211_CHAN_DISABLED)
524 goto bad_res;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200525 }
526
527 result = rdev->ops->set_channel(&rdev->wiphy, chan,
Sujith094d05d2008-12-12 11:57:43 +0530528 channel_type);
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200529 if (result)
530 goto bad_res;
531 }
532
533
Johannes Berg306d6112008-12-08 12:39:04 +0100534 bad_res:
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100535 mutex_unlock(&rdev->mtx);
536 unlock:
537 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400538 return result;
539}
540
541
542static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
Johannes Bergd7264052009-04-19 16:23:20 +0200543 struct cfg80211_registered_device *rdev,
Johannes Berg55682962007-09-20 13:09:35 -0400544 struct net_device *dev)
545{
546 void *hdr;
547
548 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
549 if (!hdr)
550 return -1;
551
552 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
Johannes Bergd7264052009-04-19 16:23:20 +0200553 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400554 NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
Johannes Berg60719ff2008-09-16 14:55:09 +0200555 NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype);
Johannes Berg55682962007-09-20 13:09:35 -0400556 return genlmsg_end(msg, hdr);
557
558 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700559 genlmsg_cancel(msg, hdr);
560 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400561}
562
563static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
564{
565 int wp_idx = 0;
566 int if_idx = 0;
567 int wp_start = cb->args[0];
568 int if_start = cb->args[1];
569 struct cfg80211_registered_device *dev;
570 struct wireless_dev *wdev;
571
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500572 mutex_lock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400573 list_for_each_entry(dev, &cfg80211_drv_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +0200574 if (wp_idx < wp_start) {
575 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400576 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200577 }
Johannes Berg55682962007-09-20 13:09:35 -0400578 if_idx = 0;
579
580 mutex_lock(&dev->devlist_mtx);
581 list_for_each_entry(wdev, &dev->netdev_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +0200582 if (if_idx < if_start) {
583 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400584 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200585 }
Johannes Berg55682962007-09-20 13:09:35 -0400586 if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
587 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Bergd7264052009-04-19 16:23:20 +0200588 dev, wdev->netdev) < 0) {
Johannes Bergbba95fe2008-07-29 13:22:51 +0200589 mutex_unlock(&dev->devlist_mtx);
590 goto out;
591 }
592 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400593 }
594 mutex_unlock(&dev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +0200595
596 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400597 }
Johannes Bergbba95fe2008-07-29 13:22:51 +0200598 out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500599 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400600
601 cb->args[0] = wp_idx;
602 cb->args[1] = if_idx;
603
604 return skb->len;
605}
606
607static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
608{
609 struct sk_buff *msg;
610 struct cfg80211_registered_device *dev;
611 struct net_device *netdev;
612 int err;
613
Johannes Bergbba95fe2008-07-29 13:22:51 +0200614 err = get_drv_dev_by_info_ifindex(info->attrs, &dev, &netdev);
Johannes Berg55682962007-09-20 13:09:35 -0400615 if (err)
616 return err;
617
618 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
619 if (!msg)
620 goto out_err;
621
Johannes Bergd7264052009-04-19 16:23:20 +0200622 if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0,
623 dev, netdev) < 0)
Johannes Berg55682962007-09-20 13:09:35 -0400624 goto out_free;
625
626 dev_put(netdev);
627 cfg80211_put_dev(dev);
628
629 return genlmsg_unicast(msg, info->snd_pid);
630
631 out_free:
632 nlmsg_free(msg);
633 out_err:
634 dev_put(netdev);
635 cfg80211_put_dev(dev);
636 return -ENOBUFS;
637}
638
Michael Wu66f7ac52008-01-31 19:48:22 +0100639static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
640 [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
641 [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
642 [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
643 [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
644 [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
645};
646
647static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
648{
649 struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
650 int flag;
651
652 *mntrflags = 0;
653
654 if (!nla)
655 return -EINVAL;
656
657 if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX,
658 nla, mntr_flags_policy))
659 return -EINVAL;
660
661 for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
662 if (flags[flag])
663 *mntrflags |= (1<<flag);
664
665 return 0;
666}
667
Johannes Berg55682962007-09-20 13:09:35 -0400668static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
669{
670 struct cfg80211_registered_device *drv;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100671 struct vif_params params;
Johannes Berg55682962007-09-20 13:09:35 -0400672 int err, ifindex;
Johannes Berg04a773a2009-04-19 21:24:32 +0200673 enum nl80211_iftype otype, ntype;
Johannes Berg55682962007-09-20 13:09:35 -0400674 struct net_device *dev;
Johannes Berg92ffe052008-09-16 20:39:36 +0200675 u32 _flags, *flags = NULL;
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100676 bool change = false;
Johannes Berg55682962007-09-20 13:09:35 -0400677
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100678 memset(&params, 0, sizeof(params));
679
Johannes Berg3b858752009-03-12 09:55:09 +0100680 rtnl_lock();
681
Johannes Bergbba95fe2008-07-29 13:22:51 +0200682 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg55682962007-09-20 13:09:35 -0400683 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +0100684 goto unlock_rtnl;
685
Johannes Berg55682962007-09-20 13:09:35 -0400686 ifindex = dev->ifindex;
Johannes Berg04a773a2009-04-19 21:24:32 +0200687 otype = ntype = dev->ieee80211_ptr->iftype;
Johannes Berg55682962007-09-20 13:09:35 -0400688 dev_put(dev);
689
Johannes Berg723b0382008-09-16 20:22:09 +0200690 if (info->attrs[NL80211_ATTR_IFTYPE]) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100691 ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
Johannes Berg04a773a2009-04-19 21:24:32 +0200692 if (otype != ntype)
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100693 change = true;
Johannes Berg04a773a2009-04-19 21:24:32 +0200694 if (ntype > NL80211_IFTYPE_MAX) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100695 err = -EINVAL;
Johannes Berg723b0382008-09-16 20:22:09 +0200696 goto unlock;
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100697 }
Johannes Berg723b0382008-09-16 20:22:09 +0200698 }
699
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700700 if (!drv->ops->change_virtual_intf ||
Johannes Berg04a773a2009-04-19 21:24:32 +0200701 !(drv->wiphy.interface_modes & (1 << ntype))) {
Johannes Berg55682962007-09-20 13:09:35 -0400702 err = -EOPNOTSUPP;
703 goto unlock;
704 }
705
Johannes Berg92ffe052008-09-16 20:39:36 +0200706 if (info->attrs[NL80211_ATTR_MESH_ID]) {
Johannes Berg04a773a2009-04-19 21:24:32 +0200707 if (ntype != NL80211_IFTYPE_MESH_POINT) {
Johannes Berg92ffe052008-09-16 20:39:36 +0200708 err = -EINVAL;
709 goto unlock;
710 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100711 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
712 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100713 change = true;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100714 }
715
Johannes Berg92ffe052008-09-16 20:39:36 +0200716 if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
Johannes Berg04a773a2009-04-19 21:24:32 +0200717 if (ntype != NL80211_IFTYPE_MONITOR) {
Johannes Berg92ffe052008-09-16 20:39:36 +0200718 err = -EINVAL;
719 goto unlock;
720 }
721 err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
722 &_flags);
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100723 if (err)
724 goto unlock;
725
726 flags = &_flags;
727 change = true;
Johannes Berg92ffe052008-09-16 20:39:36 +0200728 }
Johannes Berg3b858752009-03-12 09:55:09 +0100729
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100730 if (change)
731 err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex,
Johannes Berg04a773a2009-04-19 21:24:32 +0200732 ntype, flags, &params);
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100733 else
734 err = 0;
Johannes Berg60719ff2008-09-16 14:55:09 +0200735
736 dev = __dev_get_by_index(&init_net, ifindex);
Johannes Berg04a773a2009-04-19 21:24:32 +0200737 WARN_ON(!dev || (!err && dev->ieee80211_ptr->iftype != ntype));
738
739 if (dev && !err && (ntype != otype)) {
740 if (otype == NL80211_IFTYPE_ADHOC)
741 cfg80211_clear_ibss(dev);
742 }
Johannes Berg60719ff2008-09-16 14:55:09 +0200743
Johannes Berg55682962007-09-20 13:09:35 -0400744 unlock:
745 cfg80211_put_dev(drv);
Johannes Berg3b858752009-03-12 09:55:09 +0100746 unlock_rtnl:
747 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400748 return err;
749}
750
751static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
752{
753 struct cfg80211_registered_device *drv;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100754 struct vif_params params;
Johannes Berg55682962007-09-20 13:09:35 -0400755 int err;
756 enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
Michael Wu66f7ac52008-01-31 19:48:22 +0100757 u32 flags;
Johannes Berg55682962007-09-20 13:09:35 -0400758
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100759 memset(&params, 0, sizeof(params));
760
Johannes Berg55682962007-09-20 13:09:35 -0400761 if (!info->attrs[NL80211_ATTR_IFNAME])
762 return -EINVAL;
763
764 if (info->attrs[NL80211_ATTR_IFTYPE]) {
765 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
766 if (type > NL80211_IFTYPE_MAX)
767 return -EINVAL;
768 }
769
Johannes Berg3b858752009-03-12 09:55:09 +0100770 rtnl_lock();
771
Johannes Berg55682962007-09-20 13:09:35 -0400772 drv = cfg80211_get_dev_from_info(info);
Johannes Berg3b858752009-03-12 09:55:09 +0100773 if (IS_ERR(drv)) {
774 err = PTR_ERR(drv);
775 goto unlock_rtnl;
776 }
Johannes Berg55682962007-09-20 13:09:35 -0400777
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700778 if (!drv->ops->add_virtual_intf ||
779 !(drv->wiphy.interface_modes & (1 << type))) {
Johannes Berg55682962007-09-20 13:09:35 -0400780 err = -EOPNOTSUPP;
781 goto unlock;
782 }
783
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100784 if (type == NL80211_IFTYPE_MESH_POINT &&
785 info->attrs[NL80211_ATTR_MESH_ID]) {
786 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
787 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
788 }
789
Michael Wu66f7ac52008-01-31 19:48:22 +0100790 err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
791 info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
792 &flags);
Johannes Berg55682962007-09-20 13:09:35 -0400793 err = drv->ops->add_virtual_intf(&drv->wiphy,
Michael Wu66f7ac52008-01-31 19:48:22 +0100794 nla_data(info->attrs[NL80211_ATTR_IFNAME]),
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100795 type, err ? NULL : &flags, &params);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100796
Johannes Berg55682962007-09-20 13:09:35 -0400797 unlock:
798 cfg80211_put_dev(drv);
Johannes Berg3b858752009-03-12 09:55:09 +0100799 unlock_rtnl:
800 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400801 return err;
802}
803
804static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
805{
806 struct cfg80211_registered_device *drv;
807 int ifindex, err;
808 struct net_device *dev;
809
Johannes Berg3b858752009-03-12 09:55:09 +0100810 rtnl_lock();
811
Johannes Bergbba95fe2008-07-29 13:22:51 +0200812 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg55682962007-09-20 13:09:35 -0400813 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +0100814 goto unlock_rtnl;
Johannes Berg55682962007-09-20 13:09:35 -0400815 ifindex = dev->ifindex;
816 dev_put(dev);
817
818 if (!drv->ops->del_virtual_intf) {
819 err = -EOPNOTSUPP;
820 goto out;
821 }
822
Johannes Berg55682962007-09-20 13:09:35 -0400823 err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex);
Johannes Berg55682962007-09-20 13:09:35 -0400824
825 out:
826 cfg80211_put_dev(drv);
Johannes Berg3b858752009-03-12 09:55:09 +0100827 unlock_rtnl:
828 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400829 return err;
830}
831
Johannes Berg41ade002007-12-19 02:03:29 +0100832struct get_key_cookie {
833 struct sk_buff *msg;
834 int error;
835};
836
837static void get_key_callback(void *c, struct key_params *params)
838{
839 struct get_key_cookie *cookie = c;
840
841 if (params->key)
842 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA,
843 params->key_len, params->key);
844
845 if (params->seq)
846 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ,
847 params->seq_len, params->seq);
848
849 if (params->cipher)
850 NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
851 params->cipher);
852
853 return;
854 nla_put_failure:
855 cookie->error = 1;
856}
857
858static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
859{
860 struct cfg80211_registered_device *drv;
861 int err;
862 struct net_device *dev;
863 u8 key_idx = 0;
864 u8 *mac_addr = NULL;
865 struct get_key_cookie cookie = {
866 .error = 0,
867 };
868 void *hdr;
869 struct sk_buff *msg;
870
871 if (info->attrs[NL80211_ATTR_KEY_IDX])
872 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
873
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +0200874 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +0100875 return -EINVAL;
876
877 if (info->attrs[NL80211_ATTR_MAC])
878 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
879
Johannes Berg3b858752009-03-12 09:55:09 +0100880 rtnl_lock();
881
Johannes Bergbba95fe2008-07-29 13:22:51 +0200882 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +0100883 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +0100884 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +0100885
886 if (!drv->ops->get_key) {
887 err = -EOPNOTSUPP;
888 goto out;
889 }
890
891 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
892 if (!msg) {
893 err = -ENOMEM;
894 goto out;
895 }
896
897 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
898 NL80211_CMD_NEW_KEY);
899
900 if (IS_ERR(hdr)) {
901 err = PTR_ERR(hdr);
902 goto out;
903 }
904
905 cookie.msg = msg;
906
907 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
908 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
909 if (mac_addr)
910 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
911
Johannes Berg41ade002007-12-19 02:03:29 +0100912 err = drv->ops->get_key(&drv->wiphy, dev, key_idx, mac_addr,
913 &cookie, get_key_callback);
Johannes Berg41ade002007-12-19 02:03:29 +0100914
915 if (err)
916 goto out;
917
918 if (cookie.error)
919 goto nla_put_failure;
920
921 genlmsg_end(msg, hdr);
922 err = genlmsg_unicast(msg, info->snd_pid);
923 goto out;
924
925 nla_put_failure:
926 err = -ENOBUFS;
927 nlmsg_free(msg);
928 out:
929 cfg80211_put_dev(drv);
930 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +0100931 unlock_rtnl:
932 rtnl_unlock();
933
Johannes Berg41ade002007-12-19 02:03:29 +0100934 return err;
935}
936
937static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
938{
939 struct cfg80211_registered_device *drv;
940 int err;
941 struct net_device *dev;
942 u8 key_idx;
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +0200943 int (*func)(struct wiphy *wiphy, struct net_device *netdev,
944 u8 key_index);
Johannes Berg41ade002007-12-19 02:03:29 +0100945
946 if (!info->attrs[NL80211_ATTR_KEY_IDX])
947 return -EINVAL;
948
949 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
950
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +0200951 if (info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) {
952 if (key_idx < 4 || key_idx > 5)
953 return -EINVAL;
954 } else if (key_idx > 3)
Johannes Berg41ade002007-12-19 02:03:29 +0100955 return -EINVAL;
956
957 /* currently only support setting default key */
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +0200958 if (!info->attrs[NL80211_ATTR_KEY_DEFAULT] &&
959 !info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT])
Johannes Berg41ade002007-12-19 02:03:29 +0100960 return -EINVAL;
961
Johannes Berg3b858752009-03-12 09:55:09 +0100962 rtnl_lock();
963
Johannes Bergbba95fe2008-07-29 13:22:51 +0200964 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +0100965 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +0100966 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +0100967
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +0200968 if (info->attrs[NL80211_ATTR_KEY_DEFAULT])
969 func = drv->ops->set_default_key;
970 else
971 func = drv->ops->set_default_mgmt_key;
972
973 if (!func) {
Johannes Berg41ade002007-12-19 02:03:29 +0100974 err = -EOPNOTSUPP;
975 goto out;
976 }
977
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +0200978 err = func(&drv->wiphy, dev, key_idx);
Johannes Berg41ade002007-12-19 02:03:29 +0100979
980 out:
981 cfg80211_put_dev(drv);
982 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +0100983
984 unlock_rtnl:
985 rtnl_unlock();
986
Johannes Berg41ade002007-12-19 02:03:29 +0100987 return err;
988}
989
990static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
991{
992 struct cfg80211_registered_device *drv;
Johannes Berg25e47c12009-04-02 20:14:06 +0200993 int err, i;
Johannes Berg41ade002007-12-19 02:03:29 +0100994 struct net_device *dev;
995 struct key_params params;
996 u8 key_idx = 0;
997 u8 *mac_addr = NULL;
998
999 memset(&params, 0, sizeof(params));
1000
1001 if (!info->attrs[NL80211_ATTR_KEY_CIPHER])
1002 return -EINVAL;
1003
1004 if (info->attrs[NL80211_ATTR_KEY_DATA]) {
1005 params.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
1006 params.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
1007 }
1008
1009 if (info->attrs[NL80211_ATTR_KEY_IDX])
1010 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1011
1012 params.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
1013
1014 if (info->attrs[NL80211_ATTR_MAC])
1015 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1016
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001017 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +01001018 return -EINVAL;
1019
1020 /*
1021 * Disallow pairwise keys with non-zero index unless it's WEP
1022 * (because current deployments use pairwise WEP keys with
1023 * non-zero indizes but 802.11i clearly specifies to use zero)
1024 */
1025 if (mac_addr && key_idx &&
1026 params.cipher != WLAN_CIPHER_SUITE_WEP40 &&
1027 params.cipher != WLAN_CIPHER_SUITE_WEP104)
1028 return -EINVAL;
1029
1030 /* TODO: add definitions for the lengths to linux/ieee80211.h */
1031 switch (params.cipher) {
1032 case WLAN_CIPHER_SUITE_WEP40:
1033 if (params.key_len != 5)
1034 return -EINVAL;
1035 break;
1036 case WLAN_CIPHER_SUITE_TKIP:
1037 if (params.key_len != 32)
1038 return -EINVAL;
1039 break;
1040 case WLAN_CIPHER_SUITE_CCMP:
1041 if (params.key_len != 16)
1042 return -EINVAL;
1043 break;
1044 case WLAN_CIPHER_SUITE_WEP104:
1045 if (params.key_len != 13)
1046 return -EINVAL;
1047 break;
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001048 case WLAN_CIPHER_SUITE_AES_CMAC:
1049 if (params.key_len != 16)
1050 return -EINVAL;
1051 break;
Johannes Berg41ade002007-12-19 02:03:29 +01001052 default:
1053 return -EINVAL;
1054 }
1055
Johannes Berg3b858752009-03-12 09:55:09 +01001056 rtnl_lock();
1057
Johannes Bergbba95fe2008-07-29 13:22:51 +02001058 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001059 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001060 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001061
Johannes Berg25e47c12009-04-02 20:14:06 +02001062 for (i = 0; i < drv->wiphy.n_cipher_suites; i++)
1063 if (params.cipher == drv->wiphy.cipher_suites[i])
1064 break;
1065 if (i == drv->wiphy.n_cipher_suites) {
1066 err = -EINVAL;
1067 goto out;
1068 }
1069
Johannes Berg41ade002007-12-19 02:03:29 +01001070 if (!drv->ops->add_key) {
1071 err = -EOPNOTSUPP;
1072 goto out;
1073 }
1074
Johannes Berg41ade002007-12-19 02:03:29 +01001075 err = drv->ops->add_key(&drv->wiphy, dev, key_idx, mac_addr, &params);
Johannes Berg41ade002007-12-19 02:03:29 +01001076
1077 out:
1078 cfg80211_put_dev(drv);
1079 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001080 unlock_rtnl:
1081 rtnl_unlock();
1082
Johannes Berg41ade002007-12-19 02:03:29 +01001083 return err;
1084}
1085
1086static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
1087{
1088 struct cfg80211_registered_device *drv;
1089 int err;
1090 struct net_device *dev;
1091 u8 key_idx = 0;
1092 u8 *mac_addr = NULL;
1093
1094 if (info->attrs[NL80211_ATTR_KEY_IDX])
1095 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1096
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001097 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +01001098 return -EINVAL;
1099
1100 if (info->attrs[NL80211_ATTR_MAC])
1101 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1102
Johannes Berg3b858752009-03-12 09:55:09 +01001103 rtnl_lock();
1104
Johannes Bergbba95fe2008-07-29 13:22:51 +02001105 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001106 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001107 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001108
1109 if (!drv->ops->del_key) {
1110 err = -EOPNOTSUPP;
1111 goto out;
1112 }
1113
Johannes Berg41ade002007-12-19 02:03:29 +01001114 err = drv->ops->del_key(&drv->wiphy, dev, key_idx, mac_addr);
Johannes Berg41ade002007-12-19 02:03:29 +01001115
1116 out:
1117 cfg80211_put_dev(drv);
1118 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001119
1120 unlock_rtnl:
1121 rtnl_unlock();
1122
Johannes Berg41ade002007-12-19 02:03:29 +01001123 return err;
1124}
1125
Johannes Berged1b6cc2007-12-19 02:03:32 +01001126static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
1127{
1128 int (*call)(struct wiphy *wiphy, struct net_device *dev,
1129 struct beacon_parameters *info);
1130 struct cfg80211_registered_device *drv;
1131 int err;
1132 struct net_device *dev;
1133 struct beacon_parameters params;
1134 int haveinfo = 0;
1135
Johannes Bergf4a11bb2009-03-27 12:40:28 +01001136 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]))
1137 return -EINVAL;
1138
Johannes Berg3b858752009-03-12 09:55:09 +01001139 rtnl_lock();
1140
Johannes Bergbba95fe2008-07-29 13:22:51 +02001141 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001142 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001143 goto unlock_rtnl;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001144
Jouni Malineneec60b02009-03-20 21:21:19 +02001145 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
1146 err = -EOPNOTSUPP;
1147 goto out;
1148 }
1149
Johannes Berged1b6cc2007-12-19 02:03:32 +01001150 switch (info->genlhdr->cmd) {
1151 case NL80211_CMD_NEW_BEACON:
1152 /* these are required for NEW_BEACON */
1153 if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
1154 !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
1155 !info->attrs[NL80211_ATTR_BEACON_HEAD]) {
1156 err = -EINVAL;
1157 goto out;
1158 }
1159
1160 call = drv->ops->add_beacon;
1161 break;
1162 case NL80211_CMD_SET_BEACON:
1163 call = drv->ops->set_beacon;
1164 break;
1165 default:
1166 WARN_ON(1);
1167 err = -EOPNOTSUPP;
1168 goto out;
1169 }
1170
1171 if (!call) {
1172 err = -EOPNOTSUPP;
1173 goto out;
1174 }
1175
1176 memset(&params, 0, sizeof(params));
1177
1178 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
1179 params.interval =
1180 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
1181 haveinfo = 1;
1182 }
1183
1184 if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) {
1185 params.dtim_period =
1186 nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
1187 haveinfo = 1;
1188 }
1189
1190 if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
1191 params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
1192 params.head_len =
1193 nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
1194 haveinfo = 1;
1195 }
1196
1197 if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
1198 params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
1199 params.tail_len =
1200 nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
1201 haveinfo = 1;
1202 }
1203
1204 if (!haveinfo) {
1205 err = -EINVAL;
1206 goto out;
1207 }
1208
Johannes Berged1b6cc2007-12-19 02:03:32 +01001209 err = call(&drv->wiphy, dev, &params);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001210
1211 out:
1212 cfg80211_put_dev(drv);
1213 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001214 unlock_rtnl:
1215 rtnl_unlock();
1216
Johannes Berged1b6cc2007-12-19 02:03:32 +01001217 return err;
1218}
1219
1220static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
1221{
1222 struct cfg80211_registered_device *drv;
1223 int err;
1224 struct net_device *dev;
1225
Johannes Berg3b858752009-03-12 09:55:09 +01001226 rtnl_lock();
1227
Johannes Bergbba95fe2008-07-29 13:22:51 +02001228 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001229 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001230 goto unlock_rtnl;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001231
1232 if (!drv->ops->del_beacon) {
1233 err = -EOPNOTSUPP;
1234 goto out;
1235 }
1236
Jouni Malineneec60b02009-03-20 21:21:19 +02001237 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
1238 err = -EOPNOTSUPP;
1239 goto out;
1240 }
Johannes Berged1b6cc2007-12-19 02:03:32 +01001241 err = drv->ops->del_beacon(&drv->wiphy, dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001242
1243 out:
1244 cfg80211_put_dev(drv);
1245 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001246 unlock_rtnl:
1247 rtnl_unlock();
1248
Johannes Berged1b6cc2007-12-19 02:03:32 +01001249 return err;
1250}
1251
Johannes Berg5727ef12007-12-19 02:03:34 +01001252static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
1253 [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
1254 [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
1255 [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
1256};
1257
1258static int parse_station_flags(struct nlattr *nla, u32 *staflags)
1259{
1260 struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
1261 int flag;
1262
1263 *staflags = 0;
1264
1265 if (!nla)
1266 return 0;
1267
1268 if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX,
1269 nla, sta_flags_policy))
1270 return -EINVAL;
1271
1272 *staflags = STATION_FLAG_CHANGED;
1273
1274 for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++)
1275 if (flags[flag])
1276 *staflags |= (1<<flag);
1277
1278 return 0;
1279}
1280
Henning Rogge420e7fa2008-12-11 22:04:19 +01001281static u16 nl80211_calculate_bitrate(struct rate_info *rate)
1282{
1283 int modulation, streams, bitrate;
1284
1285 if (!(rate->flags & RATE_INFO_FLAGS_MCS))
1286 return rate->legacy;
1287
1288 /* the formula below does only work for MCS values smaller than 32 */
1289 if (rate->mcs >= 32)
1290 return 0;
1291
1292 modulation = rate->mcs & 7;
1293 streams = (rate->mcs >> 3) + 1;
1294
1295 bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ?
1296 13500000 : 6500000;
1297
1298 if (modulation < 4)
1299 bitrate *= (modulation + 1);
1300 else if (modulation == 4)
1301 bitrate *= (modulation + 2);
1302 else
1303 bitrate *= (modulation + 3);
1304
1305 bitrate *= streams;
1306
1307 if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
1308 bitrate = (bitrate / 9) * 10;
1309
1310 /* do NOT round down here */
1311 return (bitrate + 50000) / 100000;
1312}
1313
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001314static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
1315 int flags, struct net_device *dev,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001316 u8 *mac_addr, struct station_info *sinfo)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001317{
1318 void *hdr;
Henning Rogge420e7fa2008-12-11 22:04:19 +01001319 struct nlattr *sinfoattr, *txrate;
1320 u16 bitrate;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001321
1322 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
1323 if (!hdr)
1324 return -1;
1325
1326 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1327 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1328
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001329 sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
1330 if (!sinfoattr)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001331 goto nla_put_failure;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001332 if (sinfo->filled & STATION_INFO_INACTIVE_TIME)
1333 NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME,
1334 sinfo->inactive_time);
1335 if (sinfo->filled & STATION_INFO_RX_BYTES)
1336 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES,
1337 sinfo->rx_bytes);
1338 if (sinfo->filled & STATION_INFO_TX_BYTES)
1339 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES,
1340 sinfo->tx_bytes);
1341 if (sinfo->filled & STATION_INFO_LLID)
1342 NLA_PUT_U16(msg, NL80211_STA_INFO_LLID,
1343 sinfo->llid);
1344 if (sinfo->filled & STATION_INFO_PLID)
1345 NLA_PUT_U16(msg, NL80211_STA_INFO_PLID,
1346 sinfo->plid);
1347 if (sinfo->filled & STATION_INFO_PLINK_STATE)
1348 NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
1349 sinfo->plink_state);
Henning Rogge420e7fa2008-12-11 22:04:19 +01001350 if (sinfo->filled & STATION_INFO_SIGNAL)
1351 NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL,
1352 sinfo->signal);
1353 if (sinfo->filled & STATION_INFO_TX_BITRATE) {
1354 txrate = nla_nest_start(msg, NL80211_STA_INFO_TX_BITRATE);
1355 if (!txrate)
1356 goto nla_put_failure;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001357
Henning Rogge420e7fa2008-12-11 22:04:19 +01001358 /* nl80211_calculate_bitrate will return 0 for mcs >= 32 */
1359 bitrate = nl80211_calculate_bitrate(&sinfo->txrate);
1360 if (bitrate > 0)
1361 NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
1362
1363 if (sinfo->txrate.flags & RATE_INFO_FLAGS_MCS)
1364 NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS,
1365 sinfo->txrate.mcs);
1366 if (sinfo->txrate.flags & RATE_INFO_FLAGS_40_MHZ_WIDTH)
1367 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH);
1368 if (sinfo->txrate.flags & RATE_INFO_FLAGS_SHORT_GI)
1369 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI);
1370
1371 nla_nest_end(msg, txrate);
1372 }
Jouni Malinen98c8a60a2009-02-17 13:24:57 +02001373 if (sinfo->filled & STATION_INFO_RX_PACKETS)
1374 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS,
1375 sinfo->rx_packets);
1376 if (sinfo->filled & STATION_INFO_TX_PACKETS)
1377 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS,
1378 sinfo->tx_packets);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001379 nla_nest_end(msg, sinfoattr);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001380
1381 return genlmsg_end(msg, hdr);
1382
1383 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001384 genlmsg_cancel(msg, hdr);
1385 return -EMSGSIZE;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001386}
1387
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001388static int nl80211_dump_station(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02001389 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001390{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001391 struct station_info sinfo;
1392 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001393 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001394 u8 mac_addr[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02001395 int ifidx = cb->args[0];
1396 int sta_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001397 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001398
Johannes Bergbba95fe2008-07-29 13:22:51 +02001399 if (!ifidx) {
1400 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
1401 nl80211_fam.attrbuf, nl80211_fam.maxattr,
1402 nl80211_policy);
1403 if (err)
1404 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001405
Johannes Bergbba95fe2008-07-29 13:22:51 +02001406 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
1407 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001408
Johannes Bergbba95fe2008-07-29 13:22:51 +02001409 ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
1410 if (!ifidx)
1411 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001412 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001413
Johannes Berg3b858752009-03-12 09:55:09 +01001414 rtnl_lock();
1415
1416 netdev = __dev_get_by_index(&init_net, ifidx);
1417 if (!netdev) {
1418 err = -ENODEV;
1419 goto out_rtnl;
1420 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001421
Johannes Bergbba95fe2008-07-29 13:22:51 +02001422 dev = cfg80211_get_dev_from_ifindex(ifidx);
1423 if (IS_ERR(dev)) {
1424 err = PTR_ERR(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001425 goto out_rtnl;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001426 }
1427
1428 if (!dev->ops->dump_station) {
Jouni Malineneec60b02009-03-20 21:21:19 +02001429 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001430 goto out_err;
1431 }
1432
Johannes Bergbba95fe2008-07-29 13:22:51 +02001433 while (1) {
1434 err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
1435 mac_addr, &sinfo);
1436 if (err == -ENOENT)
1437 break;
1438 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001439 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001440
1441 if (nl80211_send_station(skb,
1442 NETLINK_CB(cb->skb).pid,
1443 cb->nlh->nlmsg_seq, NLM_F_MULTI,
1444 netdev, mac_addr,
1445 &sinfo) < 0)
1446 goto out;
1447
1448 sta_idx++;
1449 }
1450
1451
1452 out:
1453 cb->args[1] = sta_idx;
1454 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001455 out_err:
1456 cfg80211_put_dev(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001457 out_rtnl:
1458 rtnl_unlock();
Johannes Bergbba95fe2008-07-29 13:22:51 +02001459
1460 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001461}
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001462
Johannes Berg5727ef12007-12-19 02:03:34 +01001463static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
1464{
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001465 struct cfg80211_registered_device *drv;
1466 int err;
1467 struct net_device *dev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001468 struct station_info sinfo;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001469 struct sk_buff *msg;
1470 u8 *mac_addr = NULL;
1471
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001472 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001473
1474 if (!info->attrs[NL80211_ATTR_MAC])
1475 return -EINVAL;
1476
1477 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1478
Johannes Berg3b858752009-03-12 09:55:09 +01001479 rtnl_lock();
1480
Johannes Bergbba95fe2008-07-29 13:22:51 +02001481 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001482 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001483 goto out_rtnl;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001484
1485 if (!drv->ops->get_station) {
1486 err = -EOPNOTSUPP;
1487 goto out;
1488 }
1489
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001490 err = drv->ops->get_station(&drv->wiphy, dev, mac_addr, &sinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001491 if (err)
1492 goto out;
1493
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001494 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1495 if (!msg)
1496 goto out;
1497
1498 if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001499 dev, mac_addr, &sinfo) < 0)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001500 goto out_free;
1501
1502 err = genlmsg_unicast(msg, info->snd_pid);
1503 goto out;
1504
1505 out_free:
1506 nlmsg_free(msg);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001507 out:
1508 cfg80211_put_dev(drv);
1509 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001510 out_rtnl:
1511 rtnl_unlock();
1512
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001513 return err;
Johannes Berg5727ef12007-12-19 02:03:34 +01001514}
1515
1516/*
1517 * Get vlan interface making sure it is on the right wiphy.
1518 */
1519static int get_vlan(struct nlattr *vlanattr,
1520 struct cfg80211_registered_device *rdev,
1521 struct net_device **vlan)
1522{
1523 *vlan = NULL;
1524
1525 if (vlanattr) {
1526 *vlan = dev_get_by_index(&init_net, nla_get_u32(vlanattr));
1527 if (!*vlan)
1528 return -ENODEV;
1529 if (!(*vlan)->ieee80211_ptr)
1530 return -EINVAL;
1531 if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy)
1532 return -EINVAL;
1533 }
1534 return 0;
1535}
1536
1537static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
1538{
1539 struct cfg80211_registered_device *drv;
1540 int err;
1541 struct net_device *dev;
1542 struct station_parameters params;
1543 u8 *mac_addr = NULL;
1544
1545 memset(&params, 0, sizeof(params));
1546
1547 params.listen_interval = -1;
1548
1549 if (info->attrs[NL80211_ATTR_STA_AID])
1550 return -EINVAL;
1551
1552 if (!info->attrs[NL80211_ATTR_MAC])
1553 return -EINVAL;
1554
1555 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1556
1557 if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
1558 params.supported_rates =
1559 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1560 params.supported_rates_len =
1561 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1562 }
1563
1564 if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
1565 params.listen_interval =
1566 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
1567
Jouni Malinen36aedc92008-08-25 11:58:58 +03001568 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
1569 params.ht_capa =
1570 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
1571
Johannes Berg5727ef12007-12-19 02:03:34 +01001572 if (parse_station_flags(info->attrs[NL80211_ATTR_STA_FLAGS],
1573 &params.station_flags))
1574 return -EINVAL;
1575
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001576 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
1577 params.plink_action =
1578 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
1579
Johannes Berg3b858752009-03-12 09:55:09 +01001580 rtnl_lock();
1581
Johannes Bergbba95fe2008-07-29 13:22:51 +02001582 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001583 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001584 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01001585
1586 err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, &params.vlan);
1587 if (err)
1588 goto out;
1589
1590 if (!drv->ops->change_station) {
1591 err = -EOPNOTSUPP;
1592 goto out;
1593 }
1594
Johannes Berg5727ef12007-12-19 02:03:34 +01001595 err = drv->ops->change_station(&drv->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01001596
1597 out:
1598 if (params.vlan)
1599 dev_put(params.vlan);
1600 cfg80211_put_dev(drv);
1601 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001602 out_rtnl:
1603 rtnl_unlock();
1604
Johannes Berg5727ef12007-12-19 02:03:34 +01001605 return err;
1606}
1607
1608static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
1609{
1610 struct cfg80211_registered_device *drv;
1611 int err;
1612 struct net_device *dev;
1613 struct station_parameters params;
1614 u8 *mac_addr = NULL;
1615
1616 memset(&params, 0, sizeof(params));
1617
1618 if (!info->attrs[NL80211_ATTR_MAC])
1619 return -EINVAL;
1620
1621 if (!info->attrs[NL80211_ATTR_STA_AID])
1622 return -EINVAL;
1623
1624 if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
1625 return -EINVAL;
1626
1627 if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
1628 return -EINVAL;
1629
1630 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1631 params.supported_rates =
1632 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1633 params.supported_rates_len =
1634 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1635 params.listen_interval =
1636 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
Johannes Berg16f2e852008-04-07 14:35:46 +02001637 params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
Jouni Malinen36aedc92008-08-25 11:58:58 +03001638 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
1639 params.ht_capa =
1640 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
Johannes Berg5727ef12007-12-19 02:03:34 +01001641
1642 if (parse_station_flags(info->attrs[NL80211_ATTR_STA_FLAGS],
1643 &params.station_flags))
1644 return -EINVAL;
1645
Johannes Berg3b858752009-03-12 09:55:09 +01001646 rtnl_lock();
1647
Johannes Bergbba95fe2008-07-29 13:22:51 +02001648 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001649 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001650 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01001651
1652 err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, &params.vlan);
1653 if (err)
1654 goto out;
1655
1656 if (!drv->ops->add_station) {
1657 err = -EOPNOTSUPP;
1658 goto out;
1659 }
1660
Jouni Malinen35a8efe2009-03-20 21:21:18 +02001661 if (!netif_running(dev)) {
1662 err = -ENETDOWN;
1663 goto out;
1664 }
1665
Johannes Berg5727ef12007-12-19 02:03:34 +01001666 err = drv->ops->add_station(&drv->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01001667
1668 out:
1669 if (params.vlan)
1670 dev_put(params.vlan);
1671 cfg80211_put_dev(drv);
1672 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001673 out_rtnl:
1674 rtnl_unlock();
1675
Johannes Berg5727ef12007-12-19 02:03:34 +01001676 return err;
1677}
1678
1679static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
1680{
1681 struct cfg80211_registered_device *drv;
1682 int err;
1683 struct net_device *dev;
1684 u8 *mac_addr = NULL;
1685
1686 if (info->attrs[NL80211_ATTR_MAC])
1687 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1688
Johannes Berg3b858752009-03-12 09:55:09 +01001689 rtnl_lock();
1690
Johannes Bergbba95fe2008-07-29 13:22:51 +02001691 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001692 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001693 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01001694
1695 if (!drv->ops->del_station) {
1696 err = -EOPNOTSUPP;
1697 goto out;
1698 }
1699
Johannes Berg5727ef12007-12-19 02:03:34 +01001700 err = drv->ops->del_station(&drv->wiphy, dev, mac_addr);
Johannes Berg5727ef12007-12-19 02:03:34 +01001701
1702 out:
1703 cfg80211_put_dev(drv);
1704 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001705 out_rtnl:
1706 rtnl_unlock();
1707
Johannes Berg5727ef12007-12-19 02:03:34 +01001708 return err;
1709}
1710
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001711static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq,
1712 int flags, struct net_device *dev,
1713 u8 *dst, u8 *next_hop,
1714 struct mpath_info *pinfo)
1715{
1716 void *hdr;
1717 struct nlattr *pinfoattr;
1718
1719 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
1720 if (!hdr)
1721 return -1;
1722
1723 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1724 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
1725 NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop);
1726
1727 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
1728 if (!pinfoattr)
1729 goto nla_put_failure;
1730 if (pinfo->filled & MPATH_INFO_FRAME_QLEN)
1731 NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
1732 pinfo->frame_qlen);
1733 if (pinfo->filled & MPATH_INFO_DSN)
1734 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DSN,
1735 pinfo->dsn);
1736 if (pinfo->filled & MPATH_INFO_METRIC)
1737 NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC,
1738 pinfo->metric);
1739 if (pinfo->filled & MPATH_INFO_EXPTIME)
1740 NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME,
1741 pinfo->exptime);
1742 if (pinfo->filled & MPATH_INFO_FLAGS)
1743 NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS,
1744 pinfo->flags);
1745 if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT)
1746 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
1747 pinfo->discovery_timeout);
1748 if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES)
1749 NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
1750 pinfo->discovery_retries);
1751
1752 nla_nest_end(msg, pinfoattr);
1753
1754 return genlmsg_end(msg, hdr);
1755
1756 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001757 genlmsg_cancel(msg, hdr);
1758 return -EMSGSIZE;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001759}
1760
1761static int nl80211_dump_mpath(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02001762 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001763{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001764 struct mpath_info pinfo;
1765 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001766 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001767 u8 dst[ETH_ALEN];
1768 u8 next_hop[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02001769 int ifidx = cb->args[0];
1770 int path_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001771 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001772
Johannes Bergbba95fe2008-07-29 13:22:51 +02001773 if (!ifidx) {
1774 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
1775 nl80211_fam.attrbuf, nl80211_fam.maxattr,
1776 nl80211_policy);
1777 if (err)
1778 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001779
Johannes Bergbba95fe2008-07-29 13:22:51 +02001780 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
1781 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001782
Johannes Bergbba95fe2008-07-29 13:22:51 +02001783 ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
1784 if (!ifidx)
1785 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001786 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001787
Johannes Berg3b858752009-03-12 09:55:09 +01001788 rtnl_lock();
1789
1790 netdev = __dev_get_by_index(&init_net, ifidx);
1791 if (!netdev) {
1792 err = -ENODEV;
1793 goto out_rtnl;
1794 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001795
Johannes Bergbba95fe2008-07-29 13:22:51 +02001796 dev = cfg80211_get_dev_from_ifindex(ifidx);
1797 if (IS_ERR(dev)) {
1798 err = PTR_ERR(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001799 goto out_rtnl;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001800 }
1801
1802 if (!dev->ops->dump_mpath) {
Jouni Malineneec60b02009-03-20 21:21:19 +02001803 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001804 goto out_err;
1805 }
1806
Jouni Malineneec60b02009-03-20 21:21:19 +02001807 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
1808 err = -EOPNOTSUPP;
1809 goto out;
1810 }
1811
Johannes Bergbba95fe2008-07-29 13:22:51 +02001812 while (1) {
1813 err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
1814 dst, next_hop, &pinfo);
1815 if (err == -ENOENT)
1816 break;
1817 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001818 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001819
1820 if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid,
1821 cb->nlh->nlmsg_seq, NLM_F_MULTI,
1822 netdev, dst, next_hop,
1823 &pinfo) < 0)
1824 goto out;
1825
1826 path_idx++;
1827 }
1828
1829
1830 out:
1831 cb->args[1] = path_idx;
1832 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001833 out_err:
1834 cfg80211_put_dev(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001835 out_rtnl:
1836 rtnl_unlock();
Johannes Bergbba95fe2008-07-29 13:22:51 +02001837
1838 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001839}
1840
1841static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
1842{
1843 struct cfg80211_registered_device *drv;
1844 int err;
1845 struct net_device *dev;
1846 struct mpath_info pinfo;
1847 struct sk_buff *msg;
1848 u8 *dst = NULL;
1849 u8 next_hop[ETH_ALEN];
1850
1851 memset(&pinfo, 0, sizeof(pinfo));
1852
1853 if (!info->attrs[NL80211_ATTR_MAC])
1854 return -EINVAL;
1855
1856 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
1857
Johannes Berg3b858752009-03-12 09:55:09 +01001858 rtnl_lock();
1859
Johannes Bergbba95fe2008-07-29 13:22:51 +02001860 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001861 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001862 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001863
1864 if (!drv->ops->get_mpath) {
1865 err = -EOPNOTSUPP;
1866 goto out;
1867 }
1868
Jouni Malineneec60b02009-03-20 21:21:19 +02001869 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
1870 err = -EOPNOTSUPP;
1871 goto out;
1872 }
1873
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001874 err = drv->ops->get_mpath(&drv->wiphy, dev, dst, next_hop, &pinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001875 if (err)
1876 goto out;
1877
1878 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1879 if (!msg)
1880 goto out;
1881
1882 if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0,
1883 dev, dst, next_hop, &pinfo) < 0)
1884 goto out_free;
1885
1886 err = genlmsg_unicast(msg, info->snd_pid);
1887 goto out;
1888
1889 out_free:
1890 nlmsg_free(msg);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001891 out:
1892 cfg80211_put_dev(drv);
1893 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001894 out_rtnl:
1895 rtnl_unlock();
1896
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001897 return err;
1898}
1899
1900static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
1901{
1902 struct cfg80211_registered_device *drv;
1903 int err;
1904 struct net_device *dev;
1905 u8 *dst = NULL;
1906 u8 *next_hop = NULL;
1907
1908 if (!info->attrs[NL80211_ATTR_MAC])
1909 return -EINVAL;
1910
1911 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
1912 return -EINVAL;
1913
1914 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
1915 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
1916
Johannes Berg3b858752009-03-12 09:55:09 +01001917 rtnl_lock();
1918
Johannes Bergbba95fe2008-07-29 13:22:51 +02001919 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001920 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001921 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001922
1923 if (!drv->ops->change_mpath) {
1924 err = -EOPNOTSUPP;
1925 goto out;
1926 }
1927
Jouni Malineneec60b02009-03-20 21:21:19 +02001928 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
1929 err = -EOPNOTSUPP;
1930 goto out;
1931 }
1932
Jouni Malinen35a8efe2009-03-20 21:21:18 +02001933 if (!netif_running(dev)) {
1934 err = -ENETDOWN;
1935 goto out;
1936 }
1937
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001938 err = drv->ops->change_mpath(&drv->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001939
1940 out:
1941 cfg80211_put_dev(drv);
1942 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001943 out_rtnl:
1944 rtnl_unlock();
1945
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001946 return err;
1947}
1948static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
1949{
1950 struct cfg80211_registered_device *drv;
1951 int err;
1952 struct net_device *dev;
1953 u8 *dst = NULL;
1954 u8 *next_hop = NULL;
1955
1956 if (!info->attrs[NL80211_ATTR_MAC])
1957 return -EINVAL;
1958
1959 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
1960 return -EINVAL;
1961
1962 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
1963 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
1964
Johannes Berg3b858752009-03-12 09:55:09 +01001965 rtnl_lock();
1966
Johannes Bergbba95fe2008-07-29 13:22:51 +02001967 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001968 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001969 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001970
1971 if (!drv->ops->add_mpath) {
1972 err = -EOPNOTSUPP;
1973 goto out;
1974 }
1975
Jouni Malineneec60b02009-03-20 21:21:19 +02001976 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
1977 err = -EOPNOTSUPP;
1978 goto out;
1979 }
1980
Jouni Malinen35a8efe2009-03-20 21:21:18 +02001981 if (!netif_running(dev)) {
1982 err = -ENETDOWN;
1983 goto out;
1984 }
1985
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001986 err = drv->ops->add_mpath(&drv->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001987
1988 out:
1989 cfg80211_put_dev(drv);
1990 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001991 out_rtnl:
1992 rtnl_unlock();
1993
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001994 return err;
1995}
1996
1997static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
1998{
1999 struct cfg80211_registered_device *drv;
2000 int err;
2001 struct net_device *dev;
2002 u8 *dst = NULL;
2003
2004 if (info->attrs[NL80211_ATTR_MAC])
2005 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2006
Johannes Berg3b858752009-03-12 09:55:09 +01002007 rtnl_lock();
2008
Johannes Bergbba95fe2008-07-29 13:22:51 +02002009 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002010 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002011 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002012
2013 if (!drv->ops->del_mpath) {
2014 err = -EOPNOTSUPP;
2015 goto out;
2016 }
2017
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002018 err = drv->ops->del_mpath(&drv->wiphy, dev, dst);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002019
2020 out:
2021 cfg80211_put_dev(drv);
2022 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002023 out_rtnl:
2024 rtnl_unlock();
2025
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002026 return err;
2027}
2028
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002029static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
2030{
2031 struct cfg80211_registered_device *drv;
2032 int err;
2033 struct net_device *dev;
2034 struct bss_parameters params;
2035
2036 memset(&params, 0, sizeof(params));
2037 /* default to not changing parameters */
2038 params.use_cts_prot = -1;
2039 params.use_short_preamble = -1;
2040 params.use_short_slot_time = -1;
2041
2042 if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
2043 params.use_cts_prot =
2044 nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
2045 if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
2046 params.use_short_preamble =
2047 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
2048 if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
2049 params.use_short_slot_time =
2050 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
Jouni Malinen90c97a02008-10-30 16:59:22 +02002051 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
2052 params.basic_rates =
2053 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2054 params.basic_rates_len =
2055 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2056 }
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002057
Johannes Berg3b858752009-03-12 09:55:09 +01002058 rtnl_lock();
2059
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002060 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2061 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002062 goto out_rtnl;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002063
2064 if (!drv->ops->change_bss) {
2065 err = -EOPNOTSUPP;
2066 goto out;
2067 }
2068
Jouni Malineneec60b02009-03-20 21:21:19 +02002069 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
2070 err = -EOPNOTSUPP;
2071 goto out;
2072 }
2073
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002074 err = drv->ops->change_bss(&drv->wiphy, dev, &params);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002075
2076 out:
2077 cfg80211_put_dev(drv);
2078 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002079 out_rtnl:
2080 rtnl_unlock();
2081
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002082 return err;
2083}
2084
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002085static const struct nla_policy
2086 reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
2087 [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
2088 [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
2089 [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
2090 [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
2091 [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
2092 [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
2093};
2094
2095static int parse_reg_rule(struct nlattr *tb[],
2096 struct ieee80211_reg_rule *reg_rule)
2097{
2098 struct ieee80211_freq_range *freq_range = &reg_rule->freq_range;
2099 struct ieee80211_power_rule *power_rule = &reg_rule->power_rule;
2100
2101 if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
2102 return -EINVAL;
2103 if (!tb[NL80211_ATTR_FREQ_RANGE_START])
2104 return -EINVAL;
2105 if (!tb[NL80211_ATTR_FREQ_RANGE_END])
2106 return -EINVAL;
2107 if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
2108 return -EINVAL;
2109 if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
2110 return -EINVAL;
2111
2112 reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
2113
2114 freq_range->start_freq_khz =
2115 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
2116 freq_range->end_freq_khz =
2117 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
2118 freq_range->max_bandwidth_khz =
2119 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
2120
2121 power_rule->max_eirp =
2122 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
2123
2124 if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
2125 power_rule->max_antenna_gain =
2126 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
2127
2128 return 0;
2129}
2130
2131static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
2132{
2133 int r;
2134 char *data = NULL;
2135
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002136 /*
2137 * You should only get this when cfg80211 hasn't yet initialized
2138 * completely when built-in to the kernel right between the time
2139 * window between nl80211_init() and regulatory_init(), if that is
2140 * even possible.
2141 */
2142 mutex_lock(&cfg80211_mutex);
2143 if (unlikely(!cfg80211_regdomain)) {
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002144 mutex_unlock(&cfg80211_mutex);
2145 return -EINPROGRESS;
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002146 }
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002147 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002148
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002149 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
2150 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002151
2152 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
2153
2154#ifdef CONFIG_WIRELESS_OLD_REGULATORY
2155 /* We ignore world regdom requests with the old regdom setup */
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002156 if (is_world_regdom(data))
2157 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002158#endif
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002159
2160 r = regulatory_hint_user(data);
2161
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002162 return r;
2163}
2164
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002165static int nl80211_get_mesh_params(struct sk_buff *skb,
2166 struct genl_info *info)
2167{
2168 struct cfg80211_registered_device *drv;
2169 struct mesh_config cur_params;
2170 int err;
2171 struct net_device *dev;
2172 void *hdr;
2173 struct nlattr *pinfoattr;
2174 struct sk_buff *msg;
2175
Johannes Berg3b858752009-03-12 09:55:09 +01002176 rtnl_lock();
2177
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002178 /* Look up our device */
2179 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2180 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002181 goto out_rtnl;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002182
Jouni Malinenf3f92582009-03-20 17:57:36 +02002183 if (!drv->ops->get_mesh_params) {
2184 err = -EOPNOTSUPP;
2185 goto out;
2186 }
2187
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002188 /* Get the mesh params */
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002189 err = drv->ops->get_mesh_params(&drv->wiphy, dev, &cur_params);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002190 if (err)
2191 goto out;
2192
2193 /* Draw up a netlink message to send back */
2194 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
2195 if (!msg) {
2196 err = -ENOBUFS;
2197 goto out;
2198 }
2199 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
2200 NL80211_CMD_GET_MESH_PARAMS);
2201 if (!hdr)
2202 goto nla_put_failure;
2203 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS);
2204 if (!pinfoattr)
2205 goto nla_put_failure;
2206 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2207 NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
2208 cur_params.dot11MeshRetryTimeout);
2209 NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
2210 cur_params.dot11MeshConfirmTimeout);
2211 NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
2212 cur_params.dot11MeshHoldingTimeout);
2213 NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
2214 cur_params.dot11MeshMaxPeerLinks);
2215 NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES,
2216 cur_params.dot11MeshMaxRetries);
2217 NLA_PUT_U8(msg, NL80211_MESHCONF_TTL,
2218 cur_params.dot11MeshTTL);
2219 NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
2220 cur_params.auto_open_plinks);
2221 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
2222 cur_params.dot11MeshHWMPmaxPREQretries);
2223 NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
2224 cur_params.path_refresh_time);
2225 NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
2226 cur_params.min_discovery_timeout);
2227 NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
2228 cur_params.dot11MeshHWMPactivePathTimeout);
2229 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
2230 cur_params.dot11MeshHWMPpreqMinInterval);
2231 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
2232 cur_params.dot11MeshHWMPnetDiameterTraversalTime);
2233 nla_nest_end(msg, pinfoattr);
2234 genlmsg_end(msg, hdr);
2235 err = genlmsg_unicast(msg, info->snd_pid);
2236 goto out;
2237
Johannes Berg3b858752009-03-12 09:55:09 +01002238 nla_put_failure:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002239 genlmsg_cancel(msg, hdr);
2240 err = -EMSGSIZE;
Johannes Berg3b858752009-03-12 09:55:09 +01002241 out:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002242 /* Cleanup */
2243 cfg80211_put_dev(drv);
2244 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002245 out_rtnl:
2246 rtnl_unlock();
2247
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002248 return err;
2249}
2250
2251#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \
2252do {\
2253 if (table[attr_num]) {\
2254 cfg.param = nla_fn(table[attr_num]); \
2255 mask |= (1 << (attr_num - 1)); \
2256 } \
2257} while (0);\
2258
2259static struct nla_policy
2260nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] __read_mostly = {
2261 [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
2262 [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
2263 [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
2264 [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
2265 [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
2266 [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
2267 [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
2268
2269 [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
2270 [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
2271 [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
2272 [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
2273 [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
2274 [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
2275};
2276
2277static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
2278{
2279 int err;
2280 u32 mask;
2281 struct cfg80211_registered_device *drv;
2282 struct net_device *dev;
2283 struct mesh_config cfg;
2284 struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
2285 struct nlattr *parent_attr;
2286
2287 parent_attr = info->attrs[NL80211_ATTR_MESH_PARAMS];
2288 if (!parent_attr)
2289 return -EINVAL;
2290 if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
2291 parent_attr, nl80211_meshconf_params_policy))
2292 return -EINVAL;
2293
Johannes Berg3b858752009-03-12 09:55:09 +01002294 rtnl_lock();
2295
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002296 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2297 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002298 goto out_rtnl;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002299
Jouni Malinenf3f92582009-03-20 17:57:36 +02002300 if (!drv->ops->set_mesh_params) {
2301 err = -EOPNOTSUPP;
2302 goto out;
2303 }
2304
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002305 /* This makes sure that there aren't more than 32 mesh config
2306 * parameters (otherwise our bitfield scheme would not work.) */
2307 BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
2308
2309 /* Fill in the params struct */
2310 mask = 0;
2311 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout,
2312 mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16);
2313 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout,
2314 mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16);
2315 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout,
2316 mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16);
2317 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks,
2318 mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16);
2319 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries,
2320 mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8);
2321 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL,
2322 mask, NL80211_MESHCONF_TTL, nla_get_u8);
2323 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
2324 mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8);
2325 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
2326 mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
2327 nla_get_u8);
2328 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time,
2329 mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32);
2330 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout,
2331 mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
2332 nla_get_u16);
2333 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
2334 mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
2335 nla_get_u32);
2336 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
2337 mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
2338 nla_get_u16);
2339 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
2340 dot11MeshHWMPnetDiameterTraversalTime,
2341 mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
2342 nla_get_u16);
2343
2344 /* Apply changes */
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002345 err = drv->ops->set_mesh_params(&drv->wiphy, dev, &cfg, mask);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002346
Jouni Malinenf3f92582009-03-20 17:57:36 +02002347 out:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002348 /* cleanup */
2349 cfg80211_put_dev(drv);
2350 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002351 out_rtnl:
2352 rtnl_unlock();
2353
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002354 return err;
2355}
2356
2357#undef FILL_IN_MESH_PARAM_IF_SET
2358
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002359static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
2360{
2361 struct sk_buff *msg;
2362 void *hdr = NULL;
2363 struct nlattr *nl_reg_rules;
2364 unsigned int i;
2365 int err = -EINVAL;
2366
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002367 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002368
2369 if (!cfg80211_regdomain)
2370 goto out;
2371
2372 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
2373 if (!msg) {
2374 err = -ENOBUFS;
2375 goto out;
2376 }
2377
2378 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
2379 NL80211_CMD_GET_REG);
2380 if (!hdr)
2381 goto nla_put_failure;
2382
2383 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2,
2384 cfg80211_regdomain->alpha2);
2385
2386 nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
2387 if (!nl_reg_rules)
2388 goto nla_put_failure;
2389
2390 for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) {
2391 struct nlattr *nl_reg_rule;
2392 const struct ieee80211_reg_rule *reg_rule;
2393 const struct ieee80211_freq_range *freq_range;
2394 const struct ieee80211_power_rule *power_rule;
2395
2396 reg_rule = &cfg80211_regdomain->reg_rules[i];
2397 freq_range = &reg_rule->freq_range;
2398 power_rule = &reg_rule->power_rule;
2399
2400 nl_reg_rule = nla_nest_start(msg, i);
2401 if (!nl_reg_rule)
2402 goto nla_put_failure;
2403
2404 NLA_PUT_U32(msg, NL80211_ATTR_REG_RULE_FLAGS,
2405 reg_rule->flags);
2406 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_START,
2407 freq_range->start_freq_khz);
2408 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_END,
2409 freq_range->end_freq_khz);
2410 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
2411 freq_range->max_bandwidth_khz);
2412 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
2413 power_rule->max_antenna_gain);
2414 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
2415 power_rule->max_eirp);
2416
2417 nla_nest_end(msg, nl_reg_rule);
2418 }
2419
2420 nla_nest_end(msg, nl_reg_rules);
2421
2422 genlmsg_end(msg, hdr);
2423 err = genlmsg_unicast(msg, info->snd_pid);
2424 goto out;
2425
2426nla_put_failure:
2427 genlmsg_cancel(msg, hdr);
2428 err = -EMSGSIZE;
2429out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002430 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002431 return err;
2432}
2433
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002434static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
2435{
2436 struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
2437 struct nlattr *nl_reg_rule;
2438 char *alpha2 = NULL;
2439 int rem_reg_rules = 0, r = 0;
2440 u32 num_rules = 0, rule_idx = 0, size_of_regd;
2441 struct ieee80211_regdomain *rd = NULL;
2442
2443 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
2444 return -EINVAL;
2445
2446 if (!info->attrs[NL80211_ATTR_REG_RULES])
2447 return -EINVAL;
2448
2449 alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
2450
2451 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
2452 rem_reg_rules) {
2453 num_rules++;
2454 if (num_rules > NL80211_MAX_SUPP_REG_RULES)
2455 goto bad_reg;
2456 }
2457
2458 if (!reg_is_valid_request(alpha2))
2459 return -EINVAL;
2460
2461 size_of_regd = sizeof(struct ieee80211_regdomain) +
2462 (num_rules * sizeof(struct ieee80211_reg_rule));
2463
2464 rd = kzalloc(size_of_regd, GFP_KERNEL);
2465 if (!rd)
2466 return -ENOMEM;
2467
2468 rd->n_reg_rules = num_rules;
2469 rd->alpha2[0] = alpha2[0];
2470 rd->alpha2[1] = alpha2[1];
2471
2472 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
2473 rem_reg_rules) {
2474 nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
2475 nla_data(nl_reg_rule), nla_len(nl_reg_rule),
2476 reg_rule_policy);
2477 r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
2478 if (r)
2479 goto bad_reg;
2480
2481 rule_idx++;
2482
2483 if (rule_idx > NL80211_MAX_SUPP_REG_RULES)
2484 goto bad_reg;
2485 }
2486
2487 BUG_ON(rule_idx != num_rules);
2488
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002489 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002490 r = set_regdom(rd);
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002491 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002492 return r;
2493
Johannes Bergd2372b32008-10-24 20:32:20 +02002494 bad_reg:
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002495 kfree(rd);
2496 return -EINVAL;
2497}
2498
Johannes Berg2a519312009-02-10 21:25:55 +01002499static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
2500{
2501 struct cfg80211_registered_device *drv;
2502 struct net_device *dev;
2503 struct cfg80211_scan_request *request;
2504 struct cfg80211_ssid *ssid;
2505 struct ieee80211_channel *channel;
2506 struct nlattr *attr;
2507 struct wiphy *wiphy;
2508 int err, tmp, n_ssids = 0, n_channels = 0, i;
2509 enum ieee80211_band band;
Jouni Malinen70692ad2009-02-16 19:39:13 +02002510 size_t ie_len;
Johannes Berg2a519312009-02-10 21:25:55 +01002511
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002512 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
2513 return -EINVAL;
2514
Johannes Berg3b858752009-03-12 09:55:09 +01002515 rtnl_lock();
2516
Johannes Berg2a519312009-02-10 21:25:55 +01002517 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2518 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002519 goto out_rtnl;
Johannes Berg2a519312009-02-10 21:25:55 +01002520
2521 wiphy = &drv->wiphy;
2522
2523 if (!drv->ops->scan) {
2524 err = -EOPNOTSUPP;
2525 goto out;
2526 }
2527
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002528 if (!netif_running(dev)) {
2529 err = -ENETDOWN;
2530 goto out;
2531 }
2532
Johannes Berg2a519312009-02-10 21:25:55 +01002533 if (drv->scan_req) {
2534 err = -EBUSY;
Johannes Berg3b858752009-03-12 09:55:09 +01002535 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002536 }
2537
2538 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
2539 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp)
2540 n_channels++;
2541 if (!n_channels) {
2542 err = -EINVAL;
Johannes Berg3b858752009-03-12 09:55:09 +01002543 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002544 }
2545 } else {
2546 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
2547 if (wiphy->bands[band])
2548 n_channels += wiphy->bands[band]->n_channels;
2549 }
2550
2551 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
2552 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
2553 n_ssids++;
2554
2555 if (n_ssids > wiphy->max_scan_ssids) {
2556 err = -EINVAL;
Johannes Berg3b858752009-03-12 09:55:09 +01002557 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002558 }
2559
Jouni Malinen70692ad2009-02-16 19:39:13 +02002560 if (info->attrs[NL80211_ATTR_IE])
2561 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
2562 else
2563 ie_len = 0;
2564
Johannes Berg18a83652009-03-31 12:12:05 +02002565 if (ie_len > wiphy->max_scan_ie_len) {
2566 err = -EINVAL;
2567 goto out;
2568 }
2569
Johannes Berg2a519312009-02-10 21:25:55 +01002570 request = kzalloc(sizeof(*request)
2571 + sizeof(*ssid) * n_ssids
Jouni Malinen70692ad2009-02-16 19:39:13 +02002572 + sizeof(channel) * n_channels
2573 + ie_len, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01002574 if (!request) {
2575 err = -ENOMEM;
Johannes Berg3b858752009-03-12 09:55:09 +01002576 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002577 }
2578
2579 request->channels = (void *)((char *)request + sizeof(*request));
2580 request->n_channels = n_channels;
2581 if (n_ssids)
2582 request->ssids = (void *)(request->channels + n_channels);
2583 request->n_ssids = n_ssids;
Jouni Malinen70692ad2009-02-16 19:39:13 +02002584 if (ie_len) {
2585 if (request->ssids)
2586 request->ie = (void *)(request->ssids + n_ssids);
2587 else
2588 request->ie = (void *)(request->channels + n_channels);
2589 }
Johannes Berg2a519312009-02-10 21:25:55 +01002590
2591 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
2592 /* user specified, bail out if channel not found */
2593 request->n_channels = n_channels;
2594 i = 0;
2595 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
2596 request->channels[i] = ieee80211_get_channel(wiphy, nla_get_u32(attr));
2597 if (!request->channels[i]) {
2598 err = -EINVAL;
2599 goto out_free;
2600 }
2601 i++;
2602 }
2603 } else {
2604 /* all channels */
2605 i = 0;
2606 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
2607 int j;
2608 if (!wiphy->bands[band])
2609 continue;
2610 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
2611 request->channels[i] = &wiphy->bands[band]->channels[j];
2612 i++;
2613 }
2614 }
2615 }
2616
2617 i = 0;
2618 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
2619 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
2620 if (request->ssids[i].ssid_len > IEEE80211_MAX_SSID_LEN) {
2621 err = -EINVAL;
2622 goto out_free;
2623 }
2624 memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
2625 request->ssids[i].ssid_len = nla_len(attr);
2626 i++;
2627 }
2628 }
2629
Jouni Malinen70692ad2009-02-16 19:39:13 +02002630 if (info->attrs[NL80211_ATTR_IE]) {
2631 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Johannes Bergde95a542009-04-01 11:58:36 +02002632 memcpy((void *)request->ie,
2633 nla_data(info->attrs[NL80211_ATTR_IE]),
Jouni Malinen70692ad2009-02-16 19:39:13 +02002634 request->ie_len);
2635 }
2636
Johannes Berg2a519312009-02-10 21:25:55 +01002637 request->ifidx = dev->ifindex;
2638 request->wiphy = &drv->wiphy;
2639
2640 drv->scan_req = request;
2641 err = drv->ops->scan(&drv->wiphy, dev, request);
2642
2643 out_free:
2644 if (err) {
2645 drv->scan_req = NULL;
2646 kfree(request);
2647 }
Johannes Berg2a519312009-02-10 21:25:55 +01002648 out:
2649 cfg80211_put_dev(drv);
2650 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002651 out_rtnl:
2652 rtnl_unlock();
2653
Johannes Berg2a519312009-02-10 21:25:55 +01002654 return err;
2655}
2656
2657static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
2658 struct cfg80211_registered_device *rdev,
2659 struct net_device *dev,
2660 struct cfg80211_bss *res)
2661{
2662 void *hdr;
2663 struct nlattr *bss;
2664
2665 hdr = nl80211hdr_put(msg, pid, seq, flags,
2666 NL80211_CMD_NEW_SCAN_RESULTS);
2667 if (!hdr)
2668 return -1;
2669
2670 NLA_PUT_U32(msg, NL80211_ATTR_SCAN_GENERATION,
2671 rdev->bss_generation);
2672 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2673
2674 bss = nla_nest_start(msg, NL80211_ATTR_BSS);
2675 if (!bss)
2676 goto nla_put_failure;
2677 if (!is_zero_ether_addr(res->bssid))
2678 NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid);
2679 if (res->information_elements && res->len_information_elements)
2680 NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS,
2681 res->len_information_elements,
2682 res->information_elements);
2683 if (res->tsf)
2684 NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf);
2685 if (res->beacon_interval)
2686 NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval);
2687 NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability);
2688 NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq);
2689
Johannes Berg77965c92009-02-18 18:45:06 +01002690 switch (rdev->wiphy.signal_type) {
Johannes Berg2a519312009-02-10 21:25:55 +01002691 case CFG80211_SIGNAL_TYPE_MBM:
2692 NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal);
2693 break;
2694 case CFG80211_SIGNAL_TYPE_UNSPEC:
2695 NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal);
2696 break;
2697 default:
2698 break;
2699 }
2700
2701 nla_nest_end(msg, bss);
2702
2703 return genlmsg_end(msg, hdr);
2704
2705 nla_put_failure:
2706 genlmsg_cancel(msg, hdr);
2707 return -EMSGSIZE;
2708}
2709
2710static int nl80211_dump_scan(struct sk_buff *skb,
2711 struct netlink_callback *cb)
2712{
2713 struct cfg80211_registered_device *dev;
2714 struct net_device *netdev;
2715 struct cfg80211_internal_bss *scan;
2716 int ifidx = cb->args[0];
2717 int start = cb->args[1], idx = 0;
2718 int err;
2719
2720 if (!ifidx) {
2721 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
2722 nl80211_fam.attrbuf, nl80211_fam.maxattr,
2723 nl80211_policy);
2724 if (err)
2725 return err;
2726
2727 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
2728 return -EINVAL;
2729
2730 ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
2731 if (!ifidx)
2732 return -EINVAL;
2733 cb->args[0] = ifidx;
2734 }
2735
2736 netdev = dev_get_by_index(&init_net, ifidx);
2737 if (!netdev)
2738 return -ENODEV;
2739
2740 dev = cfg80211_get_dev_from_ifindex(ifidx);
2741 if (IS_ERR(dev)) {
2742 err = PTR_ERR(dev);
2743 goto out_put_netdev;
2744 }
2745
2746 spin_lock_bh(&dev->bss_lock);
2747 cfg80211_bss_expire(dev);
2748
2749 list_for_each_entry(scan, &dev->bss_list, list) {
2750 if (++idx <= start)
2751 continue;
2752 if (nl80211_send_bss(skb,
2753 NETLINK_CB(cb->skb).pid,
2754 cb->nlh->nlmsg_seq, NLM_F_MULTI,
2755 dev, netdev, &scan->pub) < 0) {
2756 idx--;
2757 goto out;
2758 }
2759 }
2760
2761 out:
2762 spin_unlock_bh(&dev->bss_lock);
2763
2764 cb->args[1] = idx;
2765 err = skb->len;
2766 cfg80211_put_dev(dev);
2767 out_put_netdev:
2768 dev_put(netdev);
2769
2770 return err;
2771}
2772
Jouni Malinen255e7372009-03-20 21:21:17 +02002773static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
2774{
2775 return auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM ||
2776 auth_type == NL80211_AUTHTYPE_SHARED_KEY ||
2777 auth_type == NL80211_AUTHTYPE_FT ||
2778 auth_type == NL80211_AUTHTYPE_NETWORK_EAP;
2779}
2780
Jouni Malinen636a5d32009-03-19 13:39:22 +02002781static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
2782{
2783 struct cfg80211_registered_device *drv;
2784 struct net_device *dev;
2785 struct cfg80211_auth_request req;
2786 struct wiphy *wiphy;
2787 int err;
2788
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002789 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
2790 return -EINVAL;
2791
2792 if (!info->attrs[NL80211_ATTR_MAC])
2793 return -EINVAL;
2794
Jouni Malinen17780922009-03-27 20:52:47 +02002795 if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
2796 return -EINVAL;
2797
Jouni Malinen636a5d32009-03-19 13:39:22 +02002798 rtnl_lock();
2799
2800 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2801 if (err)
2802 goto unlock_rtnl;
2803
2804 if (!drv->ops->auth) {
2805 err = -EOPNOTSUPP;
2806 goto out;
2807 }
2808
Jouni Malineneec60b02009-03-20 21:21:19 +02002809 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
2810 err = -EOPNOTSUPP;
2811 goto out;
2812 }
2813
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002814 if (!netif_running(dev)) {
2815 err = -ENETDOWN;
2816 goto out;
2817 }
2818
Jouni Malinen636a5d32009-03-19 13:39:22 +02002819 wiphy = &drv->wiphy;
2820 memset(&req, 0, sizeof(req));
2821
2822 req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2823
2824 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
2825 req.chan = ieee80211_get_channel(
2826 wiphy,
2827 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
2828 if (!req.chan) {
2829 err = -EINVAL;
2830 goto out;
2831 }
2832 }
2833
2834 if (info->attrs[NL80211_ATTR_SSID]) {
2835 req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
2836 req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
2837 }
2838
2839 if (info->attrs[NL80211_ATTR_IE]) {
2840 req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
2841 req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
2842 }
2843
Jouni Malinen17780922009-03-27 20:52:47 +02002844 req.auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
2845 if (!nl80211_valid_auth_type(req.auth_type)) {
2846 err = -EINVAL;
2847 goto out;
Jouni Malinen636a5d32009-03-19 13:39:22 +02002848 }
2849
2850 err = drv->ops->auth(&drv->wiphy, dev, &req);
2851
2852out:
2853 cfg80211_put_dev(drv);
2854 dev_put(dev);
2855unlock_rtnl:
2856 rtnl_unlock();
2857 return err;
2858}
2859
2860static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
2861{
2862 struct cfg80211_registered_device *drv;
2863 struct net_device *dev;
2864 struct cfg80211_assoc_request req;
2865 struct wiphy *wiphy;
2866 int err;
2867
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002868 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
2869 return -EINVAL;
2870
2871 if (!info->attrs[NL80211_ATTR_MAC] ||
2872 !info->attrs[NL80211_ATTR_SSID])
2873 return -EINVAL;
2874
Jouni Malinen636a5d32009-03-19 13:39:22 +02002875 rtnl_lock();
2876
2877 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2878 if (err)
2879 goto unlock_rtnl;
2880
2881 if (!drv->ops->assoc) {
2882 err = -EOPNOTSUPP;
2883 goto out;
2884 }
2885
Jouni Malineneec60b02009-03-20 21:21:19 +02002886 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
2887 err = -EOPNOTSUPP;
2888 goto out;
2889 }
2890
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002891 if (!netif_running(dev)) {
2892 err = -ENETDOWN;
2893 goto out;
2894 }
2895
Jouni Malinen636a5d32009-03-19 13:39:22 +02002896 wiphy = &drv->wiphy;
2897 memset(&req, 0, sizeof(req));
2898
2899 req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2900
2901 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
2902 req.chan = ieee80211_get_channel(
2903 wiphy,
2904 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
2905 if (!req.chan) {
2906 err = -EINVAL;
2907 goto out;
2908 }
2909 }
2910
Jouni Malinen636a5d32009-03-19 13:39:22 +02002911 req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
2912 req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
2913
2914 if (info->attrs[NL80211_ATTR_IE]) {
2915 req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
2916 req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
2917 }
2918
2919 err = drv->ops->assoc(&drv->wiphy, dev, &req);
2920
2921out:
2922 cfg80211_put_dev(drv);
2923 dev_put(dev);
2924unlock_rtnl:
2925 rtnl_unlock();
2926 return err;
2927}
2928
2929static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
2930{
2931 struct cfg80211_registered_device *drv;
2932 struct net_device *dev;
2933 struct cfg80211_deauth_request req;
2934 struct wiphy *wiphy;
2935 int err;
2936
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002937 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
2938 return -EINVAL;
2939
2940 if (!info->attrs[NL80211_ATTR_MAC])
2941 return -EINVAL;
2942
2943 if (!info->attrs[NL80211_ATTR_REASON_CODE])
2944 return -EINVAL;
2945
Jouni Malinen636a5d32009-03-19 13:39:22 +02002946 rtnl_lock();
2947
2948 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2949 if (err)
2950 goto unlock_rtnl;
2951
2952 if (!drv->ops->deauth) {
2953 err = -EOPNOTSUPP;
2954 goto out;
2955 }
2956
Jouni Malineneec60b02009-03-20 21:21:19 +02002957 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
2958 err = -EOPNOTSUPP;
2959 goto out;
2960 }
2961
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002962 if (!netif_running(dev)) {
2963 err = -ENETDOWN;
2964 goto out;
2965 }
2966
Jouni Malinen636a5d32009-03-19 13:39:22 +02002967 wiphy = &drv->wiphy;
2968 memset(&req, 0, sizeof(req));
2969
2970 req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2971
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002972 req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
2973 if (req.reason_code == 0) {
2974 /* Reason Code 0 is reserved */
2975 err = -EINVAL;
2976 goto out;
Jouni Malinen255e7372009-03-20 21:21:17 +02002977 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02002978
2979 if (info->attrs[NL80211_ATTR_IE]) {
2980 req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
2981 req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
2982 }
2983
2984 err = drv->ops->deauth(&drv->wiphy, dev, &req);
2985
2986out:
2987 cfg80211_put_dev(drv);
2988 dev_put(dev);
2989unlock_rtnl:
2990 rtnl_unlock();
2991 return err;
2992}
2993
2994static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
2995{
2996 struct cfg80211_registered_device *drv;
2997 struct net_device *dev;
2998 struct cfg80211_disassoc_request req;
2999 struct wiphy *wiphy;
3000 int err;
3001
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003002 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3003 return -EINVAL;
3004
3005 if (!info->attrs[NL80211_ATTR_MAC])
3006 return -EINVAL;
3007
3008 if (!info->attrs[NL80211_ATTR_REASON_CODE])
3009 return -EINVAL;
3010
Jouni Malinen636a5d32009-03-19 13:39:22 +02003011 rtnl_lock();
3012
3013 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
3014 if (err)
3015 goto unlock_rtnl;
3016
3017 if (!drv->ops->disassoc) {
3018 err = -EOPNOTSUPP;
3019 goto out;
3020 }
3021
Jouni Malineneec60b02009-03-20 21:21:19 +02003022 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
3023 err = -EOPNOTSUPP;
3024 goto out;
3025 }
3026
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003027 if (!netif_running(dev)) {
3028 err = -ENETDOWN;
3029 goto out;
3030 }
3031
Jouni Malinen636a5d32009-03-19 13:39:22 +02003032 wiphy = &drv->wiphy;
3033 memset(&req, 0, sizeof(req));
3034
3035 req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
3036
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003037 req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
3038 if (req.reason_code == 0) {
3039 /* Reason Code 0 is reserved */
3040 err = -EINVAL;
3041 goto out;
Jouni Malinen255e7372009-03-20 21:21:17 +02003042 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02003043
3044 if (info->attrs[NL80211_ATTR_IE]) {
3045 req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3046 req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3047 }
3048
3049 err = drv->ops->disassoc(&drv->wiphy, dev, &req);
3050
3051out:
3052 cfg80211_put_dev(drv);
3053 dev_put(dev);
3054unlock_rtnl:
3055 rtnl_unlock();
3056 return err;
3057}
3058
Johannes Berg04a773a2009-04-19 21:24:32 +02003059static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
3060{
3061 struct cfg80211_registered_device *drv;
3062 struct net_device *dev;
3063 struct cfg80211_ibss_params ibss;
3064 struct wiphy *wiphy;
3065 int err;
3066
3067 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3068 return -EINVAL;
3069
3070 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
3071 !info->attrs[NL80211_ATTR_SSID] ||
3072 !nla_len(info->attrs[NL80211_ATTR_SSID]))
3073 return -EINVAL;
3074
3075 rtnl_lock();
3076
3077 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
3078 if (err)
3079 goto unlock_rtnl;
3080
3081 if (!drv->ops->join_ibss) {
3082 err = -EOPNOTSUPP;
3083 goto out;
3084 }
3085
3086 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
3087 err = -EOPNOTSUPP;
3088 goto out;
3089 }
3090
3091 if (!netif_running(dev)) {
3092 err = -ENETDOWN;
3093 goto out;
3094 }
3095
3096 wiphy = &drv->wiphy;
3097 memset(&ibss, 0, sizeof(ibss));
3098
3099 if (info->attrs[NL80211_ATTR_MAC])
3100 ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
3101 ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3102 ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
3103
3104 if (info->attrs[NL80211_ATTR_IE]) {
3105 ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3106 ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3107 }
3108
3109 ibss.channel = ieee80211_get_channel(wiphy,
3110 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
3111 if (!ibss.channel ||
3112 ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
3113 ibss.channel->flags & IEEE80211_CHAN_DISABLED) {
3114 err = -EINVAL;
3115 goto out;
3116 }
3117
3118 ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
3119
3120 err = cfg80211_join_ibss(drv, dev, &ibss);
3121
3122out:
3123 cfg80211_put_dev(drv);
3124 dev_put(dev);
3125unlock_rtnl:
3126 rtnl_unlock();
3127 return err;
3128}
3129
3130static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
3131{
3132 struct cfg80211_registered_device *drv;
3133 struct net_device *dev;
3134 int err;
3135
3136 rtnl_lock();
3137
3138 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
3139 if (err)
3140 goto unlock_rtnl;
3141
3142 if (!drv->ops->leave_ibss) {
3143 err = -EOPNOTSUPP;
3144 goto out;
3145 }
3146
3147 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
3148 err = -EOPNOTSUPP;
3149 goto out;
3150 }
3151
3152 if (!netif_running(dev)) {
3153 err = -ENETDOWN;
3154 goto out;
3155 }
3156
3157 err = cfg80211_leave_ibss(drv, dev);
3158
3159out:
3160 cfg80211_put_dev(drv);
3161 dev_put(dev);
3162unlock_rtnl:
3163 rtnl_unlock();
3164 return err;
3165}
3166
Johannes Berg55682962007-09-20 13:09:35 -04003167static struct genl_ops nl80211_ops[] = {
3168 {
3169 .cmd = NL80211_CMD_GET_WIPHY,
3170 .doit = nl80211_get_wiphy,
3171 .dumpit = nl80211_dump_wiphy,
3172 .policy = nl80211_policy,
3173 /* can be retrieved by unprivileged users */
3174 },
3175 {
3176 .cmd = NL80211_CMD_SET_WIPHY,
3177 .doit = nl80211_set_wiphy,
3178 .policy = nl80211_policy,
3179 .flags = GENL_ADMIN_PERM,
3180 },
3181 {
3182 .cmd = NL80211_CMD_GET_INTERFACE,
3183 .doit = nl80211_get_interface,
3184 .dumpit = nl80211_dump_interface,
3185 .policy = nl80211_policy,
3186 /* can be retrieved by unprivileged users */
3187 },
3188 {
3189 .cmd = NL80211_CMD_SET_INTERFACE,
3190 .doit = nl80211_set_interface,
3191 .policy = nl80211_policy,
3192 .flags = GENL_ADMIN_PERM,
3193 },
3194 {
3195 .cmd = NL80211_CMD_NEW_INTERFACE,
3196 .doit = nl80211_new_interface,
3197 .policy = nl80211_policy,
3198 .flags = GENL_ADMIN_PERM,
3199 },
3200 {
3201 .cmd = NL80211_CMD_DEL_INTERFACE,
3202 .doit = nl80211_del_interface,
3203 .policy = nl80211_policy,
3204 .flags = GENL_ADMIN_PERM,
3205 },
Johannes Berg41ade002007-12-19 02:03:29 +01003206 {
3207 .cmd = NL80211_CMD_GET_KEY,
3208 .doit = nl80211_get_key,
3209 .policy = nl80211_policy,
3210 .flags = GENL_ADMIN_PERM,
3211 },
3212 {
3213 .cmd = NL80211_CMD_SET_KEY,
3214 .doit = nl80211_set_key,
3215 .policy = nl80211_policy,
3216 .flags = GENL_ADMIN_PERM,
3217 },
3218 {
3219 .cmd = NL80211_CMD_NEW_KEY,
3220 .doit = nl80211_new_key,
3221 .policy = nl80211_policy,
3222 .flags = GENL_ADMIN_PERM,
3223 },
3224 {
3225 .cmd = NL80211_CMD_DEL_KEY,
3226 .doit = nl80211_del_key,
3227 .policy = nl80211_policy,
3228 .flags = GENL_ADMIN_PERM,
3229 },
Johannes Berged1b6cc2007-12-19 02:03:32 +01003230 {
3231 .cmd = NL80211_CMD_SET_BEACON,
3232 .policy = nl80211_policy,
3233 .flags = GENL_ADMIN_PERM,
3234 .doit = nl80211_addset_beacon,
3235 },
3236 {
3237 .cmd = NL80211_CMD_NEW_BEACON,
3238 .policy = nl80211_policy,
3239 .flags = GENL_ADMIN_PERM,
3240 .doit = nl80211_addset_beacon,
3241 },
3242 {
3243 .cmd = NL80211_CMD_DEL_BEACON,
3244 .policy = nl80211_policy,
3245 .flags = GENL_ADMIN_PERM,
3246 .doit = nl80211_del_beacon,
3247 },
Johannes Berg5727ef12007-12-19 02:03:34 +01003248 {
3249 .cmd = NL80211_CMD_GET_STATION,
3250 .doit = nl80211_get_station,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003251 .dumpit = nl80211_dump_station,
Johannes Berg5727ef12007-12-19 02:03:34 +01003252 .policy = nl80211_policy,
Johannes Berg5727ef12007-12-19 02:03:34 +01003253 },
3254 {
3255 .cmd = NL80211_CMD_SET_STATION,
3256 .doit = nl80211_set_station,
3257 .policy = nl80211_policy,
3258 .flags = GENL_ADMIN_PERM,
3259 },
3260 {
3261 .cmd = NL80211_CMD_NEW_STATION,
3262 .doit = nl80211_new_station,
3263 .policy = nl80211_policy,
3264 .flags = GENL_ADMIN_PERM,
3265 },
3266 {
3267 .cmd = NL80211_CMD_DEL_STATION,
3268 .doit = nl80211_del_station,
3269 .policy = nl80211_policy,
3270 .flags = GENL_ADMIN_PERM,
3271 },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003272 {
3273 .cmd = NL80211_CMD_GET_MPATH,
3274 .doit = nl80211_get_mpath,
3275 .dumpit = nl80211_dump_mpath,
3276 .policy = nl80211_policy,
3277 .flags = GENL_ADMIN_PERM,
3278 },
3279 {
3280 .cmd = NL80211_CMD_SET_MPATH,
3281 .doit = nl80211_set_mpath,
3282 .policy = nl80211_policy,
3283 .flags = GENL_ADMIN_PERM,
3284 },
3285 {
3286 .cmd = NL80211_CMD_NEW_MPATH,
3287 .doit = nl80211_new_mpath,
3288 .policy = nl80211_policy,
3289 .flags = GENL_ADMIN_PERM,
3290 },
3291 {
3292 .cmd = NL80211_CMD_DEL_MPATH,
3293 .doit = nl80211_del_mpath,
3294 .policy = nl80211_policy,
3295 .flags = GENL_ADMIN_PERM,
3296 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003297 {
3298 .cmd = NL80211_CMD_SET_BSS,
3299 .doit = nl80211_set_bss,
3300 .policy = nl80211_policy,
3301 .flags = GENL_ADMIN_PERM,
3302 },
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003303 {
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003304 .cmd = NL80211_CMD_GET_REG,
3305 .doit = nl80211_get_reg,
3306 .policy = nl80211_policy,
3307 /* can be retrieved by unprivileged users */
3308 },
3309 {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003310 .cmd = NL80211_CMD_SET_REG,
3311 .doit = nl80211_set_reg,
3312 .policy = nl80211_policy,
3313 .flags = GENL_ADMIN_PERM,
3314 },
3315 {
3316 .cmd = NL80211_CMD_REQ_SET_REG,
3317 .doit = nl80211_req_set_reg,
3318 .policy = nl80211_policy,
3319 .flags = GENL_ADMIN_PERM,
3320 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003321 {
3322 .cmd = NL80211_CMD_GET_MESH_PARAMS,
3323 .doit = nl80211_get_mesh_params,
3324 .policy = nl80211_policy,
3325 /* can be retrieved by unprivileged users */
3326 },
3327 {
3328 .cmd = NL80211_CMD_SET_MESH_PARAMS,
3329 .doit = nl80211_set_mesh_params,
3330 .policy = nl80211_policy,
3331 .flags = GENL_ADMIN_PERM,
3332 },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +02003333 {
Johannes Berg2a519312009-02-10 21:25:55 +01003334 .cmd = NL80211_CMD_TRIGGER_SCAN,
3335 .doit = nl80211_trigger_scan,
3336 .policy = nl80211_policy,
3337 .flags = GENL_ADMIN_PERM,
3338 },
3339 {
3340 .cmd = NL80211_CMD_GET_SCAN,
3341 .policy = nl80211_policy,
3342 .dumpit = nl80211_dump_scan,
3343 },
Jouni Malinen636a5d32009-03-19 13:39:22 +02003344 {
3345 .cmd = NL80211_CMD_AUTHENTICATE,
3346 .doit = nl80211_authenticate,
3347 .policy = nl80211_policy,
3348 .flags = GENL_ADMIN_PERM,
3349 },
3350 {
3351 .cmd = NL80211_CMD_ASSOCIATE,
3352 .doit = nl80211_associate,
3353 .policy = nl80211_policy,
3354 .flags = GENL_ADMIN_PERM,
3355 },
3356 {
3357 .cmd = NL80211_CMD_DEAUTHENTICATE,
3358 .doit = nl80211_deauthenticate,
3359 .policy = nl80211_policy,
3360 .flags = GENL_ADMIN_PERM,
3361 },
3362 {
3363 .cmd = NL80211_CMD_DISASSOCIATE,
3364 .doit = nl80211_disassociate,
3365 .policy = nl80211_policy,
3366 .flags = GENL_ADMIN_PERM,
3367 },
Johannes Berg04a773a2009-04-19 21:24:32 +02003368 {
3369 .cmd = NL80211_CMD_JOIN_IBSS,
3370 .doit = nl80211_join_ibss,
3371 .policy = nl80211_policy,
3372 .flags = GENL_ADMIN_PERM,
3373 },
3374 {
3375 .cmd = NL80211_CMD_LEAVE_IBSS,
3376 .doit = nl80211_leave_ibss,
3377 .policy = nl80211_policy,
3378 .flags = GENL_ADMIN_PERM,
3379 },
Johannes Berg55682962007-09-20 13:09:35 -04003380};
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003381static struct genl_multicast_group nl80211_mlme_mcgrp = {
3382 .name = "mlme",
3383};
Johannes Berg55682962007-09-20 13:09:35 -04003384
3385/* multicast groups */
3386static struct genl_multicast_group nl80211_config_mcgrp = {
3387 .name = "config",
3388};
Johannes Berg2a519312009-02-10 21:25:55 +01003389static struct genl_multicast_group nl80211_scan_mcgrp = {
3390 .name = "scan",
3391};
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04003392static struct genl_multicast_group nl80211_regulatory_mcgrp = {
3393 .name = "regulatory",
3394};
Johannes Berg55682962007-09-20 13:09:35 -04003395
3396/* notification functions */
3397
3398void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
3399{
3400 struct sk_buff *msg;
3401
3402 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3403 if (!msg)
3404 return;
3405
3406 if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
3407 nlmsg_free(msg);
3408 return;
3409 }
3410
3411 genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL);
3412}
3413
Johannes Berg2a519312009-02-10 21:25:55 +01003414static int nl80211_send_scan_donemsg(struct sk_buff *msg,
3415 struct cfg80211_registered_device *rdev,
3416 struct net_device *netdev,
3417 u32 pid, u32 seq, int flags,
3418 u32 cmd)
3419{
3420 void *hdr;
3421
3422 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
3423 if (!hdr)
3424 return -1;
3425
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -05003426 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg2a519312009-02-10 21:25:55 +01003427 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
3428
3429 /* XXX: we should probably bounce back the request? */
3430
3431 return genlmsg_end(msg, hdr);
3432
3433 nla_put_failure:
3434 genlmsg_cancel(msg, hdr);
3435 return -EMSGSIZE;
3436}
3437
3438void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
3439 struct net_device *netdev)
3440{
3441 struct sk_buff *msg;
3442
3443 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3444 if (!msg)
3445 return;
3446
3447 if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0,
3448 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
3449 nlmsg_free(msg);
3450 return;
3451 }
3452
3453 genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL);
3454}
3455
3456void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
3457 struct net_device *netdev)
3458{
3459 struct sk_buff *msg;
3460
3461 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3462 if (!msg)
3463 return;
3464
3465 if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0,
3466 NL80211_CMD_SCAN_ABORTED) < 0) {
3467 nlmsg_free(msg);
3468 return;
3469 }
3470
3471 genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL);
3472}
3473
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04003474/*
3475 * This can happen on global regulatory changes or device specific settings
3476 * based on custom world regulatory domains.
3477 */
3478void nl80211_send_reg_change_event(struct regulatory_request *request)
3479{
3480 struct sk_buff *msg;
3481 void *hdr;
3482
3483 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3484 if (!msg)
3485 return;
3486
3487 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
3488 if (!hdr) {
3489 nlmsg_free(msg);
3490 return;
3491 }
3492
3493 /* Userspace can always count this one always being set */
3494 NLA_PUT_U8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator);
3495
3496 if (request->alpha2[0] == '0' && request->alpha2[1] == '0')
3497 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
3498 NL80211_REGDOM_TYPE_WORLD);
3499 else if (request->alpha2[0] == '9' && request->alpha2[1] == '9')
3500 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
3501 NL80211_REGDOM_TYPE_CUSTOM_WORLD);
3502 else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
3503 request->intersect)
3504 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
3505 NL80211_REGDOM_TYPE_INTERSECTION);
3506 else {
3507 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
3508 NL80211_REGDOM_TYPE_COUNTRY);
3509 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2);
3510 }
3511
3512 if (wiphy_idx_valid(request->wiphy_idx))
3513 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx);
3514
3515 if (genlmsg_end(msg, hdr) < 0) {
3516 nlmsg_free(msg);
3517 return;
3518 }
3519
3520 genlmsg_multicast(msg, 0, nl80211_regulatory_mcgrp.id, GFP_KERNEL);
3521
3522 return;
3523
3524nla_put_failure:
3525 genlmsg_cancel(msg, hdr);
3526 nlmsg_free(msg);
3527}
3528
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003529static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
3530 struct net_device *netdev,
3531 const u8 *buf, size_t len,
3532 enum nl80211_commands cmd)
3533{
3534 struct sk_buff *msg;
3535 void *hdr;
3536
Jouni Malinend91c01c2009-04-18 21:53:15 +03003537 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003538 if (!msg)
3539 return;
3540
3541 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
3542 if (!hdr) {
3543 nlmsg_free(msg);
3544 return;
3545 }
3546
3547 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
3548 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
3549 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
3550
3551 if (genlmsg_end(msg, hdr) < 0) {
3552 nlmsg_free(msg);
3553 return;
3554 }
3555
Jouni Malinend91c01c2009-04-18 21:53:15 +03003556 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_ATOMIC);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003557 return;
3558
3559 nla_put_failure:
3560 genlmsg_cancel(msg, hdr);
3561 nlmsg_free(msg);
3562}
3563
3564void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
3565 struct net_device *netdev, const u8 *buf, size_t len)
3566{
3567 nl80211_send_mlme_event(rdev, netdev, buf, len,
3568 NL80211_CMD_AUTHENTICATE);
3569}
3570
3571void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
3572 struct net_device *netdev, const u8 *buf,
3573 size_t len)
3574{
3575 nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_ASSOCIATE);
3576}
3577
Jouni Malinen53b46b82009-03-27 20:53:56 +02003578void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
3579 struct net_device *netdev, const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003580{
3581 nl80211_send_mlme_event(rdev, netdev, buf, len,
3582 NL80211_CMD_DEAUTHENTICATE);
3583}
3584
Jouni Malinen53b46b82009-03-27 20:53:56 +02003585void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
3586 struct net_device *netdev, const u8 *buf,
3587 size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003588{
3589 nl80211_send_mlme_event(rdev, netdev, buf, len,
3590 NL80211_CMD_DISASSOCIATE);
3591}
3592
Johannes Berg04a773a2009-04-19 21:24:32 +02003593void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
3594 struct net_device *netdev, const u8 *bssid,
3595 gfp_t gfp)
3596{
3597 struct sk_buff *msg;
3598 void *hdr;
3599
3600 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
3601 if (!msg)
3602 return;
3603
3604 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS);
3605 if (!hdr) {
3606 nlmsg_free(msg);
3607 return;
3608 }
3609
3610 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
3611 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
3612 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
3613
3614 if (genlmsg_end(msg, hdr) < 0) {
3615 nlmsg_free(msg);
3616 return;
3617 }
3618
3619 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
3620 return;
3621
3622 nla_put_failure:
3623 genlmsg_cancel(msg, hdr);
3624 nlmsg_free(msg);
3625}
3626
Jouni Malinena3b8b052009-03-27 21:59:49 +02003627void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
3628 struct net_device *netdev, const u8 *addr,
3629 enum nl80211_key_type key_type, int key_id,
3630 const u8 *tsc)
3631{
3632 struct sk_buff *msg;
3633 void *hdr;
3634
3635 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3636 if (!msg)
3637 return;
3638
3639 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
3640 if (!hdr) {
3641 nlmsg_free(msg);
3642 return;
3643 }
3644
3645 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
3646 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
3647 if (addr)
3648 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
3649 NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, key_type);
3650 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id);
3651 if (tsc)
3652 NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc);
3653
3654 if (genlmsg_end(msg, hdr) < 0) {
3655 nlmsg_free(msg);
3656 return;
3657 }
3658
3659 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_KERNEL);
3660 return;
3661
3662 nla_put_failure:
3663 genlmsg_cancel(msg, hdr);
3664 nlmsg_free(msg);
3665}
3666
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04003667void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
3668 struct ieee80211_channel *channel_before,
3669 struct ieee80211_channel *channel_after)
3670{
3671 struct sk_buff *msg;
3672 void *hdr;
3673 struct nlattr *nl_freq;
3674
3675 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
3676 if (!msg)
3677 return;
3678
3679 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
3680 if (!hdr) {
3681 nlmsg_free(msg);
3682 return;
3683 }
3684
3685 /*
3686 * Since we are applying the beacon hint to a wiphy we know its
3687 * wiphy_idx is valid
3688 */
3689 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy));
3690
3691 /* Before */
3692 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
3693 if (!nl_freq)
3694 goto nla_put_failure;
3695 if (nl80211_msg_put_channel(msg, channel_before))
3696 goto nla_put_failure;
3697 nla_nest_end(msg, nl_freq);
3698
3699 /* After */
3700 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
3701 if (!nl_freq)
3702 goto nla_put_failure;
3703 if (nl80211_msg_put_channel(msg, channel_after))
3704 goto nla_put_failure;
3705 nla_nest_end(msg, nl_freq);
3706
3707 if (genlmsg_end(msg, hdr) < 0) {
3708 nlmsg_free(msg);
3709 return;
3710 }
3711
3712 genlmsg_multicast(msg, 0, nl80211_regulatory_mcgrp.id, GFP_ATOMIC);
3713
3714 return;
3715
3716nla_put_failure:
3717 genlmsg_cancel(msg, hdr);
3718 nlmsg_free(msg);
3719}
3720
Johannes Berg55682962007-09-20 13:09:35 -04003721/* initialisation/exit functions */
3722
3723int nl80211_init(void)
3724{
3725 int err, i;
3726
3727 err = genl_register_family(&nl80211_fam);
3728 if (err)
3729 return err;
3730
3731 for (i = 0; i < ARRAY_SIZE(nl80211_ops); i++) {
3732 err = genl_register_ops(&nl80211_fam, &nl80211_ops[i]);
3733 if (err)
3734 goto err_out;
3735 }
3736
3737 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
3738 if (err)
3739 goto err_out;
3740
Johannes Berg2a519312009-02-10 21:25:55 +01003741 err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
3742 if (err)
3743 goto err_out;
3744
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04003745 err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
3746 if (err)
3747 goto err_out;
3748
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003749 err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
3750 if (err)
3751 goto err_out;
3752
Johannes Berg55682962007-09-20 13:09:35 -04003753 return 0;
3754 err_out:
3755 genl_unregister_family(&nl80211_fam);
3756 return err;
3757}
3758
3759void nl80211_exit(void)
3760{
3761 genl_unregister_family(&nl80211_fam);
3762}