blob: 3b21b3e89e9646d444949046f39de869e798ad92 [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 },
Johannes Berg55682962007-09-20 13:09:35 -0400124};
125
Johannes Bergf4a11bb2009-03-27 12:40:28 +0100126/* IE validation */
127static bool is_valid_ie_attr(const struct nlattr *attr)
128{
129 const u8 *pos;
130 int len;
131
132 if (!attr)
133 return true;
134
135 pos = nla_data(attr);
136 len = nla_len(attr);
137
138 while (len) {
139 u8 elemlen;
140
141 if (len < 2)
142 return false;
143 len -= 2;
144
145 elemlen = pos[1];
146 if (elemlen > len)
147 return false;
148
149 len -= elemlen;
150 pos += 2 + elemlen;
151 }
152
153 return true;
154}
155
Johannes Berg55682962007-09-20 13:09:35 -0400156/* message building helper */
157static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
158 int flags, u8 cmd)
159{
160 /* since there is no private header just add the generic one */
161 return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
162}
163
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400164static int nl80211_msg_put_channel(struct sk_buff *msg,
165 struct ieee80211_channel *chan)
166{
167 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
168 chan->center_freq);
169
170 if (chan->flags & IEEE80211_CHAN_DISABLED)
171 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
172 if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
173 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
174 if (chan->flags & IEEE80211_CHAN_NO_IBSS)
175 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
176 if (chan->flags & IEEE80211_CHAN_RADAR)
177 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
178
179 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
180 DBM_TO_MBM(chan->max_power));
181
182 return 0;
183
184 nla_put_failure:
185 return -ENOBUFS;
186}
187
Johannes Berg55682962007-09-20 13:09:35 -0400188/* netlink command implementations */
189
190static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
191 struct cfg80211_registered_device *dev)
192{
193 void *hdr;
Johannes Bergee688b002008-01-24 19:38:39 +0100194 struct nlattr *nl_bands, *nl_band;
195 struct nlattr *nl_freqs, *nl_freq;
196 struct nlattr *nl_rates, *nl_rate;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700197 struct nlattr *nl_modes;
Johannes Berg8fdc6212009-03-14 09:34:01 +0100198 struct nlattr *nl_cmds;
Johannes Bergee688b002008-01-24 19:38:39 +0100199 enum ieee80211_band band;
200 struct ieee80211_channel *chan;
201 struct ieee80211_rate *rate;
202 int i;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700203 u16 ifmodes = dev->wiphy.interface_modes;
Johannes Berg55682962007-09-20 13:09:35 -0400204
205 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
206 if (!hdr)
207 return -1;
208
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -0500209 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400210 NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200211
212 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
213 dev->wiphy.retry_short);
214 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
215 dev->wiphy.retry_long);
216 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
217 dev->wiphy.frag_threshold);
218 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
219 dev->wiphy.rts_threshold);
220
Johannes Berg2a519312009-02-10 21:25:55 +0100221 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
222 dev->wiphy.max_scan_ssids);
Johannes Berg18a83652009-03-31 12:12:05 +0200223 NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
224 dev->wiphy.max_scan_ie_len);
Johannes Bergee688b002008-01-24 19:38:39 +0100225
Johannes Berg25e47c182009-04-02 20:14:06 +0200226 NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
227 sizeof(u32) * dev->wiphy.n_cipher_suites,
228 dev->wiphy.cipher_suites);
229
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700230 nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES);
231 if (!nl_modes)
232 goto nla_put_failure;
233
234 i = 0;
235 while (ifmodes) {
236 if (ifmodes & 1)
237 NLA_PUT_FLAG(msg, i);
238 ifmodes >>= 1;
239 i++;
240 }
241
242 nla_nest_end(msg, nl_modes);
243
Johannes Bergee688b002008-01-24 19:38:39 +0100244 nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
245 if (!nl_bands)
246 goto nla_put_failure;
247
248 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
249 if (!dev->wiphy.bands[band])
250 continue;
251
252 nl_band = nla_nest_start(msg, band);
253 if (!nl_band)
254 goto nla_put_failure;
255
Johannes Bergd51626d2008-10-09 12:20:13 +0200256 /* add HT info */
257 if (dev->wiphy.bands[band]->ht_cap.ht_supported) {
258 NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET,
259 sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
260 &dev->wiphy.bands[band]->ht_cap.mcs);
261 NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA,
262 dev->wiphy.bands[band]->ht_cap.cap);
263 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
264 dev->wiphy.bands[band]->ht_cap.ampdu_factor);
265 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
266 dev->wiphy.bands[band]->ht_cap.ampdu_density);
267 }
268
Johannes Bergee688b002008-01-24 19:38:39 +0100269 /* add frequencies */
270 nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
271 if (!nl_freqs)
272 goto nla_put_failure;
273
274 for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
275 nl_freq = nla_nest_start(msg, i);
276 if (!nl_freq)
277 goto nla_put_failure;
278
279 chan = &dev->wiphy.bands[band]->channels[i];
Johannes Bergee688b002008-01-24 19:38:39 +0100280
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400281 if (nl80211_msg_put_channel(msg, chan))
282 goto nla_put_failure;
Jouni Malinene2f367f262008-11-21 19:01:30 +0200283
Johannes Bergee688b002008-01-24 19:38:39 +0100284 nla_nest_end(msg, nl_freq);
285 }
286
287 nla_nest_end(msg, nl_freqs);
288
289 /* add bitrates */
290 nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
291 if (!nl_rates)
292 goto nla_put_failure;
293
294 for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
295 nl_rate = nla_nest_start(msg, i);
296 if (!nl_rate)
297 goto nla_put_failure;
298
299 rate = &dev->wiphy.bands[band]->bitrates[i];
300 NLA_PUT_U32(msg, NL80211_BITRATE_ATTR_RATE,
301 rate->bitrate);
302 if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
303 NLA_PUT_FLAG(msg,
304 NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE);
305
306 nla_nest_end(msg, nl_rate);
307 }
308
309 nla_nest_end(msg, nl_rates);
310
311 nla_nest_end(msg, nl_band);
312 }
313 nla_nest_end(msg, nl_bands);
314
Johannes Berg8fdc6212009-03-14 09:34:01 +0100315 nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
316 if (!nl_cmds)
317 goto nla_put_failure;
318
319 i = 0;
320#define CMD(op, n) \
321 do { \
322 if (dev->ops->op) { \
323 i++; \
324 NLA_PUT_U32(msg, i, NL80211_CMD_ ## n); \
325 } \
326 } while (0)
327
328 CMD(add_virtual_intf, NEW_INTERFACE);
329 CMD(change_virtual_intf, SET_INTERFACE);
330 CMD(add_key, NEW_KEY);
331 CMD(add_beacon, NEW_BEACON);
332 CMD(add_station, NEW_STATION);
333 CMD(add_mpath, NEW_MPATH);
334 CMD(set_mesh_params, SET_MESH_PARAMS);
335 CMD(change_bss, SET_BSS);
Jouni Malinen636a5d32009-03-19 13:39:22 +0200336 CMD(auth, AUTHENTICATE);
337 CMD(assoc, ASSOCIATE);
338 CMD(deauth, DEAUTHENTICATE);
339 CMD(disassoc, DISASSOCIATE);
Johannes Berg04a773a2009-04-19 21:24:32 +0200340 CMD(join_ibss, JOIN_IBSS);
Johannes Berg8fdc6212009-03-14 09:34:01 +0100341
342#undef CMD
343 nla_nest_end(msg, nl_cmds);
344
Johannes Berg55682962007-09-20 13:09:35 -0400345 return genlmsg_end(msg, hdr);
346
347 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700348 genlmsg_cancel(msg, hdr);
349 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400350}
351
352static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
353{
354 int idx = 0;
355 int start = cb->args[0];
356 struct cfg80211_registered_device *dev;
357
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500358 mutex_lock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400359 list_for_each_entry(dev, &cfg80211_drv_list, list) {
Julius Volzb4637272008-07-08 14:02:19 +0200360 if (++idx <= start)
Johannes Berg55682962007-09-20 13:09:35 -0400361 continue;
362 if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
363 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Julius Volzb4637272008-07-08 14:02:19 +0200364 dev) < 0) {
365 idx--;
Johannes Berg55682962007-09-20 13:09:35 -0400366 break;
Julius Volzb4637272008-07-08 14:02:19 +0200367 }
Johannes Berg55682962007-09-20 13:09:35 -0400368 }
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500369 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400370
371 cb->args[0] = idx;
372
373 return skb->len;
374}
375
376static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
377{
378 struct sk_buff *msg;
379 struct cfg80211_registered_device *dev;
380
381 dev = cfg80211_get_dev_from_info(info);
382 if (IS_ERR(dev))
383 return PTR_ERR(dev);
384
385 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
386 if (!msg)
387 goto out_err;
388
389 if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0)
390 goto out_free;
391
392 cfg80211_put_dev(dev);
393
394 return genlmsg_unicast(msg, info->snd_pid);
395
396 out_free:
397 nlmsg_free(msg);
398 out_err:
399 cfg80211_put_dev(dev);
400 return -ENOBUFS;
401}
402
Jouni Malinen31888482008-10-30 16:59:24 +0200403static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
404 [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
405 [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
406 [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
407 [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
408 [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
409};
410
411static int parse_txq_params(struct nlattr *tb[],
412 struct ieee80211_txq_params *txq_params)
413{
414 if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] ||
415 !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
416 !tb[NL80211_TXQ_ATTR_AIFS])
417 return -EINVAL;
418
419 txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]);
420 txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
421 txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
422 txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
423 txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
424
425 return 0;
426}
427
Johannes Berg55682962007-09-20 13:09:35 -0400428static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
429{
430 struct cfg80211_registered_device *rdev;
Jouni Malinen31888482008-10-30 16:59:24 +0200431 int result = 0, rem_txq_params = 0;
432 struct nlattr *nl_txq_params;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200433 u32 changed;
434 u8 retry_short = 0, retry_long = 0;
435 u32 frag_threshold = 0, rts_threshold = 0;
Johannes Berg55682962007-09-20 13:09:35 -0400436
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100437 rtnl_lock();
Johannes Berg55682962007-09-20 13:09:35 -0400438
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100439 mutex_lock(&cfg80211_mutex);
440
441 rdev = __cfg80211_drv_from_info(info);
442 if (IS_ERR(rdev)) {
443 result = PTR_ERR(rdev);
444 goto unlock;
445 }
446
447 mutex_lock(&rdev->mtx);
448
449 if (info->attrs[NL80211_ATTR_WIPHY_NAME])
Jouni Malinen31888482008-10-30 16:59:24 +0200450 result = cfg80211_dev_rename(
451 rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100452
453 mutex_unlock(&cfg80211_mutex);
454
455 if (result)
456 goto bad_res;
Johannes Berg55682962007-09-20 13:09:35 -0400457
Jouni Malinen31888482008-10-30 16:59:24 +0200458 if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
459 struct ieee80211_txq_params txq_params;
460 struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
461
462 if (!rdev->ops->set_txq_params) {
463 result = -EOPNOTSUPP;
464 goto bad_res;
465 }
466
467 nla_for_each_nested(nl_txq_params,
468 info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
469 rem_txq_params) {
470 nla_parse(tb, NL80211_TXQ_ATTR_MAX,
471 nla_data(nl_txq_params),
472 nla_len(nl_txq_params),
473 txq_params_policy);
474 result = parse_txq_params(tb, &txq_params);
475 if (result)
476 goto bad_res;
477
478 result = rdev->ops->set_txq_params(&rdev->wiphy,
479 &txq_params);
480 if (result)
481 goto bad_res;
482 }
483 }
484
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200485 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Sujith094d05d2008-12-12 11:57:43 +0530486 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200487 struct ieee80211_channel *chan;
Johannes Berg306d6112008-12-08 12:39:04 +0100488 struct ieee80211_sta_ht_cap *ht_cap;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200489 u32 freq, sec_freq;
490
491 if (!rdev->ops->set_channel) {
492 result = -EOPNOTSUPP;
493 goto bad_res;
494 }
495
Johannes Berg306d6112008-12-08 12:39:04 +0100496 result = -EINVAL;
497
Sujith094d05d2008-12-12 11:57:43 +0530498 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
499 channel_type = nla_get_u32(info->attrs[
500 NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
501 if (channel_type != NL80211_CHAN_NO_HT &&
502 channel_type != NL80211_CHAN_HT20 &&
503 channel_type != NL80211_CHAN_HT40PLUS &&
504 channel_type != NL80211_CHAN_HT40MINUS)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200505 goto bad_res;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200506 }
507
508 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
509 chan = ieee80211_get_channel(&rdev->wiphy, freq);
Johannes Berg306d6112008-12-08 12:39:04 +0100510
511 /* Primary channel not allowed */
512 if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200513 goto bad_res;
Johannes Berg306d6112008-12-08 12:39:04 +0100514
Sujith094d05d2008-12-12 11:57:43 +0530515 if (channel_type == NL80211_CHAN_HT40MINUS)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200516 sec_freq = freq - 20;
Sujith094d05d2008-12-12 11:57:43 +0530517 else if (channel_type == NL80211_CHAN_HT40PLUS)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200518 sec_freq = freq + 20;
519 else
520 sec_freq = 0;
521
Johannes Berg306d6112008-12-08 12:39:04 +0100522 ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
523
524 /* no HT capabilities */
Sujith094d05d2008-12-12 11:57:43 +0530525 if (channel_type != NL80211_CHAN_NO_HT &&
Johannes Berg306d6112008-12-08 12:39:04 +0100526 !ht_cap->ht_supported)
527 goto bad_res;
528
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200529 if (sec_freq) {
530 struct ieee80211_channel *schan;
Johannes Berg306d6112008-12-08 12:39:04 +0100531
532 /* no 40 MHz capabilities */
533 if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
534 (ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200535 goto bad_res;
Johannes Berg306d6112008-12-08 12:39:04 +0100536
537 schan = ieee80211_get_channel(&rdev->wiphy, sec_freq);
538
539 /* Secondary channel not allowed */
540 if (!schan || schan->flags & IEEE80211_CHAN_DISABLED)
541 goto bad_res;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200542 }
543
544 result = rdev->ops->set_channel(&rdev->wiphy, chan,
Sujith094d05d2008-12-12 11:57:43 +0530545 channel_type);
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200546 if (result)
547 goto bad_res;
548 }
549
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200550 changed = 0;
551
552 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {
553 retry_short = nla_get_u8(
554 info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]);
555 if (retry_short == 0) {
556 result = -EINVAL;
557 goto bad_res;
558 }
559 changed |= WIPHY_PARAM_RETRY_SHORT;
560 }
561
562 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {
563 retry_long = nla_get_u8(
564 info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]);
565 if (retry_long == 0) {
566 result = -EINVAL;
567 goto bad_res;
568 }
569 changed |= WIPHY_PARAM_RETRY_LONG;
570 }
571
572 if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
573 frag_threshold = nla_get_u32(
574 info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
575 if (frag_threshold < 256) {
576 result = -EINVAL;
577 goto bad_res;
578 }
579 if (frag_threshold != (u32) -1) {
580 /*
581 * Fragments (apart from the last one) are required to
582 * have even length. Make the fragmentation code
583 * simpler by stripping LSB should someone try to use
584 * odd threshold value.
585 */
586 frag_threshold &= ~0x1;
587 }
588 changed |= WIPHY_PARAM_FRAG_THRESHOLD;
589 }
590
591 if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
592 rts_threshold = nla_get_u32(
593 info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
594 changed |= WIPHY_PARAM_RTS_THRESHOLD;
595 }
596
597 if (changed) {
598 u8 old_retry_short, old_retry_long;
599 u32 old_frag_threshold, old_rts_threshold;
600
601 if (!rdev->ops->set_wiphy_params) {
602 result = -EOPNOTSUPP;
603 goto bad_res;
604 }
605
606 old_retry_short = rdev->wiphy.retry_short;
607 old_retry_long = rdev->wiphy.retry_long;
608 old_frag_threshold = rdev->wiphy.frag_threshold;
609 old_rts_threshold = rdev->wiphy.rts_threshold;
610
611 if (changed & WIPHY_PARAM_RETRY_SHORT)
612 rdev->wiphy.retry_short = retry_short;
613 if (changed & WIPHY_PARAM_RETRY_LONG)
614 rdev->wiphy.retry_long = retry_long;
615 if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
616 rdev->wiphy.frag_threshold = frag_threshold;
617 if (changed & WIPHY_PARAM_RTS_THRESHOLD)
618 rdev->wiphy.rts_threshold = rts_threshold;
619
620 result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed);
621 if (result) {
622 rdev->wiphy.retry_short = old_retry_short;
623 rdev->wiphy.retry_long = old_retry_long;
624 rdev->wiphy.frag_threshold = old_frag_threshold;
625 rdev->wiphy.rts_threshold = old_rts_threshold;
626 }
627 }
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200628
Johannes Berg306d6112008-12-08 12:39:04 +0100629 bad_res:
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100630 mutex_unlock(&rdev->mtx);
631 unlock:
632 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400633 return result;
634}
635
636
637static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
Johannes Bergd7264052009-04-19 16:23:20 +0200638 struct cfg80211_registered_device *rdev,
Johannes Berg55682962007-09-20 13:09:35 -0400639 struct net_device *dev)
640{
641 void *hdr;
642
643 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
644 if (!hdr)
645 return -1;
646
647 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
Johannes Bergd7264052009-04-19 16:23:20 +0200648 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400649 NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
Johannes Berg60719ff2008-09-16 14:55:09 +0200650 NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype);
Johannes Berg55682962007-09-20 13:09:35 -0400651 return genlmsg_end(msg, hdr);
652
653 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700654 genlmsg_cancel(msg, hdr);
655 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400656}
657
658static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
659{
660 int wp_idx = 0;
661 int if_idx = 0;
662 int wp_start = cb->args[0];
663 int if_start = cb->args[1];
664 struct cfg80211_registered_device *dev;
665 struct wireless_dev *wdev;
666
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500667 mutex_lock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400668 list_for_each_entry(dev, &cfg80211_drv_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +0200669 if (wp_idx < wp_start) {
670 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400671 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200672 }
Johannes Berg55682962007-09-20 13:09:35 -0400673 if_idx = 0;
674
675 mutex_lock(&dev->devlist_mtx);
676 list_for_each_entry(wdev, &dev->netdev_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +0200677 if (if_idx < if_start) {
678 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400679 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200680 }
Johannes Berg55682962007-09-20 13:09:35 -0400681 if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
682 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Bergd7264052009-04-19 16:23:20 +0200683 dev, wdev->netdev) < 0) {
Johannes Bergbba95fe2008-07-29 13:22:51 +0200684 mutex_unlock(&dev->devlist_mtx);
685 goto out;
686 }
687 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400688 }
689 mutex_unlock(&dev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +0200690
691 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400692 }
Johannes Bergbba95fe2008-07-29 13:22:51 +0200693 out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500694 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400695
696 cb->args[0] = wp_idx;
697 cb->args[1] = if_idx;
698
699 return skb->len;
700}
701
702static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
703{
704 struct sk_buff *msg;
705 struct cfg80211_registered_device *dev;
706 struct net_device *netdev;
707 int err;
708
Johannes Bergbba95fe2008-07-29 13:22:51 +0200709 err = get_drv_dev_by_info_ifindex(info->attrs, &dev, &netdev);
Johannes Berg55682962007-09-20 13:09:35 -0400710 if (err)
711 return err;
712
713 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
714 if (!msg)
715 goto out_err;
716
Johannes Bergd7264052009-04-19 16:23:20 +0200717 if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0,
718 dev, netdev) < 0)
Johannes Berg55682962007-09-20 13:09:35 -0400719 goto out_free;
720
721 dev_put(netdev);
722 cfg80211_put_dev(dev);
723
724 return genlmsg_unicast(msg, info->snd_pid);
725
726 out_free:
727 nlmsg_free(msg);
728 out_err:
729 dev_put(netdev);
730 cfg80211_put_dev(dev);
731 return -ENOBUFS;
732}
733
Michael Wu66f7ac52008-01-31 19:48:22 +0100734static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
735 [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
736 [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
737 [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
738 [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
739 [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
740};
741
742static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
743{
744 struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
745 int flag;
746
747 *mntrflags = 0;
748
749 if (!nla)
750 return -EINVAL;
751
752 if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX,
753 nla, mntr_flags_policy))
754 return -EINVAL;
755
756 for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
757 if (flags[flag])
758 *mntrflags |= (1<<flag);
759
760 return 0;
761}
762
Johannes Berg55682962007-09-20 13:09:35 -0400763static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
764{
765 struct cfg80211_registered_device *drv;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100766 struct vif_params params;
Johannes Berg55682962007-09-20 13:09:35 -0400767 int err, ifindex;
Johannes Berg04a773a2009-04-19 21:24:32 +0200768 enum nl80211_iftype otype, ntype;
Johannes Berg55682962007-09-20 13:09:35 -0400769 struct net_device *dev;
Johannes Berg92ffe052008-09-16 20:39:36 +0200770 u32 _flags, *flags = NULL;
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100771 bool change = false;
Johannes Berg55682962007-09-20 13:09:35 -0400772
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100773 memset(&params, 0, sizeof(params));
774
Johannes Berg3b858752009-03-12 09:55:09 +0100775 rtnl_lock();
776
Johannes Bergbba95fe2008-07-29 13:22:51 +0200777 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg55682962007-09-20 13:09:35 -0400778 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +0100779 goto unlock_rtnl;
780
Johannes Berg55682962007-09-20 13:09:35 -0400781 ifindex = dev->ifindex;
Johannes Berg04a773a2009-04-19 21:24:32 +0200782 otype = ntype = dev->ieee80211_ptr->iftype;
Johannes Berg55682962007-09-20 13:09:35 -0400783 dev_put(dev);
784
Johannes Berg723b0382008-09-16 20:22:09 +0200785 if (info->attrs[NL80211_ATTR_IFTYPE]) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100786 ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
Johannes Berg04a773a2009-04-19 21:24:32 +0200787 if (otype != ntype)
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100788 change = true;
Johannes Berg04a773a2009-04-19 21:24:32 +0200789 if (ntype > NL80211_IFTYPE_MAX) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100790 err = -EINVAL;
Johannes Berg723b0382008-09-16 20:22:09 +0200791 goto unlock;
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100792 }
Johannes Berg723b0382008-09-16 20:22:09 +0200793 }
794
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700795 if (!drv->ops->change_virtual_intf ||
Johannes Berg04a773a2009-04-19 21:24:32 +0200796 !(drv->wiphy.interface_modes & (1 << ntype))) {
Johannes Berg55682962007-09-20 13:09:35 -0400797 err = -EOPNOTSUPP;
798 goto unlock;
799 }
800
Johannes Berg92ffe052008-09-16 20:39:36 +0200801 if (info->attrs[NL80211_ATTR_MESH_ID]) {
Johannes Berg04a773a2009-04-19 21:24:32 +0200802 if (ntype != NL80211_IFTYPE_MESH_POINT) {
Johannes Berg92ffe052008-09-16 20:39:36 +0200803 err = -EINVAL;
804 goto unlock;
805 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100806 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
807 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100808 change = true;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100809 }
810
Johannes Berg92ffe052008-09-16 20:39:36 +0200811 if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
Johannes Berg04a773a2009-04-19 21:24:32 +0200812 if (ntype != NL80211_IFTYPE_MONITOR) {
Johannes Berg92ffe052008-09-16 20:39:36 +0200813 err = -EINVAL;
814 goto unlock;
815 }
816 err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
817 &_flags);
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100818 if (err)
819 goto unlock;
820
821 flags = &_flags;
822 change = true;
Johannes Berg92ffe052008-09-16 20:39:36 +0200823 }
Johannes Berg3b858752009-03-12 09:55:09 +0100824
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100825 if (change)
826 err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex,
Johannes Berg04a773a2009-04-19 21:24:32 +0200827 ntype, flags, &params);
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100828 else
829 err = 0;
Johannes Berg60719ff2008-09-16 14:55:09 +0200830
831 dev = __dev_get_by_index(&init_net, ifindex);
Johannes Berg04a773a2009-04-19 21:24:32 +0200832 WARN_ON(!dev || (!err && dev->ieee80211_ptr->iftype != ntype));
833
834 if (dev && !err && (ntype != otype)) {
835 if (otype == NL80211_IFTYPE_ADHOC)
Johannes Berg9d308422009-04-20 18:43:46 +0200836 cfg80211_clear_ibss(dev, false);
Johannes Berg04a773a2009-04-19 21:24:32 +0200837 }
Johannes Berg60719ff2008-09-16 14:55:09 +0200838
Johannes Berg55682962007-09-20 13:09:35 -0400839 unlock:
840 cfg80211_put_dev(drv);
Johannes Berg3b858752009-03-12 09:55:09 +0100841 unlock_rtnl:
842 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400843 return err;
844}
845
846static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
847{
848 struct cfg80211_registered_device *drv;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100849 struct vif_params params;
Johannes Berg55682962007-09-20 13:09:35 -0400850 int err;
851 enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
Michael Wu66f7ac52008-01-31 19:48:22 +0100852 u32 flags;
Johannes Berg55682962007-09-20 13:09:35 -0400853
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100854 memset(&params, 0, sizeof(params));
855
Johannes Berg55682962007-09-20 13:09:35 -0400856 if (!info->attrs[NL80211_ATTR_IFNAME])
857 return -EINVAL;
858
859 if (info->attrs[NL80211_ATTR_IFTYPE]) {
860 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
861 if (type > NL80211_IFTYPE_MAX)
862 return -EINVAL;
863 }
864
Johannes Berg3b858752009-03-12 09:55:09 +0100865 rtnl_lock();
866
Johannes Berg55682962007-09-20 13:09:35 -0400867 drv = cfg80211_get_dev_from_info(info);
Johannes Berg3b858752009-03-12 09:55:09 +0100868 if (IS_ERR(drv)) {
869 err = PTR_ERR(drv);
870 goto unlock_rtnl;
871 }
Johannes Berg55682962007-09-20 13:09:35 -0400872
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700873 if (!drv->ops->add_virtual_intf ||
874 !(drv->wiphy.interface_modes & (1 << type))) {
Johannes Berg55682962007-09-20 13:09:35 -0400875 err = -EOPNOTSUPP;
876 goto unlock;
877 }
878
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100879 if (type == NL80211_IFTYPE_MESH_POINT &&
880 info->attrs[NL80211_ATTR_MESH_ID]) {
881 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
882 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
883 }
884
Michael Wu66f7ac52008-01-31 19:48:22 +0100885 err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
886 info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
887 &flags);
Johannes Berg55682962007-09-20 13:09:35 -0400888 err = drv->ops->add_virtual_intf(&drv->wiphy,
Michael Wu66f7ac52008-01-31 19:48:22 +0100889 nla_data(info->attrs[NL80211_ATTR_IFNAME]),
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100890 type, err ? NULL : &flags, &params);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100891
Johannes Berg55682962007-09-20 13:09:35 -0400892 unlock:
893 cfg80211_put_dev(drv);
Johannes Berg3b858752009-03-12 09:55:09 +0100894 unlock_rtnl:
895 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400896 return err;
897}
898
899static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
900{
901 struct cfg80211_registered_device *drv;
902 int ifindex, err;
903 struct net_device *dev;
904
Johannes Berg3b858752009-03-12 09:55:09 +0100905 rtnl_lock();
906
Johannes Bergbba95fe2008-07-29 13:22:51 +0200907 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg55682962007-09-20 13:09:35 -0400908 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +0100909 goto unlock_rtnl;
Johannes Berg55682962007-09-20 13:09:35 -0400910 ifindex = dev->ifindex;
911 dev_put(dev);
912
913 if (!drv->ops->del_virtual_intf) {
914 err = -EOPNOTSUPP;
915 goto out;
916 }
917
Johannes Berg55682962007-09-20 13:09:35 -0400918 err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex);
Johannes Berg55682962007-09-20 13:09:35 -0400919
920 out:
921 cfg80211_put_dev(drv);
Johannes Berg3b858752009-03-12 09:55:09 +0100922 unlock_rtnl:
923 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400924 return err;
925}
926
Johannes Berg41ade002007-12-19 02:03:29 +0100927struct get_key_cookie {
928 struct sk_buff *msg;
929 int error;
930};
931
932static void get_key_callback(void *c, struct key_params *params)
933{
934 struct get_key_cookie *cookie = c;
935
936 if (params->key)
937 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA,
938 params->key_len, params->key);
939
940 if (params->seq)
941 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ,
942 params->seq_len, params->seq);
943
944 if (params->cipher)
945 NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
946 params->cipher);
947
948 return;
949 nla_put_failure:
950 cookie->error = 1;
951}
952
953static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
954{
955 struct cfg80211_registered_device *drv;
956 int err;
957 struct net_device *dev;
958 u8 key_idx = 0;
959 u8 *mac_addr = NULL;
960 struct get_key_cookie cookie = {
961 .error = 0,
962 };
963 void *hdr;
964 struct sk_buff *msg;
965
966 if (info->attrs[NL80211_ATTR_KEY_IDX])
967 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
968
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +0200969 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +0100970 return -EINVAL;
971
972 if (info->attrs[NL80211_ATTR_MAC])
973 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
974
Johannes Berg3b858752009-03-12 09:55:09 +0100975 rtnl_lock();
976
Johannes Bergbba95fe2008-07-29 13:22:51 +0200977 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +0100978 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +0100979 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +0100980
981 if (!drv->ops->get_key) {
982 err = -EOPNOTSUPP;
983 goto out;
984 }
985
986 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
987 if (!msg) {
988 err = -ENOMEM;
989 goto out;
990 }
991
992 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
993 NL80211_CMD_NEW_KEY);
994
995 if (IS_ERR(hdr)) {
996 err = PTR_ERR(hdr);
997 goto out;
998 }
999
1000 cookie.msg = msg;
1001
1002 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1003 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
1004 if (mac_addr)
1005 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1006
Johannes Berg41ade002007-12-19 02:03:29 +01001007 err = drv->ops->get_key(&drv->wiphy, dev, key_idx, mac_addr,
1008 &cookie, get_key_callback);
Johannes Berg41ade002007-12-19 02:03:29 +01001009
1010 if (err)
1011 goto out;
1012
1013 if (cookie.error)
1014 goto nla_put_failure;
1015
1016 genlmsg_end(msg, hdr);
1017 err = genlmsg_unicast(msg, info->snd_pid);
1018 goto out;
1019
1020 nla_put_failure:
1021 err = -ENOBUFS;
1022 nlmsg_free(msg);
1023 out:
1024 cfg80211_put_dev(drv);
1025 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001026 unlock_rtnl:
1027 rtnl_unlock();
1028
Johannes Berg41ade002007-12-19 02:03:29 +01001029 return err;
1030}
1031
1032static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
1033{
1034 struct cfg80211_registered_device *drv;
1035 int err;
1036 struct net_device *dev;
1037 u8 key_idx;
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001038 int (*func)(struct wiphy *wiphy, struct net_device *netdev,
1039 u8 key_index);
Johannes Berg41ade002007-12-19 02:03:29 +01001040
1041 if (!info->attrs[NL80211_ATTR_KEY_IDX])
1042 return -EINVAL;
1043
1044 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1045
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001046 if (info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) {
1047 if (key_idx < 4 || key_idx > 5)
1048 return -EINVAL;
1049 } else if (key_idx > 3)
Johannes Berg41ade002007-12-19 02:03:29 +01001050 return -EINVAL;
1051
1052 /* currently only support setting default key */
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001053 if (!info->attrs[NL80211_ATTR_KEY_DEFAULT] &&
1054 !info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT])
Johannes Berg41ade002007-12-19 02:03:29 +01001055 return -EINVAL;
1056
Johannes Berg3b858752009-03-12 09:55:09 +01001057 rtnl_lock();
1058
Johannes Bergbba95fe2008-07-29 13:22:51 +02001059 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001060 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001061 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001062
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001063 if (info->attrs[NL80211_ATTR_KEY_DEFAULT])
1064 func = drv->ops->set_default_key;
1065 else
1066 func = drv->ops->set_default_mgmt_key;
1067
1068 if (!func) {
Johannes Berg41ade002007-12-19 02:03:29 +01001069 err = -EOPNOTSUPP;
1070 goto out;
1071 }
1072
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001073 err = func(&drv->wiphy, dev, key_idx);
Johannes Berg41ade002007-12-19 02:03:29 +01001074
1075 out:
1076 cfg80211_put_dev(drv);
1077 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001078
1079 unlock_rtnl:
1080 rtnl_unlock();
1081
Johannes Berg41ade002007-12-19 02:03:29 +01001082 return err;
1083}
1084
1085static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
1086{
1087 struct cfg80211_registered_device *drv;
Johannes Berg25e47c182009-04-02 20:14:06 +02001088 int err, i;
Johannes Berg41ade002007-12-19 02:03:29 +01001089 struct net_device *dev;
1090 struct key_params params;
1091 u8 key_idx = 0;
1092 u8 *mac_addr = NULL;
1093
1094 memset(&params, 0, sizeof(params));
1095
1096 if (!info->attrs[NL80211_ATTR_KEY_CIPHER])
1097 return -EINVAL;
1098
1099 if (info->attrs[NL80211_ATTR_KEY_DATA]) {
1100 params.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
1101 params.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
1102 }
1103
1104 if (info->attrs[NL80211_ATTR_KEY_IDX])
1105 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1106
1107 params.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
1108
1109 if (info->attrs[NL80211_ATTR_MAC])
1110 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1111
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001112 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +01001113 return -EINVAL;
1114
1115 /*
1116 * Disallow pairwise keys with non-zero index unless it's WEP
1117 * (because current deployments use pairwise WEP keys with
1118 * non-zero indizes but 802.11i clearly specifies to use zero)
1119 */
1120 if (mac_addr && key_idx &&
1121 params.cipher != WLAN_CIPHER_SUITE_WEP40 &&
1122 params.cipher != WLAN_CIPHER_SUITE_WEP104)
1123 return -EINVAL;
1124
1125 /* TODO: add definitions for the lengths to linux/ieee80211.h */
1126 switch (params.cipher) {
1127 case WLAN_CIPHER_SUITE_WEP40:
1128 if (params.key_len != 5)
1129 return -EINVAL;
1130 break;
1131 case WLAN_CIPHER_SUITE_TKIP:
1132 if (params.key_len != 32)
1133 return -EINVAL;
1134 break;
1135 case WLAN_CIPHER_SUITE_CCMP:
1136 if (params.key_len != 16)
1137 return -EINVAL;
1138 break;
1139 case WLAN_CIPHER_SUITE_WEP104:
1140 if (params.key_len != 13)
1141 return -EINVAL;
1142 break;
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001143 case WLAN_CIPHER_SUITE_AES_CMAC:
1144 if (params.key_len != 16)
1145 return -EINVAL;
1146 break;
Johannes Berg41ade002007-12-19 02:03:29 +01001147 default:
1148 return -EINVAL;
1149 }
1150
Johannes Berg3b858752009-03-12 09:55:09 +01001151 rtnl_lock();
1152
Johannes Bergbba95fe2008-07-29 13:22:51 +02001153 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001154 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001155 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001156
Johannes Berg25e47c182009-04-02 20:14:06 +02001157 for (i = 0; i < drv->wiphy.n_cipher_suites; i++)
1158 if (params.cipher == drv->wiphy.cipher_suites[i])
1159 break;
1160 if (i == drv->wiphy.n_cipher_suites) {
1161 err = -EINVAL;
1162 goto out;
1163 }
1164
Johannes Berg41ade002007-12-19 02:03:29 +01001165 if (!drv->ops->add_key) {
1166 err = -EOPNOTSUPP;
1167 goto out;
1168 }
1169
Johannes Berg41ade002007-12-19 02:03:29 +01001170 err = drv->ops->add_key(&drv->wiphy, dev, key_idx, mac_addr, &params);
Johannes Berg41ade002007-12-19 02:03:29 +01001171
1172 out:
1173 cfg80211_put_dev(drv);
1174 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001175 unlock_rtnl:
1176 rtnl_unlock();
1177
Johannes Berg41ade002007-12-19 02:03:29 +01001178 return err;
1179}
1180
1181static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
1182{
1183 struct cfg80211_registered_device *drv;
1184 int err;
1185 struct net_device *dev;
1186 u8 key_idx = 0;
1187 u8 *mac_addr = NULL;
1188
1189 if (info->attrs[NL80211_ATTR_KEY_IDX])
1190 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1191
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001192 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +01001193 return -EINVAL;
1194
1195 if (info->attrs[NL80211_ATTR_MAC])
1196 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1197
Johannes Berg3b858752009-03-12 09:55:09 +01001198 rtnl_lock();
1199
Johannes Bergbba95fe2008-07-29 13:22:51 +02001200 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001201 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001202 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001203
1204 if (!drv->ops->del_key) {
1205 err = -EOPNOTSUPP;
1206 goto out;
1207 }
1208
Johannes Berg41ade002007-12-19 02:03:29 +01001209 err = drv->ops->del_key(&drv->wiphy, dev, key_idx, mac_addr);
Johannes Berg41ade002007-12-19 02:03:29 +01001210
1211 out:
1212 cfg80211_put_dev(drv);
1213 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001214
1215 unlock_rtnl:
1216 rtnl_unlock();
1217
Johannes Berg41ade002007-12-19 02:03:29 +01001218 return err;
1219}
1220
Johannes Berged1b6cc2007-12-19 02:03:32 +01001221static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
1222{
1223 int (*call)(struct wiphy *wiphy, struct net_device *dev,
1224 struct beacon_parameters *info);
1225 struct cfg80211_registered_device *drv;
1226 int err;
1227 struct net_device *dev;
1228 struct beacon_parameters params;
1229 int haveinfo = 0;
1230
Johannes Bergf4a11bb2009-03-27 12:40:28 +01001231 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]))
1232 return -EINVAL;
1233
Johannes Berg3b858752009-03-12 09:55:09 +01001234 rtnl_lock();
1235
Johannes Bergbba95fe2008-07-29 13:22:51 +02001236 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001237 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001238 goto unlock_rtnl;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001239
Jouni Malineneec60b02009-03-20 21:21:19 +02001240 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
1241 err = -EOPNOTSUPP;
1242 goto out;
1243 }
1244
Johannes Berged1b6cc2007-12-19 02:03:32 +01001245 switch (info->genlhdr->cmd) {
1246 case NL80211_CMD_NEW_BEACON:
1247 /* these are required for NEW_BEACON */
1248 if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
1249 !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
1250 !info->attrs[NL80211_ATTR_BEACON_HEAD]) {
1251 err = -EINVAL;
1252 goto out;
1253 }
1254
1255 call = drv->ops->add_beacon;
1256 break;
1257 case NL80211_CMD_SET_BEACON:
1258 call = drv->ops->set_beacon;
1259 break;
1260 default:
1261 WARN_ON(1);
1262 err = -EOPNOTSUPP;
1263 goto out;
1264 }
1265
1266 if (!call) {
1267 err = -EOPNOTSUPP;
1268 goto out;
1269 }
1270
1271 memset(&params, 0, sizeof(params));
1272
1273 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
1274 params.interval =
1275 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
1276 haveinfo = 1;
1277 }
1278
1279 if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) {
1280 params.dtim_period =
1281 nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
1282 haveinfo = 1;
1283 }
1284
1285 if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
1286 params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
1287 params.head_len =
1288 nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
1289 haveinfo = 1;
1290 }
1291
1292 if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
1293 params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
1294 params.tail_len =
1295 nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
1296 haveinfo = 1;
1297 }
1298
1299 if (!haveinfo) {
1300 err = -EINVAL;
1301 goto out;
1302 }
1303
Johannes Berged1b6cc2007-12-19 02:03:32 +01001304 err = call(&drv->wiphy, dev, &params);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001305
1306 out:
1307 cfg80211_put_dev(drv);
1308 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001309 unlock_rtnl:
1310 rtnl_unlock();
1311
Johannes Berged1b6cc2007-12-19 02:03:32 +01001312 return err;
1313}
1314
1315static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
1316{
1317 struct cfg80211_registered_device *drv;
1318 int err;
1319 struct net_device *dev;
1320
Johannes Berg3b858752009-03-12 09:55:09 +01001321 rtnl_lock();
1322
Johannes Bergbba95fe2008-07-29 13:22:51 +02001323 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001324 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001325 goto unlock_rtnl;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001326
1327 if (!drv->ops->del_beacon) {
1328 err = -EOPNOTSUPP;
1329 goto out;
1330 }
1331
Jouni Malineneec60b02009-03-20 21:21:19 +02001332 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
1333 err = -EOPNOTSUPP;
1334 goto out;
1335 }
Johannes Berged1b6cc2007-12-19 02:03:32 +01001336 err = drv->ops->del_beacon(&drv->wiphy, dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001337
1338 out:
1339 cfg80211_put_dev(drv);
1340 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001341 unlock_rtnl:
1342 rtnl_unlock();
1343
Johannes Berged1b6cc2007-12-19 02:03:32 +01001344 return err;
1345}
1346
Johannes Berg5727ef12007-12-19 02:03:34 +01001347static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
1348 [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
1349 [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
1350 [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
1351};
1352
1353static int parse_station_flags(struct nlattr *nla, u32 *staflags)
1354{
1355 struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
1356 int flag;
1357
1358 *staflags = 0;
1359
1360 if (!nla)
1361 return 0;
1362
1363 if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX,
1364 nla, sta_flags_policy))
1365 return -EINVAL;
1366
1367 *staflags = STATION_FLAG_CHANGED;
1368
1369 for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++)
1370 if (flags[flag])
1371 *staflags |= (1<<flag);
1372
1373 return 0;
1374}
1375
Henning Rogge420e7fa2008-12-11 22:04:19 +01001376static u16 nl80211_calculate_bitrate(struct rate_info *rate)
1377{
1378 int modulation, streams, bitrate;
1379
1380 if (!(rate->flags & RATE_INFO_FLAGS_MCS))
1381 return rate->legacy;
1382
1383 /* the formula below does only work for MCS values smaller than 32 */
1384 if (rate->mcs >= 32)
1385 return 0;
1386
1387 modulation = rate->mcs & 7;
1388 streams = (rate->mcs >> 3) + 1;
1389
1390 bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ?
1391 13500000 : 6500000;
1392
1393 if (modulation < 4)
1394 bitrate *= (modulation + 1);
1395 else if (modulation == 4)
1396 bitrate *= (modulation + 2);
1397 else
1398 bitrate *= (modulation + 3);
1399
1400 bitrate *= streams;
1401
1402 if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
1403 bitrate = (bitrate / 9) * 10;
1404
1405 /* do NOT round down here */
1406 return (bitrate + 50000) / 100000;
1407}
1408
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001409static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
1410 int flags, struct net_device *dev,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001411 u8 *mac_addr, struct station_info *sinfo)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001412{
1413 void *hdr;
Henning Rogge420e7fa2008-12-11 22:04:19 +01001414 struct nlattr *sinfoattr, *txrate;
1415 u16 bitrate;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001416
1417 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
1418 if (!hdr)
1419 return -1;
1420
1421 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1422 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1423
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001424 sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
1425 if (!sinfoattr)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001426 goto nla_put_failure;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001427 if (sinfo->filled & STATION_INFO_INACTIVE_TIME)
1428 NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME,
1429 sinfo->inactive_time);
1430 if (sinfo->filled & STATION_INFO_RX_BYTES)
1431 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES,
1432 sinfo->rx_bytes);
1433 if (sinfo->filled & STATION_INFO_TX_BYTES)
1434 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES,
1435 sinfo->tx_bytes);
1436 if (sinfo->filled & STATION_INFO_LLID)
1437 NLA_PUT_U16(msg, NL80211_STA_INFO_LLID,
1438 sinfo->llid);
1439 if (sinfo->filled & STATION_INFO_PLID)
1440 NLA_PUT_U16(msg, NL80211_STA_INFO_PLID,
1441 sinfo->plid);
1442 if (sinfo->filled & STATION_INFO_PLINK_STATE)
1443 NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
1444 sinfo->plink_state);
Henning Rogge420e7fa2008-12-11 22:04:19 +01001445 if (sinfo->filled & STATION_INFO_SIGNAL)
1446 NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL,
1447 sinfo->signal);
1448 if (sinfo->filled & STATION_INFO_TX_BITRATE) {
1449 txrate = nla_nest_start(msg, NL80211_STA_INFO_TX_BITRATE);
1450 if (!txrate)
1451 goto nla_put_failure;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001452
Henning Rogge420e7fa2008-12-11 22:04:19 +01001453 /* nl80211_calculate_bitrate will return 0 for mcs >= 32 */
1454 bitrate = nl80211_calculate_bitrate(&sinfo->txrate);
1455 if (bitrate > 0)
1456 NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
1457
1458 if (sinfo->txrate.flags & RATE_INFO_FLAGS_MCS)
1459 NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS,
1460 sinfo->txrate.mcs);
1461 if (sinfo->txrate.flags & RATE_INFO_FLAGS_40_MHZ_WIDTH)
1462 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH);
1463 if (sinfo->txrate.flags & RATE_INFO_FLAGS_SHORT_GI)
1464 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI);
1465
1466 nla_nest_end(msg, txrate);
1467 }
Jouni Malinen98c8a60a2009-02-17 13:24:57 +02001468 if (sinfo->filled & STATION_INFO_RX_PACKETS)
1469 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS,
1470 sinfo->rx_packets);
1471 if (sinfo->filled & STATION_INFO_TX_PACKETS)
1472 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS,
1473 sinfo->tx_packets);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001474 nla_nest_end(msg, sinfoattr);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001475
1476 return genlmsg_end(msg, hdr);
1477
1478 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001479 genlmsg_cancel(msg, hdr);
1480 return -EMSGSIZE;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001481}
1482
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001483static int nl80211_dump_station(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02001484 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001485{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001486 struct station_info sinfo;
1487 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001488 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001489 u8 mac_addr[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02001490 int ifidx = cb->args[0];
1491 int sta_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001492 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001493
Johannes Bergbba95fe2008-07-29 13:22:51 +02001494 if (!ifidx) {
1495 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
1496 nl80211_fam.attrbuf, nl80211_fam.maxattr,
1497 nl80211_policy);
1498 if (err)
1499 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001500
Johannes Bergbba95fe2008-07-29 13:22:51 +02001501 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
1502 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001503
Johannes Bergbba95fe2008-07-29 13:22:51 +02001504 ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
1505 if (!ifidx)
1506 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001507 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001508
Johannes Berg3b858752009-03-12 09:55:09 +01001509 rtnl_lock();
1510
1511 netdev = __dev_get_by_index(&init_net, ifidx);
1512 if (!netdev) {
1513 err = -ENODEV;
1514 goto out_rtnl;
1515 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001516
Johannes Bergbba95fe2008-07-29 13:22:51 +02001517 dev = cfg80211_get_dev_from_ifindex(ifidx);
1518 if (IS_ERR(dev)) {
1519 err = PTR_ERR(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001520 goto out_rtnl;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001521 }
1522
1523 if (!dev->ops->dump_station) {
Jouni Malineneec60b02009-03-20 21:21:19 +02001524 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001525 goto out_err;
1526 }
1527
Johannes Bergbba95fe2008-07-29 13:22:51 +02001528 while (1) {
1529 err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
1530 mac_addr, &sinfo);
1531 if (err == -ENOENT)
1532 break;
1533 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001534 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001535
1536 if (nl80211_send_station(skb,
1537 NETLINK_CB(cb->skb).pid,
1538 cb->nlh->nlmsg_seq, NLM_F_MULTI,
1539 netdev, mac_addr,
1540 &sinfo) < 0)
1541 goto out;
1542
1543 sta_idx++;
1544 }
1545
1546
1547 out:
1548 cb->args[1] = sta_idx;
1549 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001550 out_err:
1551 cfg80211_put_dev(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001552 out_rtnl:
1553 rtnl_unlock();
Johannes Bergbba95fe2008-07-29 13:22:51 +02001554
1555 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001556}
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001557
Johannes Berg5727ef12007-12-19 02:03:34 +01001558static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
1559{
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001560 struct cfg80211_registered_device *drv;
1561 int err;
1562 struct net_device *dev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001563 struct station_info sinfo;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001564 struct sk_buff *msg;
1565 u8 *mac_addr = NULL;
1566
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001567 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001568
1569 if (!info->attrs[NL80211_ATTR_MAC])
1570 return -EINVAL;
1571
1572 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1573
Johannes Berg3b858752009-03-12 09:55:09 +01001574 rtnl_lock();
1575
Johannes Bergbba95fe2008-07-29 13:22:51 +02001576 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001577 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001578 goto out_rtnl;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001579
1580 if (!drv->ops->get_station) {
1581 err = -EOPNOTSUPP;
1582 goto out;
1583 }
1584
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001585 err = drv->ops->get_station(&drv->wiphy, dev, mac_addr, &sinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001586 if (err)
1587 goto out;
1588
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001589 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1590 if (!msg)
1591 goto out;
1592
1593 if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001594 dev, mac_addr, &sinfo) < 0)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001595 goto out_free;
1596
1597 err = genlmsg_unicast(msg, info->snd_pid);
1598 goto out;
1599
1600 out_free:
1601 nlmsg_free(msg);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001602 out:
1603 cfg80211_put_dev(drv);
1604 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001605 out_rtnl:
1606 rtnl_unlock();
1607
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001608 return err;
Johannes Berg5727ef12007-12-19 02:03:34 +01001609}
1610
1611/*
1612 * Get vlan interface making sure it is on the right wiphy.
1613 */
1614static int get_vlan(struct nlattr *vlanattr,
1615 struct cfg80211_registered_device *rdev,
1616 struct net_device **vlan)
1617{
1618 *vlan = NULL;
1619
1620 if (vlanattr) {
1621 *vlan = dev_get_by_index(&init_net, nla_get_u32(vlanattr));
1622 if (!*vlan)
1623 return -ENODEV;
1624 if (!(*vlan)->ieee80211_ptr)
1625 return -EINVAL;
1626 if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy)
1627 return -EINVAL;
1628 }
1629 return 0;
1630}
1631
1632static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
1633{
1634 struct cfg80211_registered_device *drv;
1635 int err;
1636 struct net_device *dev;
1637 struct station_parameters params;
1638 u8 *mac_addr = NULL;
1639
1640 memset(&params, 0, sizeof(params));
1641
1642 params.listen_interval = -1;
1643
1644 if (info->attrs[NL80211_ATTR_STA_AID])
1645 return -EINVAL;
1646
1647 if (!info->attrs[NL80211_ATTR_MAC])
1648 return -EINVAL;
1649
1650 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1651
1652 if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
1653 params.supported_rates =
1654 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1655 params.supported_rates_len =
1656 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1657 }
1658
1659 if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
1660 params.listen_interval =
1661 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
1662
Jouni Malinen36aedc92008-08-25 11:58:58 +03001663 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
1664 params.ht_capa =
1665 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
1666
Johannes Berg5727ef12007-12-19 02:03:34 +01001667 if (parse_station_flags(info->attrs[NL80211_ATTR_STA_FLAGS],
1668 &params.station_flags))
1669 return -EINVAL;
1670
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001671 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
1672 params.plink_action =
1673 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
1674
Johannes Berg3b858752009-03-12 09:55:09 +01001675 rtnl_lock();
1676
Johannes Bergbba95fe2008-07-29 13:22:51 +02001677 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001678 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001679 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01001680
1681 err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, &params.vlan);
1682 if (err)
1683 goto out;
1684
1685 if (!drv->ops->change_station) {
1686 err = -EOPNOTSUPP;
1687 goto out;
1688 }
1689
Johannes Berg5727ef12007-12-19 02:03:34 +01001690 err = drv->ops->change_station(&drv->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01001691
1692 out:
1693 if (params.vlan)
1694 dev_put(params.vlan);
1695 cfg80211_put_dev(drv);
1696 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001697 out_rtnl:
1698 rtnl_unlock();
1699
Johannes Berg5727ef12007-12-19 02:03:34 +01001700 return err;
1701}
1702
1703static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
1704{
1705 struct cfg80211_registered_device *drv;
1706 int err;
1707 struct net_device *dev;
1708 struct station_parameters params;
1709 u8 *mac_addr = NULL;
1710
1711 memset(&params, 0, sizeof(params));
1712
1713 if (!info->attrs[NL80211_ATTR_MAC])
1714 return -EINVAL;
1715
1716 if (!info->attrs[NL80211_ATTR_STA_AID])
1717 return -EINVAL;
1718
1719 if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
1720 return -EINVAL;
1721
1722 if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
1723 return -EINVAL;
1724
1725 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1726 params.supported_rates =
1727 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1728 params.supported_rates_len =
1729 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1730 params.listen_interval =
1731 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
Johannes Berg16f2e852008-04-07 14:35:46 +02001732 params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
Jouni Malinen36aedc92008-08-25 11:58:58 +03001733 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
1734 params.ht_capa =
1735 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
Johannes Berg5727ef12007-12-19 02:03:34 +01001736
1737 if (parse_station_flags(info->attrs[NL80211_ATTR_STA_FLAGS],
1738 &params.station_flags))
1739 return -EINVAL;
1740
Johannes Berg3b858752009-03-12 09:55:09 +01001741 rtnl_lock();
1742
Johannes Bergbba95fe2008-07-29 13:22:51 +02001743 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001744 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001745 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01001746
1747 err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, &params.vlan);
1748 if (err)
1749 goto out;
1750
1751 if (!drv->ops->add_station) {
1752 err = -EOPNOTSUPP;
1753 goto out;
1754 }
1755
Jouni Malinen35a8efe2009-03-20 21:21:18 +02001756 if (!netif_running(dev)) {
1757 err = -ENETDOWN;
1758 goto out;
1759 }
1760
Johannes Berg5727ef12007-12-19 02:03:34 +01001761 err = drv->ops->add_station(&drv->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01001762
1763 out:
1764 if (params.vlan)
1765 dev_put(params.vlan);
1766 cfg80211_put_dev(drv);
1767 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001768 out_rtnl:
1769 rtnl_unlock();
1770
Johannes Berg5727ef12007-12-19 02:03:34 +01001771 return err;
1772}
1773
1774static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
1775{
1776 struct cfg80211_registered_device *drv;
1777 int err;
1778 struct net_device *dev;
1779 u8 *mac_addr = NULL;
1780
1781 if (info->attrs[NL80211_ATTR_MAC])
1782 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1783
Johannes Berg3b858752009-03-12 09:55:09 +01001784 rtnl_lock();
1785
Johannes Bergbba95fe2008-07-29 13:22:51 +02001786 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001787 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001788 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01001789
1790 if (!drv->ops->del_station) {
1791 err = -EOPNOTSUPP;
1792 goto out;
1793 }
1794
Johannes Berg5727ef12007-12-19 02:03:34 +01001795 err = drv->ops->del_station(&drv->wiphy, dev, mac_addr);
Johannes Berg5727ef12007-12-19 02:03:34 +01001796
1797 out:
1798 cfg80211_put_dev(drv);
1799 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001800 out_rtnl:
1801 rtnl_unlock();
1802
Johannes Berg5727ef12007-12-19 02:03:34 +01001803 return err;
1804}
1805
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001806static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq,
1807 int flags, struct net_device *dev,
1808 u8 *dst, u8 *next_hop,
1809 struct mpath_info *pinfo)
1810{
1811 void *hdr;
1812 struct nlattr *pinfoattr;
1813
1814 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
1815 if (!hdr)
1816 return -1;
1817
1818 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1819 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
1820 NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop);
1821
1822 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
1823 if (!pinfoattr)
1824 goto nla_put_failure;
1825 if (pinfo->filled & MPATH_INFO_FRAME_QLEN)
1826 NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
1827 pinfo->frame_qlen);
1828 if (pinfo->filled & MPATH_INFO_DSN)
1829 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DSN,
1830 pinfo->dsn);
1831 if (pinfo->filled & MPATH_INFO_METRIC)
1832 NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC,
1833 pinfo->metric);
1834 if (pinfo->filled & MPATH_INFO_EXPTIME)
1835 NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME,
1836 pinfo->exptime);
1837 if (pinfo->filled & MPATH_INFO_FLAGS)
1838 NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS,
1839 pinfo->flags);
1840 if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT)
1841 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
1842 pinfo->discovery_timeout);
1843 if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES)
1844 NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
1845 pinfo->discovery_retries);
1846
1847 nla_nest_end(msg, pinfoattr);
1848
1849 return genlmsg_end(msg, hdr);
1850
1851 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001852 genlmsg_cancel(msg, hdr);
1853 return -EMSGSIZE;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001854}
1855
1856static int nl80211_dump_mpath(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02001857 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001858{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001859 struct mpath_info pinfo;
1860 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001861 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001862 u8 dst[ETH_ALEN];
1863 u8 next_hop[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02001864 int ifidx = cb->args[0];
1865 int path_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001866 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001867
Johannes Bergbba95fe2008-07-29 13:22:51 +02001868 if (!ifidx) {
1869 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
1870 nl80211_fam.attrbuf, nl80211_fam.maxattr,
1871 nl80211_policy);
1872 if (err)
1873 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001874
Johannes Bergbba95fe2008-07-29 13:22:51 +02001875 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
1876 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001877
Johannes Bergbba95fe2008-07-29 13:22:51 +02001878 ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
1879 if (!ifidx)
1880 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001881 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001882
Johannes Berg3b858752009-03-12 09:55:09 +01001883 rtnl_lock();
1884
1885 netdev = __dev_get_by_index(&init_net, ifidx);
1886 if (!netdev) {
1887 err = -ENODEV;
1888 goto out_rtnl;
1889 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001890
Johannes Bergbba95fe2008-07-29 13:22:51 +02001891 dev = cfg80211_get_dev_from_ifindex(ifidx);
1892 if (IS_ERR(dev)) {
1893 err = PTR_ERR(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001894 goto out_rtnl;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001895 }
1896
1897 if (!dev->ops->dump_mpath) {
Jouni Malineneec60b02009-03-20 21:21:19 +02001898 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001899 goto out_err;
1900 }
1901
Jouni Malineneec60b02009-03-20 21:21:19 +02001902 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
1903 err = -EOPNOTSUPP;
1904 goto out;
1905 }
1906
Johannes Bergbba95fe2008-07-29 13:22:51 +02001907 while (1) {
1908 err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
1909 dst, next_hop, &pinfo);
1910 if (err == -ENOENT)
1911 break;
1912 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001913 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001914
1915 if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid,
1916 cb->nlh->nlmsg_seq, NLM_F_MULTI,
1917 netdev, dst, next_hop,
1918 &pinfo) < 0)
1919 goto out;
1920
1921 path_idx++;
1922 }
1923
1924
1925 out:
1926 cb->args[1] = path_idx;
1927 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001928 out_err:
1929 cfg80211_put_dev(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001930 out_rtnl:
1931 rtnl_unlock();
Johannes Bergbba95fe2008-07-29 13:22:51 +02001932
1933 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001934}
1935
1936static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
1937{
1938 struct cfg80211_registered_device *drv;
1939 int err;
1940 struct net_device *dev;
1941 struct mpath_info pinfo;
1942 struct sk_buff *msg;
1943 u8 *dst = NULL;
1944 u8 next_hop[ETH_ALEN];
1945
1946 memset(&pinfo, 0, sizeof(pinfo));
1947
1948 if (!info->attrs[NL80211_ATTR_MAC])
1949 return -EINVAL;
1950
1951 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
1952
Johannes Berg3b858752009-03-12 09:55:09 +01001953 rtnl_lock();
1954
Johannes Bergbba95fe2008-07-29 13:22:51 +02001955 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001956 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001957 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001958
1959 if (!drv->ops->get_mpath) {
1960 err = -EOPNOTSUPP;
1961 goto out;
1962 }
1963
Jouni Malineneec60b02009-03-20 21:21:19 +02001964 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
1965 err = -EOPNOTSUPP;
1966 goto out;
1967 }
1968
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001969 err = drv->ops->get_mpath(&drv->wiphy, dev, dst, next_hop, &pinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001970 if (err)
1971 goto out;
1972
1973 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1974 if (!msg)
1975 goto out;
1976
1977 if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0,
1978 dev, dst, next_hop, &pinfo) < 0)
1979 goto out_free;
1980
1981 err = genlmsg_unicast(msg, info->snd_pid);
1982 goto out;
1983
1984 out_free:
1985 nlmsg_free(msg);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001986 out:
1987 cfg80211_put_dev(drv);
1988 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001989 out_rtnl:
1990 rtnl_unlock();
1991
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001992 return err;
1993}
1994
1995static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
1996{
1997 struct cfg80211_registered_device *drv;
1998 int err;
1999 struct net_device *dev;
2000 u8 *dst = NULL;
2001 u8 *next_hop = NULL;
2002
2003 if (!info->attrs[NL80211_ATTR_MAC])
2004 return -EINVAL;
2005
2006 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
2007 return -EINVAL;
2008
2009 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2010 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
2011
Johannes Berg3b858752009-03-12 09:55:09 +01002012 rtnl_lock();
2013
Johannes Bergbba95fe2008-07-29 13:22:51 +02002014 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002015 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002016 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002017
2018 if (!drv->ops->change_mpath) {
2019 err = -EOPNOTSUPP;
2020 goto out;
2021 }
2022
Jouni Malineneec60b02009-03-20 21:21:19 +02002023 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2024 err = -EOPNOTSUPP;
2025 goto out;
2026 }
2027
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002028 if (!netif_running(dev)) {
2029 err = -ENETDOWN;
2030 goto out;
2031 }
2032
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002033 err = drv->ops->change_mpath(&drv->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002034
2035 out:
2036 cfg80211_put_dev(drv);
2037 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002038 out_rtnl:
2039 rtnl_unlock();
2040
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002041 return err;
2042}
2043static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
2044{
2045 struct cfg80211_registered_device *drv;
2046 int err;
2047 struct net_device *dev;
2048 u8 *dst = NULL;
2049 u8 *next_hop = NULL;
2050
2051 if (!info->attrs[NL80211_ATTR_MAC])
2052 return -EINVAL;
2053
2054 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
2055 return -EINVAL;
2056
2057 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2058 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
2059
Johannes Berg3b858752009-03-12 09:55:09 +01002060 rtnl_lock();
2061
Johannes Bergbba95fe2008-07-29 13:22:51 +02002062 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002063 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002064 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002065
2066 if (!drv->ops->add_mpath) {
2067 err = -EOPNOTSUPP;
2068 goto out;
2069 }
2070
Jouni Malineneec60b02009-03-20 21:21:19 +02002071 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2072 err = -EOPNOTSUPP;
2073 goto out;
2074 }
2075
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002076 if (!netif_running(dev)) {
2077 err = -ENETDOWN;
2078 goto out;
2079 }
2080
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002081 err = drv->ops->add_mpath(&drv->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002082
2083 out:
2084 cfg80211_put_dev(drv);
2085 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002086 out_rtnl:
2087 rtnl_unlock();
2088
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002089 return err;
2090}
2091
2092static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
2093{
2094 struct cfg80211_registered_device *drv;
2095 int err;
2096 struct net_device *dev;
2097 u8 *dst = NULL;
2098
2099 if (info->attrs[NL80211_ATTR_MAC])
2100 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2101
Johannes Berg3b858752009-03-12 09:55:09 +01002102 rtnl_lock();
2103
Johannes Bergbba95fe2008-07-29 13:22:51 +02002104 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002105 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002106 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002107
2108 if (!drv->ops->del_mpath) {
2109 err = -EOPNOTSUPP;
2110 goto out;
2111 }
2112
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002113 err = drv->ops->del_mpath(&drv->wiphy, dev, dst);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002114
2115 out:
2116 cfg80211_put_dev(drv);
2117 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002118 out_rtnl:
2119 rtnl_unlock();
2120
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002121 return err;
2122}
2123
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002124static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
2125{
2126 struct cfg80211_registered_device *drv;
2127 int err;
2128 struct net_device *dev;
2129 struct bss_parameters params;
2130
2131 memset(&params, 0, sizeof(params));
2132 /* default to not changing parameters */
2133 params.use_cts_prot = -1;
2134 params.use_short_preamble = -1;
2135 params.use_short_slot_time = -1;
2136
2137 if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
2138 params.use_cts_prot =
2139 nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
2140 if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
2141 params.use_short_preamble =
2142 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
2143 if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
2144 params.use_short_slot_time =
2145 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
Jouni Malinen90c97a02008-10-30 16:59:22 +02002146 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
2147 params.basic_rates =
2148 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2149 params.basic_rates_len =
2150 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2151 }
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002152
Johannes Berg3b858752009-03-12 09:55:09 +01002153 rtnl_lock();
2154
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002155 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2156 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002157 goto out_rtnl;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002158
2159 if (!drv->ops->change_bss) {
2160 err = -EOPNOTSUPP;
2161 goto out;
2162 }
2163
Jouni Malineneec60b02009-03-20 21:21:19 +02002164 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
2165 err = -EOPNOTSUPP;
2166 goto out;
2167 }
2168
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002169 err = drv->ops->change_bss(&drv->wiphy, dev, &params);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002170
2171 out:
2172 cfg80211_put_dev(drv);
2173 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002174 out_rtnl:
2175 rtnl_unlock();
2176
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002177 return err;
2178}
2179
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002180static const struct nla_policy
2181 reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
2182 [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
2183 [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
2184 [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
2185 [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
2186 [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
2187 [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
2188};
2189
2190static int parse_reg_rule(struct nlattr *tb[],
2191 struct ieee80211_reg_rule *reg_rule)
2192{
2193 struct ieee80211_freq_range *freq_range = &reg_rule->freq_range;
2194 struct ieee80211_power_rule *power_rule = &reg_rule->power_rule;
2195
2196 if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
2197 return -EINVAL;
2198 if (!tb[NL80211_ATTR_FREQ_RANGE_START])
2199 return -EINVAL;
2200 if (!tb[NL80211_ATTR_FREQ_RANGE_END])
2201 return -EINVAL;
2202 if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
2203 return -EINVAL;
2204 if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
2205 return -EINVAL;
2206
2207 reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
2208
2209 freq_range->start_freq_khz =
2210 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
2211 freq_range->end_freq_khz =
2212 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
2213 freq_range->max_bandwidth_khz =
2214 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
2215
2216 power_rule->max_eirp =
2217 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
2218
2219 if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
2220 power_rule->max_antenna_gain =
2221 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
2222
2223 return 0;
2224}
2225
2226static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
2227{
2228 int r;
2229 char *data = NULL;
2230
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002231 /*
2232 * You should only get this when cfg80211 hasn't yet initialized
2233 * completely when built-in to the kernel right between the time
2234 * window between nl80211_init() and regulatory_init(), if that is
2235 * even possible.
2236 */
2237 mutex_lock(&cfg80211_mutex);
2238 if (unlikely(!cfg80211_regdomain)) {
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002239 mutex_unlock(&cfg80211_mutex);
2240 return -EINPROGRESS;
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002241 }
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002242 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002243
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002244 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
2245 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002246
2247 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
2248
2249#ifdef CONFIG_WIRELESS_OLD_REGULATORY
2250 /* We ignore world regdom requests with the old regdom setup */
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002251 if (is_world_regdom(data))
2252 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002253#endif
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002254
2255 r = regulatory_hint_user(data);
2256
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002257 return r;
2258}
2259
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002260static int nl80211_get_mesh_params(struct sk_buff *skb,
2261 struct genl_info *info)
2262{
2263 struct cfg80211_registered_device *drv;
2264 struct mesh_config cur_params;
2265 int err;
2266 struct net_device *dev;
2267 void *hdr;
2268 struct nlattr *pinfoattr;
2269 struct sk_buff *msg;
2270
Johannes Berg3b858752009-03-12 09:55:09 +01002271 rtnl_lock();
2272
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002273 /* Look up our device */
2274 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2275 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002276 goto out_rtnl;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002277
Jouni Malinenf3f92582009-03-20 17:57:36 +02002278 if (!drv->ops->get_mesh_params) {
2279 err = -EOPNOTSUPP;
2280 goto out;
2281 }
2282
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002283 /* Get the mesh params */
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002284 err = drv->ops->get_mesh_params(&drv->wiphy, dev, &cur_params);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002285 if (err)
2286 goto out;
2287
2288 /* Draw up a netlink message to send back */
2289 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
2290 if (!msg) {
2291 err = -ENOBUFS;
2292 goto out;
2293 }
2294 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
2295 NL80211_CMD_GET_MESH_PARAMS);
2296 if (!hdr)
2297 goto nla_put_failure;
2298 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS);
2299 if (!pinfoattr)
2300 goto nla_put_failure;
2301 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2302 NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
2303 cur_params.dot11MeshRetryTimeout);
2304 NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
2305 cur_params.dot11MeshConfirmTimeout);
2306 NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
2307 cur_params.dot11MeshHoldingTimeout);
2308 NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
2309 cur_params.dot11MeshMaxPeerLinks);
2310 NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES,
2311 cur_params.dot11MeshMaxRetries);
2312 NLA_PUT_U8(msg, NL80211_MESHCONF_TTL,
2313 cur_params.dot11MeshTTL);
2314 NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
2315 cur_params.auto_open_plinks);
2316 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
2317 cur_params.dot11MeshHWMPmaxPREQretries);
2318 NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
2319 cur_params.path_refresh_time);
2320 NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
2321 cur_params.min_discovery_timeout);
2322 NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
2323 cur_params.dot11MeshHWMPactivePathTimeout);
2324 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
2325 cur_params.dot11MeshHWMPpreqMinInterval);
2326 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
2327 cur_params.dot11MeshHWMPnetDiameterTraversalTime);
2328 nla_nest_end(msg, pinfoattr);
2329 genlmsg_end(msg, hdr);
2330 err = genlmsg_unicast(msg, info->snd_pid);
2331 goto out;
2332
Johannes Berg3b858752009-03-12 09:55:09 +01002333 nla_put_failure:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002334 genlmsg_cancel(msg, hdr);
2335 err = -EMSGSIZE;
Johannes Berg3b858752009-03-12 09:55:09 +01002336 out:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002337 /* Cleanup */
2338 cfg80211_put_dev(drv);
2339 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002340 out_rtnl:
2341 rtnl_unlock();
2342
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002343 return err;
2344}
2345
2346#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \
2347do {\
2348 if (table[attr_num]) {\
2349 cfg.param = nla_fn(table[attr_num]); \
2350 mask |= (1 << (attr_num - 1)); \
2351 } \
2352} while (0);\
2353
2354static struct nla_policy
2355nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] __read_mostly = {
2356 [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
2357 [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
2358 [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
2359 [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
2360 [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
2361 [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
2362 [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
2363
2364 [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
2365 [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
2366 [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
2367 [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
2368 [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
2369 [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
2370};
2371
2372static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
2373{
2374 int err;
2375 u32 mask;
2376 struct cfg80211_registered_device *drv;
2377 struct net_device *dev;
2378 struct mesh_config cfg;
2379 struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
2380 struct nlattr *parent_attr;
2381
2382 parent_attr = info->attrs[NL80211_ATTR_MESH_PARAMS];
2383 if (!parent_attr)
2384 return -EINVAL;
2385 if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
2386 parent_attr, nl80211_meshconf_params_policy))
2387 return -EINVAL;
2388
Johannes Berg3b858752009-03-12 09:55:09 +01002389 rtnl_lock();
2390
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002391 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2392 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002393 goto out_rtnl;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002394
Jouni Malinenf3f92582009-03-20 17:57:36 +02002395 if (!drv->ops->set_mesh_params) {
2396 err = -EOPNOTSUPP;
2397 goto out;
2398 }
2399
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002400 /* This makes sure that there aren't more than 32 mesh config
2401 * parameters (otherwise our bitfield scheme would not work.) */
2402 BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
2403
2404 /* Fill in the params struct */
2405 mask = 0;
2406 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout,
2407 mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16);
2408 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout,
2409 mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16);
2410 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout,
2411 mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16);
2412 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks,
2413 mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16);
2414 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries,
2415 mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8);
2416 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL,
2417 mask, NL80211_MESHCONF_TTL, nla_get_u8);
2418 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
2419 mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8);
2420 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
2421 mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
2422 nla_get_u8);
2423 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time,
2424 mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32);
2425 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout,
2426 mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
2427 nla_get_u16);
2428 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
2429 mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
2430 nla_get_u32);
2431 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
2432 mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
2433 nla_get_u16);
2434 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
2435 dot11MeshHWMPnetDiameterTraversalTime,
2436 mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
2437 nla_get_u16);
2438
2439 /* Apply changes */
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002440 err = drv->ops->set_mesh_params(&drv->wiphy, dev, &cfg, mask);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002441
Jouni Malinenf3f92582009-03-20 17:57:36 +02002442 out:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002443 /* cleanup */
2444 cfg80211_put_dev(drv);
2445 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002446 out_rtnl:
2447 rtnl_unlock();
2448
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002449 return err;
2450}
2451
2452#undef FILL_IN_MESH_PARAM_IF_SET
2453
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002454static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
2455{
2456 struct sk_buff *msg;
2457 void *hdr = NULL;
2458 struct nlattr *nl_reg_rules;
2459 unsigned int i;
2460 int err = -EINVAL;
2461
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002462 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002463
2464 if (!cfg80211_regdomain)
2465 goto out;
2466
2467 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
2468 if (!msg) {
2469 err = -ENOBUFS;
2470 goto out;
2471 }
2472
2473 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
2474 NL80211_CMD_GET_REG);
2475 if (!hdr)
2476 goto nla_put_failure;
2477
2478 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2,
2479 cfg80211_regdomain->alpha2);
2480
2481 nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
2482 if (!nl_reg_rules)
2483 goto nla_put_failure;
2484
2485 for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) {
2486 struct nlattr *nl_reg_rule;
2487 const struct ieee80211_reg_rule *reg_rule;
2488 const struct ieee80211_freq_range *freq_range;
2489 const struct ieee80211_power_rule *power_rule;
2490
2491 reg_rule = &cfg80211_regdomain->reg_rules[i];
2492 freq_range = &reg_rule->freq_range;
2493 power_rule = &reg_rule->power_rule;
2494
2495 nl_reg_rule = nla_nest_start(msg, i);
2496 if (!nl_reg_rule)
2497 goto nla_put_failure;
2498
2499 NLA_PUT_U32(msg, NL80211_ATTR_REG_RULE_FLAGS,
2500 reg_rule->flags);
2501 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_START,
2502 freq_range->start_freq_khz);
2503 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_END,
2504 freq_range->end_freq_khz);
2505 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
2506 freq_range->max_bandwidth_khz);
2507 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
2508 power_rule->max_antenna_gain);
2509 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
2510 power_rule->max_eirp);
2511
2512 nla_nest_end(msg, nl_reg_rule);
2513 }
2514
2515 nla_nest_end(msg, nl_reg_rules);
2516
2517 genlmsg_end(msg, hdr);
2518 err = genlmsg_unicast(msg, info->snd_pid);
2519 goto out;
2520
2521nla_put_failure:
2522 genlmsg_cancel(msg, hdr);
2523 err = -EMSGSIZE;
2524out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002525 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002526 return err;
2527}
2528
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002529static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
2530{
2531 struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
2532 struct nlattr *nl_reg_rule;
2533 char *alpha2 = NULL;
2534 int rem_reg_rules = 0, r = 0;
2535 u32 num_rules = 0, rule_idx = 0, size_of_regd;
2536 struct ieee80211_regdomain *rd = NULL;
2537
2538 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
2539 return -EINVAL;
2540
2541 if (!info->attrs[NL80211_ATTR_REG_RULES])
2542 return -EINVAL;
2543
2544 alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
2545
2546 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
2547 rem_reg_rules) {
2548 num_rules++;
2549 if (num_rules > NL80211_MAX_SUPP_REG_RULES)
2550 goto bad_reg;
2551 }
2552
2553 if (!reg_is_valid_request(alpha2))
2554 return -EINVAL;
2555
2556 size_of_regd = sizeof(struct ieee80211_regdomain) +
2557 (num_rules * sizeof(struct ieee80211_reg_rule));
2558
2559 rd = kzalloc(size_of_regd, GFP_KERNEL);
2560 if (!rd)
2561 return -ENOMEM;
2562
2563 rd->n_reg_rules = num_rules;
2564 rd->alpha2[0] = alpha2[0];
2565 rd->alpha2[1] = alpha2[1];
2566
2567 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
2568 rem_reg_rules) {
2569 nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
2570 nla_data(nl_reg_rule), nla_len(nl_reg_rule),
2571 reg_rule_policy);
2572 r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
2573 if (r)
2574 goto bad_reg;
2575
2576 rule_idx++;
2577
2578 if (rule_idx > NL80211_MAX_SUPP_REG_RULES)
2579 goto bad_reg;
2580 }
2581
2582 BUG_ON(rule_idx != num_rules);
2583
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002584 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002585 r = set_regdom(rd);
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002586 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002587 return r;
2588
Johannes Bergd2372b32008-10-24 20:32:20 +02002589 bad_reg:
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002590 kfree(rd);
2591 return -EINVAL;
2592}
2593
Johannes Berg2a519312009-02-10 21:25:55 +01002594static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
2595{
2596 struct cfg80211_registered_device *drv;
2597 struct net_device *dev;
2598 struct cfg80211_scan_request *request;
2599 struct cfg80211_ssid *ssid;
2600 struct ieee80211_channel *channel;
2601 struct nlattr *attr;
2602 struct wiphy *wiphy;
2603 int err, tmp, n_ssids = 0, n_channels = 0, i;
2604 enum ieee80211_band band;
Jouni Malinen70692ad2009-02-16 19:39:13 +02002605 size_t ie_len;
Johannes Berg2a519312009-02-10 21:25:55 +01002606
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002607 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
2608 return -EINVAL;
2609
Johannes Berg3b858752009-03-12 09:55:09 +01002610 rtnl_lock();
2611
Johannes Berg2a519312009-02-10 21:25:55 +01002612 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2613 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002614 goto out_rtnl;
Johannes Berg2a519312009-02-10 21:25:55 +01002615
2616 wiphy = &drv->wiphy;
2617
2618 if (!drv->ops->scan) {
2619 err = -EOPNOTSUPP;
2620 goto out;
2621 }
2622
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002623 if (!netif_running(dev)) {
2624 err = -ENETDOWN;
2625 goto out;
2626 }
2627
Johannes Berg2a519312009-02-10 21:25:55 +01002628 if (drv->scan_req) {
2629 err = -EBUSY;
Johannes Berg3b858752009-03-12 09:55:09 +01002630 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002631 }
2632
2633 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
2634 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp)
2635 n_channels++;
2636 if (!n_channels) {
2637 err = -EINVAL;
Johannes Berg3b858752009-03-12 09:55:09 +01002638 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002639 }
2640 } else {
2641 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
2642 if (wiphy->bands[band])
2643 n_channels += wiphy->bands[band]->n_channels;
2644 }
2645
2646 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
2647 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
2648 n_ssids++;
2649
2650 if (n_ssids > wiphy->max_scan_ssids) {
2651 err = -EINVAL;
Johannes Berg3b858752009-03-12 09:55:09 +01002652 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002653 }
2654
Jouni Malinen70692ad2009-02-16 19:39:13 +02002655 if (info->attrs[NL80211_ATTR_IE])
2656 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
2657 else
2658 ie_len = 0;
2659
Johannes Berg18a83652009-03-31 12:12:05 +02002660 if (ie_len > wiphy->max_scan_ie_len) {
2661 err = -EINVAL;
2662 goto out;
2663 }
2664
Johannes Berg2a519312009-02-10 21:25:55 +01002665 request = kzalloc(sizeof(*request)
2666 + sizeof(*ssid) * n_ssids
Jouni Malinen70692ad2009-02-16 19:39:13 +02002667 + sizeof(channel) * n_channels
2668 + ie_len, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01002669 if (!request) {
2670 err = -ENOMEM;
Johannes Berg3b858752009-03-12 09:55:09 +01002671 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002672 }
2673
2674 request->channels = (void *)((char *)request + sizeof(*request));
2675 request->n_channels = n_channels;
2676 if (n_ssids)
2677 request->ssids = (void *)(request->channels + n_channels);
2678 request->n_ssids = n_ssids;
Jouni Malinen70692ad2009-02-16 19:39:13 +02002679 if (ie_len) {
2680 if (request->ssids)
2681 request->ie = (void *)(request->ssids + n_ssids);
2682 else
2683 request->ie = (void *)(request->channels + n_channels);
2684 }
Johannes Berg2a519312009-02-10 21:25:55 +01002685
2686 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
2687 /* user specified, bail out if channel not found */
2688 request->n_channels = n_channels;
2689 i = 0;
2690 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
2691 request->channels[i] = ieee80211_get_channel(wiphy, nla_get_u32(attr));
2692 if (!request->channels[i]) {
2693 err = -EINVAL;
2694 goto out_free;
2695 }
2696 i++;
2697 }
2698 } else {
2699 /* all channels */
2700 i = 0;
2701 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
2702 int j;
2703 if (!wiphy->bands[band])
2704 continue;
2705 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
2706 request->channels[i] = &wiphy->bands[band]->channels[j];
2707 i++;
2708 }
2709 }
2710 }
2711
2712 i = 0;
2713 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
2714 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
2715 if (request->ssids[i].ssid_len > IEEE80211_MAX_SSID_LEN) {
2716 err = -EINVAL;
2717 goto out_free;
2718 }
2719 memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
2720 request->ssids[i].ssid_len = nla_len(attr);
2721 i++;
2722 }
2723 }
2724
Jouni Malinen70692ad2009-02-16 19:39:13 +02002725 if (info->attrs[NL80211_ATTR_IE]) {
2726 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Johannes Bergde95a542009-04-01 11:58:36 +02002727 memcpy((void *)request->ie,
2728 nla_data(info->attrs[NL80211_ATTR_IE]),
Jouni Malinen70692ad2009-02-16 19:39:13 +02002729 request->ie_len);
2730 }
2731
Johannes Berg2a519312009-02-10 21:25:55 +01002732 request->ifidx = dev->ifindex;
2733 request->wiphy = &drv->wiphy;
2734
2735 drv->scan_req = request;
2736 err = drv->ops->scan(&drv->wiphy, dev, request);
2737
2738 out_free:
2739 if (err) {
2740 drv->scan_req = NULL;
2741 kfree(request);
2742 }
Johannes Berg2a519312009-02-10 21:25:55 +01002743 out:
2744 cfg80211_put_dev(drv);
2745 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002746 out_rtnl:
2747 rtnl_unlock();
2748
Johannes Berg2a519312009-02-10 21:25:55 +01002749 return err;
2750}
2751
2752static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
2753 struct cfg80211_registered_device *rdev,
2754 struct net_device *dev,
2755 struct cfg80211_bss *res)
2756{
2757 void *hdr;
2758 struct nlattr *bss;
2759
2760 hdr = nl80211hdr_put(msg, pid, seq, flags,
2761 NL80211_CMD_NEW_SCAN_RESULTS);
2762 if (!hdr)
2763 return -1;
2764
2765 NLA_PUT_U32(msg, NL80211_ATTR_SCAN_GENERATION,
2766 rdev->bss_generation);
2767 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2768
2769 bss = nla_nest_start(msg, NL80211_ATTR_BSS);
2770 if (!bss)
2771 goto nla_put_failure;
2772 if (!is_zero_ether_addr(res->bssid))
2773 NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid);
2774 if (res->information_elements && res->len_information_elements)
2775 NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS,
2776 res->len_information_elements,
2777 res->information_elements);
2778 if (res->tsf)
2779 NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf);
2780 if (res->beacon_interval)
2781 NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval);
2782 NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability);
2783 NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq);
2784
Johannes Berg77965c92009-02-18 18:45:06 +01002785 switch (rdev->wiphy.signal_type) {
Johannes Berg2a519312009-02-10 21:25:55 +01002786 case CFG80211_SIGNAL_TYPE_MBM:
2787 NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal);
2788 break;
2789 case CFG80211_SIGNAL_TYPE_UNSPEC:
2790 NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal);
2791 break;
2792 default:
2793 break;
2794 }
2795
2796 nla_nest_end(msg, bss);
2797
2798 return genlmsg_end(msg, hdr);
2799
2800 nla_put_failure:
2801 genlmsg_cancel(msg, hdr);
2802 return -EMSGSIZE;
2803}
2804
2805static int nl80211_dump_scan(struct sk_buff *skb,
2806 struct netlink_callback *cb)
2807{
2808 struct cfg80211_registered_device *dev;
2809 struct net_device *netdev;
2810 struct cfg80211_internal_bss *scan;
2811 int ifidx = cb->args[0];
2812 int start = cb->args[1], idx = 0;
2813 int err;
2814
2815 if (!ifidx) {
2816 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
2817 nl80211_fam.attrbuf, nl80211_fam.maxattr,
2818 nl80211_policy);
2819 if (err)
2820 return err;
2821
2822 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
2823 return -EINVAL;
2824
2825 ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
2826 if (!ifidx)
2827 return -EINVAL;
2828 cb->args[0] = ifidx;
2829 }
2830
2831 netdev = dev_get_by_index(&init_net, ifidx);
2832 if (!netdev)
2833 return -ENODEV;
2834
2835 dev = cfg80211_get_dev_from_ifindex(ifidx);
2836 if (IS_ERR(dev)) {
2837 err = PTR_ERR(dev);
2838 goto out_put_netdev;
2839 }
2840
2841 spin_lock_bh(&dev->bss_lock);
2842 cfg80211_bss_expire(dev);
2843
2844 list_for_each_entry(scan, &dev->bss_list, list) {
2845 if (++idx <= start)
2846 continue;
2847 if (nl80211_send_bss(skb,
2848 NETLINK_CB(cb->skb).pid,
2849 cb->nlh->nlmsg_seq, NLM_F_MULTI,
2850 dev, netdev, &scan->pub) < 0) {
2851 idx--;
2852 goto out;
2853 }
2854 }
2855
2856 out:
2857 spin_unlock_bh(&dev->bss_lock);
2858
2859 cb->args[1] = idx;
2860 err = skb->len;
2861 cfg80211_put_dev(dev);
2862 out_put_netdev:
2863 dev_put(netdev);
2864
2865 return err;
2866}
2867
Jouni Malinen255e7372009-03-20 21:21:17 +02002868static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
2869{
2870 return auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM ||
2871 auth_type == NL80211_AUTHTYPE_SHARED_KEY ||
2872 auth_type == NL80211_AUTHTYPE_FT ||
2873 auth_type == NL80211_AUTHTYPE_NETWORK_EAP;
2874}
2875
Jouni Malinen636a5d32009-03-19 13:39:22 +02002876static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
2877{
2878 struct cfg80211_registered_device *drv;
2879 struct net_device *dev;
2880 struct cfg80211_auth_request req;
2881 struct wiphy *wiphy;
2882 int err;
2883
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002884 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
2885 return -EINVAL;
2886
2887 if (!info->attrs[NL80211_ATTR_MAC])
2888 return -EINVAL;
2889
Jouni Malinen17780922009-03-27 20:52:47 +02002890 if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
2891 return -EINVAL;
2892
Jouni Malinen636a5d32009-03-19 13:39:22 +02002893 rtnl_lock();
2894
2895 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2896 if (err)
2897 goto unlock_rtnl;
2898
2899 if (!drv->ops->auth) {
2900 err = -EOPNOTSUPP;
2901 goto out;
2902 }
2903
Jouni Malineneec60b02009-03-20 21:21:19 +02002904 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
2905 err = -EOPNOTSUPP;
2906 goto out;
2907 }
2908
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002909 if (!netif_running(dev)) {
2910 err = -ENETDOWN;
2911 goto out;
2912 }
2913
Jouni Malinen636a5d32009-03-19 13:39:22 +02002914 wiphy = &drv->wiphy;
2915 memset(&req, 0, sizeof(req));
2916
2917 req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2918
2919 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
2920 req.chan = ieee80211_get_channel(
2921 wiphy,
2922 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
2923 if (!req.chan) {
2924 err = -EINVAL;
2925 goto out;
2926 }
2927 }
2928
2929 if (info->attrs[NL80211_ATTR_SSID]) {
2930 req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
2931 req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
2932 }
2933
2934 if (info->attrs[NL80211_ATTR_IE]) {
2935 req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
2936 req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
2937 }
2938
Jouni Malinen17780922009-03-27 20:52:47 +02002939 req.auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
2940 if (!nl80211_valid_auth_type(req.auth_type)) {
2941 err = -EINVAL;
2942 goto out;
Jouni Malinen636a5d32009-03-19 13:39:22 +02002943 }
2944
2945 err = drv->ops->auth(&drv->wiphy, dev, &req);
2946
2947out:
2948 cfg80211_put_dev(drv);
2949 dev_put(dev);
2950unlock_rtnl:
2951 rtnl_unlock();
2952 return err;
2953}
2954
2955static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
2956{
2957 struct cfg80211_registered_device *drv;
2958 struct net_device *dev;
2959 struct cfg80211_assoc_request req;
2960 struct wiphy *wiphy;
2961 int err;
2962
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002963 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
2964 return -EINVAL;
2965
2966 if (!info->attrs[NL80211_ATTR_MAC] ||
2967 !info->attrs[NL80211_ATTR_SSID])
2968 return -EINVAL;
2969
Jouni Malinen636a5d32009-03-19 13:39:22 +02002970 rtnl_lock();
2971
2972 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2973 if (err)
2974 goto unlock_rtnl;
2975
2976 if (!drv->ops->assoc) {
2977 err = -EOPNOTSUPP;
2978 goto out;
2979 }
2980
Jouni Malineneec60b02009-03-20 21:21:19 +02002981 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
2982 err = -EOPNOTSUPP;
2983 goto out;
2984 }
2985
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002986 if (!netif_running(dev)) {
2987 err = -ENETDOWN;
2988 goto out;
2989 }
2990
Jouni Malinen636a5d32009-03-19 13:39:22 +02002991 wiphy = &drv->wiphy;
2992 memset(&req, 0, sizeof(req));
2993
2994 req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2995
2996 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
2997 req.chan = ieee80211_get_channel(
2998 wiphy,
2999 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
3000 if (!req.chan) {
3001 err = -EINVAL;
3002 goto out;
3003 }
3004 }
3005
Jouni Malinen636a5d32009-03-19 13:39:22 +02003006 req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3007 req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
3008
3009 if (info->attrs[NL80211_ATTR_IE]) {
3010 req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3011 req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3012 }
3013
3014 err = drv->ops->assoc(&drv->wiphy, dev, &req);
3015
3016out:
3017 cfg80211_put_dev(drv);
3018 dev_put(dev);
3019unlock_rtnl:
3020 rtnl_unlock();
3021 return err;
3022}
3023
3024static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
3025{
3026 struct cfg80211_registered_device *drv;
3027 struct net_device *dev;
3028 struct cfg80211_deauth_request req;
3029 struct wiphy *wiphy;
3030 int err;
3031
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003032 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3033 return -EINVAL;
3034
3035 if (!info->attrs[NL80211_ATTR_MAC])
3036 return -EINVAL;
3037
3038 if (!info->attrs[NL80211_ATTR_REASON_CODE])
3039 return -EINVAL;
3040
Jouni Malinen636a5d32009-03-19 13:39:22 +02003041 rtnl_lock();
3042
3043 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
3044 if (err)
3045 goto unlock_rtnl;
3046
3047 if (!drv->ops->deauth) {
3048 err = -EOPNOTSUPP;
3049 goto out;
3050 }
3051
Jouni Malineneec60b02009-03-20 21:21:19 +02003052 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
3053 err = -EOPNOTSUPP;
3054 goto out;
3055 }
3056
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003057 if (!netif_running(dev)) {
3058 err = -ENETDOWN;
3059 goto out;
3060 }
3061
Jouni Malinen636a5d32009-03-19 13:39:22 +02003062 wiphy = &drv->wiphy;
3063 memset(&req, 0, sizeof(req));
3064
3065 req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
3066
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003067 req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
3068 if (req.reason_code == 0) {
3069 /* Reason Code 0 is reserved */
3070 err = -EINVAL;
3071 goto out;
Jouni Malinen255e7372009-03-20 21:21:17 +02003072 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02003073
3074 if (info->attrs[NL80211_ATTR_IE]) {
3075 req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3076 req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3077 }
3078
3079 err = drv->ops->deauth(&drv->wiphy, dev, &req);
3080
3081out:
3082 cfg80211_put_dev(drv);
3083 dev_put(dev);
3084unlock_rtnl:
3085 rtnl_unlock();
3086 return err;
3087}
3088
3089static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
3090{
3091 struct cfg80211_registered_device *drv;
3092 struct net_device *dev;
3093 struct cfg80211_disassoc_request req;
3094 struct wiphy *wiphy;
3095 int err;
3096
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003097 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3098 return -EINVAL;
3099
3100 if (!info->attrs[NL80211_ATTR_MAC])
3101 return -EINVAL;
3102
3103 if (!info->attrs[NL80211_ATTR_REASON_CODE])
3104 return -EINVAL;
3105
Jouni Malinen636a5d32009-03-19 13:39:22 +02003106 rtnl_lock();
3107
3108 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
3109 if (err)
3110 goto unlock_rtnl;
3111
3112 if (!drv->ops->disassoc) {
3113 err = -EOPNOTSUPP;
3114 goto out;
3115 }
3116
Jouni Malineneec60b02009-03-20 21:21:19 +02003117 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
3118 err = -EOPNOTSUPP;
3119 goto out;
3120 }
3121
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003122 if (!netif_running(dev)) {
3123 err = -ENETDOWN;
3124 goto out;
3125 }
3126
Jouni Malinen636a5d32009-03-19 13:39:22 +02003127 wiphy = &drv->wiphy;
3128 memset(&req, 0, sizeof(req));
3129
3130 req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
3131
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003132 req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
3133 if (req.reason_code == 0) {
3134 /* Reason Code 0 is reserved */
3135 err = -EINVAL;
3136 goto out;
Jouni Malinen255e7372009-03-20 21:21:17 +02003137 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02003138
3139 if (info->attrs[NL80211_ATTR_IE]) {
3140 req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3141 req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3142 }
3143
3144 err = drv->ops->disassoc(&drv->wiphy, dev, &req);
3145
3146out:
3147 cfg80211_put_dev(drv);
3148 dev_put(dev);
3149unlock_rtnl:
3150 rtnl_unlock();
3151 return err;
3152}
3153
Johannes Berg04a773a2009-04-19 21:24:32 +02003154static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
3155{
3156 struct cfg80211_registered_device *drv;
3157 struct net_device *dev;
3158 struct cfg80211_ibss_params ibss;
3159 struct wiphy *wiphy;
3160 int err;
3161
Johannes Berg8e30bc52009-04-22 17:45:38 +02003162 memset(&ibss, 0, sizeof(ibss));
3163
Johannes Berg04a773a2009-04-19 21:24:32 +02003164 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3165 return -EINVAL;
3166
3167 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
3168 !info->attrs[NL80211_ATTR_SSID] ||
3169 !nla_len(info->attrs[NL80211_ATTR_SSID]))
3170 return -EINVAL;
3171
Johannes Berg8e30bc52009-04-22 17:45:38 +02003172 ibss.beacon_interval = 100;
3173
3174 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
3175 ibss.beacon_interval =
3176 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
3177 if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000)
3178 return -EINVAL;
3179 }
3180
Johannes Berg04a773a2009-04-19 21:24:32 +02003181 rtnl_lock();
3182
3183 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
3184 if (err)
3185 goto unlock_rtnl;
3186
3187 if (!drv->ops->join_ibss) {
3188 err = -EOPNOTSUPP;
3189 goto out;
3190 }
3191
3192 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
3193 err = -EOPNOTSUPP;
3194 goto out;
3195 }
3196
3197 if (!netif_running(dev)) {
3198 err = -ENETDOWN;
3199 goto out;
3200 }
3201
3202 wiphy = &drv->wiphy;
Johannes Berg04a773a2009-04-19 21:24:32 +02003203
3204 if (info->attrs[NL80211_ATTR_MAC])
3205 ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
3206 ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3207 ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
3208
3209 if (info->attrs[NL80211_ATTR_IE]) {
3210 ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3211 ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3212 }
3213
3214 ibss.channel = ieee80211_get_channel(wiphy,
3215 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
3216 if (!ibss.channel ||
3217 ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
3218 ibss.channel->flags & IEEE80211_CHAN_DISABLED) {
3219 err = -EINVAL;
3220 goto out;
3221 }
3222
3223 ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
3224
3225 err = cfg80211_join_ibss(drv, dev, &ibss);
3226
3227out:
3228 cfg80211_put_dev(drv);
3229 dev_put(dev);
3230unlock_rtnl:
3231 rtnl_unlock();
3232 return err;
3233}
3234
3235static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
3236{
3237 struct cfg80211_registered_device *drv;
3238 struct net_device *dev;
3239 int err;
3240
3241 rtnl_lock();
3242
3243 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
3244 if (err)
3245 goto unlock_rtnl;
3246
3247 if (!drv->ops->leave_ibss) {
3248 err = -EOPNOTSUPP;
3249 goto out;
3250 }
3251
3252 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
3253 err = -EOPNOTSUPP;
3254 goto out;
3255 }
3256
3257 if (!netif_running(dev)) {
3258 err = -ENETDOWN;
3259 goto out;
3260 }
3261
Johannes Berg9d308422009-04-20 18:43:46 +02003262 err = cfg80211_leave_ibss(drv, dev, false);
Johannes Berg04a773a2009-04-19 21:24:32 +02003263
3264out:
3265 cfg80211_put_dev(drv);
3266 dev_put(dev);
3267unlock_rtnl:
3268 rtnl_unlock();
3269 return err;
3270}
3271
Johannes Berg55682962007-09-20 13:09:35 -04003272static struct genl_ops nl80211_ops[] = {
3273 {
3274 .cmd = NL80211_CMD_GET_WIPHY,
3275 .doit = nl80211_get_wiphy,
3276 .dumpit = nl80211_dump_wiphy,
3277 .policy = nl80211_policy,
3278 /* can be retrieved by unprivileged users */
3279 },
3280 {
3281 .cmd = NL80211_CMD_SET_WIPHY,
3282 .doit = nl80211_set_wiphy,
3283 .policy = nl80211_policy,
3284 .flags = GENL_ADMIN_PERM,
3285 },
3286 {
3287 .cmd = NL80211_CMD_GET_INTERFACE,
3288 .doit = nl80211_get_interface,
3289 .dumpit = nl80211_dump_interface,
3290 .policy = nl80211_policy,
3291 /* can be retrieved by unprivileged users */
3292 },
3293 {
3294 .cmd = NL80211_CMD_SET_INTERFACE,
3295 .doit = nl80211_set_interface,
3296 .policy = nl80211_policy,
3297 .flags = GENL_ADMIN_PERM,
3298 },
3299 {
3300 .cmd = NL80211_CMD_NEW_INTERFACE,
3301 .doit = nl80211_new_interface,
3302 .policy = nl80211_policy,
3303 .flags = GENL_ADMIN_PERM,
3304 },
3305 {
3306 .cmd = NL80211_CMD_DEL_INTERFACE,
3307 .doit = nl80211_del_interface,
3308 .policy = nl80211_policy,
3309 .flags = GENL_ADMIN_PERM,
3310 },
Johannes Berg41ade002007-12-19 02:03:29 +01003311 {
3312 .cmd = NL80211_CMD_GET_KEY,
3313 .doit = nl80211_get_key,
3314 .policy = nl80211_policy,
3315 .flags = GENL_ADMIN_PERM,
3316 },
3317 {
3318 .cmd = NL80211_CMD_SET_KEY,
3319 .doit = nl80211_set_key,
3320 .policy = nl80211_policy,
3321 .flags = GENL_ADMIN_PERM,
3322 },
3323 {
3324 .cmd = NL80211_CMD_NEW_KEY,
3325 .doit = nl80211_new_key,
3326 .policy = nl80211_policy,
3327 .flags = GENL_ADMIN_PERM,
3328 },
3329 {
3330 .cmd = NL80211_CMD_DEL_KEY,
3331 .doit = nl80211_del_key,
3332 .policy = nl80211_policy,
3333 .flags = GENL_ADMIN_PERM,
3334 },
Johannes Berged1b6cc2007-12-19 02:03:32 +01003335 {
3336 .cmd = NL80211_CMD_SET_BEACON,
3337 .policy = nl80211_policy,
3338 .flags = GENL_ADMIN_PERM,
3339 .doit = nl80211_addset_beacon,
3340 },
3341 {
3342 .cmd = NL80211_CMD_NEW_BEACON,
3343 .policy = nl80211_policy,
3344 .flags = GENL_ADMIN_PERM,
3345 .doit = nl80211_addset_beacon,
3346 },
3347 {
3348 .cmd = NL80211_CMD_DEL_BEACON,
3349 .policy = nl80211_policy,
3350 .flags = GENL_ADMIN_PERM,
3351 .doit = nl80211_del_beacon,
3352 },
Johannes Berg5727ef12007-12-19 02:03:34 +01003353 {
3354 .cmd = NL80211_CMD_GET_STATION,
3355 .doit = nl80211_get_station,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003356 .dumpit = nl80211_dump_station,
Johannes Berg5727ef12007-12-19 02:03:34 +01003357 .policy = nl80211_policy,
Johannes Berg5727ef12007-12-19 02:03:34 +01003358 },
3359 {
3360 .cmd = NL80211_CMD_SET_STATION,
3361 .doit = nl80211_set_station,
3362 .policy = nl80211_policy,
3363 .flags = GENL_ADMIN_PERM,
3364 },
3365 {
3366 .cmd = NL80211_CMD_NEW_STATION,
3367 .doit = nl80211_new_station,
3368 .policy = nl80211_policy,
3369 .flags = GENL_ADMIN_PERM,
3370 },
3371 {
3372 .cmd = NL80211_CMD_DEL_STATION,
3373 .doit = nl80211_del_station,
3374 .policy = nl80211_policy,
3375 .flags = GENL_ADMIN_PERM,
3376 },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003377 {
3378 .cmd = NL80211_CMD_GET_MPATH,
3379 .doit = nl80211_get_mpath,
3380 .dumpit = nl80211_dump_mpath,
3381 .policy = nl80211_policy,
3382 .flags = GENL_ADMIN_PERM,
3383 },
3384 {
3385 .cmd = NL80211_CMD_SET_MPATH,
3386 .doit = nl80211_set_mpath,
3387 .policy = nl80211_policy,
3388 .flags = GENL_ADMIN_PERM,
3389 },
3390 {
3391 .cmd = NL80211_CMD_NEW_MPATH,
3392 .doit = nl80211_new_mpath,
3393 .policy = nl80211_policy,
3394 .flags = GENL_ADMIN_PERM,
3395 },
3396 {
3397 .cmd = NL80211_CMD_DEL_MPATH,
3398 .doit = nl80211_del_mpath,
3399 .policy = nl80211_policy,
3400 .flags = GENL_ADMIN_PERM,
3401 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003402 {
3403 .cmd = NL80211_CMD_SET_BSS,
3404 .doit = nl80211_set_bss,
3405 .policy = nl80211_policy,
3406 .flags = GENL_ADMIN_PERM,
3407 },
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003408 {
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003409 .cmd = NL80211_CMD_GET_REG,
3410 .doit = nl80211_get_reg,
3411 .policy = nl80211_policy,
3412 /* can be retrieved by unprivileged users */
3413 },
3414 {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003415 .cmd = NL80211_CMD_SET_REG,
3416 .doit = nl80211_set_reg,
3417 .policy = nl80211_policy,
3418 .flags = GENL_ADMIN_PERM,
3419 },
3420 {
3421 .cmd = NL80211_CMD_REQ_SET_REG,
3422 .doit = nl80211_req_set_reg,
3423 .policy = nl80211_policy,
3424 .flags = GENL_ADMIN_PERM,
3425 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003426 {
3427 .cmd = NL80211_CMD_GET_MESH_PARAMS,
3428 .doit = nl80211_get_mesh_params,
3429 .policy = nl80211_policy,
3430 /* can be retrieved by unprivileged users */
3431 },
3432 {
3433 .cmd = NL80211_CMD_SET_MESH_PARAMS,
3434 .doit = nl80211_set_mesh_params,
3435 .policy = nl80211_policy,
3436 .flags = GENL_ADMIN_PERM,
3437 },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +02003438 {
Johannes Berg2a519312009-02-10 21:25:55 +01003439 .cmd = NL80211_CMD_TRIGGER_SCAN,
3440 .doit = nl80211_trigger_scan,
3441 .policy = nl80211_policy,
3442 .flags = GENL_ADMIN_PERM,
3443 },
3444 {
3445 .cmd = NL80211_CMD_GET_SCAN,
3446 .policy = nl80211_policy,
3447 .dumpit = nl80211_dump_scan,
3448 },
Jouni Malinen636a5d32009-03-19 13:39:22 +02003449 {
3450 .cmd = NL80211_CMD_AUTHENTICATE,
3451 .doit = nl80211_authenticate,
3452 .policy = nl80211_policy,
3453 .flags = GENL_ADMIN_PERM,
3454 },
3455 {
3456 .cmd = NL80211_CMD_ASSOCIATE,
3457 .doit = nl80211_associate,
3458 .policy = nl80211_policy,
3459 .flags = GENL_ADMIN_PERM,
3460 },
3461 {
3462 .cmd = NL80211_CMD_DEAUTHENTICATE,
3463 .doit = nl80211_deauthenticate,
3464 .policy = nl80211_policy,
3465 .flags = GENL_ADMIN_PERM,
3466 },
3467 {
3468 .cmd = NL80211_CMD_DISASSOCIATE,
3469 .doit = nl80211_disassociate,
3470 .policy = nl80211_policy,
3471 .flags = GENL_ADMIN_PERM,
3472 },
Johannes Berg04a773a2009-04-19 21:24:32 +02003473 {
3474 .cmd = NL80211_CMD_JOIN_IBSS,
3475 .doit = nl80211_join_ibss,
3476 .policy = nl80211_policy,
3477 .flags = GENL_ADMIN_PERM,
3478 },
3479 {
3480 .cmd = NL80211_CMD_LEAVE_IBSS,
3481 .doit = nl80211_leave_ibss,
3482 .policy = nl80211_policy,
3483 .flags = GENL_ADMIN_PERM,
3484 },
Johannes Berg55682962007-09-20 13:09:35 -04003485};
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003486static struct genl_multicast_group nl80211_mlme_mcgrp = {
3487 .name = "mlme",
3488};
Johannes Berg55682962007-09-20 13:09:35 -04003489
3490/* multicast groups */
3491static struct genl_multicast_group nl80211_config_mcgrp = {
3492 .name = "config",
3493};
Johannes Berg2a519312009-02-10 21:25:55 +01003494static struct genl_multicast_group nl80211_scan_mcgrp = {
3495 .name = "scan",
3496};
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04003497static struct genl_multicast_group nl80211_regulatory_mcgrp = {
3498 .name = "regulatory",
3499};
Johannes Berg55682962007-09-20 13:09:35 -04003500
3501/* notification functions */
3502
3503void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
3504{
3505 struct sk_buff *msg;
3506
3507 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3508 if (!msg)
3509 return;
3510
3511 if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
3512 nlmsg_free(msg);
3513 return;
3514 }
3515
3516 genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL);
3517}
3518
Johannes Berg2a519312009-02-10 21:25:55 +01003519static int nl80211_send_scan_donemsg(struct sk_buff *msg,
3520 struct cfg80211_registered_device *rdev,
3521 struct net_device *netdev,
3522 u32 pid, u32 seq, int flags,
3523 u32 cmd)
3524{
3525 void *hdr;
3526
3527 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
3528 if (!hdr)
3529 return -1;
3530
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -05003531 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg2a519312009-02-10 21:25:55 +01003532 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
3533
3534 /* XXX: we should probably bounce back the request? */
3535
3536 return genlmsg_end(msg, hdr);
3537
3538 nla_put_failure:
3539 genlmsg_cancel(msg, hdr);
3540 return -EMSGSIZE;
3541}
3542
3543void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
3544 struct net_device *netdev)
3545{
3546 struct sk_buff *msg;
3547
3548 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3549 if (!msg)
3550 return;
3551
3552 if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0,
3553 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
3554 nlmsg_free(msg);
3555 return;
3556 }
3557
3558 genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL);
3559}
3560
3561void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
3562 struct net_device *netdev)
3563{
3564 struct sk_buff *msg;
3565
3566 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3567 if (!msg)
3568 return;
3569
3570 if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0,
3571 NL80211_CMD_SCAN_ABORTED) < 0) {
3572 nlmsg_free(msg);
3573 return;
3574 }
3575
3576 genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL);
3577}
3578
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04003579/*
3580 * This can happen on global regulatory changes or device specific settings
3581 * based on custom world regulatory domains.
3582 */
3583void nl80211_send_reg_change_event(struct regulatory_request *request)
3584{
3585 struct sk_buff *msg;
3586 void *hdr;
3587
3588 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3589 if (!msg)
3590 return;
3591
3592 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
3593 if (!hdr) {
3594 nlmsg_free(msg);
3595 return;
3596 }
3597
3598 /* Userspace can always count this one always being set */
3599 NLA_PUT_U8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator);
3600
3601 if (request->alpha2[0] == '0' && request->alpha2[1] == '0')
3602 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
3603 NL80211_REGDOM_TYPE_WORLD);
3604 else if (request->alpha2[0] == '9' && request->alpha2[1] == '9')
3605 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
3606 NL80211_REGDOM_TYPE_CUSTOM_WORLD);
3607 else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
3608 request->intersect)
3609 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
3610 NL80211_REGDOM_TYPE_INTERSECTION);
3611 else {
3612 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
3613 NL80211_REGDOM_TYPE_COUNTRY);
3614 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2);
3615 }
3616
3617 if (wiphy_idx_valid(request->wiphy_idx))
3618 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx);
3619
3620 if (genlmsg_end(msg, hdr) < 0) {
3621 nlmsg_free(msg);
3622 return;
3623 }
3624
3625 genlmsg_multicast(msg, 0, nl80211_regulatory_mcgrp.id, GFP_KERNEL);
3626
3627 return;
3628
3629nla_put_failure:
3630 genlmsg_cancel(msg, hdr);
3631 nlmsg_free(msg);
3632}
3633
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003634static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
3635 struct net_device *netdev,
3636 const u8 *buf, size_t len,
3637 enum nl80211_commands cmd)
3638{
3639 struct sk_buff *msg;
3640 void *hdr;
3641
Jouni Malinend91c01c2009-04-18 21:53:15 +03003642 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003643 if (!msg)
3644 return;
3645
3646 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
3647 if (!hdr) {
3648 nlmsg_free(msg);
3649 return;
3650 }
3651
3652 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
3653 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
3654 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
3655
3656 if (genlmsg_end(msg, hdr) < 0) {
3657 nlmsg_free(msg);
3658 return;
3659 }
3660
Jouni Malinend91c01c2009-04-18 21:53:15 +03003661 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_ATOMIC);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003662 return;
3663
3664 nla_put_failure:
3665 genlmsg_cancel(msg, hdr);
3666 nlmsg_free(msg);
3667}
3668
3669void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
3670 struct net_device *netdev, const u8 *buf, size_t len)
3671{
3672 nl80211_send_mlme_event(rdev, netdev, buf, len,
3673 NL80211_CMD_AUTHENTICATE);
3674}
3675
3676void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
3677 struct net_device *netdev, const u8 *buf,
3678 size_t len)
3679{
3680 nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_ASSOCIATE);
3681}
3682
Jouni Malinen53b46b82009-03-27 20:53:56 +02003683void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
3684 struct net_device *netdev, const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003685{
3686 nl80211_send_mlme_event(rdev, netdev, buf, len,
3687 NL80211_CMD_DEAUTHENTICATE);
3688}
3689
Jouni Malinen53b46b82009-03-27 20:53:56 +02003690void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
3691 struct net_device *netdev, const u8 *buf,
3692 size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003693{
3694 nl80211_send_mlme_event(rdev, netdev, buf, len,
3695 NL80211_CMD_DISASSOCIATE);
3696}
3697
Johannes Berg04a773a2009-04-19 21:24:32 +02003698void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
3699 struct net_device *netdev, const u8 *bssid,
3700 gfp_t gfp)
3701{
3702 struct sk_buff *msg;
3703 void *hdr;
3704
3705 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
3706 if (!msg)
3707 return;
3708
3709 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS);
3710 if (!hdr) {
3711 nlmsg_free(msg);
3712 return;
3713 }
3714
3715 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
3716 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
3717 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
3718
3719 if (genlmsg_end(msg, hdr) < 0) {
3720 nlmsg_free(msg);
3721 return;
3722 }
3723
3724 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
3725 return;
3726
3727 nla_put_failure:
3728 genlmsg_cancel(msg, hdr);
3729 nlmsg_free(msg);
3730}
3731
Jouni Malinena3b8b052009-03-27 21:59:49 +02003732void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
3733 struct net_device *netdev, const u8 *addr,
3734 enum nl80211_key_type key_type, int key_id,
3735 const u8 *tsc)
3736{
3737 struct sk_buff *msg;
3738 void *hdr;
3739
3740 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3741 if (!msg)
3742 return;
3743
3744 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
3745 if (!hdr) {
3746 nlmsg_free(msg);
3747 return;
3748 }
3749
3750 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
3751 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
3752 if (addr)
3753 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
3754 NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, key_type);
3755 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id);
3756 if (tsc)
3757 NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc);
3758
3759 if (genlmsg_end(msg, hdr) < 0) {
3760 nlmsg_free(msg);
3761 return;
3762 }
3763
3764 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_KERNEL);
3765 return;
3766
3767 nla_put_failure:
3768 genlmsg_cancel(msg, hdr);
3769 nlmsg_free(msg);
3770}
3771
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04003772void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
3773 struct ieee80211_channel *channel_before,
3774 struct ieee80211_channel *channel_after)
3775{
3776 struct sk_buff *msg;
3777 void *hdr;
3778 struct nlattr *nl_freq;
3779
3780 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
3781 if (!msg)
3782 return;
3783
3784 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
3785 if (!hdr) {
3786 nlmsg_free(msg);
3787 return;
3788 }
3789
3790 /*
3791 * Since we are applying the beacon hint to a wiphy we know its
3792 * wiphy_idx is valid
3793 */
3794 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy));
3795
3796 /* Before */
3797 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
3798 if (!nl_freq)
3799 goto nla_put_failure;
3800 if (nl80211_msg_put_channel(msg, channel_before))
3801 goto nla_put_failure;
3802 nla_nest_end(msg, nl_freq);
3803
3804 /* After */
3805 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
3806 if (!nl_freq)
3807 goto nla_put_failure;
3808 if (nl80211_msg_put_channel(msg, channel_after))
3809 goto nla_put_failure;
3810 nla_nest_end(msg, nl_freq);
3811
3812 if (genlmsg_end(msg, hdr) < 0) {
3813 nlmsg_free(msg);
3814 return;
3815 }
3816
3817 genlmsg_multicast(msg, 0, nl80211_regulatory_mcgrp.id, GFP_ATOMIC);
3818
3819 return;
3820
3821nla_put_failure:
3822 genlmsg_cancel(msg, hdr);
3823 nlmsg_free(msg);
3824}
3825
Johannes Berg55682962007-09-20 13:09:35 -04003826/* initialisation/exit functions */
3827
3828int nl80211_init(void)
3829{
3830 int err, i;
3831
3832 err = genl_register_family(&nl80211_fam);
3833 if (err)
3834 return err;
3835
3836 for (i = 0; i < ARRAY_SIZE(nl80211_ops); i++) {
3837 err = genl_register_ops(&nl80211_fam, &nl80211_ops[i]);
3838 if (err)
3839 goto err_out;
3840 }
3841
3842 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
3843 if (err)
3844 goto err_out;
3845
Johannes Berg2a519312009-02-10 21:25:55 +01003846 err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
3847 if (err)
3848 goto err_out;
3849
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04003850 err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
3851 if (err)
3852 goto err_out;
3853
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003854 err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
3855 if (err)
3856 goto err_out;
3857
Johannes Berg55682962007-09-20 13:09:35 -04003858 return 0;
3859 err_out:
3860 genl_unregister_family(&nl80211_fam);
3861 return err;
3862}
3863
3864void nl80211_exit(void)
3865{
3866 genl_unregister_family(&nl80211_fam);
3867}