blob: 79927706937a2162b266820272bac0e16422bc9d [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 },
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +020064 [NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 },
65 [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
66 [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
67 [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 },
Johannes Berg55682962007-09-20 13:09:35 -040068
69 [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
70 [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
71 [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
Johannes Berg41ade002007-12-19 02:03:29 +010072
73 [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN },
74
75 [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
76 .len = WLAN_MAX_KEY_LEN },
77 [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
78 [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
79 [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
Johannes Berged1b6cc2007-12-19 02:03:32 +010080
81 [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
82 [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
83 [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY,
84 .len = IEEE80211_MAX_DATA_LEN },
85 [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY,
86 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg5727ef12007-12-19 02:03:34 +010087 [NL80211_ATTR_STA_AID] = { .type = NLA_U16 },
88 [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED },
89 [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 },
90 [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY,
91 .len = NL80211_MAX_SUPP_RATES },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +010092 [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
Johannes Berg5727ef12007-12-19 02:03:34 +010093 [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
Johannes Berg0a9542e2008-10-15 11:54:04 +020094 [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +010095 [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
96 .len = IEEE80211_MAX_MESH_ID_LEN },
97 [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +030098
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -070099 [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 },
100 [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED },
101
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300102 [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
103 [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
104 [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
Jouni Malinen90c97a02008-10-30 16:59:22 +0200105 [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
106 .len = NL80211_MAX_SUPP_RATES },
Jouni Malinen36aedc92008-08-25 11:58:58 +0300107
colin@cozybit.com93da9cc2008-10-21 12:03:48 -0700108 [NL80211_ATTR_MESH_PARAMS] = { .type = NLA_NESTED },
109
Jouni Malinen36aedc92008-08-25 11:58:58 +0300110 [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY,
111 .len = NL80211_HT_CAPABILITY_LEN },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +0200112
113 [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
114 [NL80211_ATTR_IE] = { .type = NLA_BINARY,
115 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg2a519312009-02-10 21:25:55 +0100116 [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
117 [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
Jouni Malinen636a5d32009-03-19 13:39:22 +0200118
119 [NL80211_ATTR_SSID] = { .type = NLA_BINARY,
120 .len = IEEE80211_MAX_SSID_LEN },
121 [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
122 [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
Johannes Berg04a773a2009-04-19 21:24:32 +0200123 [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
Jouni Malinen1965c852009-04-22 21:38:25 +0300124 [NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG },
Jouni Malinendc6382c2009-05-06 22:09:37 +0300125 [NL80211_ATTR_USE_MFP] = { .type = NLA_U32 },
Johannes Berg55682962007-09-20 13:09:35 -0400126};
127
Johannes Bergf4a11bb2009-03-27 12:40:28 +0100128/* IE validation */
129static bool is_valid_ie_attr(const struct nlattr *attr)
130{
131 const u8 *pos;
132 int len;
133
134 if (!attr)
135 return true;
136
137 pos = nla_data(attr);
138 len = nla_len(attr);
139
140 while (len) {
141 u8 elemlen;
142
143 if (len < 2)
144 return false;
145 len -= 2;
146
147 elemlen = pos[1];
148 if (elemlen > len)
149 return false;
150
151 len -= elemlen;
152 pos += 2 + elemlen;
153 }
154
155 return true;
156}
157
Johannes Berg55682962007-09-20 13:09:35 -0400158/* message building helper */
159static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
160 int flags, u8 cmd)
161{
162 /* since there is no private header just add the generic one */
163 return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
164}
165
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400166static int nl80211_msg_put_channel(struct sk_buff *msg,
167 struct ieee80211_channel *chan)
168{
169 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
170 chan->center_freq);
171
172 if (chan->flags & IEEE80211_CHAN_DISABLED)
173 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
174 if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
175 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
176 if (chan->flags & IEEE80211_CHAN_NO_IBSS)
177 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
178 if (chan->flags & IEEE80211_CHAN_RADAR)
179 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
180
181 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
182 DBM_TO_MBM(chan->max_power));
183
184 return 0;
185
186 nla_put_failure:
187 return -ENOBUFS;
188}
189
Johannes Berg55682962007-09-20 13:09:35 -0400190/* netlink command implementations */
191
192static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
193 struct cfg80211_registered_device *dev)
194{
195 void *hdr;
Johannes Bergee688b02008-01-24 19:38:39 +0100196 struct nlattr *nl_bands, *nl_band;
197 struct nlattr *nl_freqs, *nl_freq;
198 struct nlattr *nl_rates, *nl_rate;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700199 struct nlattr *nl_modes;
Johannes Berg8fdc6212009-03-14 09:34:01 +0100200 struct nlattr *nl_cmds;
Johannes Bergee688b02008-01-24 19:38:39 +0100201 enum ieee80211_band band;
202 struct ieee80211_channel *chan;
203 struct ieee80211_rate *rate;
204 int i;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700205 u16 ifmodes = dev->wiphy.interface_modes;
Johannes Berg55682962007-09-20 13:09:35 -0400206
207 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
208 if (!hdr)
209 return -1;
210
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -0500211 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400212 NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200213
214 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
215 dev->wiphy.retry_short);
216 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
217 dev->wiphy.retry_long);
218 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
219 dev->wiphy.frag_threshold);
220 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
221 dev->wiphy.rts_threshold);
222
Johannes Berg2a519312009-02-10 21:25:55 +0100223 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
224 dev->wiphy.max_scan_ssids);
Johannes Berg18a83652009-03-31 12:12:05 +0200225 NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
226 dev->wiphy.max_scan_ie_len);
Johannes Bergee688b02008-01-24 19:38:39 +0100227
Johannes Berg25e47c12009-04-02 20:14:06 +0200228 NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
229 sizeof(u32) * dev->wiphy.n_cipher_suites,
230 dev->wiphy.cipher_suites);
231
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700232 nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES);
233 if (!nl_modes)
234 goto nla_put_failure;
235
236 i = 0;
237 while (ifmodes) {
238 if (ifmodes & 1)
239 NLA_PUT_FLAG(msg, i);
240 ifmodes >>= 1;
241 i++;
242 }
243
244 nla_nest_end(msg, nl_modes);
245
Johannes Bergee688b02008-01-24 19:38:39 +0100246 nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
247 if (!nl_bands)
248 goto nla_put_failure;
249
250 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
251 if (!dev->wiphy.bands[band])
252 continue;
253
254 nl_band = nla_nest_start(msg, band);
255 if (!nl_band)
256 goto nla_put_failure;
257
Johannes Bergd51626d2008-10-09 12:20:13 +0200258 /* add HT info */
259 if (dev->wiphy.bands[band]->ht_cap.ht_supported) {
260 NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET,
261 sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
262 &dev->wiphy.bands[band]->ht_cap.mcs);
263 NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA,
264 dev->wiphy.bands[band]->ht_cap.cap);
265 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
266 dev->wiphy.bands[band]->ht_cap.ampdu_factor);
267 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
268 dev->wiphy.bands[band]->ht_cap.ampdu_density);
269 }
270
Johannes Bergee688b02008-01-24 19:38:39 +0100271 /* add frequencies */
272 nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
273 if (!nl_freqs)
274 goto nla_put_failure;
275
276 for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
277 nl_freq = nla_nest_start(msg, i);
278 if (!nl_freq)
279 goto nla_put_failure;
280
281 chan = &dev->wiphy.bands[band]->channels[i];
Johannes Bergee688b02008-01-24 19:38:39 +0100282
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400283 if (nl80211_msg_put_channel(msg, chan))
284 goto nla_put_failure;
Jouni Malinene2f367f262008-11-21 19:01:30 +0200285
Johannes Bergee688b02008-01-24 19:38:39 +0100286 nla_nest_end(msg, nl_freq);
287 }
288
289 nla_nest_end(msg, nl_freqs);
290
291 /* add bitrates */
292 nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
293 if (!nl_rates)
294 goto nla_put_failure;
295
296 for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
297 nl_rate = nla_nest_start(msg, i);
298 if (!nl_rate)
299 goto nla_put_failure;
300
301 rate = &dev->wiphy.bands[band]->bitrates[i];
302 NLA_PUT_U32(msg, NL80211_BITRATE_ATTR_RATE,
303 rate->bitrate);
304 if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
305 NLA_PUT_FLAG(msg,
306 NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE);
307
308 nla_nest_end(msg, nl_rate);
309 }
310
311 nla_nest_end(msg, nl_rates);
312
313 nla_nest_end(msg, nl_band);
314 }
315 nla_nest_end(msg, nl_bands);
316
Johannes Berg8fdc6212009-03-14 09:34:01 +0100317 nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
318 if (!nl_cmds)
319 goto nla_put_failure;
320
321 i = 0;
322#define CMD(op, n) \
323 do { \
324 if (dev->ops->op) { \
325 i++; \
326 NLA_PUT_U32(msg, i, NL80211_CMD_ ## n); \
327 } \
328 } while (0)
329
330 CMD(add_virtual_intf, NEW_INTERFACE);
331 CMD(change_virtual_intf, SET_INTERFACE);
332 CMD(add_key, NEW_KEY);
333 CMD(add_beacon, NEW_BEACON);
334 CMD(add_station, NEW_STATION);
335 CMD(add_mpath, NEW_MPATH);
336 CMD(set_mesh_params, SET_MESH_PARAMS);
337 CMD(change_bss, SET_BSS);
Jouni Malinen636a5d32009-03-19 13:39:22 +0200338 CMD(auth, AUTHENTICATE);
339 CMD(assoc, ASSOCIATE);
340 CMD(deauth, DEAUTHENTICATE);
341 CMD(disassoc, DISASSOCIATE);
Johannes Berg04a773a2009-04-19 21:24:32 +0200342 CMD(join_ibss, JOIN_IBSS);
Johannes Berg8fdc6212009-03-14 09:34:01 +0100343
344#undef CMD
345 nla_nest_end(msg, nl_cmds);
346
Johannes Berg55682962007-09-20 13:09:35 -0400347 return genlmsg_end(msg, hdr);
348
349 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700350 genlmsg_cancel(msg, hdr);
351 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400352}
353
354static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
355{
356 int idx = 0;
357 int start = cb->args[0];
358 struct cfg80211_registered_device *dev;
359
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500360 mutex_lock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400361 list_for_each_entry(dev, &cfg80211_drv_list, list) {
Julius Volzb4637272008-07-08 14:02:19 +0200362 if (++idx <= start)
Johannes Berg55682962007-09-20 13:09:35 -0400363 continue;
364 if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
365 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Julius Volzb4637272008-07-08 14:02:19 +0200366 dev) < 0) {
367 idx--;
Johannes Berg55682962007-09-20 13:09:35 -0400368 break;
Julius Volzb4637272008-07-08 14:02:19 +0200369 }
Johannes Berg55682962007-09-20 13:09:35 -0400370 }
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500371 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400372
373 cb->args[0] = idx;
374
375 return skb->len;
376}
377
378static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
379{
380 struct sk_buff *msg;
381 struct cfg80211_registered_device *dev;
382
383 dev = cfg80211_get_dev_from_info(info);
384 if (IS_ERR(dev))
385 return PTR_ERR(dev);
386
387 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
388 if (!msg)
389 goto out_err;
390
391 if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0)
392 goto out_free;
393
394 cfg80211_put_dev(dev);
395
396 return genlmsg_unicast(msg, info->snd_pid);
397
398 out_free:
399 nlmsg_free(msg);
400 out_err:
401 cfg80211_put_dev(dev);
402 return -ENOBUFS;
403}
404
Jouni Malinen31888482008-10-30 16:59:24 +0200405static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
406 [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
407 [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
408 [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
409 [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
410 [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
411};
412
413static int parse_txq_params(struct nlattr *tb[],
414 struct ieee80211_txq_params *txq_params)
415{
416 if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] ||
417 !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
418 !tb[NL80211_TXQ_ATTR_AIFS])
419 return -EINVAL;
420
421 txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]);
422 txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
423 txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
424 txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
425 txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
426
427 return 0;
428}
429
Johannes Berg55682962007-09-20 13:09:35 -0400430static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
431{
432 struct cfg80211_registered_device *rdev;
Jouni Malinen31888482008-10-30 16:59:24 +0200433 int result = 0, rem_txq_params = 0;
434 struct nlattr *nl_txq_params;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200435 u32 changed;
436 u8 retry_short = 0, retry_long = 0;
437 u32 frag_threshold = 0, rts_threshold = 0;
Johannes Berg55682962007-09-20 13:09:35 -0400438
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100439 rtnl_lock();
Johannes Berg55682962007-09-20 13:09:35 -0400440
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100441 mutex_lock(&cfg80211_mutex);
442
443 rdev = __cfg80211_drv_from_info(info);
444 if (IS_ERR(rdev)) {
445 result = PTR_ERR(rdev);
446 goto unlock;
447 }
448
449 mutex_lock(&rdev->mtx);
450
451 if (info->attrs[NL80211_ATTR_WIPHY_NAME])
Jouni Malinen31888482008-10-30 16:59:24 +0200452 result = cfg80211_dev_rename(
453 rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100454
455 mutex_unlock(&cfg80211_mutex);
456
457 if (result)
458 goto bad_res;
Johannes Berg55682962007-09-20 13:09:35 -0400459
Jouni Malinen31888482008-10-30 16:59:24 +0200460 if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
461 struct ieee80211_txq_params txq_params;
462 struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
463
464 if (!rdev->ops->set_txq_params) {
465 result = -EOPNOTSUPP;
466 goto bad_res;
467 }
468
469 nla_for_each_nested(nl_txq_params,
470 info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
471 rem_txq_params) {
472 nla_parse(tb, NL80211_TXQ_ATTR_MAX,
473 nla_data(nl_txq_params),
474 nla_len(nl_txq_params),
475 txq_params_policy);
476 result = parse_txq_params(tb, &txq_params);
477 if (result)
478 goto bad_res;
479
480 result = rdev->ops->set_txq_params(&rdev->wiphy,
481 &txq_params);
482 if (result)
483 goto bad_res;
484 }
485 }
486
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200487 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Sujith094d05d2008-12-12 11:57:43 +0530488 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200489 struct ieee80211_channel *chan;
Johannes Berg306d6112008-12-08 12:39:04 +0100490 struct ieee80211_sta_ht_cap *ht_cap;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200491 u32 freq, sec_freq;
492
493 if (!rdev->ops->set_channel) {
494 result = -EOPNOTSUPP;
495 goto bad_res;
496 }
497
Johannes Berg306d6112008-12-08 12:39:04 +0100498 result = -EINVAL;
499
Sujith094d05d2008-12-12 11:57:43 +0530500 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
501 channel_type = nla_get_u32(info->attrs[
502 NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
503 if (channel_type != NL80211_CHAN_NO_HT &&
504 channel_type != NL80211_CHAN_HT20 &&
505 channel_type != NL80211_CHAN_HT40PLUS &&
506 channel_type != NL80211_CHAN_HT40MINUS)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200507 goto bad_res;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200508 }
509
510 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
511 chan = ieee80211_get_channel(&rdev->wiphy, freq);
Johannes Berg306d6112008-12-08 12:39:04 +0100512
513 /* Primary channel not allowed */
514 if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200515 goto bad_res;
Johannes Berg306d6112008-12-08 12:39:04 +0100516
Sujith094d05d2008-12-12 11:57:43 +0530517 if (channel_type == NL80211_CHAN_HT40MINUS)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200518 sec_freq = freq - 20;
Sujith094d05d2008-12-12 11:57:43 +0530519 else if (channel_type == NL80211_CHAN_HT40PLUS)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200520 sec_freq = freq + 20;
521 else
522 sec_freq = 0;
523
Johannes Berg306d6112008-12-08 12:39:04 +0100524 ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
525
526 /* no HT capabilities */
Sujith094d05d2008-12-12 11:57:43 +0530527 if (channel_type != NL80211_CHAN_NO_HT &&
Johannes Berg306d6112008-12-08 12:39:04 +0100528 !ht_cap->ht_supported)
529 goto bad_res;
530
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200531 if (sec_freq) {
532 struct ieee80211_channel *schan;
Johannes Berg306d6112008-12-08 12:39:04 +0100533
534 /* no 40 MHz capabilities */
535 if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
536 (ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200537 goto bad_res;
Johannes Berg306d6112008-12-08 12:39:04 +0100538
539 schan = ieee80211_get_channel(&rdev->wiphy, sec_freq);
540
541 /* Secondary channel not allowed */
542 if (!schan || schan->flags & IEEE80211_CHAN_DISABLED)
543 goto bad_res;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200544 }
545
546 result = rdev->ops->set_channel(&rdev->wiphy, chan,
Sujith094d05d2008-12-12 11:57:43 +0530547 channel_type);
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200548 if (result)
549 goto bad_res;
550 }
551
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200552 changed = 0;
553
554 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {
555 retry_short = nla_get_u8(
556 info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]);
557 if (retry_short == 0) {
558 result = -EINVAL;
559 goto bad_res;
560 }
561 changed |= WIPHY_PARAM_RETRY_SHORT;
562 }
563
564 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {
565 retry_long = nla_get_u8(
566 info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]);
567 if (retry_long == 0) {
568 result = -EINVAL;
569 goto bad_res;
570 }
571 changed |= WIPHY_PARAM_RETRY_LONG;
572 }
573
574 if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
575 frag_threshold = nla_get_u32(
576 info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
577 if (frag_threshold < 256) {
578 result = -EINVAL;
579 goto bad_res;
580 }
581 if (frag_threshold != (u32) -1) {
582 /*
583 * Fragments (apart from the last one) are required to
584 * have even length. Make the fragmentation code
585 * simpler by stripping LSB should someone try to use
586 * odd threshold value.
587 */
588 frag_threshold &= ~0x1;
589 }
590 changed |= WIPHY_PARAM_FRAG_THRESHOLD;
591 }
592
593 if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
594 rts_threshold = nla_get_u32(
595 info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
596 changed |= WIPHY_PARAM_RTS_THRESHOLD;
597 }
598
599 if (changed) {
600 u8 old_retry_short, old_retry_long;
601 u32 old_frag_threshold, old_rts_threshold;
602
603 if (!rdev->ops->set_wiphy_params) {
604 result = -EOPNOTSUPP;
605 goto bad_res;
606 }
607
608 old_retry_short = rdev->wiphy.retry_short;
609 old_retry_long = rdev->wiphy.retry_long;
610 old_frag_threshold = rdev->wiphy.frag_threshold;
611 old_rts_threshold = rdev->wiphy.rts_threshold;
612
613 if (changed & WIPHY_PARAM_RETRY_SHORT)
614 rdev->wiphy.retry_short = retry_short;
615 if (changed & WIPHY_PARAM_RETRY_LONG)
616 rdev->wiphy.retry_long = retry_long;
617 if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
618 rdev->wiphy.frag_threshold = frag_threshold;
619 if (changed & WIPHY_PARAM_RTS_THRESHOLD)
620 rdev->wiphy.rts_threshold = rts_threshold;
621
622 result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed);
623 if (result) {
624 rdev->wiphy.retry_short = old_retry_short;
625 rdev->wiphy.retry_long = old_retry_long;
626 rdev->wiphy.frag_threshold = old_frag_threshold;
627 rdev->wiphy.rts_threshold = old_rts_threshold;
628 }
629 }
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200630
Johannes Berg306d6112008-12-08 12:39:04 +0100631 bad_res:
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100632 mutex_unlock(&rdev->mtx);
633 unlock:
634 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400635 return result;
636}
637
638
639static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
Johannes Bergd7264052009-04-19 16:23:20 +0200640 struct cfg80211_registered_device *rdev,
Johannes Berg55682962007-09-20 13:09:35 -0400641 struct net_device *dev)
642{
643 void *hdr;
644
645 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
646 if (!hdr)
647 return -1;
648
649 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
Johannes Bergd7264052009-04-19 16:23:20 +0200650 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400651 NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
Johannes Berg60719ff2008-09-16 14:55:09 +0200652 NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype);
Johannes Berg55682962007-09-20 13:09:35 -0400653 return genlmsg_end(msg, hdr);
654
655 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700656 genlmsg_cancel(msg, hdr);
657 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400658}
659
660static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
661{
662 int wp_idx = 0;
663 int if_idx = 0;
664 int wp_start = cb->args[0];
665 int if_start = cb->args[1];
666 struct cfg80211_registered_device *dev;
667 struct wireless_dev *wdev;
668
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500669 mutex_lock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400670 list_for_each_entry(dev, &cfg80211_drv_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +0200671 if (wp_idx < wp_start) {
672 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400673 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200674 }
Johannes Berg55682962007-09-20 13:09:35 -0400675 if_idx = 0;
676
677 mutex_lock(&dev->devlist_mtx);
678 list_for_each_entry(wdev, &dev->netdev_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +0200679 if (if_idx < if_start) {
680 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400681 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200682 }
Johannes Berg55682962007-09-20 13:09:35 -0400683 if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
684 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Bergd7264052009-04-19 16:23:20 +0200685 dev, wdev->netdev) < 0) {
Johannes Bergbba95fe2008-07-29 13:22:51 +0200686 mutex_unlock(&dev->devlist_mtx);
687 goto out;
688 }
689 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400690 }
691 mutex_unlock(&dev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +0200692
693 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400694 }
Johannes Bergbba95fe2008-07-29 13:22:51 +0200695 out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500696 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400697
698 cb->args[0] = wp_idx;
699 cb->args[1] = if_idx;
700
701 return skb->len;
702}
703
704static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
705{
706 struct sk_buff *msg;
707 struct cfg80211_registered_device *dev;
708 struct net_device *netdev;
709 int err;
710
Johannes Bergbba95fe2008-07-29 13:22:51 +0200711 err = get_drv_dev_by_info_ifindex(info->attrs, &dev, &netdev);
Johannes Berg55682962007-09-20 13:09:35 -0400712 if (err)
713 return err;
714
715 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
716 if (!msg)
717 goto out_err;
718
Johannes Bergd7264052009-04-19 16:23:20 +0200719 if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0,
720 dev, netdev) < 0)
Johannes Berg55682962007-09-20 13:09:35 -0400721 goto out_free;
722
723 dev_put(netdev);
724 cfg80211_put_dev(dev);
725
726 return genlmsg_unicast(msg, info->snd_pid);
727
728 out_free:
729 nlmsg_free(msg);
730 out_err:
731 dev_put(netdev);
732 cfg80211_put_dev(dev);
733 return -ENOBUFS;
734}
735
Michael Wu66f7ac52008-01-31 19:48:22 +0100736static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
737 [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
738 [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
739 [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
740 [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
741 [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
742};
743
744static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
745{
746 struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
747 int flag;
748
749 *mntrflags = 0;
750
751 if (!nla)
752 return -EINVAL;
753
754 if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX,
755 nla, mntr_flags_policy))
756 return -EINVAL;
757
758 for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
759 if (flags[flag])
760 *mntrflags |= (1<<flag);
761
762 return 0;
763}
764
Johannes Berg55682962007-09-20 13:09:35 -0400765static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
766{
767 struct cfg80211_registered_device *drv;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100768 struct vif_params params;
Johannes Berg55682962007-09-20 13:09:35 -0400769 int err, ifindex;
Johannes Berg04a773a2009-04-19 21:24:32 +0200770 enum nl80211_iftype otype, ntype;
Johannes Berg55682962007-09-20 13:09:35 -0400771 struct net_device *dev;
Johannes Berg92ffe052008-09-16 20:39:36 +0200772 u32 _flags, *flags = NULL;
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100773 bool change = false;
Johannes Berg55682962007-09-20 13:09:35 -0400774
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100775 memset(&params, 0, sizeof(params));
776
Johannes Berg3b858752009-03-12 09:55:09 +0100777 rtnl_lock();
778
Johannes Bergbba95fe2008-07-29 13:22:51 +0200779 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg55682962007-09-20 13:09:35 -0400780 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +0100781 goto unlock_rtnl;
782
Johannes Berg55682962007-09-20 13:09:35 -0400783 ifindex = dev->ifindex;
Johannes Berg04a773a2009-04-19 21:24:32 +0200784 otype = ntype = dev->ieee80211_ptr->iftype;
Johannes Berg55682962007-09-20 13:09:35 -0400785 dev_put(dev);
786
Johannes Berg723b0382008-09-16 20:22:09 +0200787 if (info->attrs[NL80211_ATTR_IFTYPE]) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100788 ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
Johannes Berg04a773a2009-04-19 21:24:32 +0200789 if (otype != ntype)
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100790 change = true;
Johannes Berg04a773a2009-04-19 21:24:32 +0200791 if (ntype > NL80211_IFTYPE_MAX) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100792 err = -EINVAL;
Johannes Berg723b0382008-09-16 20:22:09 +0200793 goto unlock;
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100794 }
Johannes Berg723b0382008-09-16 20:22:09 +0200795 }
796
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700797 if (!drv->ops->change_virtual_intf ||
Johannes Berg04a773a2009-04-19 21:24:32 +0200798 !(drv->wiphy.interface_modes & (1 << ntype))) {
Johannes Berg55682962007-09-20 13:09:35 -0400799 err = -EOPNOTSUPP;
800 goto unlock;
801 }
802
Johannes Berg92ffe052008-09-16 20:39:36 +0200803 if (info->attrs[NL80211_ATTR_MESH_ID]) {
Johannes Berg04a773a2009-04-19 21:24:32 +0200804 if (ntype != NL80211_IFTYPE_MESH_POINT) {
Johannes Berg92ffe052008-09-16 20:39:36 +0200805 err = -EINVAL;
806 goto unlock;
807 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100808 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
809 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100810 change = true;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100811 }
812
Johannes Berg92ffe052008-09-16 20:39:36 +0200813 if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
Johannes Berg04a773a2009-04-19 21:24:32 +0200814 if (ntype != NL80211_IFTYPE_MONITOR) {
Johannes Berg92ffe052008-09-16 20:39:36 +0200815 err = -EINVAL;
816 goto unlock;
817 }
818 err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
819 &_flags);
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100820 if (err)
821 goto unlock;
822
823 flags = &_flags;
824 change = true;
Johannes Berg92ffe052008-09-16 20:39:36 +0200825 }
Johannes Berg3b858752009-03-12 09:55:09 +0100826
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100827 if (change)
828 err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex,
Johannes Berg04a773a2009-04-19 21:24:32 +0200829 ntype, flags, &params);
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100830 else
831 err = 0;
Johannes Berg60719ff2008-09-16 14:55:09 +0200832
833 dev = __dev_get_by_index(&init_net, ifindex);
Johannes Berg04a773a2009-04-19 21:24:32 +0200834 WARN_ON(!dev || (!err && dev->ieee80211_ptr->iftype != ntype));
835
836 if (dev && !err && (ntype != otype)) {
837 if (otype == NL80211_IFTYPE_ADHOC)
Johannes Berg9d308422009-04-20 18:43:46 +0200838 cfg80211_clear_ibss(dev, false);
Johannes Berg04a773a2009-04-19 21:24:32 +0200839 }
Johannes Berg60719ff2008-09-16 14:55:09 +0200840
Johannes Berg55682962007-09-20 13:09:35 -0400841 unlock:
842 cfg80211_put_dev(drv);
Johannes Berg3b858752009-03-12 09:55:09 +0100843 unlock_rtnl:
844 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400845 return err;
846}
847
848static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
849{
850 struct cfg80211_registered_device *drv;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100851 struct vif_params params;
Johannes Berg55682962007-09-20 13:09:35 -0400852 int err;
853 enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
Michael Wu66f7ac52008-01-31 19:48:22 +0100854 u32 flags;
Johannes Berg55682962007-09-20 13:09:35 -0400855
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100856 memset(&params, 0, sizeof(params));
857
Johannes Berg55682962007-09-20 13:09:35 -0400858 if (!info->attrs[NL80211_ATTR_IFNAME])
859 return -EINVAL;
860
861 if (info->attrs[NL80211_ATTR_IFTYPE]) {
862 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
863 if (type > NL80211_IFTYPE_MAX)
864 return -EINVAL;
865 }
866
Johannes Berg3b858752009-03-12 09:55:09 +0100867 rtnl_lock();
868
Johannes Berg55682962007-09-20 13:09:35 -0400869 drv = cfg80211_get_dev_from_info(info);
Johannes Berg3b858752009-03-12 09:55:09 +0100870 if (IS_ERR(drv)) {
871 err = PTR_ERR(drv);
872 goto unlock_rtnl;
873 }
Johannes Berg55682962007-09-20 13:09:35 -0400874
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700875 if (!drv->ops->add_virtual_intf ||
876 !(drv->wiphy.interface_modes & (1 << type))) {
Johannes Berg55682962007-09-20 13:09:35 -0400877 err = -EOPNOTSUPP;
878 goto unlock;
879 }
880
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100881 if (type == NL80211_IFTYPE_MESH_POINT &&
882 info->attrs[NL80211_ATTR_MESH_ID]) {
883 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
884 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
885 }
886
Michael Wu66f7ac52008-01-31 19:48:22 +0100887 err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
888 info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
889 &flags);
Johannes Berg55682962007-09-20 13:09:35 -0400890 err = drv->ops->add_virtual_intf(&drv->wiphy,
Michael Wu66f7ac52008-01-31 19:48:22 +0100891 nla_data(info->attrs[NL80211_ATTR_IFNAME]),
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100892 type, err ? NULL : &flags, &params);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100893
Johannes Berg55682962007-09-20 13:09:35 -0400894 unlock:
895 cfg80211_put_dev(drv);
Johannes Berg3b858752009-03-12 09:55:09 +0100896 unlock_rtnl:
897 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400898 return err;
899}
900
901static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
902{
903 struct cfg80211_registered_device *drv;
904 int ifindex, err;
905 struct net_device *dev;
906
Johannes Berg3b858752009-03-12 09:55:09 +0100907 rtnl_lock();
908
Johannes Bergbba95fe2008-07-29 13:22:51 +0200909 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg55682962007-09-20 13:09:35 -0400910 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +0100911 goto unlock_rtnl;
Johannes Berg55682962007-09-20 13:09:35 -0400912 ifindex = dev->ifindex;
913 dev_put(dev);
914
915 if (!drv->ops->del_virtual_intf) {
916 err = -EOPNOTSUPP;
917 goto out;
918 }
919
Johannes Berg55682962007-09-20 13:09:35 -0400920 err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex);
Johannes Berg55682962007-09-20 13:09:35 -0400921
922 out:
923 cfg80211_put_dev(drv);
Johannes Berg3b858752009-03-12 09:55:09 +0100924 unlock_rtnl:
925 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400926 return err;
927}
928
Johannes Berg41ade002007-12-19 02:03:29 +0100929struct get_key_cookie {
930 struct sk_buff *msg;
931 int error;
932};
933
934static void get_key_callback(void *c, struct key_params *params)
935{
936 struct get_key_cookie *cookie = c;
937
938 if (params->key)
939 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA,
940 params->key_len, params->key);
941
942 if (params->seq)
943 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ,
944 params->seq_len, params->seq);
945
946 if (params->cipher)
947 NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
948 params->cipher);
949
950 return;
951 nla_put_failure:
952 cookie->error = 1;
953}
954
955static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
956{
957 struct cfg80211_registered_device *drv;
958 int err;
959 struct net_device *dev;
960 u8 key_idx = 0;
961 u8 *mac_addr = NULL;
962 struct get_key_cookie cookie = {
963 .error = 0,
964 };
965 void *hdr;
966 struct sk_buff *msg;
967
968 if (info->attrs[NL80211_ATTR_KEY_IDX])
969 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
970
Jouni Malinen3cfcf6a2009-01-08 13:32:02 +0200971 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +0100972 return -EINVAL;
973
974 if (info->attrs[NL80211_ATTR_MAC])
975 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
976
Johannes Berg3b858752009-03-12 09:55:09 +0100977 rtnl_lock();
978
Johannes Bergbba95fe2008-07-29 13:22:51 +0200979 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +0100980 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +0100981 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +0100982
983 if (!drv->ops->get_key) {
984 err = -EOPNOTSUPP;
985 goto out;
986 }
987
988 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
989 if (!msg) {
990 err = -ENOMEM;
991 goto out;
992 }
993
994 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
995 NL80211_CMD_NEW_KEY);
996
997 if (IS_ERR(hdr)) {
998 err = PTR_ERR(hdr);
999 goto out;
1000 }
1001
1002 cookie.msg = msg;
1003
1004 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1005 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
1006 if (mac_addr)
1007 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1008
Johannes Berg41ade002007-12-19 02:03:29 +01001009 err = drv->ops->get_key(&drv->wiphy, dev, key_idx, mac_addr,
1010 &cookie, get_key_callback);
Johannes Berg41ade002007-12-19 02:03:29 +01001011
1012 if (err)
1013 goto out;
1014
1015 if (cookie.error)
1016 goto nla_put_failure;
1017
1018 genlmsg_end(msg, hdr);
1019 err = genlmsg_unicast(msg, info->snd_pid);
1020 goto out;
1021
1022 nla_put_failure:
1023 err = -ENOBUFS;
1024 nlmsg_free(msg);
1025 out:
1026 cfg80211_put_dev(drv);
1027 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001028 unlock_rtnl:
1029 rtnl_unlock();
1030
Johannes Berg41ade002007-12-19 02:03:29 +01001031 return err;
1032}
1033
1034static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
1035{
1036 struct cfg80211_registered_device *drv;
1037 int err;
1038 struct net_device *dev;
1039 u8 key_idx;
Jouni Malinen3cfcf6a2009-01-08 13:32:02 +02001040 int (*func)(struct wiphy *wiphy, struct net_device *netdev,
1041 u8 key_index);
Johannes Berg41ade002007-12-19 02:03:29 +01001042
1043 if (!info->attrs[NL80211_ATTR_KEY_IDX])
1044 return -EINVAL;
1045
1046 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1047
Jouni Malinen3cfcf6a2009-01-08 13:32:02 +02001048 if (info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) {
1049 if (key_idx < 4 || key_idx > 5)
1050 return -EINVAL;
1051 } else if (key_idx > 3)
Johannes Berg41ade002007-12-19 02:03:29 +01001052 return -EINVAL;
1053
1054 /* currently only support setting default key */
Jouni Malinen3cfcf6a2009-01-08 13:32:02 +02001055 if (!info->attrs[NL80211_ATTR_KEY_DEFAULT] &&
1056 !info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT])
Johannes Berg41ade002007-12-19 02:03:29 +01001057 return -EINVAL;
1058
Johannes Berg3b858752009-03-12 09:55:09 +01001059 rtnl_lock();
1060
Johannes Bergbba95fe2008-07-29 13:22:51 +02001061 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001062 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001063 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001064
Jouni Malinen3cfcf6a2009-01-08 13:32:02 +02001065 if (info->attrs[NL80211_ATTR_KEY_DEFAULT])
1066 func = drv->ops->set_default_key;
1067 else
1068 func = drv->ops->set_default_mgmt_key;
1069
1070 if (!func) {
Johannes Berg41ade002007-12-19 02:03:29 +01001071 err = -EOPNOTSUPP;
1072 goto out;
1073 }
1074
Jouni Malinen3cfcf6a2009-01-08 13:32:02 +02001075 err = func(&drv->wiphy, dev, key_idx);
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
1081 unlock_rtnl:
1082 rtnl_unlock();
1083
Johannes Berg41ade002007-12-19 02:03:29 +01001084 return err;
1085}
1086
1087static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
1088{
1089 struct cfg80211_registered_device *drv;
Johannes Berg25e47c12009-04-02 20:14:06 +02001090 int err, i;
Johannes Berg41ade002007-12-19 02:03:29 +01001091 struct net_device *dev;
1092 struct key_params params;
1093 u8 key_idx = 0;
1094 u8 *mac_addr = NULL;
1095
1096 memset(&params, 0, sizeof(params));
1097
1098 if (!info->attrs[NL80211_ATTR_KEY_CIPHER])
1099 return -EINVAL;
1100
1101 if (info->attrs[NL80211_ATTR_KEY_DATA]) {
1102 params.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
1103 params.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
1104 }
1105
1106 if (info->attrs[NL80211_ATTR_KEY_IDX])
1107 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1108
1109 params.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
1110
1111 if (info->attrs[NL80211_ATTR_MAC])
1112 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1113
Jouni Malinen3cfcf6a2009-01-08 13:32:02 +02001114 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +01001115 return -EINVAL;
1116
1117 /*
1118 * Disallow pairwise keys with non-zero index unless it's WEP
1119 * (because current deployments use pairwise WEP keys with
1120 * non-zero indizes but 802.11i clearly specifies to use zero)
1121 */
1122 if (mac_addr && key_idx &&
1123 params.cipher != WLAN_CIPHER_SUITE_WEP40 &&
1124 params.cipher != WLAN_CIPHER_SUITE_WEP104)
1125 return -EINVAL;
1126
1127 /* TODO: add definitions for the lengths to linux/ieee80211.h */
1128 switch (params.cipher) {
1129 case WLAN_CIPHER_SUITE_WEP40:
1130 if (params.key_len != 5)
1131 return -EINVAL;
1132 break;
1133 case WLAN_CIPHER_SUITE_TKIP:
1134 if (params.key_len != 32)
1135 return -EINVAL;
1136 break;
1137 case WLAN_CIPHER_SUITE_CCMP:
1138 if (params.key_len != 16)
1139 return -EINVAL;
1140 break;
1141 case WLAN_CIPHER_SUITE_WEP104:
1142 if (params.key_len != 13)
1143 return -EINVAL;
1144 break;
Jouni Malinen3cfcf6a2009-01-08 13:32:02 +02001145 case WLAN_CIPHER_SUITE_AES_CMAC:
1146 if (params.key_len != 16)
1147 return -EINVAL;
1148 break;
Johannes Berg41ade002007-12-19 02:03:29 +01001149 default:
1150 return -EINVAL;
1151 }
1152
Johannes Berg3b858752009-03-12 09:55:09 +01001153 rtnl_lock();
1154
Johannes Bergbba95fe2008-07-29 13:22:51 +02001155 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001156 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001157 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001158
Johannes Berg25e47c12009-04-02 20:14:06 +02001159 for (i = 0; i < drv->wiphy.n_cipher_suites; i++)
1160 if (params.cipher == drv->wiphy.cipher_suites[i])
1161 break;
1162 if (i == drv->wiphy.n_cipher_suites) {
1163 err = -EINVAL;
1164 goto out;
1165 }
1166
Johannes Berg41ade002007-12-19 02:03:29 +01001167 if (!drv->ops->add_key) {
1168 err = -EOPNOTSUPP;
1169 goto out;
1170 }
1171
Johannes Berg41ade002007-12-19 02:03:29 +01001172 err = drv->ops->add_key(&drv->wiphy, dev, key_idx, mac_addr, &params);
Johannes Berg41ade002007-12-19 02:03:29 +01001173
1174 out:
1175 cfg80211_put_dev(drv);
1176 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001177 unlock_rtnl:
1178 rtnl_unlock();
1179
Johannes Berg41ade002007-12-19 02:03:29 +01001180 return err;
1181}
1182
1183static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
1184{
1185 struct cfg80211_registered_device *drv;
1186 int err;
1187 struct net_device *dev;
1188 u8 key_idx = 0;
1189 u8 *mac_addr = NULL;
1190
1191 if (info->attrs[NL80211_ATTR_KEY_IDX])
1192 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1193
Jouni Malinen3cfcf6a2009-01-08 13:32:02 +02001194 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +01001195 return -EINVAL;
1196
1197 if (info->attrs[NL80211_ATTR_MAC])
1198 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1199
Johannes Berg3b858752009-03-12 09:55:09 +01001200 rtnl_lock();
1201
Johannes Bergbba95fe2008-07-29 13:22:51 +02001202 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001203 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001204 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001205
1206 if (!drv->ops->del_key) {
1207 err = -EOPNOTSUPP;
1208 goto out;
1209 }
1210
Johannes Berg41ade002007-12-19 02:03:29 +01001211 err = drv->ops->del_key(&drv->wiphy, dev, key_idx, mac_addr);
Johannes Berg41ade002007-12-19 02:03:29 +01001212
1213 out:
1214 cfg80211_put_dev(drv);
1215 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001216
1217 unlock_rtnl:
1218 rtnl_unlock();
1219
Johannes Berg41ade002007-12-19 02:03:29 +01001220 return err;
1221}
1222
Johannes Berged1b6cc2007-12-19 02:03:32 +01001223static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
1224{
1225 int (*call)(struct wiphy *wiphy, struct net_device *dev,
1226 struct beacon_parameters *info);
1227 struct cfg80211_registered_device *drv;
1228 int err;
1229 struct net_device *dev;
1230 struct beacon_parameters params;
1231 int haveinfo = 0;
1232
Johannes Bergf4a11bb2009-03-27 12:40:28 +01001233 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]))
1234 return -EINVAL;
1235
Johannes Berg3b858752009-03-12 09:55:09 +01001236 rtnl_lock();
1237
Johannes Bergbba95fe2008-07-29 13:22:51 +02001238 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001239 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001240 goto unlock_rtnl;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001241
Jouni Malineneec60b02009-03-20 21:21:19 +02001242 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
1243 err = -EOPNOTSUPP;
1244 goto out;
1245 }
1246
Johannes Berged1b6cc2007-12-19 02:03:32 +01001247 switch (info->genlhdr->cmd) {
1248 case NL80211_CMD_NEW_BEACON:
1249 /* these are required for NEW_BEACON */
1250 if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
1251 !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
1252 !info->attrs[NL80211_ATTR_BEACON_HEAD]) {
1253 err = -EINVAL;
1254 goto out;
1255 }
1256
1257 call = drv->ops->add_beacon;
1258 break;
1259 case NL80211_CMD_SET_BEACON:
1260 call = drv->ops->set_beacon;
1261 break;
1262 default:
1263 WARN_ON(1);
1264 err = -EOPNOTSUPP;
1265 goto out;
1266 }
1267
1268 if (!call) {
1269 err = -EOPNOTSUPP;
1270 goto out;
1271 }
1272
1273 memset(&params, 0, sizeof(params));
1274
1275 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
1276 params.interval =
1277 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
1278 haveinfo = 1;
1279 }
1280
1281 if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) {
1282 params.dtim_period =
1283 nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
1284 haveinfo = 1;
1285 }
1286
1287 if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
1288 params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
1289 params.head_len =
1290 nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
1291 haveinfo = 1;
1292 }
1293
1294 if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
1295 params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
1296 params.tail_len =
1297 nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
1298 haveinfo = 1;
1299 }
1300
1301 if (!haveinfo) {
1302 err = -EINVAL;
1303 goto out;
1304 }
1305
Johannes Berged1b6cc2007-12-19 02:03:32 +01001306 err = call(&drv->wiphy, dev, &params);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001307
1308 out:
1309 cfg80211_put_dev(drv);
1310 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001311 unlock_rtnl:
1312 rtnl_unlock();
1313
Johannes Berged1b6cc2007-12-19 02:03:32 +01001314 return err;
1315}
1316
1317static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
1318{
1319 struct cfg80211_registered_device *drv;
1320 int err;
1321 struct net_device *dev;
1322
Johannes Berg3b858752009-03-12 09:55:09 +01001323 rtnl_lock();
1324
Johannes Bergbba95fe2008-07-29 13:22:51 +02001325 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001326 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001327 goto unlock_rtnl;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001328
1329 if (!drv->ops->del_beacon) {
1330 err = -EOPNOTSUPP;
1331 goto out;
1332 }
1333
Jouni Malineneec60b02009-03-20 21:21:19 +02001334 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
1335 err = -EOPNOTSUPP;
1336 goto out;
1337 }
Johannes Berged1b6cc2007-12-19 02:03:32 +01001338 err = drv->ops->del_beacon(&drv->wiphy, dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001339
1340 out:
1341 cfg80211_put_dev(drv);
1342 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001343 unlock_rtnl:
1344 rtnl_unlock();
1345
Johannes Berged1b6cc2007-12-19 02:03:32 +01001346 return err;
1347}
1348
Johannes Berg5727ef12007-12-19 02:03:34 +01001349static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
1350 [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
1351 [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
1352 [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
1353};
1354
1355static int parse_station_flags(struct nlattr *nla, u32 *staflags)
1356{
1357 struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
1358 int flag;
1359
1360 *staflags = 0;
1361
1362 if (!nla)
1363 return 0;
1364
1365 if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX,
1366 nla, sta_flags_policy))
1367 return -EINVAL;
1368
1369 *staflags = STATION_FLAG_CHANGED;
1370
1371 for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++)
1372 if (flags[flag])
1373 *staflags |= (1<<flag);
1374
1375 return 0;
1376}
1377
Henning Rogge420e7fa2008-12-11 22:04:19 +01001378static u16 nl80211_calculate_bitrate(struct rate_info *rate)
1379{
1380 int modulation, streams, bitrate;
1381
1382 if (!(rate->flags & RATE_INFO_FLAGS_MCS))
1383 return rate->legacy;
1384
1385 /* the formula below does only work for MCS values smaller than 32 */
1386 if (rate->mcs >= 32)
1387 return 0;
1388
1389 modulation = rate->mcs & 7;
1390 streams = (rate->mcs >> 3) + 1;
1391
1392 bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ?
1393 13500000 : 6500000;
1394
1395 if (modulation < 4)
1396 bitrate *= (modulation + 1);
1397 else if (modulation == 4)
1398 bitrate *= (modulation + 2);
1399 else
1400 bitrate *= (modulation + 3);
1401
1402 bitrate *= streams;
1403
1404 if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
1405 bitrate = (bitrate / 9) * 10;
1406
1407 /* do NOT round down here */
1408 return (bitrate + 50000) / 100000;
1409}
1410
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001411static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
1412 int flags, struct net_device *dev,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001413 u8 *mac_addr, struct station_info *sinfo)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001414{
1415 void *hdr;
Henning Rogge420e7fa2008-12-11 22:04:19 +01001416 struct nlattr *sinfoattr, *txrate;
1417 u16 bitrate;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001418
1419 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
1420 if (!hdr)
1421 return -1;
1422
1423 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1424 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1425
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001426 sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
1427 if (!sinfoattr)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001428 goto nla_put_failure;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001429 if (sinfo->filled & STATION_INFO_INACTIVE_TIME)
1430 NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME,
1431 sinfo->inactive_time);
1432 if (sinfo->filled & STATION_INFO_RX_BYTES)
1433 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES,
1434 sinfo->rx_bytes);
1435 if (sinfo->filled & STATION_INFO_TX_BYTES)
1436 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES,
1437 sinfo->tx_bytes);
1438 if (sinfo->filled & STATION_INFO_LLID)
1439 NLA_PUT_U16(msg, NL80211_STA_INFO_LLID,
1440 sinfo->llid);
1441 if (sinfo->filled & STATION_INFO_PLID)
1442 NLA_PUT_U16(msg, NL80211_STA_INFO_PLID,
1443 sinfo->plid);
1444 if (sinfo->filled & STATION_INFO_PLINK_STATE)
1445 NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
1446 sinfo->plink_state);
Henning Rogge420e7fa2008-12-11 22:04:19 +01001447 if (sinfo->filled & STATION_INFO_SIGNAL)
1448 NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL,
1449 sinfo->signal);
1450 if (sinfo->filled & STATION_INFO_TX_BITRATE) {
1451 txrate = nla_nest_start(msg, NL80211_STA_INFO_TX_BITRATE);
1452 if (!txrate)
1453 goto nla_put_failure;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001454
Henning Rogge420e7fa2008-12-11 22:04:19 +01001455 /* nl80211_calculate_bitrate will return 0 for mcs >= 32 */
1456 bitrate = nl80211_calculate_bitrate(&sinfo->txrate);
1457 if (bitrate > 0)
1458 NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
1459
1460 if (sinfo->txrate.flags & RATE_INFO_FLAGS_MCS)
1461 NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS,
1462 sinfo->txrate.mcs);
1463 if (sinfo->txrate.flags & RATE_INFO_FLAGS_40_MHZ_WIDTH)
1464 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH);
1465 if (sinfo->txrate.flags & RATE_INFO_FLAGS_SHORT_GI)
1466 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI);
1467
1468 nla_nest_end(msg, txrate);
1469 }
Jouni Malinen98c8a60a2009-02-17 13:24:57 +02001470 if (sinfo->filled & STATION_INFO_RX_PACKETS)
1471 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS,
1472 sinfo->rx_packets);
1473 if (sinfo->filled & STATION_INFO_TX_PACKETS)
1474 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS,
1475 sinfo->tx_packets);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001476 nla_nest_end(msg, sinfoattr);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001477
1478 return genlmsg_end(msg, hdr);
1479
1480 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001481 genlmsg_cancel(msg, hdr);
1482 return -EMSGSIZE;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001483}
1484
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001485static int nl80211_dump_station(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02001486 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001487{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001488 struct station_info sinfo;
1489 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001490 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001491 u8 mac_addr[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02001492 int ifidx = cb->args[0];
1493 int sta_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001494 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001495
Johannes Bergbba95fe2008-07-29 13:22:51 +02001496 if (!ifidx) {
1497 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
1498 nl80211_fam.attrbuf, nl80211_fam.maxattr,
1499 nl80211_policy);
1500 if (err)
1501 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001502
Johannes Bergbba95fe2008-07-29 13:22:51 +02001503 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
1504 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001505
Johannes Bergbba95fe2008-07-29 13:22:51 +02001506 ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
1507 if (!ifidx)
1508 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001509 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001510
Johannes Berg3b858752009-03-12 09:55:09 +01001511 rtnl_lock();
1512
1513 netdev = __dev_get_by_index(&init_net, ifidx);
1514 if (!netdev) {
1515 err = -ENODEV;
1516 goto out_rtnl;
1517 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001518
Johannes Bergbba95fe2008-07-29 13:22:51 +02001519 dev = cfg80211_get_dev_from_ifindex(ifidx);
1520 if (IS_ERR(dev)) {
1521 err = PTR_ERR(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001522 goto out_rtnl;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001523 }
1524
1525 if (!dev->ops->dump_station) {
Jouni Malineneec60b02009-03-20 21:21:19 +02001526 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001527 goto out_err;
1528 }
1529
Johannes Bergbba95fe2008-07-29 13:22:51 +02001530 while (1) {
1531 err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
1532 mac_addr, &sinfo);
1533 if (err == -ENOENT)
1534 break;
1535 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001536 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001537
1538 if (nl80211_send_station(skb,
1539 NETLINK_CB(cb->skb).pid,
1540 cb->nlh->nlmsg_seq, NLM_F_MULTI,
1541 netdev, mac_addr,
1542 &sinfo) < 0)
1543 goto out;
1544
1545 sta_idx++;
1546 }
1547
1548
1549 out:
1550 cb->args[1] = sta_idx;
1551 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001552 out_err:
1553 cfg80211_put_dev(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001554 out_rtnl:
1555 rtnl_unlock();
Johannes Bergbba95fe2008-07-29 13:22:51 +02001556
1557 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001558}
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001559
Johannes Berg5727ef12007-12-19 02:03:34 +01001560static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
1561{
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001562 struct cfg80211_registered_device *drv;
1563 int err;
1564 struct net_device *dev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001565 struct station_info sinfo;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001566 struct sk_buff *msg;
1567 u8 *mac_addr = NULL;
1568
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001569 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001570
1571 if (!info->attrs[NL80211_ATTR_MAC])
1572 return -EINVAL;
1573
1574 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1575
Johannes Berg3b858752009-03-12 09:55:09 +01001576 rtnl_lock();
1577
Johannes Bergbba95fe2008-07-29 13:22:51 +02001578 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001579 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001580 goto out_rtnl;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001581
1582 if (!drv->ops->get_station) {
1583 err = -EOPNOTSUPP;
1584 goto out;
1585 }
1586
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001587 err = drv->ops->get_station(&drv->wiphy, dev, mac_addr, &sinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001588 if (err)
1589 goto out;
1590
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001591 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1592 if (!msg)
1593 goto out;
1594
1595 if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001596 dev, mac_addr, &sinfo) < 0)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001597 goto out_free;
1598
1599 err = genlmsg_unicast(msg, info->snd_pid);
1600 goto out;
1601
1602 out_free:
1603 nlmsg_free(msg);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001604 out:
1605 cfg80211_put_dev(drv);
1606 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001607 out_rtnl:
1608 rtnl_unlock();
1609
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001610 return err;
Johannes Berg5727ef12007-12-19 02:03:34 +01001611}
1612
1613/*
1614 * Get vlan interface making sure it is on the right wiphy.
1615 */
1616static int get_vlan(struct nlattr *vlanattr,
1617 struct cfg80211_registered_device *rdev,
1618 struct net_device **vlan)
1619{
1620 *vlan = NULL;
1621
1622 if (vlanattr) {
1623 *vlan = dev_get_by_index(&init_net, nla_get_u32(vlanattr));
1624 if (!*vlan)
1625 return -ENODEV;
1626 if (!(*vlan)->ieee80211_ptr)
1627 return -EINVAL;
1628 if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy)
1629 return -EINVAL;
1630 }
1631 return 0;
1632}
1633
1634static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
1635{
1636 struct cfg80211_registered_device *drv;
1637 int err;
1638 struct net_device *dev;
1639 struct station_parameters params;
1640 u8 *mac_addr = NULL;
1641
1642 memset(&params, 0, sizeof(params));
1643
1644 params.listen_interval = -1;
1645
1646 if (info->attrs[NL80211_ATTR_STA_AID])
1647 return -EINVAL;
1648
1649 if (!info->attrs[NL80211_ATTR_MAC])
1650 return -EINVAL;
1651
1652 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1653
1654 if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
1655 params.supported_rates =
1656 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1657 params.supported_rates_len =
1658 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1659 }
1660
1661 if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
1662 params.listen_interval =
1663 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
1664
Jouni Malinen36aedc92008-08-25 11:58:58 +03001665 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
1666 params.ht_capa =
1667 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
1668
Johannes Berg5727ef12007-12-19 02:03:34 +01001669 if (parse_station_flags(info->attrs[NL80211_ATTR_STA_FLAGS],
1670 &params.station_flags))
1671 return -EINVAL;
1672
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001673 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
1674 params.plink_action =
1675 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
1676
Johannes Berg3b858752009-03-12 09:55:09 +01001677 rtnl_lock();
1678
Johannes Bergbba95fe2008-07-29 13:22:51 +02001679 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001680 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001681 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01001682
1683 err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, &params.vlan);
1684 if (err)
1685 goto out;
1686
1687 if (!drv->ops->change_station) {
1688 err = -EOPNOTSUPP;
1689 goto out;
1690 }
1691
Johannes Berg5727ef12007-12-19 02:03:34 +01001692 err = drv->ops->change_station(&drv->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01001693
1694 out:
1695 if (params.vlan)
1696 dev_put(params.vlan);
1697 cfg80211_put_dev(drv);
1698 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001699 out_rtnl:
1700 rtnl_unlock();
1701
Johannes Berg5727ef12007-12-19 02:03:34 +01001702 return err;
1703}
1704
1705static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
1706{
1707 struct cfg80211_registered_device *drv;
1708 int err;
1709 struct net_device *dev;
1710 struct station_parameters params;
1711 u8 *mac_addr = NULL;
1712
1713 memset(&params, 0, sizeof(params));
1714
1715 if (!info->attrs[NL80211_ATTR_MAC])
1716 return -EINVAL;
1717
1718 if (!info->attrs[NL80211_ATTR_STA_AID])
1719 return -EINVAL;
1720
1721 if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
1722 return -EINVAL;
1723
1724 if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
1725 return -EINVAL;
1726
1727 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1728 params.supported_rates =
1729 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1730 params.supported_rates_len =
1731 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1732 params.listen_interval =
1733 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
Johannes Berg16f2e852008-04-07 14:35:46 +02001734 params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
Jouni Malinen36aedc92008-08-25 11:58:58 +03001735 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
1736 params.ht_capa =
1737 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
Johannes Berg5727ef12007-12-19 02:03:34 +01001738
1739 if (parse_station_flags(info->attrs[NL80211_ATTR_STA_FLAGS],
1740 &params.station_flags))
1741 return -EINVAL;
1742
Johannes Berg3b858752009-03-12 09:55:09 +01001743 rtnl_lock();
1744
Johannes Bergbba95fe2008-07-29 13:22:51 +02001745 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001746 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001747 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01001748
1749 err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, &params.vlan);
1750 if (err)
1751 goto out;
1752
1753 if (!drv->ops->add_station) {
1754 err = -EOPNOTSUPP;
1755 goto out;
1756 }
1757
Jouni Malinen35a8efe2009-03-20 21:21:18 +02001758 if (!netif_running(dev)) {
1759 err = -ENETDOWN;
1760 goto out;
1761 }
1762
Johannes Berg5727ef12007-12-19 02:03:34 +01001763 err = drv->ops->add_station(&drv->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01001764
1765 out:
1766 if (params.vlan)
1767 dev_put(params.vlan);
1768 cfg80211_put_dev(drv);
1769 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001770 out_rtnl:
1771 rtnl_unlock();
1772
Johannes Berg5727ef12007-12-19 02:03:34 +01001773 return err;
1774}
1775
1776static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
1777{
1778 struct cfg80211_registered_device *drv;
1779 int err;
1780 struct net_device *dev;
1781 u8 *mac_addr = NULL;
1782
1783 if (info->attrs[NL80211_ATTR_MAC])
1784 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1785
Johannes Berg3b858752009-03-12 09:55:09 +01001786 rtnl_lock();
1787
Johannes Bergbba95fe2008-07-29 13:22:51 +02001788 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001789 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001790 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01001791
1792 if (!drv->ops->del_station) {
1793 err = -EOPNOTSUPP;
1794 goto out;
1795 }
1796
Johannes Berg5727ef12007-12-19 02:03:34 +01001797 err = drv->ops->del_station(&drv->wiphy, dev, mac_addr);
Johannes Berg5727ef12007-12-19 02:03:34 +01001798
1799 out:
1800 cfg80211_put_dev(drv);
1801 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001802 out_rtnl:
1803 rtnl_unlock();
1804
Johannes Berg5727ef12007-12-19 02:03:34 +01001805 return err;
1806}
1807
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001808static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq,
1809 int flags, struct net_device *dev,
1810 u8 *dst, u8 *next_hop,
1811 struct mpath_info *pinfo)
1812{
1813 void *hdr;
1814 struct nlattr *pinfoattr;
1815
1816 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
1817 if (!hdr)
1818 return -1;
1819
1820 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1821 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
1822 NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop);
1823
1824 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
1825 if (!pinfoattr)
1826 goto nla_put_failure;
1827 if (pinfo->filled & MPATH_INFO_FRAME_QLEN)
1828 NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
1829 pinfo->frame_qlen);
1830 if (pinfo->filled & MPATH_INFO_DSN)
1831 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DSN,
1832 pinfo->dsn);
1833 if (pinfo->filled & MPATH_INFO_METRIC)
1834 NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC,
1835 pinfo->metric);
1836 if (pinfo->filled & MPATH_INFO_EXPTIME)
1837 NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME,
1838 pinfo->exptime);
1839 if (pinfo->filled & MPATH_INFO_FLAGS)
1840 NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS,
1841 pinfo->flags);
1842 if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT)
1843 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
1844 pinfo->discovery_timeout);
1845 if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES)
1846 NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
1847 pinfo->discovery_retries);
1848
1849 nla_nest_end(msg, pinfoattr);
1850
1851 return genlmsg_end(msg, hdr);
1852
1853 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001854 genlmsg_cancel(msg, hdr);
1855 return -EMSGSIZE;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001856}
1857
1858static int nl80211_dump_mpath(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02001859 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001860{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001861 struct mpath_info pinfo;
1862 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001863 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001864 u8 dst[ETH_ALEN];
1865 u8 next_hop[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02001866 int ifidx = cb->args[0];
1867 int path_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001868 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001869
Johannes Bergbba95fe2008-07-29 13:22:51 +02001870 if (!ifidx) {
1871 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
1872 nl80211_fam.attrbuf, nl80211_fam.maxattr,
1873 nl80211_policy);
1874 if (err)
1875 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001876
Johannes Bergbba95fe2008-07-29 13:22:51 +02001877 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
1878 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001879
Johannes Bergbba95fe2008-07-29 13:22:51 +02001880 ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
1881 if (!ifidx)
1882 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001883 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001884
Johannes Berg3b858752009-03-12 09:55:09 +01001885 rtnl_lock();
1886
1887 netdev = __dev_get_by_index(&init_net, ifidx);
1888 if (!netdev) {
1889 err = -ENODEV;
1890 goto out_rtnl;
1891 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001892
Johannes Bergbba95fe2008-07-29 13:22:51 +02001893 dev = cfg80211_get_dev_from_ifindex(ifidx);
1894 if (IS_ERR(dev)) {
1895 err = PTR_ERR(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001896 goto out_rtnl;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001897 }
1898
1899 if (!dev->ops->dump_mpath) {
Jouni Malineneec60b02009-03-20 21:21:19 +02001900 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001901 goto out_err;
1902 }
1903
Jouni Malineneec60b02009-03-20 21:21:19 +02001904 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
1905 err = -EOPNOTSUPP;
1906 goto out;
1907 }
1908
Johannes Bergbba95fe2008-07-29 13:22:51 +02001909 while (1) {
1910 err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
1911 dst, next_hop, &pinfo);
1912 if (err == -ENOENT)
1913 break;
1914 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001915 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001916
1917 if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid,
1918 cb->nlh->nlmsg_seq, NLM_F_MULTI,
1919 netdev, dst, next_hop,
1920 &pinfo) < 0)
1921 goto out;
1922
1923 path_idx++;
1924 }
1925
1926
1927 out:
1928 cb->args[1] = path_idx;
1929 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001930 out_err:
1931 cfg80211_put_dev(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001932 out_rtnl:
1933 rtnl_unlock();
Johannes Bergbba95fe2008-07-29 13:22:51 +02001934
1935 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001936}
1937
1938static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
1939{
1940 struct cfg80211_registered_device *drv;
1941 int err;
1942 struct net_device *dev;
1943 struct mpath_info pinfo;
1944 struct sk_buff *msg;
1945 u8 *dst = NULL;
1946 u8 next_hop[ETH_ALEN];
1947
1948 memset(&pinfo, 0, sizeof(pinfo));
1949
1950 if (!info->attrs[NL80211_ATTR_MAC])
1951 return -EINVAL;
1952
1953 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
1954
Johannes Berg3b858752009-03-12 09:55:09 +01001955 rtnl_lock();
1956
Johannes Bergbba95fe2008-07-29 13:22:51 +02001957 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001958 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001959 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001960
1961 if (!drv->ops->get_mpath) {
1962 err = -EOPNOTSUPP;
1963 goto out;
1964 }
1965
Jouni Malineneec60b02009-03-20 21:21:19 +02001966 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
1967 err = -EOPNOTSUPP;
1968 goto out;
1969 }
1970
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001971 err = drv->ops->get_mpath(&drv->wiphy, dev, dst, next_hop, &pinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001972 if (err)
1973 goto out;
1974
1975 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1976 if (!msg)
1977 goto out;
1978
1979 if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0,
1980 dev, dst, next_hop, &pinfo) < 0)
1981 goto out_free;
1982
1983 err = genlmsg_unicast(msg, info->snd_pid);
1984 goto out;
1985
1986 out_free:
1987 nlmsg_free(msg);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001988 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_set_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 u8 *next_hop = NULL;
2004
2005 if (!info->attrs[NL80211_ATTR_MAC])
2006 return -EINVAL;
2007
2008 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
2009 return -EINVAL;
2010
2011 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2012 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
2013
Johannes Berg3b858752009-03-12 09:55:09 +01002014 rtnl_lock();
2015
Johannes Bergbba95fe2008-07-29 13:22:51 +02002016 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002017 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002018 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002019
2020 if (!drv->ops->change_mpath) {
2021 err = -EOPNOTSUPP;
2022 goto out;
2023 }
2024
Jouni Malineneec60b02009-03-20 21:21:19 +02002025 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2026 err = -EOPNOTSUPP;
2027 goto out;
2028 }
2029
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002030 if (!netif_running(dev)) {
2031 err = -ENETDOWN;
2032 goto out;
2033 }
2034
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002035 err = drv->ops->change_mpath(&drv->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002036
2037 out:
2038 cfg80211_put_dev(drv);
2039 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002040 out_rtnl:
2041 rtnl_unlock();
2042
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002043 return err;
2044}
2045static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
2046{
2047 struct cfg80211_registered_device *drv;
2048 int err;
2049 struct net_device *dev;
2050 u8 *dst = NULL;
2051 u8 *next_hop = NULL;
2052
2053 if (!info->attrs[NL80211_ATTR_MAC])
2054 return -EINVAL;
2055
2056 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
2057 return -EINVAL;
2058
2059 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2060 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
2061
Johannes Berg3b858752009-03-12 09:55:09 +01002062 rtnl_lock();
2063
Johannes Bergbba95fe2008-07-29 13:22:51 +02002064 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002065 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002066 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002067
2068 if (!drv->ops->add_mpath) {
2069 err = -EOPNOTSUPP;
2070 goto out;
2071 }
2072
Jouni Malineneec60b02009-03-20 21:21:19 +02002073 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2074 err = -EOPNOTSUPP;
2075 goto out;
2076 }
2077
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002078 if (!netif_running(dev)) {
2079 err = -ENETDOWN;
2080 goto out;
2081 }
2082
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002083 err = drv->ops->add_mpath(&drv->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002084
2085 out:
2086 cfg80211_put_dev(drv);
2087 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002088 out_rtnl:
2089 rtnl_unlock();
2090
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002091 return err;
2092}
2093
2094static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
2095{
2096 struct cfg80211_registered_device *drv;
2097 int err;
2098 struct net_device *dev;
2099 u8 *dst = NULL;
2100
2101 if (info->attrs[NL80211_ATTR_MAC])
2102 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2103
Johannes Berg3b858752009-03-12 09:55:09 +01002104 rtnl_lock();
2105
Johannes Bergbba95fe2008-07-29 13:22:51 +02002106 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002107 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002108 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002109
2110 if (!drv->ops->del_mpath) {
2111 err = -EOPNOTSUPP;
2112 goto out;
2113 }
2114
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002115 err = drv->ops->del_mpath(&drv->wiphy, dev, dst);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002116
2117 out:
2118 cfg80211_put_dev(drv);
2119 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002120 out_rtnl:
2121 rtnl_unlock();
2122
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002123 return err;
2124}
2125
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002126static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
2127{
2128 struct cfg80211_registered_device *drv;
2129 int err;
2130 struct net_device *dev;
2131 struct bss_parameters params;
2132
2133 memset(&params, 0, sizeof(params));
2134 /* default to not changing parameters */
2135 params.use_cts_prot = -1;
2136 params.use_short_preamble = -1;
2137 params.use_short_slot_time = -1;
2138
2139 if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
2140 params.use_cts_prot =
2141 nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
2142 if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
2143 params.use_short_preamble =
2144 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
2145 if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
2146 params.use_short_slot_time =
2147 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
Jouni Malinen90c97a02008-10-30 16:59:22 +02002148 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
2149 params.basic_rates =
2150 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2151 params.basic_rates_len =
2152 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2153 }
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002154
Johannes Berg3b858752009-03-12 09:55:09 +01002155 rtnl_lock();
2156
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002157 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2158 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002159 goto out_rtnl;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002160
2161 if (!drv->ops->change_bss) {
2162 err = -EOPNOTSUPP;
2163 goto out;
2164 }
2165
Jouni Malineneec60b02009-03-20 21:21:19 +02002166 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
2167 err = -EOPNOTSUPP;
2168 goto out;
2169 }
2170
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002171 err = drv->ops->change_bss(&drv->wiphy, dev, &params);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002172
2173 out:
2174 cfg80211_put_dev(drv);
2175 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002176 out_rtnl:
2177 rtnl_unlock();
2178
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002179 return err;
2180}
2181
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002182static const struct nla_policy
2183 reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
2184 [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
2185 [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
2186 [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
2187 [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
2188 [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
2189 [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
2190};
2191
2192static int parse_reg_rule(struct nlattr *tb[],
2193 struct ieee80211_reg_rule *reg_rule)
2194{
2195 struct ieee80211_freq_range *freq_range = &reg_rule->freq_range;
2196 struct ieee80211_power_rule *power_rule = &reg_rule->power_rule;
2197
2198 if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
2199 return -EINVAL;
2200 if (!tb[NL80211_ATTR_FREQ_RANGE_START])
2201 return -EINVAL;
2202 if (!tb[NL80211_ATTR_FREQ_RANGE_END])
2203 return -EINVAL;
2204 if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
2205 return -EINVAL;
2206 if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
2207 return -EINVAL;
2208
2209 reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
2210
2211 freq_range->start_freq_khz =
2212 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
2213 freq_range->end_freq_khz =
2214 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
2215 freq_range->max_bandwidth_khz =
2216 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
2217
2218 power_rule->max_eirp =
2219 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
2220
2221 if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
2222 power_rule->max_antenna_gain =
2223 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
2224
2225 return 0;
2226}
2227
2228static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
2229{
2230 int r;
2231 char *data = NULL;
2232
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002233 /*
2234 * You should only get this when cfg80211 hasn't yet initialized
2235 * completely when built-in to the kernel right between the time
2236 * window between nl80211_init() and regulatory_init(), if that is
2237 * even possible.
2238 */
2239 mutex_lock(&cfg80211_mutex);
2240 if (unlikely(!cfg80211_regdomain)) {
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002241 mutex_unlock(&cfg80211_mutex);
2242 return -EINPROGRESS;
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002243 }
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002244 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002245
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002246 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
2247 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002248
2249 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
2250
2251#ifdef CONFIG_WIRELESS_OLD_REGULATORY
2252 /* We ignore world regdom requests with the old regdom setup */
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002253 if (is_world_regdom(data))
2254 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002255#endif
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002256
2257 r = regulatory_hint_user(data);
2258
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002259 return r;
2260}
2261
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002262static int nl80211_get_mesh_params(struct sk_buff *skb,
2263 struct genl_info *info)
2264{
2265 struct cfg80211_registered_device *drv;
2266 struct mesh_config cur_params;
2267 int err;
2268 struct net_device *dev;
2269 void *hdr;
2270 struct nlattr *pinfoattr;
2271 struct sk_buff *msg;
2272
Johannes Berg3b858752009-03-12 09:55:09 +01002273 rtnl_lock();
2274
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002275 /* Look up our device */
2276 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2277 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002278 goto out_rtnl;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002279
Jouni Malinenf3f92582009-03-20 17:57:36 +02002280 if (!drv->ops->get_mesh_params) {
2281 err = -EOPNOTSUPP;
2282 goto out;
2283 }
2284
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002285 /* Get the mesh params */
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002286 err = drv->ops->get_mesh_params(&drv->wiphy, dev, &cur_params);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002287 if (err)
2288 goto out;
2289
2290 /* Draw up a netlink message to send back */
2291 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
2292 if (!msg) {
2293 err = -ENOBUFS;
2294 goto out;
2295 }
2296 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
2297 NL80211_CMD_GET_MESH_PARAMS);
2298 if (!hdr)
2299 goto nla_put_failure;
2300 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS);
2301 if (!pinfoattr)
2302 goto nla_put_failure;
2303 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2304 NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
2305 cur_params.dot11MeshRetryTimeout);
2306 NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
2307 cur_params.dot11MeshConfirmTimeout);
2308 NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
2309 cur_params.dot11MeshHoldingTimeout);
2310 NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
2311 cur_params.dot11MeshMaxPeerLinks);
2312 NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES,
2313 cur_params.dot11MeshMaxRetries);
2314 NLA_PUT_U8(msg, NL80211_MESHCONF_TTL,
2315 cur_params.dot11MeshTTL);
2316 NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
2317 cur_params.auto_open_plinks);
2318 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
2319 cur_params.dot11MeshHWMPmaxPREQretries);
2320 NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
2321 cur_params.path_refresh_time);
2322 NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
2323 cur_params.min_discovery_timeout);
2324 NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
2325 cur_params.dot11MeshHWMPactivePathTimeout);
2326 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
2327 cur_params.dot11MeshHWMPpreqMinInterval);
2328 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
2329 cur_params.dot11MeshHWMPnetDiameterTraversalTime);
2330 nla_nest_end(msg, pinfoattr);
2331 genlmsg_end(msg, hdr);
2332 err = genlmsg_unicast(msg, info->snd_pid);
2333 goto out;
2334
Johannes Berg3b858752009-03-12 09:55:09 +01002335 nla_put_failure:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002336 genlmsg_cancel(msg, hdr);
2337 err = -EMSGSIZE;
Johannes Berg3b858752009-03-12 09:55:09 +01002338 out:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002339 /* Cleanup */
2340 cfg80211_put_dev(drv);
2341 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002342 out_rtnl:
2343 rtnl_unlock();
2344
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002345 return err;
2346}
2347
2348#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \
2349do {\
2350 if (table[attr_num]) {\
2351 cfg.param = nla_fn(table[attr_num]); \
2352 mask |= (1 << (attr_num - 1)); \
2353 } \
2354} while (0);\
2355
2356static struct nla_policy
2357nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] __read_mostly = {
2358 [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
2359 [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
2360 [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
2361 [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
2362 [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
2363 [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
2364 [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
2365
2366 [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
2367 [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
2368 [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
2369 [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
2370 [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
2371 [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
2372};
2373
2374static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
2375{
2376 int err;
2377 u32 mask;
2378 struct cfg80211_registered_device *drv;
2379 struct net_device *dev;
2380 struct mesh_config cfg;
2381 struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
2382 struct nlattr *parent_attr;
2383
2384 parent_attr = info->attrs[NL80211_ATTR_MESH_PARAMS];
2385 if (!parent_attr)
2386 return -EINVAL;
2387 if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
2388 parent_attr, nl80211_meshconf_params_policy))
2389 return -EINVAL;
2390
Johannes Berg3b858752009-03-12 09:55:09 +01002391 rtnl_lock();
2392
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002393 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2394 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002395 goto out_rtnl;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002396
Jouni Malinenf3f92582009-03-20 17:57:36 +02002397 if (!drv->ops->set_mesh_params) {
2398 err = -EOPNOTSUPP;
2399 goto out;
2400 }
2401
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002402 /* This makes sure that there aren't more than 32 mesh config
2403 * parameters (otherwise our bitfield scheme would not work.) */
2404 BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
2405
2406 /* Fill in the params struct */
2407 mask = 0;
2408 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout,
2409 mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16);
2410 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout,
2411 mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16);
2412 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout,
2413 mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16);
2414 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks,
2415 mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16);
2416 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries,
2417 mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8);
2418 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL,
2419 mask, NL80211_MESHCONF_TTL, nla_get_u8);
2420 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
2421 mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8);
2422 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
2423 mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
2424 nla_get_u8);
2425 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time,
2426 mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32);
2427 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout,
2428 mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
2429 nla_get_u16);
2430 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
2431 mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
2432 nla_get_u32);
2433 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
2434 mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
2435 nla_get_u16);
2436 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
2437 dot11MeshHWMPnetDiameterTraversalTime,
2438 mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
2439 nla_get_u16);
2440
2441 /* Apply changes */
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002442 err = drv->ops->set_mesh_params(&drv->wiphy, dev, &cfg, mask);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002443
Jouni Malinenf3f92582009-03-20 17:57:36 +02002444 out:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002445 /* cleanup */
2446 cfg80211_put_dev(drv);
2447 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002448 out_rtnl:
2449 rtnl_unlock();
2450
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002451 return err;
2452}
2453
2454#undef FILL_IN_MESH_PARAM_IF_SET
2455
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002456static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
2457{
2458 struct sk_buff *msg;
2459 void *hdr = NULL;
2460 struct nlattr *nl_reg_rules;
2461 unsigned int i;
2462 int err = -EINVAL;
2463
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002464 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002465
2466 if (!cfg80211_regdomain)
2467 goto out;
2468
2469 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
2470 if (!msg) {
2471 err = -ENOBUFS;
2472 goto out;
2473 }
2474
2475 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
2476 NL80211_CMD_GET_REG);
2477 if (!hdr)
2478 goto nla_put_failure;
2479
2480 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2,
2481 cfg80211_regdomain->alpha2);
2482
2483 nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
2484 if (!nl_reg_rules)
2485 goto nla_put_failure;
2486
2487 for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) {
2488 struct nlattr *nl_reg_rule;
2489 const struct ieee80211_reg_rule *reg_rule;
2490 const struct ieee80211_freq_range *freq_range;
2491 const struct ieee80211_power_rule *power_rule;
2492
2493 reg_rule = &cfg80211_regdomain->reg_rules[i];
2494 freq_range = &reg_rule->freq_range;
2495 power_rule = &reg_rule->power_rule;
2496
2497 nl_reg_rule = nla_nest_start(msg, i);
2498 if (!nl_reg_rule)
2499 goto nla_put_failure;
2500
2501 NLA_PUT_U32(msg, NL80211_ATTR_REG_RULE_FLAGS,
2502 reg_rule->flags);
2503 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_START,
2504 freq_range->start_freq_khz);
2505 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_END,
2506 freq_range->end_freq_khz);
2507 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
2508 freq_range->max_bandwidth_khz);
2509 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
2510 power_rule->max_antenna_gain);
2511 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
2512 power_rule->max_eirp);
2513
2514 nla_nest_end(msg, nl_reg_rule);
2515 }
2516
2517 nla_nest_end(msg, nl_reg_rules);
2518
2519 genlmsg_end(msg, hdr);
2520 err = genlmsg_unicast(msg, info->snd_pid);
2521 goto out;
2522
2523nla_put_failure:
2524 genlmsg_cancel(msg, hdr);
2525 err = -EMSGSIZE;
2526out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002527 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002528 return err;
2529}
2530
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002531static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
2532{
2533 struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
2534 struct nlattr *nl_reg_rule;
2535 char *alpha2 = NULL;
2536 int rem_reg_rules = 0, r = 0;
2537 u32 num_rules = 0, rule_idx = 0, size_of_regd;
2538 struct ieee80211_regdomain *rd = NULL;
2539
2540 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
2541 return -EINVAL;
2542
2543 if (!info->attrs[NL80211_ATTR_REG_RULES])
2544 return -EINVAL;
2545
2546 alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
2547
2548 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
2549 rem_reg_rules) {
2550 num_rules++;
2551 if (num_rules > NL80211_MAX_SUPP_REG_RULES)
2552 goto bad_reg;
2553 }
2554
2555 if (!reg_is_valid_request(alpha2))
2556 return -EINVAL;
2557
2558 size_of_regd = sizeof(struct ieee80211_regdomain) +
2559 (num_rules * sizeof(struct ieee80211_reg_rule));
2560
2561 rd = kzalloc(size_of_regd, GFP_KERNEL);
2562 if (!rd)
2563 return -ENOMEM;
2564
2565 rd->n_reg_rules = num_rules;
2566 rd->alpha2[0] = alpha2[0];
2567 rd->alpha2[1] = alpha2[1];
2568
2569 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
2570 rem_reg_rules) {
2571 nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
2572 nla_data(nl_reg_rule), nla_len(nl_reg_rule),
2573 reg_rule_policy);
2574 r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
2575 if (r)
2576 goto bad_reg;
2577
2578 rule_idx++;
2579
2580 if (rule_idx > NL80211_MAX_SUPP_REG_RULES)
2581 goto bad_reg;
2582 }
2583
2584 BUG_ON(rule_idx != num_rules);
2585
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002586 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002587 r = set_regdom(rd);
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002588 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002589 return r;
2590
Johannes Bergd2372b32008-10-24 20:32:20 +02002591 bad_reg:
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002592 kfree(rd);
2593 return -EINVAL;
2594}
2595
Johannes Berg2a519312009-02-10 21:25:55 +01002596static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
2597{
2598 struct cfg80211_registered_device *drv;
2599 struct net_device *dev;
2600 struct cfg80211_scan_request *request;
2601 struct cfg80211_ssid *ssid;
2602 struct ieee80211_channel *channel;
2603 struct nlattr *attr;
2604 struct wiphy *wiphy;
2605 int err, tmp, n_ssids = 0, n_channels = 0, i;
2606 enum ieee80211_band band;
Jouni Malinen70692ad2009-02-16 19:39:13 +02002607 size_t ie_len;
Johannes Berg2a519312009-02-10 21:25:55 +01002608
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002609 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
2610 return -EINVAL;
2611
Johannes Berg3b858752009-03-12 09:55:09 +01002612 rtnl_lock();
2613
Johannes Berg2a519312009-02-10 21:25:55 +01002614 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2615 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002616 goto out_rtnl;
Johannes Berg2a519312009-02-10 21:25:55 +01002617
2618 wiphy = &drv->wiphy;
2619
2620 if (!drv->ops->scan) {
2621 err = -EOPNOTSUPP;
2622 goto out;
2623 }
2624
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002625 if (!netif_running(dev)) {
2626 err = -ENETDOWN;
2627 goto out;
2628 }
2629
Johannes Berg2a519312009-02-10 21:25:55 +01002630 if (drv->scan_req) {
2631 err = -EBUSY;
Johannes Berg3b858752009-03-12 09:55:09 +01002632 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002633 }
2634
2635 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
2636 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp)
2637 n_channels++;
2638 if (!n_channels) {
2639 err = -EINVAL;
Johannes Berg3b858752009-03-12 09:55:09 +01002640 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002641 }
2642 } else {
2643 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
2644 if (wiphy->bands[band])
2645 n_channels += wiphy->bands[band]->n_channels;
2646 }
2647
2648 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
2649 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
2650 n_ssids++;
2651
2652 if (n_ssids > wiphy->max_scan_ssids) {
2653 err = -EINVAL;
Johannes Berg3b858752009-03-12 09:55:09 +01002654 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002655 }
2656
Jouni Malinen70692ad2009-02-16 19:39:13 +02002657 if (info->attrs[NL80211_ATTR_IE])
2658 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
2659 else
2660 ie_len = 0;
2661
Johannes Berg18a83652009-03-31 12:12:05 +02002662 if (ie_len > wiphy->max_scan_ie_len) {
2663 err = -EINVAL;
2664 goto out;
2665 }
2666
Johannes Berg2a519312009-02-10 21:25:55 +01002667 request = kzalloc(sizeof(*request)
2668 + sizeof(*ssid) * n_ssids
Jouni Malinen70692ad2009-02-16 19:39:13 +02002669 + sizeof(channel) * n_channels
2670 + ie_len, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01002671 if (!request) {
2672 err = -ENOMEM;
Johannes Berg3b858752009-03-12 09:55:09 +01002673 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002674 }
2675
2676 request->channels = (void *)((char *)request + sizeof(*request));
2677 request->n_channels = n_channels;
2678 if (n_ssids)
2679 request->ssids = (void *)(request->channels + n_channels);
2680 request->n_ssids = n_ssids;
Jouni Malinen70692ad2009-02-16 19:39:13 +02002681 if (ie_len) {
2682 if (request->ssids)
2683 request->ie = (void *)(request->ssids + n_ssids);
2684 else
2685 request->ie = (void *)(request->channels + n_channels);
2686 }
Johannes Berg2a519312009-02-10 21:25:55 +01002687
2688 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
2689 /* user specified, bail out if channel not found */
2690 request->n_channels = n_channels;
2691 i = 0;
2692 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
2693 request->channels[i] = ieee80211_get_channel(wiphy, nla_get_u32(attr));
2694 if (!request->channels[i]) {
2695 err = -EINVAL;
2696 goto out_free;
2697 }
2698 i++;
2699 }
2700 } else {
2701 /* all channels */
2702 i = 0;
2703 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
2704 int j;
2705 if (!wiphy->bands[band])
2706 continue;
2707 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
2708 request->channels[i] = &wiphy->bands[band]->channels[j];
2709 i++;
2710 }
2711 }
2712 }
2713
2714 i = 0;
2715 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
2716 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
2717 if (request->ssids[i].ssid_len > IEEE80211_MAX_SSID_LEN) {
2718 err = -EINVAL;
2719 goto out_free;
2720 }
2721 memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
2722 request->ssids[i].ssid_len = nla_len(attr);
2723 i++;
2724 }
2725 }
2726
Jouni Malinen70692ad2009-02-16 19:39:13 +02002727 if (info->attrs[NL80211_ATTR_IE]) {
2728 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Johannes Bergde95a542009-04-01 11:58:36 +02002729 memcpy((void *)request->ie,
2730 nla_data(info->attrs[NL80211_ATTR_IE]),
Jouni Malinen70692ad2009-02-16 19:39:13 +02002731 request->ie_len);
2732 }
2733
Johannes Berg2a519312009-02-10 21:25:55 +01002734 request->ifidx = dev->ifindex;
2735 request->wiphy = &drv->wiphy;
2736
2737 drv->scan_req = request;
2738 err = drv->ops->scan(&drv->wiphy, dev, request);
2739
2740 out_free:
2741 if (err) {
2742 drv->scan_req = NULL;
2743 kfree(request);
2744 }
Johannes Berg2a519312009-02-10 21:25:55 +01002745 out:
2746 cfg80211_put_dev(drv);
2747 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002748 out_rtnl:
2749 rtnl_unlock();
2750
Johannes Berg2a519312009-02-10 21:25:55 +01002751 return err;
2752}
2753
2754static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
2755 struct cfg80211_registered_device *rdev,
2756 struct net_device *dev,
2757 struct cfg80211_bss *res)
2758{
2759 void *hdr;
2760 struct nlattr *bss;
2761
2762 hdr = nl80211hdr_put(msg, pid, seq, flags,
2763 NL80211_CMD_NEW_SCAN_RESULTS);
2764 if (!hdr)
2765 return -1;
2766
2767 NLA_PUT_U32(msg, NL80211_ATTR_SCAN_GENERATION,
2768 rdev->bss_generation);
2769 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2770
2771 bss = nla_nest_start(msg, NL80211_ATTR_BSS);
2772 if (!bss)
2773 goto nla_put_failure;
2774 if (!is_zero_ether_addr(res->bssid))
2775 NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid);
2776 if (res->information_elements && res->len_information_elements)
2777 NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS,
2778 res->len_information_elements,
2779 res->information_elements);
2780 if (res->tsf)
2781 NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf);
2782 if (res->beacon_interval)
2783 NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval);
2784 NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability);
2785 NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq);
2786
Johannes Berg77965c92009-02-18 18:45:06 +01002787 switch (rdev->wiphy.signal_type) {
Johannes Berg2a519312009-02-10 21:25:55 +01002788 case CFG80211_SIGNAL_TYPE_MBM:
2789 NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal);
2790 break;
2791 case CFG80211_SIGNAL_TYPE_UNSPEC:
2792 NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal);
2793 break;
2794 default:
2795 break;
2796 }
2797
2798 nla_nest_end(msg, bss);
2799
2800 return genlmsg_end(msg, hdr);
2801
2802 nla_put_failure:
2803 genlmsg_cancel(msg, hdr);
2804 return -EMSGSIZE;
2805}
2806
2807static int nl80211_dump_scan(struct sk_buff *skb,
2808 struct netlink_callback *cb)
2809{
2810 struct cfg80211_registered_device *dev;
2811 struct net_device *netdev;
2812 struct cfg80211_internal_bss *scan;
2813 int ifidx = cb->args[0];
2814 int start = cb->args[1], idx = 0;
2815 int err;
2816
2817 if (!ifidx) {
2818 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
2819 nl80211_fam.attrbuf, nl80211_fam.maxattr,
2820 nl80211_policy);
2821 if (err)
2822 return err;
2823
2824 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
2825 return -EINVAL;
2826
2827 ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
2828 if (!ifidx)
2829 return -EINVAL;
2830 cb->args[0] = ifidx;
2831 }
2832
2833 netdev = dev_get_by_index(&init_net, ifidx);
2834 if (!netdev)
2835 return -ENODEV;
2836
2837 dev = cfg80211_get_dev_from_ifindex(ifidx);
2838 if (IS_ERR(dev)) {
2839 err = PTR_ERR(dev);
2840 goto out_put_netdev;
2841 }
2842
2843 spin_lock_bh(&dev->bss_lock);
2844 cfg80211_bss_expire(dev);
2845
2846 list_for_each_entry(scan, &dev->bss_list, list) {
2847 if (++idx <= start)
2848 continue;
2849 if (nl80211_send_bss(skb,
2850 NETLINK_CB(cb->skb).pid,
2851 cb->nlh->nlmsg_seq, NLM_F_MULTI,
2852 dev, netdev, &scan->pub) < 0) {
2853 idx--;
2854 goto out;
2855 }
2856 }
2857
2858 out:
2859 spin_unlock_bh(&dev->bss_lock);
2860
2861 cb->args[1] = idx;
2862 err = skb->len;
2863 cfg80211_put_dev(dev);
2864 out_put_netdev:
2865 dev_put(netdev);
2866
2867 return err;
2868}
2869
Jouni Malinen255e7372009-03-20 21:21:17 +02002870static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
2871{
2872 return auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM ||
2873 auth_type == NL80211_AUTHTYPE_SHARED_KEY ||
2874 auth_type == NL80211_AUTHTYPE_FT ||
2875 auth_type == NL80211_AUTHTYPE_NETWORK_EAP;
2876}
2877
Jouni Malinen636a5d32009-03-19 13:39:22 +02002878static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
2879{
2880 struct cfg80211_registered_device *drv;
2881 struct net_device *dev;
2882 struct cfg80211_auth_request req;
2883 struct wiphy *wiphy;
2884 int err;
2885
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002886 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
2887 return -EINVAL;
2888
2889 if (!info->attrs[NL80211_ATTR_MAC])
2890 return -EINVAL;
2891
Jouni Malinen17780922009-03-27 20:52:47 +02002892 if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
2893 return -EINVAL;
2894
Jouni Malinen636a5d32009-03-19 13:39:22 +02002895 rtnl_lock();
2896
2897 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2898 if (err)
2899 goto unlock_rtnl;
2900
2901 if (!drv->ops->auth) {
2902 err = -EOPNOTSUPP;
2903 goto out;
2904 }
2905
Jouni Malineneec60b02009-03-20 21:21:19 +02002906 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
2907 err = -EOPNOTSUPP;
2908 goto out;
2909 }
2910
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002911 if (!netif_running(dev)) {
2912 err = -ENETDOWN;
2913 goto out;
2914 }
2915
Jouni Malinen636a5d32009-03-19 13:39:22 +02002916 wiphy = &drv->wiphy;
2917 memset(&req, 0, sizeof(req));
2918
2919 req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2920
2921 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
2922 req.chan = ieee80211_get_channel(
2923 wiphy,
2924 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
2925 if (!req.chan) {
2926 err = -EINVAL;
2927 goto out;
2928 }
2929 }
2930
2931 if (info->attrs[NL80211_ATTR_SSID]) {
2932 req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
2933 req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
2934 }
2935
2936 if (info->attrs[NL80211_ATTR_IE]) {
2937 req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
2938 req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
2939 }
2940
Jouni Malinen17780922009-03-27 20:52:47 +02002941 req.auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
2942 if (!nl80211_valid_auth_type(req.auth_type)) {
2943 err = -EINVAL;
2944 goto out;
Jouni Malinen636a5d32009-03-19 13:39:22 +02002945 }
2946
2947 err = drv->ops->auth(&drv->wiphy, dev, &req);
2948
2949out:
2950 cfg80211_put_dev(drv);
2951 dev_put(dev);
2952unlock_rtnl:
2953 rtnl_unlock();
2954 return err;
2955}
2956
2957static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
2958{
2959 struct cfg80211_registered_device *drv;
2960 struct net_device *dev;
2961 struct cfg80211_assoc_request req;
2962 struct wiphy *wiphy;
2963 int err;
2964
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002965 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
2966 return -EINVAL;
2967
2968 if (!info->attrs[NL80211_ATTR_MAC] ||
2969 !info->attrs[NL80211_ATTR_SSID])
2970 return -EINVAL;
2971
Jouni Malinen636a5d32009-03-19 13:39:22 +02002972 rtnl_lock();
2973
2974 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2975 if (err)
2976 goto unlock_rtnl;
2977
2978 if (!drv->ops->assoc) {
2979 err = -EOPNOTSUPP;
2980 goto out;
2981 }
2982
Jouni Malineneec60b02009-03-20 21:21:19 +02002983 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
2984 err = -EOPNOTSUPP;
2985 goto out;
2986 }
2987
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002988 if (!netif_running(dev)) {
2989 err = -ENETDOWN;
2990 goto out;
2991 }
2992
Jouni Malinen636a5d32009-03-19 13:39:22 +02002993 wiphy = &drv->wiphy;
2994 memset(&req, 0, sizeof(req));
2995
2996 req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2997
2998 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
2999 req.chan = ieee80211_get_channel(
3000 wiphy,
3001 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
3002 if (!req.chan) {
3003 err = -EINVAL;
3004 goto out;
3005 }
3006 }
3007
Jouni Malinen636a5d32009-03-19 13:39:22 +02003008 req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3009 req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
3010
3011 if (info->attrs[NL80211_ATTR_IE]) {
3012 req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3013 req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3014 }
3015
Jouni Malinendc6382c2009-05-06 22:09:37 +03003016 if (info->attrs[NL80211_ATTR_USE_MFP]) {
3017 enum nl80211_mfp use_mfp =
3018 nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
3019 if (use_mfp == NL80211_MFP_REQUIRED)
3020 req.use_mfp = true;
3021 else if (use_mfp != NL80211_MFP_NO) {
3022 err = -EINVAL;
3023 goto out;
3024 }
3025 }
3026
Jouni Malinen636a5d32009-03-19 13:39:22 +02003027 err = drv->ops->assoc(&drv->wiphy, dev, &req);
3028
3029out:
3030 cfg80211_put_dev(drv);
3031 dev_put(dev);
3032unlock_rtnl:
3033 rtnl_unlock();
3034 return err;
3035}
3036
3037static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
3038{
3039 struct cfg80211_registered_device *drv;
3040 struct net_device *dev;
3041 struct cfg80211_deauth_request req;
3042 struct wiphy *wiphy;
3043 int err;
3044
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003045 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3046 return -EINVAL;
3047
3048 if (!info->attrs[NL80211_ATTR_MAC])
3049 return -EINVAL;
3050
3051 if (!info->attrs[NL80211_ATTR_REASON_CODE])
3052 return -EINVAL;
3053
Jouni Malinen636a5d32009-03-19 13:39:22 +02003054 rtnl_lock();
3055
3056 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
3057 if (err)
3058 goto unlock_rtnl;
3059
3060 if (!drv->ops->deauth) {
3061 err = -EOPNOTSUPP;
3062 goto out;
3063 }
3064
Jouni Malineneec60b02009-03-20 21:21:19 +02003065 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
3066 err = -EOPNOTSUPP;
3067 goto out;
3068 }
3069
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003070 if (!netif_running(dev)) {
3071 err = -ENETDOWN;
3072 goto out;
3073 }
3074
Jouni Malinen636a5d32009-03-19 13:39:22 +02003075 wiphy = &drv->wiphy;
3076 memset(&req, 0, sizeof(req));
3077
3078 req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
3079
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003080 req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
3081 if (req.reason_code == 0) {
3082 /* Reason Code 0 is reserved */
3083 err = -EINVAL;
3084 goto out;
Jouni Malinen255e7372009-03-20 21:21:17 +02003085 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02003086
3087 if (info->attrs[NL80211_ATTR_IE]) {
3088 req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3089 req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3090 }
3091
3092 err = drv->ops->deauth(&drv->wiphy, dev, &req);
3093
3094out:
3095 cfg80211_put_dev(drv);
3096 dev_put(dev);
3097unlock_rtnl:
3098 rtnl_unlock();
3099 return err;
3100}
3101
3102static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
3103{
3104 struct cfg80211_registered_device *drv;
3105 struct net_device *dev;
3106 struct cfg80211_disassoc_request req;
3107 struct wiphy *wiphy;
3108 int err;
3109
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003110 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3111 return -EINVAL;
3112
3113 if (!info->attrs[NL80211_ATTR_MAC])
3114 return -EINVAL;
3115
3116 if (!info->attrs[NL80211_ATTR_REASON_CODE])
3117 return -EINVAL;
3118
Jouni Malinen636a5d32009-03-19 13:39:22 +02003119 rtnl_lock();
3120
3121 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
3122 if (err)
3123 goto unlock_rtnl;
3124
3125 if (!drv->ops->disassoc) {
3126 err = -EOPNOTSUPP;
3127 goto out;
3128 }
3129
Jouni Malineneec60b02009-03-20 21:21:19 +02003130 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
3131 err = -EOPNOTSUPP;
3132 goto out;
3133 }
3134
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003135 if (!netif_running(dev)) {
3136 err = -ENETDOWN;
3137 goto out;
3138 }
3139
Jouni Malinen636a5d32009-03-19 13:39:22 +02003140 wiphy = &drv->wiphy;
3141 memset(&req, 0, sizeof(req));
3142
3143 req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
3144
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003145 req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
3146 if (req.reason_code == 0) {
3147 /* Reason Code 0 is reserved */
3148 err = -EINVAL;
3149 goto out;
Jouni Malinen255e7372009-03-20 21:21:17 +02003150 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02003151
3152 if (info->attrs[NL80211_ATTR_IE]) {
3153 req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3154 req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3155 }
3156
3157 err = drv->ops->disassoc(&drv->wiphy, dev, &req);
3158
3159out:
3160 cfg80211_put_dev(drv);
3161 dev_put(dev);
3162unlock_rtnl:
3163 rtnl_unlock();
3164 return err;
3165}
3166
Johannes Berg04a773a2009-04-19 21:24:32 +02003167static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
3168{
3169 struct cfg80211_registered_device *drv;
3170 struct net_device *dev;
3171 struct cfg80211_ibss_params ibss;
3172 struct wiphy *wiphy;
3173 int err;
3174
Johannes Berg8e30bc52009-04-22 17:45:38 +02003175 memset(&ibss, 0, sizeof(ibss));
3176
Johannes Berg04a773a2009-04-19 21:24:32 +02003177 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3178 return -EINVAL;
3179
3180 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
3181 !info->attrs[NL80211_ATTR_SSID] ||
3182 !nla_len(info->attrs[NL80211_ATTR_SSID]))
3183 return -EINVAL;
3184
Johannes Berg8e30bc52009-04-22 17:45:38 +02003185 ibss.beacon_interval = 100;
3186
3187 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
3188 ibss.beacon_interval =
3189 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
3190 if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000)
3191 return -EINVAL;
3192 }
3193
Johannes Berg04a773a2009-04-19 21:24:32 +02003194 rtnl_lock();
3195
3196 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
3197 if (err)
3198 goto unlock_rtnl;
3199
3200 if (!drv->ops->join_ibss) {
3201 err = -EOPNOTSUPP;
3202 goto out;
3203 }
3204
3205 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
3206 err = -EOPNOTSUPP;
3207 goto out;
3208 }
3209
3210 if (!netif_running(dev)) {
3211 err = -ENETDOWN;
3212 goto out;
3213 }
3214
3215 wiphy = &drv->wiphy;
Johannes Berg04a773a2009-04-19 21:24:32 +02003216
3217 if (info->attrs[NL80211_ATTR_MAC])
3218 ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
3219 ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3220 ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
3221
3222 if (info->attrs[NL80211_ATTR_IE]) {
3223 ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3224 ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3225 }
3226
3227 ibss.channel = ieee80211_get_channel(wiphy,
3228 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
3229 if (!ibss.channel ||
3230 ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
3231 ibss.channel->flags & IEEE80211_CHAN_DISABLED) {
3232 err = -EINVAL;
3233 goto out;
3234 }
3235
3236 ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
3237
3238 err = cfg80211_join_ibss(drv, dev, &ibss);
3239
3240out:
3241 cfg80211_put_dev(drv);
3242 dev_put(dev);
3243unlock_rtnl:
3244 rtnl_unlock();
3245 return err;
3246}
3247
3248static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
3249{
3250 struct cfg80211_registered_device *drv;
3251 struct net_device *dev;
3252 int err;
3253
3254 rtnl_lock();
3255
3256 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
3257 if (err)
3258 goto unlock_rtnl;
3259
3260 if (!drv->ops->leave_ibss) {
3261 err = -EOPNOTSUPP;
3262 goto out;
3263 }
3264
3265 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
3266 err = -EOPNOTSUPP;
3267 goto out;
3268 }
3269
3270 if (!netif_running(dev)) {
3271 err = -ENETDOWN;
3272 goto out;
3273 }
3274
Johannes Berg9d308422009-04-20 18:43:46 +02003275 err = cfg80211_leave_ibss(drv, dev, false);
Johannes Berg04a773a2009-04-19 21:24:32 +02003276
3277out:
3278 cfg80211_put_dev(drv);
3279 dev_put(dev);
3280unlock_rtnl:
3281 rtnl_unlock();
3282 return err;
3283}
3284
Johannes Berg55682962007-09-20 13:09:35 -04003285static struct genl_ops nl80211_ops[] = {
3286 {
3287 .cmd = NL80211_CMD_GET_WIPHY,
3288 .doit = nl80211_get_wiphy,
3289 .dumpit = nl80211_dump_wiphy,
3290 .policy = nl80211_policy,
3291 /* can be retrieved by unprivileged users */
3292 },
3293 {
3294 .cmd = NL80211_CMD_SET_WIPHY,
3295 .doit = nl80211_set_wiphy,
3296 .policy = nl80211_policy,
3297 .flags = GENL_ADMIN_PERM,
3298 },
3299 {
3300 .cmd = NL80211_CMD_GET_INTERFACE,
3301 .doit = nl80211_get_interface,
3302 .dumpit = nl80211_dump_interface,
3303 .policy = nl80211_policy,
3304 /* can be retrieved by unprivileged users */
3305 },
3306 {
3307 .cmd = NL80211_CMD_SET_INTERFACE,
3308 .doit = nl80211_set_interface,
3309 .policy = nl80211_policy,
3310 .flags = GENL_ADMIN_PERM,
3311 },
3312 {
3313 .cmd = NL80211_CMD_NEW_INTERFACE,
3314 .doit = nl80211_new_interface,
3315 .policy = nl80211_policy,
3316 .flags = GENL_ADMIN_PERM,
3317 },
3318 {
3319 .cmd = NL80211_CMD_DEL_INTERFACE,
3320 .doit = nl80211_del_interface,
3321 .policy = nl80211_policy,
3322 .flags = GENL_ADMIN_PERM,
3323 },
Johannes Berg41ade002007-12-19 02:03:29 +01003324 {
3325 .cmd = NL80211_CMD_GET_KEY,
3326 .doit = nl80211_get_key,
3327 .policy = nl80211_policy,
3328 .flags = GENL_ADMIN_PERM,
3329 },
3330 {
3331 .cmd = NL80211_CMD_SET_KEY,
3332 .doit = nl80211_set_key,
3333 .policy = nl80211_policy,
3334 .flags = GENL_ADMIN_PERM,
3335 },
3336 {
3337 .cmd = NL80211_CMD_NEW_KEY,
3338 .doit = nl80211_new_key,
3339 .policy = nl80211_policy,
3340 .flags = GENL_ADMIN_PERM,
3341 },
3342 {
3343 .cmd = NL80211_CMD_DEL_KEY,
3344 .doit = nl80211_del_key,
3345 .policy = nl80211_policy,
3346 .flags = GENL_ADMIN_PERM,
3347 },
Johannes Berged1b6cc2007-12-19 02:03:32 +01003348 {
3349 .cmd = NL80211_CMD_SET_BEACON,
3350 .policy = nl80211_policy,
3351 .flags = GENL_ADMIN_PERM,
3352 .doit = nl80211_addset_beacon,
3353 },
3354 {
3355 .cmd = NL80211_CMD_NEW_BEACON,
3356 .policy = nl80211_policy,
3357 .flags = GENL_ADMIN_PERM,
3358 .doit = nl80211_addset_beacon,
3359 },
3360 {
3361 .cmd = NL80211_CMD_DEL_BEACON,
3362 .policy = nl80211_policy,
3363 .flags = GENL_ADMIN_PERM,
3364 .doit = nl80211_del_beacon,
3365 },
Johannes Berg5727ef12007-12-19 02:03:34 +01003366 {
3367 .cmd = NL80211_CMD_GET_STATION,
3368 .doit = nl80211_get_station,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003369 .dumpit = nl80211_dump_station,
Johannes Berg5727ef12007-12-19 02:03:34 +01003370 .policy = nl80211_policy,
Johannes Berg5727ef12007-12-19 02:03:34 +01003371 },
3372 {
3373 .cmd = NL80211_CMD_SET_STATION,
3374 .doit = nl80211_set_station,
3375 .policy = nl80211_policy,
3376 .flags = GENL_ADMIN_PERM,
3377 },
3378 {
3379 .cmd = NL80211_CMD_NEW_STATION,
3380 .doit = nl80211_new_station,
3381 .policy = nl80211_policy,
3382 .flags = GENL_ADMIN_PERM,
3383 },
3384 {
3385 .cmd = NL80211_CMD_DEL_STATION,
3386 .doit = nl80211_del_station,
3387 .policy = nl80211_policy,
3388 .flags = GENL_ADMIN_PERM,
3389 },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003390 {
3391 .cmd = NL80211_CMD_GET_MPATH,
3392 .doit = nl80211_get_mpath,
3393 .dumpit = nl80211_dump_mpath,
3394 .policy = nl80211_policy,
3395 .flags = GENL_ADMIN_PERM,
3396 },
3397 {
3398 .cmd = NL80211_CMD_SET_MPATH,
3399 .doit = nl80211_set_mpath,
3400 .policy = nl80211_policy,
3401 .flags = GENL_ADMIN_PERM,
3402 },
3403 {
3404 .cmd = NL80211_CMD_NEW_MPATH,
3405 .doit = nl80211_new_mpath,
3406 .policy = nl80211_policy,
3407 .flags = GENL_ADMIN_PERM,
3408 },
3409 {
3410 .cmd = NL80211_CMD_DEL_MPATH,
3411 .doit = nl80211_del_mpath,
3412 .policy = nl80211_policy,
3413 .flags = GENL_ADMIN_PERM,
3414 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003415 {
3416 .cmd = NL80211_CMD_SET_BSS,
3417 .doit = nl80211_set_bss,
3418 .policy = nl80211_policy,
3419 .flags = GENL_ADMIN_PERM,
3420 },
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003421 {
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003422 .cmd = NL80211_CMD_GET_REG,
3423 .doit = nl80211_get_reg,
3424 .policy = nl80211_policy,
3425 /* can be retrieved by unprivileged users */
3426 },
3427 {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003428 .cmd = NL80211_CMD_SET_REG,
3429 .doit = nl80211_set_reg,
3430 .policy = nl80211_policy,
3431 .flags = GENL_ADMIN_PERM,
3432 },
3433 {
3434 .cmd = NL80211_CMD_REQ_SET_REG,
3435 .doit = nl80211_req_set_reg,
3436 .policy = nl80211_policy,
3437 .flags = GENL_ADMIN_PERM,
3438 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003439 {
3440 .cmd = NL80211_CMD_GET_MESH_PARAMS,
3441 .doit = nl80211_get_mesh_params,
3442 .policy = nl80211_policy,
3443 /* can be retrieved by unprivileged users */
3444 },
3445 {
3446 .cmd = NL80211_CMD_SET_MESH_PARAMS,
3447 .doit = nl80211_set_mesh_params,
3448 .policy = nl80211_policy,
3449 .flags = GENL_ADMIN_PERM,
3450 },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +02003451 {
Johannes Berg2a519312009-02-10 21:25:55 +01003452 .cmd = NL80211_CMD_TRIGGER_SCAN,
3453 .doit = nl80211_trigger_scan,
3454 .policy = nl80211_policy,
3455 .flags = GENL_ADMIN_PERM,
3456 },
3457 {
3458 .cmd = NL80211_CMD_GET_SCAN,
3459 .policy = nl80211_policy,
3460 .dumpit = nl80211_dump_scan,
3461 },
Jouni Malinen636a5d32009-03-19 13:39:22 +02003462 {
3463 .cmd = NL80211_CMD_AUTHENTICATE,
3464 .doit = nl80211_authenticate,
3465 .policy = nl80211_policy,
3466 .flags = GENL_ADMIN_PERM,
3467 },
3468 {
3469 .cmd = NL80211_CMD_ASSOCIATE,
3470 .doit = nl80211_associate,
3471 .policy = nl80211_policy,
3472 .flags = GENL_ADMIN_PERM,
3473 },
3474 {
3475 .cmd = NL80211_CMD_DEAUTHENTICATE,
3476 .doit = nl80211_deauthenticate,
3477 .policy = nl80211_policy,
3478 .flags = GENL_ADMIN_PERM,
3479 },
3480 {
3481 .cmd = NL80211_CMD_DISASSOCIATE,
3482 .doit = nl80211_disassociate,
3483 .policy = nl80211_policy,
3484 .flags = GENL_ADMIN_PERM,
3485 },
Johannes Berg04a773a2009-04-19 21:24:32 +02003486 {
3487 .cmd = NL80211_CMD_JOIN_IBSS,
3488 .doit = nl80211_join_ibss,
3489 .policy = nl80211_policy,
3490 .flags = GENL_ADMIN_PERM,
3491 },
3492 {
3493 .cmd = NL80211_CMD_LEAVE_IBSS,
3494 .doit = nl80211_leave_ibss,
3495 .policy = nl80211_policy,
3496 .flags = GENL_ADMIN_PERM,
3497 },
Johannes Berg55682962007-09-20 13:09:35 -04003498};
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003499static struct genl_multicast_group nl80211_mlme_mcgrp = {
3500 .name = "mlme",
3501};
Johannes Berg55682962007-09-20 13:09:35 -04003502
3503/* multicast groups */
3504static struct genl_multicast_group nl80211_config_mcgrp = {
3505 .name = "config",
3506};
Johannes Berg2a519312009-02-10 21:25:55 +01003507static struct genl_multicast_group nl80211_scan_mcgrp = {
3508 .name = "scan",
3509};
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04003510static struct genl_multicast_group nl80211_regulatory_mcgrp = {
3511 .name = "regulatory",
3512};
Johannes Berg55682962007-09-20 13:09:35 -04003513
3514/* notification functions */
3515
3516void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
3517{
3518 struct sk_buff *msg;
3519
3520 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3521 if (!msg)
3522 return;
3523
3524 if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
3525 nlmsg_free(msg);
3526 return;
3527 }
3528
3529 genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL);
3530}
3531
Johannes Berg2a519312009-02-10 21:25:55 +01003532static int nl80211_send_scan_donemsg(struct sk_buff *msg,
3533 struct cfg80211_registered_device *rdev,
3534 struct net_device *netdev,
3535 u32 pid, u32 seq, int flags,
3536 u32 cmd)
3537{
3538 void *hdr;
3539
3540 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
3541 if (!hdr)
3542 return -1;
3543
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -05003544 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg2a519312009-02-10 21:25:55 +01003545 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
3546
3547 /* XXX: we should probably bounce back the request? */
3548
3549 return genlmsg_end(msg, hdr);
3550
3551 nla_put_failure:
3552 genlmsg_cancel(msg, hdr);
3553 return -EMSGSIZE;
3554}
3555
3556void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
3557 struct net_device *netdev)
3558{
3559 struct sk_buff *msg;
3560
3561 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3562 if (!msg)
3563 return;
3564
3565 if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0,
3566 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
3567 nlmsg_free(msg);
3568 return;
3569 }
3570
3571 genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL);
3572}
3573
3574void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
3575 struct net_device *netdev)
3576{
3577 struct sk_buff *msg;
3578
3579 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3580 if (!msg)
3581 return;
3582
3583 if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0,
3584 NL80211_CMD_SCAN_ABORTED) < 0) {
3585 nlmsg_free(msg);
3586 return;
3587 }
3588
3589 genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL);
3590}
3591
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04003592/*
3593 * This can happen on global regulatory changes or device specific settings
3594 * based on custom world regulatory domains.
3595 */
3596void nl80211_send_reg_change_event(struct regulatory_request *request)
3597{
3598 struct sk_buff *msg;
3599 void *hdr;
3600
3601 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3602 if (!msg)
3603 return;
3604
3605 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
3606 if (!hdr) {
3607 nlmsg_free(msg);
3608 return;
3609 }
3610
3611 /* Userspace can always count this one always being set */
3612 NLA_PUT_U8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator);
3613
3614 if (request->alpha2[0] == '0' && request->alpha2[1] == '0')
3615 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
3616 NL80211_REGDOM_TYPE_WORLD);
3617 else if (request->alpha2[0] == '9' && request->alpha2[1] == '9')
3618 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
3619 NL80211_REGDOM_TYPE_CUSTOM_WORLD);
3620 else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
3621 request->intersect)
3622 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
3623 NL80211_REGDOM_TYPE_INTERSECTION);
3624 else {
3625 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
3626 NL80211_REGDOM_TYPE_COUNTRY);
3627 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2);
3628 }
3629
3630 if (wiphy_idx_valid(request->wiphy_idx))
3631 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx);
3632
3633 if (genlmsg_end(msg, hdr) < 0) {
3634 nlmsg_free(msg);
3635 return;
3636 }
3637
3638 genlmsg_multicast(msg, 0, nl80211_regulatory_mcgrp.id, GFP_KERNEL);
3639
3640 return;
3641
3642nla_put_failure:
3643 genlmsg_cancel(msg, hdr);
3644 nlmsg_free(msg);
3645}
3646
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003647static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
3648 struct net_device *netdev,
3649 const u8 *buf, size_t len,
3650 enum nl80211_commands cmd)
3651{
3652 struct sk_buff *msg;
3653 void *hdr;
3654
Jouni Malinend91c01c2009-04-18 21:53:15 +03003655 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003656 if (!msg)
3657 return;
3658
3659 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
3660 if (!hdr) {
3661 nlmsg_free(msg);
3662 return;
3663 }
3664
3665 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
3666 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
3667 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
3668
3669 if (genlmsg_end(msg, hdr) < 0) {
3670 nlmsg_free(msg);
3671 return;
3672 }
3673
Jouni Malinend91c01c2009-04-18 21:53:15 +03003674 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_ATOMIC);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003675 return;
3676
3677 nla_put_failure:
3678 genlmsg_cancel(msg, hdr);
3679 nlmsg_free(msg);
3680}
3681
3682void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
3683 struct net_device *netdev, const u8 *buf, size_t len)
3684{
3685 nl80211_send_mlme_event(rdev, netdev, buf, len,
3686 NL80211_CMD_AUTHENTICATE);
3687}
3688
3689void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
3690 struct net_device *netdev, const u8 *buf,
3691 size_t len)
3692{
3693 nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_ASSOCIATE);
3694}
3695
Jouni Malinen53b46b82009-03-27 20:53:56 +02003696void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
3697 struct net_device *netdev, const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003698{
3699 nl80211_send_mlme_event(rdev, netdev, buf, len,
3700 NL80211_CMD_DEAUTHENTICATE);
3701}
3702
Jouni Malinen53b46b82009-03-27 20:53:56 +02003703void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
3704 struct net_device *netdev, const u8 *buf,
3705 size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003706{
3707 nl80211_send_mlme_event(rdev, netdev, buf, len,
3708 NL80211_CMD_DISASSOCIATE);
3709}
3710
Luis R. Rodriguez1b06bb42009-05-02 00:34:48 -04003711static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
3712 struct net_device *netdev, int cmd,
3713 const u8 *addr)
Jouni Malinen1965c852009-04-22 21:38:25 +03003714{
3715 struct sk_buff *msg;
3716 void *hdr;
3717
3718 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
3719 if (!msg)
3720 return;
3721
3722 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
3723 if (!hdr) {
3724 nlmsg_free(msg);
3725 return;
3726 }
3727
3728 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
3729 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
3730 NLA_PUT_FLAG(msg, NL80211_ATTR_TIMED_OUT);
3731 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
3732
3733 if (genlmsg_end(msg, hdr) < 0) {
3734 nlmsg_free(msg);
3735 return;
3736 }
3737
3738 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_ATOMIC);
3739 return;
3740
3741 nla_put_failure:
3742 genlmsg_cancel(msg, hdr);
3743 nlmsg_free(msg);
3744}
3745
3746void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
3747 struct net_device *netdev, const u8 *addr)
3748{
3749 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE,
3750 addr);
3751}
3752
3753void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
3754 struct net_device *netdev, const u8 *addr)
3755{
3756 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE, addr);
3757}
3758
Johannes Berg04a773a2009-04-19 21:24:32 +02003759void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
3760 struct net_device *netdev, const u8 *bssid,
3761 gfp_t gfp)
3762{
3763 struct sk_buff *msg;
3764 void *hdr;
3765
3766 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
3767 if (!msg)
3768 return;
3769
3770 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS);
3771 if (!hdr) {
3772 nlmsg_free(msg);
3773 return;
3774 }
3775
3776 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
3777 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
3778 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
3779
3780 if (genlmsg_end(msg, hdr) < 0) {
3781 nlmsg_free(msg);
3782 return;
3783 }
3784
3785 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
3786 return;
3787
3788 nla_put_failure:
3789 genlmsg_cancel(msg, hdr);
3790 nlmsg_free(msg);
3791}
3792
Jouni Malinena3b8b052009-03-27 21:59:49 +02003793void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
3794 struct net_device *netdev, const u8 *addr,
3795 enum nl80211_key_type key_type, int key_id,
3796 const u8 *tsc)
3797{
3798 struct sk_buff *msg;
3799 void *hdr;
3800
3801 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3802 if (!msg)
3803 return;
3804
3805 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
3806 if (!hdr) {
3807 nlmsg_free(msg);
3808 return;
3809 }
3810
3811 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
3812 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
3813 if (addr)
3814 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
3815 NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, key_type);
3816 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id);
3817 if (tsc)
3818 NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc);
3819
3820 if (genlmsg_end(msg, hdr) < 0) {
3821 nlmsg_free(msg);
3822 return;
3823 }
3824
3825 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_KERNEL);
3826 return;
3827
3828 nla_put_failure:
3829 genlmsg_cancel(msg, hdr);
3830 nlmsg_free(msg);
3831}
3832
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04003833void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
3834 struct ieee80211_channel *channel_before,
3835 struct ieee80211_channel *channel_after)
3836{
3837 struct sk_buff *msg;
3838 void *hdr;
3839 struct nlattr *nl_freq;
3840
3841 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
3842 if (!msg)
3843 return;
3844
3845 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
3846 if (!hdr) {
3847 nlmsg_free(msg);
3848 return;
3849 }
3850
3851 /*
3852 * Since we are applying the beacon hint to a wiphy we know its
3853 * wiphy_idx is valid
3854 */
3855 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy));
3856
3857 /* Before */
3858 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
3859 if (!nl_freq)
3860 goto nla_put_failure;
3861 if (nl80211_msg_put_channel(msg, channel_before))
3862 goto nla_put_failure;
3863 nla_nest_end(msg, nl_freq);
3864
3865 /* After */
3866 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
3867 if (!nl_freq)
3868 goto nla_put_failure;
3869 if (nl80211_msg_put_channel(msg, channel_after))
3870 goto nla_put_failure;
3871 nla_nest_end(msg, nl_freq);
3872
3873 if (genlmsg_end(msg, hdr) < 0) {
3874 nlmsg_free(msg);
3875 return;
3876 }
3877
3878 genlmsg_multicast(msg, 0, nl80211_regulatory_mcgrp.id, GFP_ATOMIC);
3879
3880 return;
3881
3882nla_put_failure:
3883 genlmsg_cancel(msg, hdr);
3884 nlmsg_free(msg);
3885}
3886
Johannes Berg55682962007-09-20 13:09:35 -04003887/* initialisation/exit functions */
3888
3889int nl80211_init(void)
3890{
3891 int err, i;
3892
3893 err = genl_register_family(&nl80211_fam);
3894 if (err)
3895 return err;
3896
3897 for (i = 0; i < ARRAY_SIZE(nl80211_ops); i++) {
3898 err = genl_register_ops(&nl80211_fam, &nl80211_ops[i]);
3899 if (err)
3900 goto err_out;
3901 }
3902
3903 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
3904 if (err)
3905 goto err_out;
3906
Johannes Berg2a519312009-02-10 21:25:55 +01003907 err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
3908 if (err)
3909 goto err_out;
3910
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04003911 err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
3912 if (err)
3913 goto err_out;
3914
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003915 err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
3916 if (err)
3917 goto err_out;
3918
Johannes Berg55682962007-09-20 13:09:35 -04003919 return 0;
3920 err_out:
3921 genl_unregister_family(&nl80211_fam);
3922 return err;
3923}
3924
3925void nl80211_exit(void)
3926{
3927 genl_unregister_family(&nl80211_fam);
3928}