blob: cad281390cfa2750fcedafc4285b9a7137fa687e [file] [log] [blame]
Johannes Berg55682962007-09-20 13:09:35 -04001/*
2 * This is the new netlink-based wireless configuration interface.
3 *
Johannes Berg08645122009-05-11 13:54:58 +02004 * Copyright 2006-2009 Johannes Berg <johannes@sipsolutions.net>
Johannes Berg55682962007-09-20 13:09:35 -04005 */
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 Malinen36aedc902008-08-25 11:58:58 +0300107
colin@cozybit.com93da9cc2008-10-21 12:03:48 -0700108 [NL80211_ATTR_MESH_PARAMS] = { .type = NLA_NESTED },
109
Jouni Malinen36aedc902008-08-25 11:58:58 +0300110 [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY,
111 .len = NL80211_HT_CAPABILITY_LEN },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +0200112
113 [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
114 [NL80211_ATTR_IE] = { .type = NLA_BINARY,
115 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg2a519312009-02-10 21:25:55 +0100116 [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
117 [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
Jouni Malinen636a5d32009-03-19 13:39:22 +0200118
119 [NL80211_ATTR_SSID] = { .type = NLA_BINARY,
120 .len = IEEE80211_MAX_SSID_LEN },
121 [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
122 [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
Johannes Berg04a773a2009-04-19 21:24:32 +0200123 [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
Jouni Malinen1965c852009-04-22 21:38:25 +0300124 [NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG },
Jouni Malinendc6382c2009-05-06 22:09:37 +0300125 [NL80211_ATTR_USE_MFP] = { .type = NLA_U32 },
Johannes Bergeccb8e82009-05-11 21:57:56 +0300126 [NL80211_ATTR_STA_FLAGS2] = {
127 .len = sizeof(struct nl80211_sta_flag_update),
128 },
Jouni Malinen3f77316c2009-05-11 21:57:57 +0300129 [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG },
Johannes Berg55682962007-09-20 13:09:35 -0400130};
131
Johannes Bergf4a11bb2009-03-27 12:40:28 +0100132/* IE validation */
133static bool is_valid_ie_attr(const struct nlattr *attr)
134{
135 const u8 *pos;
136 int len;
137
138 if (!attr)
139 return true;
140
141 pos = nla_data(attr);
142 len = nla_len(attr);
143
144 while (len) {
145 u8 elemlen;
146
147 if (len < 2)
148 return false;
149 len -= 2;
150
151 elemlen = pos[1];
152 if (elemlen > len)
153 return false;
154
155 len -= elemlen;
156 pos += 2 + elemlen;
157 }
158
159 return true;
160}
161
Johannes Berg55682962007-09-20 13:09:35 -0400162/* message building helper */
163static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
164 int flags, u8 cmd)
165{
166 /* since there is no private header just add the generic one */
167 return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
168}
169
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400170static int nl80211_msg_put_channel(struct sk_buff *msg,
171 struct ieee80211_channel *chan)
172{
173 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
174 chan->center_freq);
175
176 if (chan->flags & IEEE80211_CHAN_DISABLED)
177 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
178 if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
179 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
180 if (chan->flags & IEEE80211_CHAN_NO_IBSS)
181 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
182 if (chan->flags & IEEE80211_CHAN_RADAR)
183 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
184
185 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
186 DBM_TO_MBM(chan->max_power));
187
188 return 0;
189
190 nla_put_failure:
191 return -ENOBUFS;
192}
193
Johannes Berg55682962007-09-20 13:09:35 -0400194/* netlink command implementations */
195
196static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
197 struct cfg80211_registered_device *dev)
198{
199 void *hdr;
Johannes Bergee688b002008-01-24 19:38:39 +0100200 struct nlattr *nl_bands, *nl_band;
201 struct nlattr *nl_freqs, *nl_freq;
202 struct nlattr *nl_rates, *nl_rate;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700203 struct nlattr *nl_modes;
Johannes Berg8fdc6212009-03-14 09:34:01 +0100204 struct nlattr *nl_cmds;
Johannes Bergee688b002008-01-24 19:38:39 +0100205 enum ieee80211_band band;
206 struct ieee80211_channel *chan;
207 struct ieee80211_rate *rate;
208 int i;
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700209 u16 ifmodes = dev->wiphy.interface_modes;
Johannes Berg55682962007-09-20 13:09:35 -0400210
211 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
212 if (!hdr)
213 return -1;
214
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -0500215 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400216 NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200217
218 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
219 dev->wiphy.retry_short);
220 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
221 dev->wiphy.retry_long);
222 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
223 dev->wiphy.frag_threshold);
224 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
225 dev->wiphy.rts_threshold);
226
Johannes Berg2a519312009-02-10 21:25:55 +0100227 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
228 dev->wiphy.max_scan_ssids);
Johannes Berg18a83652009-03-31 12:12:05 +0200229 NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
230 dev->wiphy.max_scan_ie_len);
Johannes Bergee688b002008-01-24 19:38:39 +0100231
Johannes Berg25e47c12009-04-02 20:14:06 +0200232 NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
233 sizeof(u32) * dev->wiphy.n_cipher_suites,
234 dev->wiphy.cipher_suites);
235
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700236 nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES);
237 if (!nl_modes)
238 goto nla_put_failure;
239
240 i = 0;
241 while (ifmodes) {
242 if (ifmodes & 1)
243 NLA_PUT_FLAG(msg, i);
244 ifmodes >>= 1;
245 i++;
246 }
247
248 nla_nest_end(msg, nl_modes);
249
Johannes Bergee688b002008-01-24 19:38:39 +0100250 nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
251 if (!nl_bands)
252 goto nla_put_failure;
253
254 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
255 if (!dev->wiphy.bands[band])
256 continue;
257
258 nl_band = nla_nest_start(msg, band);
259 if (!nl_band)
260 goto nla_put_failure;
261
Johannes Bergd51626d2008-10-09 12:20:13 +0200262 /* add HT info */
263 if (dev->wiphy.bands[band]->ht_cap.ht_supported) {
264 NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET,
265 sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
266 &dev->wiphy.bands[band]->ht_cap.mcs);
267 NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA,
268 dev->wiphy.bands[band]->ht_cap.cap);
269 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
270 dev->wiphy.bands[band]->ht_cap.ampdu_factor);
271 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
272 dev->wiphy.bands[band]->ht_cap.ampdu_density);
273 }
274
Johannes Bergee688b002008-01-24 19:38:39 +0100275 /* add frequencies */
276 nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
277 if (!nl_freqs)
278 goto nla_put_failure;
279
280 for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
281 nl_freq = nla_nest_start(msg, i);
282 if (!nl_freq)
283 goto nla_put_failure;
284
285 chan = &dev->wiphy.bands[band]->channels[i];
Johannes Bergee688b002008-01-24 19:38:39 +0100286
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400287 if (nl80211_msg_put_channel(msg, chan))
288 goto nla_put_failure;
Jouni Malinene2f367f262008-11-21 19:01:30 +0200289
Johannes Bergee688b002008-01-24 19:38:39 +0100290 nla_nest_end(msg, nl_freq);
291 }
292
293 nla_nest_end(msg, nl_freqs);
294
295 /* add bitrates */
296 nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
297 if (!nl_rates)
298 goto nla_put_failure;
299
300 for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
301 nl_rate = nla_nest_start(msg, i);
302 if (!nl_rate)
303 goto nla_put_failure;
304
305 rate = &dev->wiphy.bands[band]->bitrates[i];
306 NLA_PUT_U32(msg, NL80211_BITRATE_ATTR_RATE,
307 rate->bitrate);
308 if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
309 NLA_PUT_FLAG(msg,
310 NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE);
311
312 nla_nest_end(msg, nl_rate);
313 }
314
315 nla_nest_end(msg, nl_rates);
316
317 nla_nest_end(msg, nl_band);
318 }
319 nla_nest_end(msg, nl_bands);
320
Johannes Berg8fdc6212009-03-14 09:34:01 +0100321 nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
322 if (!nl_cmds)
323 goto nla_put_failure;
324
325 i = 0;
326#define CMD(op, n) \
327 do { \
328 if (dev->ops->op) { \
329 i++; \
330 NLA_PUT_U32(msg, i, NL80211_CMD_ ## n); \
331 } \
332 } while (0)
333
334 CMD(add_virtual_intf, NEW_INTERFACE);
335 CMD(change_virtual_intf, SET_INTERFACE);
336 CMD(add_key, NEW_KEY);
337 CMD(add_beacon, NEW_BEACON);
338 CMD(add_station, NEW_STATION);
339 CMD(add_mpath, NEW_MPATH);
340 CMD(set_mesh_params, SET_MESH_PARAMS);
341 CMD(change_bss, SET_BSS);
Jouni Malinen636a5d32009-03-19 13:39:22 +0200342 CMD(auth, AUTHENTICATE);
343 CMD(assoc, ASSOCIATE);
344 CMD(deauth, DEAUTHENTICATE);
345 CMD(disassoc, DISASSOCIATE);
Johannes Berg04a773a2009-04-19 21:24:32 +0200346 CMD(join_ibss, JOIN_IBSS);
Johannes Berg8fdc6212009-03-14 09:34:01 +0100347
348#undef CMD
349 nla_nest_end(msg, nl_cmds);
350
Johannes Berg55682962007-09-20 13:09:35 -0400351 return genlmsg_end(msg, hdr);
352
353 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700354 genlmsg_cancel(msg, hdr);
355 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400356}
357
358static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
359{
360 int idx = 0;
361 int start = cb->args[0];
362 struct cfg80211_registered_device *dev;
363
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500364 mutex_lock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400365 list_for_each_entry(dev, &cfg80211_drv_list, list) {
Julius Volzb4637272008-07-08 14:02:19 +0200366 if (++idx <= start)
Johannes Berg55682962007-09-20 13:09:35 -0400367 continue;
368 if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
369 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Julius Volzb4637272008-07-08 14:02:19 +0200370 dev) < 0) {
371 idx--;
Johannes Berg55682962007-09-20 13:09:35 -0400372 break;
Julius Volzb4637272008-07-08 14:02:19 +0200373 }
Johannes Berg55682962007-09-20 13:09:35 -0400374 }
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500375 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400376
377 cb->args[0] = idx;
378
379 return skb->len;
380}
381
382static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
383{
384 struct sk_buff *msg;
385 struct cfg80211_registered_device *dev;
386
387 dev = cfg80211_get_dev_from_info(info);
388 if (IS_ERR(dev))
389 return PTR_ERR(dev);
390
391 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
392 if (!msg)
393 goto out_err;
394
395 if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0)
396 goto out_free;
397
398 cfg80211_put_dev(dev);
399
400 return genlmsg_unicast(msg, info->snd_pid);
401
402 out_free:
403 nlmsg_free(msg);
404 out_err:
405 cfg80211_put_dev(dev);
406 return -ENOBUFS;
407}
408
Jouni Malinen31888482008-10-30 16:59:24 +0200409static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
410 [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
411 [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
412 [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
413 [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
414 [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
415};
416
417static int parse_txq_params(struct nlattr *tb[],
418 struct ieee80211_txq_params *txq_params)
419{
420 if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] ||
421 !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
422 !tb[NL80211_TXQ_ATTR_AIFS])
423 return -EINVAL;
424
425 txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]);
426 txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
427 txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
428 txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
429 txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
430
431 return 0;
432}
433
Johannes Berg55682962007-09-20 13:09:35 -0400434static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
435{
436 struct cfg80211_registered_device *rdev;
Jouni Malinen31888482008-10-30 16:59:24 +0200437 int result = 0, rem_txq_params = 0;
438 struct nlattr *nl_txq_params;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200439 u32 changed;
440 u8 retry_short = 0, retry_long = 0;
441 u32 frag_threshold = 0, rts_threshold = 0;
Johannes Berg55682962007-09-20 13:09:35 -0400442
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100443 rtnl_lock();
Johannes Berg55682962007-09-20 13:09:35 -0400444
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100445 mutex_lock(&cfg80211_mutex);
446
447 rdev = __cfg80211_drv_from_info(info);
448 if (IS_ERR(rdev)) {
449 result = PTR_ERR(rdev);
450 goto unlock;
451 }
452
453 mutex_lock(&rdev->mtx);
454
455 if (info->attrs[NL80211_ATTR_WIPHY_NAME])
Jouni Malinen31888482008-10-30 16:59:24 +0200456 result = cfg80211_dev_rename(
457 rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100458
459 mutex_unlock(&cfg80211_mutex);
460
461 if (result)
462 goto bad_res;
Johannes Berg55682962007-09-20 13:09:35 -0400463
Jouni Malinen31888482008-10-30 16:59:24 +0200464 if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
465 struct ieee80211_txq_params txq_params;
466 struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
467
468 if (!rdev->ops->set_txq_params) {
469 result = -EOPNOTSUPP;
470 goto bad_res;
471 }
472
473 nla_for_each_nested(nl_txq_params,
474 info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
475 rem_txq_params) {
476 nla_parse(tb, NL80211_TXQ_ATTR_MAX,
477 nla_data(nl_txq_params),
478 nla_len(nl_txq_params),
479 txq_params_policy);
480 result = parse_txq_params(tb, &txq_params);
481 if (result)
482 goto bad_res;
483
484 result = rdev->ops->set_txq_params(&rdev->wiphy,
485 &txq_params);
486 if (result)
487 goto bad_res;
488 }
489 }
490
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200491 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Sujith094d05d2008-12-12 11:57:43 +0530492 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200493 struct ieee80211_channel *chan;
Johannes Berg306d6112008-12-08 12:39:04 +0100494 struct ieee80211_sta_ht_cap *ht_cap;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200495 u32 freq, sec_freq;
496
497 if (!rdev->ops->set_channel) {
498 result = -EOPNOTSUPP;
499 goto bad_res;
500 }
501
Johannes Berg306d6112008-12-08 12:39:04 +0100502 result = -EINVAL;
503
Sujith094d05d2008-12-12 11:57:43 +0530504 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
505 channel_type = nla_get_u32(info->attrs[
506 NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
507 if (channel_type != NL80211_CHAN_NO_HT &&
508 channel_type != NL80211_CHAN_HT20 &&
509 channel_type != NL80211_CHAN_HT40PLUS &&
510 channel_type != NL80211_CHAN_HT40MINUS)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200511 goto bad_res;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200512 }
513
514 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
515 chan = ieee80211_get_channel(&rdev->wiphy, freq);
Johannes Berg306d6112008-12-08 12:39:04 +0100516
517 /* Primary channel not allowed */
518 if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200519 goto bad_res;
Johannes Berg306d6112008-12-08 12:39:04 +0100520
Sujith094d05d2008-12-12 11:57:43 +0530521 if (channel_type == NL80211_CHAN_HT40MINUS)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200522 sec_freq = freq - 20;
Sujith094d05d2008-12-12 11:57:43 +0530523 else if (channel_type == NL80211_CHAN_HT40PLUS)
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200524 sec_freq = freq + 20;
525 else
526 sec_freq = 0;
527
Johannes Berg306d6112008-12-08 12:39:04 +0100528 ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
529
530 /* no HT capabilities */
Sujith094d05d2008-12-12 11:57:43 +0530531 if (channel_type != NL80211_CHAN_NO_HT &&
Johannes Berg306d6112008-12-08 12:39:04 +0100532 !ht_cap->ht_supported)
533 goto bad_res;
534
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200535 if (sec_freq) {
536 struct ieee80211_channel *schan;
Johannes Berg306d6112008-12-08 12:39:04 +0100537
538 /* no 40 MHz capabilities */
539 if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
540 (ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200541 goto bad_res;
Johannes Berg306d6112008-12-08 12:39:04 +0100542
543 schan = ieee80211_get_channel(&rdev->wiphy, sec_freq);
544
545 /* Secondary channel not allowed */
546 if (!schan || schan->flags & IEEE80211_CHAN_DISABLED)
547 goto bad_res;
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200548 }
549
550 result = rdev->ops->set_channel(&rdev->wiphy, chan,
Sujith094d05d2008-12-12 11:57:43 +0530551 channel_type);
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200552 if (result)
553 goto bad_res;
554 }
555
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200556 changed = 0;
557
558 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {
559 retry_short = nla_get_u8(
560 info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]);
561 if (retry_short == 0) {
562 result = -EINVAL;
563 goto bad_res;
564 }
565 changed |= WIPHY_PARAM_RETRY_SHORT;
566 }
567
568 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {
569 retry_long = nla_get_u8(
570 info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]);
571 if (retry_long == 0) {
572 result = -EINVAL;
573 goto bad_res;
574 }
575 changed |= WIPHY_PARAM_RETRY_LONG;
576 }
577
578 if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
579 frag_threshold = nla_get_u32(
580 info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
581 if (frag_threshold < 256) {
582 result = -EINVAL;
583 goto bad_res;
584 }
585 if (frag_threshold != (u32) -1) {
586 /*
587 * Fragments (apart from the last one) are required to
588 * have even length. Make the fragmentation code
589 * simpler by stripping LSB should someone try to use
590 * odd threshold value.
591 */
592 frag_threshold &= ~0x1;
593 }
594 changed |= WIPHY_PARAM_FRAG_THRESHOLD;
595 }
596
597 if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
598 rts_threshold = nla_get_u32(
599 info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
600 changed |= WIPHY_PARAM_RTS_THRESHOLD;
601 }
602
603 if (changed) {
604 u8 old_retry_short, old_retry_long;
605 u32 old_frag_threshold, old_rts_threshold;
606
607 if (!rdev->ops->set_wiphy_params) {
608 result = -EOPNOTSUPP;
609 goto bad_res;
610 }
611
612 old_retry_short = rdev->wiphy.retry_short;
613 old_retry_long = rdev->wiphy.retry_long;
614 old_frag_threshold = rdev->wiphy.frag_threshold;
615 old_rts_threshold = rdev->wiphy.rts_threshold;
616
617 if (changed & WIPHY_PARAM_RETRY_SHORT)
618 rdev->wiphy.retry_short = retry_short;
619 if (changed & WIPHY_PARAM_RETRY_LONG)
620 rdev->wiphy.retry_long = retry_long;
621 if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
622 rdev->wiphy.frag_threshold = frag_threshold;
623 if (changed & WIPHY_PARAM_RTS_THRESHOLD)
624 rdev->wiphy.rts_threshold = rts_threshold;
625
626 result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed);
627 if (result) {
628 rdev->wiphy.retry_short = old_retry_short;
629 rdev->wiphy.retry_long = old_retry_long;
630 rdev->wiphy.frag_threshold = old_frag_threshold;
631 rdev->wiphy.rts_threshold = old_rts_threshold;
632 }
633 }
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200634
Johannes Berg306d6112008-12-08 12:39:04 +0100635 bad_res:
Johannes Berg4bbf4d52009-03-24 09:35:46 +0100636 mutex_unlock(&rdev->mtx);
637 unlock:
638 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400639 return result;
640}
641
642
643static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
Johannes Bergd7264052009-04-19 16:23:20 +0200644 struct cfg80211_registered_device *rdev,
Johannes Berg55682962007-09-20 13:09:35 -0400645 struct net_device *dev)
646{
647 void *hdr;
648
649 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
650 if (!hdr)
651 return -1;
652
653 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
Johannes Bergd7264052009-04-19 16:23:20 +0200654 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400655 NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
Johannes Berg60719ff2008-09-16 14:55:09 +0200656 NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype);
Johannes Berg55682962007-09-20 13:09:35 -0400657 return genlmsg_end(msg, hdr);
658
659 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700660 genlmsg_cancel(msg, hdr);
661 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400662}
663
664static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
665{
666 int wp_idx = 0;
667 int if_idx = 0;
668 int wp_start = cb->args[0];
669 int if_start = cb->args[1];
670 struct cfg80211_registered_device *dev;
671 struct wireless_dev *wdev;
672
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500673 mutex_lock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400674 list_for_each_entry(dev, &cfg80211_drv_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +0200675 if (wp_idx < wp_start) {
676 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400677 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200678 }
Johannes Berg55682962007-09-20 13:09:35 -0400679 if_idx = 0;
680
681 mutex_lock(&dev->devlist_mtx);
682 list_for_each_entry(wdev, &dev->netdev_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +0200683 if (if_idx < if_start) {
684 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400685 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +0200686 }
Johannes Berg55682962007-09-20 13:09:35 -0400687 if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
688 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Bergd7264052009-04-19 16:23:20 +0200689 dev, wdev->netdev) < 0) {
Johannes Bergbba95fe2008-07-29 13:22:51 +0200690 mutex_unlock(&dev->devlist_mtx);
691 goto out;
692 }
693 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400694 }
695 mutex_unlock(&dev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +0200696
697 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -0400698 }
Johannes Bergbba95fe2008-07-29 13:22:51 +0200699 out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500700 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400701
702 cb->args[0] = wp_idx;
703 cb->args[1] = if_idx;
704
705 return skb->len;
706}
707
708static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
709{
710 struct sk_buff *msg;
711 struct cfg80211_registered_device *dev;
712 struct net_device *netdev;
713 int err;
714
Johannes Bergbba95fe2008-07-29 13:22:51 +0200715 err = get_drv_dev_by_info_ifindex(info->attrs, &dev, &netdev);
Johannes Berg55682962007-09-20 13:09:35 -0400716 if (err)
717 return err;
718
719 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
720 if (!msg)
721 goto out_err;
722
Johannes Bergd7264052009-04-19 16:23:20 +0200723 if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0,
724 dev, netdev) < 0)
Johannes Berg55682962007-09-20 13:09:35 -0400725 goto out_free;
726
727 dev_put(netdev);
728 cfg80211_put_dev(dev);
729
730 return genlmsg_unicast(msg, info->snd_pid);
731
732 out_free:
733 nlmsg_free(msg);
734 out_err:
735 dev_put(netdev);
736 cfg80211_put_dev(dev);
737 return -ENOBUFS;
738}
739
Michael Wu66f7ac52008-01-31 19:48:22 +0100740static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
741 [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
742 [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
743 [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
744 [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
745 [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
746};
747
748static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
749{
750 struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
751 int flag;
752
753 *mntrflags = 0;
754
755 if (!nla)
756 return -EINVAL;
757
758 if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX,
759 nla, mntr_flags_policy))
760 return -EINVAL;
761
762 for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
763 if (flags[flag])
764 *mntrflags |= (1<<flag);
765
766 return 0;
767}
768
Johannes Berg55682962007-09-20 13:09:35 -0400769static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
770{
771 struct cfg80211_registered_device *drv;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100772 struct vif_params params;
Johannes Berg55682962007-09-20 13:09:35 -0400773 int err, ifindex;
Johannes Berg04a773a2009-04-19 21:24:32 +0200774 enum nl80211_iftype otype, ntype;
Johannes Berg55682962007-09-20 13:09:35 -0400775 struct net_device *dev;
Johannes Berg92ffe052008-09-16 20:39:36 +0200776 u32 _flags, *flags = NULL;
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100777 bool change = false;
Johannes Berg55682962007-09-20 13:09:35 -0400778
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100779 memset(&params, 0, sizeof(params));
780
Johannes Berg3b858752009-03-12 09:55:09 +0100781 rtnl_lock();
782
Johannes Bergbba95fe2008-07-29 13:22:51 +0200783 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg55682962007-09-20 13:09:35 -0400784 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +0100785 goto unlock_rtnl;
786
Johannes Berg55682962007-09-20 13:09:35 -0400787 ifindex = dev->ifindex;
Johannes Berg04a773a2009-04-19 21:24:32 +0200788 otype = ntype = dev->ieee80211_ptr->iftype;
Johannes Berg55682962007-09-20 13:09:35 -0400789 dev_put(dev);
790
Johannes Berg723b0382008-09-16 20:22:09 +0200791 if (info->attrs[NL80211_ATTR_IFTYPE]) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100792 ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
Johannes Berg04a773a2009-04-19 21:24:32 +0200793 if (otype != ntype)
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100794 change = true;
Johannes Berg04a773a2009-04-19 21:24:32 +0200795 if (ntype > NL80211_IFTYPE_MAX) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100796 err = -EINVAL;
Johannes Berg723b0382008-09-16 20:22:09 +0200797 goto unlock;
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100798 }
Johannes Berg723b0382008-09-16 20:22:09 +0200799 }
800
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700801 if (!drv->ops->change_virtual_intf ||
Johannes Berg04a773a2009-04-19 21:24:32 +0200802 !(drv->wiphy.interface_modes & (1 << ntype))) {
Johannes Berg55682962007-09-20 13:09:35 -0400803 err = -EOPNOTSUPP;
804 goto unlock;
805 }
806
Johannes Berg92ffe052008-09-16 20:39:36 +0200807 if (info->attrs[NL80211_ATTR_MESH_ID]) {
Johannes Berg04a773a2009-04-19 21:24:32 +0200808 if (ntype != NL80211_IFTYPE_MESH_POINT) {
Johannes Berg92ffe052008-09-16 20:39:36 +0200809 err = -EINVAL;
810 goto unlock;
811 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100812 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
813 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100814 change = true;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100815 }
816
Johannes Berg92ffe052008-09-16 20:39:36 +0200817 if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
Johannes Berg04a773a2009-04-19 21:24:32 +0200818 if (ntype != NL80211_IFTYPE_MONITOR) {
Johannes Berg92ffe052008-09-16 20:39:36 +0200819 err = -EINVAL;
820 goto unlock;
821 }
822 err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
823 &_flags);
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100824 if (err)
825 goto unlock;
826
827 flags = &_flags;
828 change = true;
Johannes Berg92ffe052008-09-16 20:39:36 +0200829 }
Johannes Berg3b858752009-03-12 09:55:09 +0100830
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100831 if (change)
832 err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex,
Johannes Berg04a773a2009-04-19 21:24:32 +0200833 ntype, flags, &params);
Johannes Bergac7f9cf2009-03-21 17:07:59 +0100834 else
835 err = 0;
Johannes Berg60719ff2008-09-16 14:55:09 +0200836
837 dev = __dev_get_by_index(&init_net, ifindex);
Johannes Berg04a773a2009-04-19 21:24:32 +0200838 WARN_ON(!dev || (!err && dev->ieee80211_ptr->iftype != ntype));
839
840 if (dev && !err && (ntype != otype)) {
841 if (otype == NL80211_IFTYPE_ADHOC)
Johannes Berg9d308422009-04-20 18:43:46 +0200842 cfg80211_clear_ibss(dev, false);
Johannes Berg04a773a2009-04-19 21:24:32 +0200843 }
Johannes Berg60719ff2008-09-16 14:55:09 +0200844
Johannes Berg55682962007-09-20 13:09:35 -0400845 unlock:
846 cfg80211_put_dev(drv);
Johannes Berg3b858752009-03-12 09:55:09 +0100847 unlock_rtnl:
848 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400849 return err;
850}
851
852static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
853{
854 struct cfg80211_registered_device *drv;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100855 struct vif_params params;
Johannes Berg55682962007-09-20 13:09:35 -0400856 int err;
857 enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
Michael Wu66f7ac52008-01-31 19:48:22 +0100858 u32 flags;
Johannes Berg55682962007-09-20 13:09:35 -0400859
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100860 memset(&params, 0, sizeof(params));
861
Johannes Berg55682962007-09-20 13:09:35 -0400862 if (!info->attrs[NL80211_ATTR_IFNAME])
863 return -EINVAL;
864
865 if (info->attrs[NL80211_ATTR_IFTYPE]) {
866 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
867 if (type > NL80211_IFTYPE_MAX)
868 return -EINVAL;
869 }
870
Johannes Berg3b858752009-03-12 09:55:09 +0100871 rtnl_lock();
872
Johannes Berg55682962007-09-20 13:09:35 -0400873 drv = cfg80211_get_dev_from_info(info);
Johannes Berg3b858752009-03-12 09:55:09 +0100874 if (IS_ERR(drv)) {
875 err = PTR_ERR(drv);
876 goto unlock_rtnl;
877 }
Johannes Berg55682962007-09-20 13:09:35 -0400878
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700879 if (!drv->ops->add_virtual_intf ||
880 !(drv->wiphy.interface_modes & (1 << type))) {
Johannes Berg55682962007-09-20 13:09:35 -0400881 err = -EOPNOTSUPP;
882 goto unlock;
883 }
884
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100885 if (type == NL80211_IFTYPE_MESH_POINT &&
886 info->attrs[NL80211_ATTR_MESH_ID]) {
887 params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
888 params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
889 }
890
Michael Wu66f7ac52008-01-31 19:48:22 +0100891 err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
892 info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
893 &flags);
Johannes Berg55682962007-09-20 13:09:35 -0400894 err = drv->ops->add_virtual_intf(&drv->wiphy,
Michael Wu66f7ac52008-01-31 19:48:22 +0100895 nla_data(info->attrs[NL80211_ATTR_IFNAME]),
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100896 type, err ? NULL : &flags, &params);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100897
Johannes Berg55682962007-09-20 13:09:35 -0400898 unlock:
899 cfg80211_put_dev(drv);
Johannes Berg3b858752009-03-12 09:55:09 +0100900 unlock_rtnl:
901 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400902 return err;
903}
904
905static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
906{
907 struct cfg80211_registered_device *drv;
908 int ifindex, err;
909 struct net_device *dev;
910
Johannes Berg3b858752009-03-12 09:55:09 +0100911 rtnl_lock();
912
Johannes Bergbba95fe2008-07-29 13:22:51 +0200913 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg55682962007-09-20 13:09:35 -0400914 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +0100915 goto unlock_rtnl;
Johannes Berg55682962007-09-20 13:09:35 -0400916 ifindex = dev->ifindex;
917 dev_put(dev);
918
919 if (!drv->ops->del_virtual_intf) {
920 err = -EOPNOTSUPP;
921 goto out;
922 }
923
Johannes Berg55682962007-09-20 13:09:35 -0400924 err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex);
Johannes Berg55682962007-09-20 13:09:35 -0400925
926 out:
927 cfg80211_put_dev(drv);
Johannes Berg3b858752009-03-12 09:55:09 +0100928 unlock_rtnl:
929 rtnl_unlock();
Johannes Berg55682962007-09-20 13:09:35 -0400930 return err;
931}
932
Johannes Berg41ade002007-12-19 02:03:29 +0100933struct get_key_cookie {
934 struct sk_buff *msg;
935 int error;
936};
937
938static void get_key_callback(void *c, struct key_params *params)
939{
940 struct get_key_cookie *cookie = c;
941
942 if (params->key)
943 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA,
944 params->key_len, params->key);
945
946 if (params->seq)
947 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ,
948 params->seq_len, params->seq);
949
950 if (params->cipher)
951 NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
952 params->cipher);
953
954 return;
955 nla_put_failure:
956 cookie->error = 1;
957}
958
959static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
960{
961 struct cfg80211_registered_device *drv;
962 int err;
963 struct net_device *dev;
964 u8 key_idx = 0;
965 u8 *mac_addr = NULL;
966 struct get_key_cookie cookie = {
967 .error = 0,
968 };
969 void *hdr;
970 struct sk_buff *msg;
971
972 if (info->attrs[NL80211_ATTR_KEY_IDX])
973 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
974
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +0200975 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +0100976 return -EINVAL;
977
978 if (info->attrs[NL80211_ATTR_MAC])
979 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
980
Johannes Berg3b858752009-03-12 09:55:09 +0100981 rtnl_lock();
982
Johannes Bergbba95fe2008-07-29 13:22:51 +0200983 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +0100984 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +0100985 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +0100986
987 if (!drv->ops->get_key) {
988 err = -EOPNOTSUPP;
989 goto out;
990 }
991
992 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
993 if (!msg) {
994 err = -ENOMEM;
995 goto out;
996 }
997
998 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
999 NL80211_CMD_NEW_KEY);
1000
1001 if (IS_ERR(hdr)) {
1002 err = PTR_ERR(hdr);
1003 goto out;
1004 }
1005
1006 cookie.msg = msg;
1007
1008 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1009 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
1010 if (mac_addr)
1011 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1012
Johannes Berg41ade002007-12-19 02:03:29 +01001013 err = drv->ops->get_key(&drv->wiphy, dev, key_idx, mac_addr,
1014 &cookie, get_key_callback);
Johannes Berg41ade002007-12-19 02:03:29 +01001015
1016 if (err)
1017 goto out;
1018
1019 if (cookie.error)
1020 goto nla_put_failure;
1021
1022 genlmsg_end(msg, hdr);
1023 err = genlmsg_unicast(msg, info->snd_pid);
1024 goto out;
1025
1026 nla_put_failure:
1027 err = -ENOBUFS;
1028 nlmsg_free(msg);
1029 out:
1030 cfg80211_put_dev(drv);
1031 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001032 unlock_rtnl:
1033 rtnl_unlock();
1034
Johannes Berg41ade002007-12-19 02:03:29 +01001035 return err;
1036}
1037
1038static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
1039{
1040 struct cfg80211_registered_device *drv;
1041 int err;
1042 struct net_device *dev;
1043 u8 key_idx;
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001044 int (*func)(struct wiphy *wiphy, struct net_device *netdev,
1045 u8 key_index);
Johannes Berg41ade002007-12-19 02:03:29 +01001046
1047 if (!info->attrs[NL80211_ATTR_KEY_IDX])
1048 return -EINVAL;
1049
1050 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1051
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001052 if (info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) {
1053 if (key_idx < 4 || key_idx > 5)
1054 return -EINVAL;
1055 } else if (key_idx > 3)
Johannes Berg41ade002007-12-19 02:03:29 +01001056 return -EINVAL;
1057
1058 /* currently only support setting default key */
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001059 if (!info->attrs[NL80211_ATTR_KEY_DEFAULT] &&
1060 !info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT])
Johannes Berg41ade002007-12-19 02:03:29 +01001061 return -EINVAL;
1062
Johannes Berg3b858752009-03-12 09:55:09 +01001063 rtnl_lock();
1064
Johannes Bergbba95fe2008-07-29 13:22:51 +02001065 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001066 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001067 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001068
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001069 if (info->attrs[NL80211_ATTR_KEY_DEFAULT])
1070 func = drv->ops->set_default_key;
1071 else
1072 func = drv->ops->set_default_mgmt_key;
1073
1074 if (!func) {
Johannes Berg41ade002007-12-19 02:03:29 +01001075 err = -EOPNOTSUPP;
1076 goto out;
1077 }
1078
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001079 err = func(&drv->wiphy, dev, key_idx);
Johannes Berg08645122009-05-11 13:54:58 +02001080#ifdef CONFIG_WIRELESS_EXT
1081 if (!err) {
1082 if (func == drv->ops->set_default_key)
1083 dev->ieee80211_ptr->wext.default_key = key_idx;
1084 else
1085 dev->ieee80211_ptr->wext.default_mgmt_key = key_idx;
1086 }
1087#endif
Johannes Berg41ade002007-12-19 02:03:29 +01001088
1089 out:
1090 cfg80211_put_dev(drv);
1091 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001092
1093 unlock_rtnl:
1094 rtnl_unlock();
1095
Johannes Berg41ade002007-12-19 02:03:29 +01001096 return err;
1097}
1098
1099static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
1100{
1101 struct cfg80211_registered_device *drv;
Johannes Berg25e47c12009-04-02 20:14:06 +02001102 int err, i;
Johannes Berg41ade002007-12-19 02:03:29 +01001103 struct net_device *dev;
1104 struct key_params params;
1105 u8 key_idx = 0;
1106 u8 *mac_addr = NULL;
1107
1108 memset(&params, 0, sizeof(params));
1109
1110 if (!info->attrs[NL80211_ATTR_KEY_CIPHER])
1111 return -EINVAL;
1112
1113 if (info->attrs[NL80211_ATTR_KEY_DATA]) {
1114 params.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
1115 params.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
1116 }
1117
1118 if (info->attrs[NL80211_ATTR_KEY_IDX])
1119 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1120
1121 params.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
1122
1123 if (info->attrs[NL80211_ATTR_MAC])
1124 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1125
Johannes Berg08645122009-05-11 13:54:58 +02001126 if (cfg80211_validate_key_settings(&params, key_idx, mac_addr))
Johannes Berg41ade002007-12-19 02:03:29 +01001127 return -EINVAL;
1128
Johannes Berg3b858752009-03-12 09:55:09 +01001129 rtnl_lock();
1130
Johannes Bergbba95fe2008-07-29 13:22:51 +02001131 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001132 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001133 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001134
Johannes Berg25e47c12009-04-02 20:14:06 +02001135 for (i = 0; i < drv->wiphy.n_cipher_suites; i++)
1136 if (params.cipher == drv->wiphy.cipher_suites[i])
1137 break;
1138 if (i == drv->wiphy.n_cipher_suites) {
1139 err = -EINVAL;
1140 goto out;
1141 }
1142
Johannes Berg41ade002007-12-19 02:03:29 +01001143 if (!drv->ops->add_key) {
1144 err = -EOPNOTSUPP;
1145 goto out;
1146 }
1147
Johannes Berg41ade002007-12-19 02:03:29 +01001148 err = drv->ops->add_key(&drv->wiphy, dev, key_idx, mac_addr, &params);
Johannes Berg41ade002007-12-19 02:03:29 +01001149
1150 out:
1151 cfg80211_put_dev(drv);
1152 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001153 unlock_rtnl:
1154 rtnl_unlock();
1155
Johannes Berg41ade002007-12-19 02:03:29 +01001156 return err;
1157}
1158
1159static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
1160{
1161 struct cfg80211_registered_device *drv;
1162 int err;
1163 struct net_device *dev;
1164 u8 key_idx = 0;
1165 u8 *mac_addr = NULL;
1166
1167 if (info->attrs[NL80211_ATTR_KEY_IDX])
1168 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1169
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001170 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +01001171 return -EINVAL;
1172
1173 if (info->attrs[NL80211_ATTR_MAC])
1174 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1175
Johannes Berg3b858752009-03-12 09:55:09 +01001176 rtnl_lock();
1177
Johannes Bergbba95fe2008-07-29 13:22:51 +02001178 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg41ade002007-12-19 02:03:29 +01001179 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001180 goto unlock_rtnl;
Johannes Berg41ade002007-12-19 02:03:29 +01001181
1182 if (!drv->ops->del_key) {
1183 err = -EOPNOTSUPP;
1184 goto out;
1185 }
1186
Johannes Berg41ade002007-12-19 02:03:29 +01001187 err = drv->ops->del_key(&drv->wiphy, dev, key_idx, mac_addr);
Johannes Berg41ade002007-12-19 02:03:29 +01001188
Johannes Berg08645122009-05-11 13:54:58 +02001189#ifdef CONFIG_WIRELESS_EXT
1190 if (!err) {
1191 if (key_idx == dev->ieee80211_ptr->wext.default_key)
1192 dev->ieee80211_ptr->wext.default_key = -1;
1193 else if (key_idx == dev->ieee80211_ptr->wext.default_mgmt_key)
1194 dev->ieee80211_ptr->wext.default_mgmt_key = -1;
1195 }
1196#endif
1197
Johannes Berg41ade002007-12-19 02:03:29 +01001198 out:
1199 cfg80211_put_dev(drv);
1200 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001201
1202 unlock_rtnl:
1203 rtnl_unlock();
1204
Johannes Berg41ade002007-12-19 02:03:29 +01001205 return err;
1206}
1207
Johannes Berged1b6cc2007-12-19 02:03:32 +01001208static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
1209{
1210 int (*call)(struct wiphy *wiphy, struct net_device *dev,
1211 struct beacon_parameters *info);
1212 struct cfg80211_registered_device *drv;
1213 int err;
1214 struct net_device *dev;
1215 struct beacon_parameters params;
1216 int haveinfo = 0;
1217
Johannes Bergf4a11bb2009-03-27 12:40:28 +01001218 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]))
1219 return -EINVAL;
1220
Johannes Berg3b858752009-03-12 09:55:09 +01001221 rtnl_lock();
1222
Johannes Bergbba95fe2008-07-29 13:22:51 +02001223 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001224 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001225 goto unlock_rtnl;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001226
Jouni Malineneec60b02009-03-20 21:21:19 +02001227 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
1228 err = -EOPNOTSUPP;
1229 goto out;
1230 }
1231
Johannes Berged1b6cc2007-12-19 02:03:32 +01001232 switch (info->genlhdr->cmd) {
1233 case NL80211_CMD_NEW_BEACON:
1234 /* these are required for NEW_BEACON */
1235 if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
1236 !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
1237 !info->attrs[NL80211_ATTR_BEACON_HEAD]) {
1238 err = -EINVAL;
1239 goto out;
1240 }
1241
1242 call = drv->ops->add_beacon;
1243 break;
1244 case NL80211_CMD_SET_BEACON:
1245 call = drv->ops->set_beacon;
1246 break;
1247 default:
1248 WARN_ON(1);
1249 err = -EOPNOTSUPP;
1250 goto out;
1251 }
1252
1253 if (!call) {
1254 err = -EOPNOTSUPP;
1255 goto out;
1256 }
1257
1258 memset(&params, 0, sizeof(params));
1259
1260 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
1261 params.interval =
1262 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
1263 haveinfo = 1;
1264 }
1265
1266 if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) {
1267 params.dtim_period =
1268 nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
1269 haveinfo = 1;
1270 }
1271
1272 if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
1273 params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
1274 params.head_len =
1275 nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
1276 haveinfo = 1;
1277 }
1278
1279 if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
1280 params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
1281 params.tail_len =
1282 nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
1283 haveinfo = 1;
1284 }
1285
1286 if (!haveinfo) {
1287 err = -EINVAL;
1288 goto out;
1289 }
1290
Johannes Berged1b6cc2007-12-19 02:03:32 +01001291 err = call(&drv->wiphy, dev, &params);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001292
1293 out:
1294 cfg80211_put_dev(drv);
1295 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001296 unlock_rtnl:
1297 rtnl_unlock();
1298
Johannes Berged1b6cc2007-12-19 02:03:32 +01001299 return err;
1300}
1301
1302static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
1303{
1304 struct cfg80211_registered_device *drv;
1305 int err;
1306 struct net_device *dev;
1307
Johannes Berg3b858752009-03-12 09:55:09 +01001308 rtnl_lock();
1309
Johannes Bergbba95fe2008-07-29 13:22:51 +02001310 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001311 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001312 goto unlock_rtnl;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001313
1314 if (!drv->ops->del_beacon) {
1315 err = -EOPNOTSUPP;
1316 goto out;
1317 }
1318
Jouni Malineneec60b02009-03-20 21:21:19 +02001319 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
1320 err = -EOPNOTSUPP;
1321 goto out;
1322 }
Johannes Berged1b6cc2007-12-19 02:03:32 +01001323 err = drv->ops->del_beacon(&drv->wiphy, dev);
Johannes Berged1b6cc2007-12-19 02:03:32 +01001324
1325 out:
1326 cfg80211_put_dev(drv);
1327 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001328 unlock_rtnl:
1329 rtnl_unlock();
1330
Johannes Berged1b6cc2007-12-19 02:03:32 +01001331 return err;
1332}
1333
Johannes Berg5727ef12007-12-19 02:03:34 +01001334static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
1335 [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
1336 [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
1337 [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
Jouni Malinen0e467242009-05-11 21:57:55 +03001338 [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG },
Johannes Berg5727ef12007-12-19 02:03:34 +01001339};
1340
Johannes Bergeccb8e82009-05-11 21:57:56 +03001341static int parse_station_flags(struct genl_info *info,
1342 struct station_parameters *params)
Johannes Berg5727ef12007-12-19 02:03:34 +01001343{
1344 struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
Johannes Bergeccb8e82009-05-11 21:57:56 +03001345 struct nlattr *nla;
Johannes Berg5727ef12007-12-19 02:03:34 +01001346 int flag;
1347
Johannes Bergeccb8e82009-05-11 21:57:56 +03001348 /*
1349 * Try parsing the new attribute first so userspace
1350 * can specify both for older kernels.
1351 */
1352 nla = info->attrs[NL80211_ATTR_STA_FLAGS2];
1353 if (nla) {
1354 struct nl80211_sta_flag_update *sta_flags;
Johannes Berg5727ef12007-12-19 02:03:34 +01001355
Johannes Bergeccb8e82009-05-11 21:57:56 +03001356 sta_flags = nla_data(nla);
1357 params->sta_flags_mask = sta_flags->mask;
1358 params->sta_flags_set = sta_flags->set;
1359 if ((params->sta_flags_mask |
1360 params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID))
1361 return -EINVAL;
1362 return 0;
1363 }
1364
1365 /* if present, parse the old attribute */
1366
1367 nla = info->attrs[NL80211_ATTR_STA_FLAGS];
Johannes Berg5727ef12007-12-19 02:03:34 +01001368 if (!nla)
1369 return 0;
1370
1371 if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX,
1372 nla, sta_flags_policy))
1373 return -EINVAL;
1374
Johannes Bergeccb8e82009-05-11 21:57:56 +03001375 params->sta_flags_mask = (1 << __NL80211_STA_FLAG_AFTER_LAST) - 1;
1376 params->sta_flags_mask &= ~1;
Johannes Berg5727ef12007-12-19 02:03:34 +01001377
1378 for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++)
1379 if (flags[flag])
Johannes Bergeccb8e82009-05-11 21:57:56 +03001380 params->sta_flags_set |= (1<<flag);
Johannes Berg5727ef12007-12-19 02:03:34 +01001381
1382 return 0;
1383}
1384
Henning Rogge420e7fa2008-12-11 22:04:19 +01001385static u16 nl80211_calculate_bitrate(struct rate_info *rate)
1386{
1387 int modulation, streams, bitrate;
1388
1389 if (!(rate->flags & RATE_INFO_FLAGS_MCS))
1390 return rate->legacy;
1391
1392 /* the formula below does only work for MCS values smaller than 32 */
1393 if (rate->mcs >= 32)
1394 return 0;
1395
1396 modulation = rate->mcs & 7;
1397 streams = (rate->mcs >> 3) + 1;
1398
1399 bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ?
1400 13500000 : 6500000;
1401
1402 if (modulation < 4)
1403 bitrate *= (modulation + 1);
1404 else if (modulation == 4)
1405 bitrate *= (modulation + 2);
1406 else
1407 bitrate *= (modulation + 3);
1408
1409 bitrate *= streams;
1410
1411 if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
1412 bitrate = (bitrate / 9) * 10;
1413
1414 /* do NOT round down here */
1415 return (bitrate + 50000) / 100000;
1416}
1417
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001418static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
1419 int flags, struct net_device *dev,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001420 u8 *mac_addr, struct station_info *sinfo)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001421{
1422 void *hdr;
Henning Rogge420e7fa2008-12-11 22:04:19 +01001423 struct nlattr *sinfoattr, *txrate;
1424 u16 bitrate;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001425
1426 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
1427 if (!hdr)
1428 return -1;
1429
1430 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1431 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1432
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001433 sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
1434 if (!sinfoattr)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001435 goto nla_put_failure;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001436 if (sinfo->filled & STATION_INFO_INACTIVE_TIME)
1437 NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME,
1438 sinfo->inactive_time);
1439 if (sinfo->filled & STATION_INFO_RX_BYTES)
1440 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES,
1441 sinfo->rx_bytes);
1442 if (sinfo->filled & STATION_INFO_TX_BYTES)
1443 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES,
1444 sinfo->tx_bytes);
1445 if (sinfo->filled & STATION_INFO_LLID)
1446 NLA_PUT_U16(msg, NL80211_STA_INFO_LLID,
1447 sinfo->llid);
1448 if (sinfo->filled & STATION_INFO_PLID)
1449 NLA_PUT_U16(msg, NL80211_STA_INFO_PLID,
1450 sinfo->plid);
1451 if (sinfo->filled & STATION_INFO_PLINK_STATE)
1452 NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
1453 sinfo->plink_state);
Henning Rogge420e7fa2008-12-11 22:04:19 +01001454 if (sinfo->filled & STATION_INFO_SIGNAL)
1455 NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL,
1456 sinfo->signal);
1457 if (sinfo->filled & STATION_INFO_TX_BITRATE) {
1458 txrate = nla_nest_start(msg, NL80211_STA_INFO_TX_BITRATE);
1459 if (!txrate)
1460 goto nla_put_failure;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001461
Henning Rogge420e7fa2008-12-11 22:04:19 +01001462 /* nl80211_calculate_bitrate will return 0 for mcs >= 32 */
1463 bitrate = nl80211_calculate_bitrate(&sinfo->txrate);
1464 if (bitrate > 0)
1465 NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
1466
1467 if (sinfo->txrate.flags & RATE_INFO_FLAGS_MCS)
1468 NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS,
1469 sinfo->txrate.mcs);
1470 if (sinfo->txrate.flags & RATE_INFO_FLAGS_40_MHZ_WIDTH)
1471 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH);
1472 if (sinfo->txrate.flags & RATE_INFO_FLAGS_SHORT_GI)
1473 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI);
1474
1475 nla_nest_end(msg, txrate);
1476 }
Jouni Malinen98c8a60a2009-02-17 13:24:57 +02001477 if (sinfo->filled & STATION_INFO_RX_PACKETS)
1478 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS,
1479 sinfo->rx_packets);
1480 if (sinfo->filled & STATION_INFO_TX_PACKETS)
1481 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS,
1482 sinfo->tx_packets);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001483 nla_nest_end(msg, sinfoattr);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001484
1485 return genlmsg_end(msg, hdr);
1486
1487 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001488 genlmsg_cancel(msg, hdr);
1489 return -EMSGSIZE;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001490}
1491
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001492static int nl80211_dump_station(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02001493 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001494{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001495 struct station_info sinfo;
1496 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001497 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001498 u8 mac_addr[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02001499 int ifidx = cb->args[0];
1500 int sta_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001501 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001502
Johannes Bergbba95fe2008-07-29 13:22:51 +02001503 if (!ifidx) {
1504 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
1505 nl80211_fam.attrbuf, nl80211_fam.maxattr,
1506 nl80211_policy);
1507 if (err)
1508 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001509
Johannes Bergbba95fe2008-07-29 13:22:51 +02001510 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
1511 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001512
Johannes Bergbba95fe2008-07-29 13:22:51 +02001513 ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
1514 if (!ifidx)
1515 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001516 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001517
Johannes Berg3b858752009-03-12 09:55:09 +01001518 rtnl_lock();
1519
1520 netdev = __dev_get_by_index(&init_net, ifidx);
1521 if (!netdev) {
1522 err = -ENODEV;
1523 goto out_rtnl;
1524 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001525
Johannes Bergbba95fe2008-07-29 13:22:51 +02001526 dev = cfg80211_get_dev_from_ifindex(ifidx);
1527 if (IS_ERR(dev)) {
1528 err = PTR_ERR(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001529 goto out_rtnl;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001530 }
1531
1532 if (!dev->ops->dump_station) {
Jouni Malineneec60b02009-03-20 21:21:19 +02001533 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001534 goto out_err;
1535 }
1536
Johannes Bergbba95fe2008-07-29 13:22:51 +02001537 while (1) {
1538 err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
1539 mac_addr, &sinfo);
1540 if (err == -ENOENT)
1541 break;
1542 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001543 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001544
1545 if (nl80211_send_station(skb,
1546 NETLINK_CB(cb->skb).pid,
1547 cb->nlh->nlmsg_seq, NLM_F_MULTI,
1548 netdev, mac_addr,
1549 &sinfo) < 0)
1550 goto out;
1551
1552 sta_idx++;
1553 }
1554
1555
1556 out:
1557 cb->args[1] = sta_idx;
1558 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001559 out_err:
1560 cfg80211_put_dev(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001561 out_rtnl:
1562 rtnl_unlock();
Johannes Bergbba95fe2008-07-29 13:22:51 +02001563
1564 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001565}
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001566
Johannes Berg5727ef12007-12-19 02:03:34 +01001567static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
1568{
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001569 struct cfg80211_registered_device *drv;
1570 int err;
1571 struct net_device *dev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001572 struct station_info sinfo;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001573 struct sk_buff *msg;
1574 u8 *mac_addr = NULL;
1575
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001576 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001577
1578 if (!info->attrs[NL80211_ATTR_MAC])
1579 return -EINVAL;
1580
1581 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1582
Johannes Berg3b858752009-03-12 09:55:09 +01001583 rtnl_lock();
1584
Johannes Bergbba95fe2008-07-29 13:22:51 +02001585 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001586 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001587 goto out_rtnl;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001588
1589 if (!drv->ops->get_station) {
1590 err = -EOPNOTSUPP;
1591 goto out;
1592 }
1593
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001594 err = drv->ops->get_station(&drv->wiphy, dev, mac_addr, &sinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001595 if (err)
1596 goto out;
1597
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001598 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1599 if (!msg)
1600 goto out;
1601
1602 if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001603 dev, mac_addr, &sinfo) < 0)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001604 goto out_free;
1605
1606 err = genlmsg_unicast(msg, info->snd_pid);
1607 goto out;
1608
1609 out_free:
1610 nlmsg_free(msg);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001611 out:
1612 cfg80211_put_dev(drv);
1613 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001614 out_rtnl:
1615 rtnl_unlock();
1616
Johannes Bergfd5b74d2007-12-19 02:03:36 +01001617 return err;
Johannes Berg5727ef12007-12-19 02:03:34 +01001618}
1619
1620/*
1621 * Get vlan interface making sure it is on the right wiphy.
1622 */
1623static int get_vlan(struct nlattr *vlanattr,
1624 struct cfg80211_registered_device *rdev,
1625 struct net_device **vlan)
1626{
1627 *vlan = NULL;
1628
1629 if (vlanattr) {
1630 *vlan = dev_get_by_index(&init_net, nla_get_u32(vlanattr));
1631 if (!*vlan)
1632 return -ENODEV;
1633 if (!(*vlan)->ieee80211_ptr)
1634 return -EINVAL;
1635 if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy)
1636 return -EINVAL;
1637 }
1638 return 0;
1639}
1640
1641static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
1642{
1643 struct cfg80211_registered_device *drv;
1644 int err;
1645 struct net_device *dev;
1646 struct station_parameters params;
1647 u8 *mac_addr = NULL;
1648
1649 memset(&params, 0, sizeof(params));
1650
1651 params.listen_interval = -1;
1652
1653 if (info->attrs[NL80211_ATTR_STA_AID])
1654 return -EINVAL;
1655
1656 if (!info->attrs[NL80211_ATTR_MAC])
1657 return -EINVAL;
1658
1659 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1660
1661 if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
1662 params.supported_rates =
1663 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1664 params.supported_rates_len =
1665 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1666 }
1667
1668 if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
1669 params.listen_interval =
1670 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
1671
Jouni Malinen36aedc902008-08-25 11:58:58 +03001672 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
1673 params.ht_capa =
1674 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
1675
Johannes Bergeccb8e82009-05-11 21:57:56 +03001676 if (parse_station_flags(info, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01001677 return -EINVAL;
1678
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001679 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
1680 params.plink_action =
1681 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
1682
Johannes Berg3b858752009-03-12 09:55:09 +01001683 rtnl_lock();
1684
Johannes Bergbba95fe2008-07-29 13:22:51 +02001685 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001686 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001687 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01001688
1689 err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, &params.vlan);
1690 if (err)
1691 goto out;
1692
1693 if (!drv->ops->change_station) {
1694 err = -EOPNOTSUPP;
1695 goto out;
1696 }
1697
Johannes Berg5727ef12007-12-19 02:03:34 +01001698 err = drv->ops->change_station(&drv->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01001699
1700 out:
1701 if (params.vlan)
1702 dev_put(params.vlan);
1703 cfg80211_put_dev(drv);
1704 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001705 out_rtnl:
1706 rtnl_unlock();
1707
Johannes Berg5727ef12007-12-19 02:03:34 +01001708 return err;
1709}
1710
1711static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
1712{
1713 struct cfg80211_registered_device *drv;
1714 int err;
1715 struct net_device *dev;
1716 struct station_parameters params;
1717 u8 *mac_addr = NULL;
1718
1719 memset(&params, 0, sizeof(params));
1720
1721 if (!info->attrs[NL80211_ATTR_MAC])
1722 return -EINVAL;
1723
1724 if (!info->attrs[NL80211_ATTR_STA_AID])
1725 return -EINVAL;
1726
1727 if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
1728 return -EINVAL;
1729
1730 if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
1731 return -EINVAL;
1732
1733 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1734 params.supported_rates =
1735 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1736 params.supported_rates_len =
1737 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
1738 params.listen_interval =
1739 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
Johannes Berg16f2e852008-04-07 14:35:46 +02001740 params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
Jouni Malinen36aedc902008-08-25 11:58:58 +03001741 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
1742 params.ht_capa =
1743 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
Johannes Berg5727ef12007-12-19 02:03:34 +01001744
Johannes Bergeccb8e82009-05-11 21:57:56 +03001745 if (parse_station_flags(info, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01001746 return -EINVAL;
1747
Johannes Berg3b858752009-03-12 09:55:09 +01001748 rtnl_lock();
1749
Johannes Bergbba95fe2008-07-29 13:22:51 +02001750 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001751 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001752 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01001753
Johannes Berge80cf852009-05-11 14:43:13 +02001754 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
1755 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN) {
1756 err = -EINVAL;
1757 goto out;
1758 }
1759
Johannes Berg5727ef12007-12-19 02:03:34 +01001760 err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, &params.vlan);
1761 if (err)
1762 goto out;
1763
1764 if (!drv->ops->add_station) {
1765 err = -EOPNOTSUPP;
1766 goto out;
1767 }
1768
Jouni Malinen35a8efe2009-03-20 21:21:18 +02001769 if (!netif_running(dev)) {
1770 err = -ENETDOWN;
1771 goto out;
1772 }
1773
Johannes Berg5727ef12007-12-19 02:03:34 +01001774 err = drv->ops->add_station(&drv->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01001775
1776 out:
1777 if (params.vlan)
1778 dev_put(params.vlan);
1779 cfg80211_put_dev(drv);
1780 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001781 out_rtnl:
1782 rtnl_unlock();
1783
Johannes Berg5727ef12007-12-19 02:03:34 +01001784 return err;
1785}
1786
1787static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
1788{
1789 struct cfg80211_registered_device *drv;
1790 int err;
1791 struct net_device *dev;
1792 u8 *mac_addr = NULL;
1793
1794 if (info->attrs[NL80211_ATTR_MAC])
1795 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1796
Johannes Berg3b858752009-03-12 09:55:09 +01001797 rtnl_lock();
1798
Johannes Bergbba95fe2008-07-29 13:22:51 +02001799 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Johannes Berg5727ef12007-12-19 02:03:34 +01001800 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001801 goto out_rtnl;
Johannes Berg5727ef12007-12-19 02:03:34 +01001802
Johannes Berge80cf852009-05-11 14:43:13 +02001803 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
1804 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN) {
1805 err = -EINVAL;
1806 goto out;
1807 }
1808
Johannes Berg5727ef12007-12-19 02:03:34 +01001809 if (!drv->ops->del_station) {
1810 err = -EOPNOTSUPP;
1811 goto out;
1812 }
1813
Johannes Berg5727ef12007-12-19 02:03:34 +01001814 err = drv->ops->del_station(&drv->wiphy, dev, mac_addr);
Johannes Berg5727ef12007-12-19 02:03:34 +01001815
1816 out:
1817 cfg80211_put_dev(drv);
1818 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001819 out_rtnl:
1820 rtnl_unlock();
1821
Johannes Berg5727ef12007-12-19 02:03:34 +01001822 return err;
1823}
1824
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001825static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq,
1826 int flags, struct net_device *dev,
1827 u8 *dst, u8 *next_hop,
1828 struct mpath_info *pinfo)
1829{
1830 void *hdr;
1831 struct nlattr *pinfoattr;
1832
1833 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
1834 if (!hdr)
1835 return -1;
1836
1837 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1838 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
1839 NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop);
1840
1841 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
1842 if (!pinfoattr)
1843 goto nla_put_failure;
1844 if (pinfo->filled & MPATH_INFO_FRAME_QLEN)
1845 NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
1846 pinfo->frame_qlen);
1847 if (pinfo->filled & MPATH_INFO_DSN)
1848 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DSN,
1849 pinfo->dsn);
1850 if (pinfo->filled & MPATH_INFO_METRIC)
1851 NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC,
1852 pinfo->metric);
1853 if (pinfo->filled & MPATH_INFO_EXPTIME)
1854 NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME,
1855 pinfo->exptime);
1856 if (pinfo->filled & MPATH_INFO_FLAGS)
1857 NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS,
1858 pinfo->flags);
1859 if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT)
1860 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
1861 pinfo->discovery_timeout);
1862 if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES)
1863 NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
1864 pinfo->discovery_retries);
1865
1866 nla_nest_end(msg, pinfoattr);
1867
1868 return genlmsg_end(msg, hdr);
1869
1870 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001871 genlmsg_cancel(msg, hdr);
1872 return -EMSGSIZE;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001873}
1874
1875static int nl80211_dump_mpath(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02001876 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001877{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001878 struct mpath_info pinfo;
1879 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001880 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001881 u8 dst[ETH_ALEN];
1882 u8 next_hop[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02001883 int ifidx = cb->args[0];
1884 int path_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001885 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001886
Johannes Bergbba95fe2008-07-29 13:22:51 +02001887 if (!ifidx) {
1888 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
1889 nl80211_fam.attrbuf, nl80211_fam.maxattr,
1890 nl80211_policy);
1891 if (err)
1892 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001893
Johannes Bergbba95fe2008-07-29 13:22:51 +02001894 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
1895 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001896
Johannes Bergbba95fe2008-07-29 13:22:51 +02001897 ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
1898 if (!ifidx)
1899 return -EINVAL;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001900 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001901
Johannes Berg3b858752009-03-12 09:55:09 +01001902 rtnl_lock();
1903
1904 netdev = __dev_get_by_index(&init_net, ifidx);
1905 if (!netdev) {
1906 err = -ENODEV;
1907 goto out_rtnl;
1908 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001909
Johannes Bergbba95fe2008-07-29 13:22:51 +02001910 dev = cfg80211_get_dev_from_ifindex(ifidx);
1911 if (IS_ERR(dev)) {
1912 err = PTR_ERR(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001913 goto out_rtnl;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001914 }
1915
1916 if (!dev->ops->dump_mpath) {
Jouni Malineneec60b02009-03-20 21:21:19 +02001917 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001918 goto out_err;
1919 }
1920
Jouni Malineneec60b02009-03-20 21:21:19 +02001921 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
1922 err = -EOPNOTSUPP;
1923 goto out;
1924 }
1925
Johannes Bergbba95fe2008-07-29 13:22:51 +02001926 while (1) {
1927 err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
1928 dst, next_hop, &pinfo);
1929 if (err == -ENOENT)
1930 break;
1931 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001932 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001933
1934 if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid,
1935 cb->nlh->nlmsg_seq, NLM_F_MULTI,
1936 netdev, dst, next_hop,
1937 &pinfo) < 0)
1938 goto out;
1939
1940 path_idx++;
1941 }
1942
1943
1944 out:
1945 cb->args[1] = path_idx;
1946 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001947 out_err:
1948 cfg80211_put_dev(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01001949 out_rtnl:
1950 rtnl_unlock();
Johannes Bergbba95fe2008-07-29 13:22:51 +02001951
1952 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001953}
1954
1955static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
1956{
1957 struct cfg80211_registered_device *drv;
1958 int err;
1959 struct net_device *dev;
1960 struct mpath_info pinfo;
1961 struct sk_buff *msg;
1962 u8 *dst = NULL;
1963 u8 next_hop[ETH_ALEN];
1964
1965 memset(&pinfo, 0, sizeof(pinfo));
1966
1967 if (!info->attrs[NL80211_ATTR_MAC])
1968 return -EINVAL;
1969
1970 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
1971
Johannes Berg3b858752009-03-12 09:55:09 +01001972 rtnl_lock();
1973
Johannes Bergbba95fe2008-07-29 13:22:51 +02001974 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001975 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01001976 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001977
1978 if (!drv->ops->get_mpath) {
1979 err = -EOPNOTSUPP;
1980 goto out;
1981 }
1982
Jouni Malineneec60b02009-03-20 21:21:19 +02001983 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
1984 err = -EOPNOTSUPP;
1985 goto out;
1986 }
1987
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001988 err = drv->ops->get_mpath(&drv->wiphy, dev, dst, next_hop, &pinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001989 if (err)
1990 goto out;
1991
1992 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1993 if (!msg)
1994 goto out;
1995
1996 if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0,
1997 dev, dst, next_hop, &pinfo) < 0)
1998 goto out_free;
1999
2000 err = genlmsg_unicast(msg, info->snd_pid);
2001 goto out;
2002
2003 out_free:
2004 nlmsg_free(msg);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002005 out:
2006 cfg80211_put_dev(drv);
2007 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002008 out_rtnl:
2009 rtnl_unlock();
2010
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002011 return err;
2012}
2013
2014static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
2015{
2016 struct cfg80211_registered_device *drv;
2017 int err;
2018 struct net_device *dev;
2019 u8 *dst = NULL;
2020 u8 *next_hop = NULL;
2021
2022 if (!info->attrs[NL80211_ATTR_MAC])
2023 return -EINVAL;
2024
2025 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
2026 return -EINVAL;
2027
2028 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2029 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
2030
Johannes Berg3b858752009-03-12 09:55:09 +01002031 rtnl_lock();
2032
Johannes Bergbba95fe2008-07-29 13:22:51 +02002033 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002034 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002035 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002036
2037 if (!drv->ops->change_mpath) {
2038 err = -EOPNOTSUPP;
2039 goto out;
2040 }
2041
Jouni Malineneec60b02009-03-20 21:21:19 +02002042 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2043 err = -EOPNOTSUPP;
2044 goto out;
2045 }
2046
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002047 if (!netif_running(dev)) {
2048 err = -ENETDOWN;
2049 goto out;
2050 }
2051
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002052 err = drv->ops->change_mpath(&drv->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002053
2054 out:
2055 cfg80211_put_dev(drv);
2056 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002057 out_rtnl:
2058 rtnl_unlock();
2059
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002060 return err;
2061}
2062static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
2063{
2064 struct cfg80211_registered_device *drv;
2065 int err;
2066 struct net_device *dev;
2067 u8 *dst = NULL;
2068 u8 *next_hop = NULL;
2069
2070 if (!info->attrs[NL80211_ATTR_MAC])
2071 return -EINVAL;
2072
2073 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
2074 return -EINVAL;
2075
2076 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2077 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
2078
Johannes Berg3b858752009-03-12 09:55:09 +01002079 rtnl_lock();
2080
Johannes Bergbba95fe2008-07-29 13:22:51 +02002081 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002082 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002083 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002084
2085 if (!drv->ops->add_mpath) {
2086 err = -EOPNOTSUPP;
2087 goto out;
2088 }
2089
Jouni Malineneec60b02009-03-20 21:21:19 +02002090 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2091 err = -EOPNOTSUPP;
2092 goto out;
2093 }
2094
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002095 if (!netif_running(dev)) {
2096 err = -ENETDOWN;
2097 goto out;
2098 }
2099
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002100 err = drv->ops->add_mpath(&drv->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002101
2102 out:
2103 cfg80211_put_dev(drv);
2104 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002105 out_rtnl:
2106 rtnl_unlock();
2107
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002108 return err;
2109}
2110
2111static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
2112{
2113 struct cfg80211_registered_device *drv;
2114 int err;
2115 struct net_device *dev;
2116 u8 *dst = NULL;
2117
2118 if (info->attrs[NL80211_ATTR_MAC])
2119 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2120
Johannes Berg3b858752009-03-12 09:55:09 +01002121 rtnl_lock();
2122
Johannes Bergbba95fe2008-07-29 13:22:51 +02002123 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002124 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002125 goto out_rtnl;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002126
2127 if (!drv->ops->del_mpath) {
2128 err = -EOPNOTSUPP;
2129 goto out;
2130 }
2131
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002132 err = drv->ops->del_mpath(&drv->wiphy, dev, dst);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002133
2134 out:
2135 cfg80211_put_dev(drv);
2136 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002137 out_rtnl:
2138 rtnl_unlock();
2139
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002140 return err;
2141}
2142
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002143static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
2144{
2145 struct cfg80211_registered_device *drv;
2146 int err;
2147 struct net_device *dev;
2148 struct bss_parameters params;
2149
2150 memset(&params, 0, sizeof(params));
2151 /* default to not changing parameters */
2152 params.use_cts_prot = -1;
2153 params.use_short_preamble = -1;
2154 params.use_short_slot_time = -1;
2155
2156 if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
2157 params.use_cts_prot =
2158 nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
2159 if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
2160 params.use_short_preamble =
2161 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
2162 if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
2163 params.use_short_slot_time =
2164 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
Jouni Malinen90c97a02008-10-30 16:59:22 +02002165 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
2166 params.basic_rates =
2167 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2168 params.basic_rates_len =
2169 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2170 }
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002171
Johannes Berg3b858752009-03-12 09:55:09 +01002172 rtnl_lock();
2173
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002174 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2175 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002176 goto out_rtnl;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002177
2178 if (!drv->ops->change_bss) {
2179 err = -EOPNOTSUPP;
2180 goto out;
2181 }
2182
Jouni Malineneec60b02009-03-20 21:21:19 +02002183 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
2184 err = -EOPNOTSUPP;
2185 goto out;
2186 }
2187
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002188 err = drv->ops->change_bss(&drv->wiphy, dev, &params);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002189
2190 out:
2191 cfg80211_put_dev(drv);
2192 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002193 out_rtnl:
2194 rtnl_unlock();
2195
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002196 return err;
2197}
2198
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002199static const struct nla_policy
2200 reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
2201 [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
2202 [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
2203 [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
2204 [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
2205 [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
2206 [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
2207};
2208
2209static int parse_reg_rule(struct nlattr *tb[],
2210 struct ieee80211_reg_rule *reg_rule)
2211{
2212 struct ieee80211_freq_range *freq_range = &reg_rule->freq_range;
2213 struct ieee80211_power_rule *power_rule = &reg_rule->power_rule;
2214
2215 if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
2216 return -EINVAL;
2217 if (!tb[NL80211_ATTR_FREQ_RANGE_START])
2218 return -EINVAL;
2219 if (!tb[NL80211_ATTR_FREQ_RANGE_END])
2220 return -EINVAL;
2221 if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
2222 return -EINVAL;
2223 if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
2224 return -EINVAL;
2225
2226 reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
2227
2228 freq_range->start_freq_khz =
2229 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
2230 freq_range->end_freq_khz =
2231 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
2232 freq_range->max_bandwidth_khz =
2233 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
2234
2235 power_rule->max_eirp =
2236 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
2237
2238 if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
2239 power_rule->max_antenna_gain =
2240 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
2241
2242 return 0;
2243}
2244
2245static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
2246{
2247 int r;
2248 char *data = NULL;
2249
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002250 /*
2251 * You should only get this when cfg80211 hasn't yet initialized
2252 * completely when built-in to the kernel right between the time
2253 * window between nl80211_init() and regulatory_init(), if that is
2254 * even possible.
2255 */
2256 mutex_lock(&cfg80211_mutex);
2257 if (unlikely(!cfg80211_regdomain)) {
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002258 mutex_unlock(&cfg80211_mutex);
2259 return -EINPROGRESS;
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002260 }
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002261 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002262
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002263 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
2264 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002265
2266 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
2267
2268#ifdef CONFIG_WIRELESS_OLD_REGULATORY
2269 /* We ignore world regdom requests with the old regdom setup */
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002270 if (is_world_regdom(data))
2271 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002272#endif
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002273
2274 r = regulatory_hint_user(data);
2275
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002276 return r;
2277}
2278
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002279static int nl80211_get_mesh_params(struct sk_buff *skb,
2280 struct genl_info *info)
2281{
2282 struct cfg80211_registered_device *drv;
2283 struct mesh_config cur_params;
2284 int err;
2285 struct net_device *dev;
2286 void *hdr;
2287 struct nlattr *pinfoattr;
2288 struct sk_buff *msg;
2289
Johannes Berg3b858752009-03-12 09:55:09 +01002290 rtnl_lock();
2291
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002292 /* Look up our device */
2293 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2294 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002295 goto out_rtnl;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002296
Jouni Malinenf3f92582009-03-20 17:57:36 +02002297 if (!drv->ops->get_mesh_params) {
2298 err = -EOPNOTSUPP;
2299 goto out;
2300 }
2301
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002302 /* Get the mesh params */
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002303 err = drv->ops->get_mesh_params(&drv->wiphy, dev, &cur_params);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002304 if (err)
2305 goto out;
2306
2307 /* Draw up a netlink message to send back */
2308 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
2309 if (!msg) {
2310 err = -ENOBUFS;
2311 goto out;
2312 }
2313 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
2314 NL80211_CMD_GET_MESH_PARAMS);
2315 if (!hdr)
2316 goto nla_put_failure;
2317 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS);
2318 if (!pinfoattr)
2319 goto nla_put_failure;
2320 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2321 NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
2322 cur_params.dot11MeshRetryTimeout);
2323 NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
2324 cur_params.dot11MeshConfirmTimeout);
2325 NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
2326 cur_params.dot11MeshHoldingTimeout);
2327 NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
2328 cur_params.dot11MeshMaxPeerLinks);
2329 NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES,
2330 cur_params.dot11MeshMaxRetries);
2331 NLA_PUT_U8(msg, NL80211_MESHCONF_TTL,
2332 cur_params.dot11MeshTTL);
2333 NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
2334 cur_params.auto_open_plinks);
2335 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
2336 cur_params.dot11MeshHWMPmaxPREQretries);
2337 NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
2338 cur_params.path_refresh_time);
2339 NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
2340 cur_params.min_discovery_timeout);
2341 NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
2342 cur_params.dot11MeshHWMPactivePathTimeout);
2343 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
2344 cur_params.dot11MeshHWMPpreqMinInterval);
2345 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
2346 cur_params.dot11MeshHWMPnetDiameterTraversalTime);
2347 nla_nest_end(msg, pinfoattr);
2348 genlmsg_end(msg, hdr);
2349 err = genlmsg_unicast(msg, info->snd_pid);
2350 goto out;
2351
Johannes Berg3b858752009-03-12 09:55:09 +01002352 nla_put_failure:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002353 genlmsg_cancel(msg, hdr);
2354 err = -EMSGSIZE;
Johannes Berg3b858752009-03-12 09:55:09 +01002355 out:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002356 /* Cleanup */
2357 cfg80211_put_dev(drv);
2358 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002359 out_rtnl:
2360 rtnl_unlock();
2361
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002362 return err;
2363}
2364
2365#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \
2366do {\
2367 if (table[attr_num]) {\
2368 cfg.param = nla_fn(table[attr_num]); \
2369 mask |= (1 << (attr_num - 1)); \
2370 } \
2371} while (0);\
2372
2373static struct nla_policy
2374nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] __read_mostly = {
2375 [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
2376 [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
2377 [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
2378 [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
2379 [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
2380 [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
2381 [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
2382
2383 [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
2384 [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
2385 [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
2386 [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
2387 [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
2388 [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
2389};
2390
2391static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
2392{
2393 int err;
2394 u32 mask;
2395 struct cfg80211_registered_device *drv;
2396 struct net_device *dev;
2397 struct mesh_config cfg;
2398 struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
2399 struct nlattr *parent_attr;
2400
2401 parent_attr = info->attrs[NL80211_ATTR_MESH_PARAMS];
2402 if (!parent_attr)
2403 return -EINVAL;
2404 if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
2405 parent_attr, nl80211_meshconf_params_policy))
2406 return -EINVAL;
2407
Johannes Berg3b858752009-03-12 09:55:09 +01002408 rtnl_lock();
2409
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002410 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2411 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002412 goto out_rtnl;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002413
Jouni Malinenf3f92582009-03-20 17:57:36 +02002414 if (!drv->ops->set_mesh_params) {
2415 err = -EOPNOTSUPP;
2416 goto out;
2417 }
2418
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002419 /* This makes sure that there aren't more than 32 mesh config
2420 * parameters (otherwise our bitfield scheme would not work.) */
2421 BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
2422
2423 /* Fill in the params struct */
2424 mask = 0;
2425 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout,
2426 mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16);
2427 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout,
2428 mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16);
2429 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout,
2430 mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16);
2431 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks,
2432 mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16);
2433 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries,
2434 mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8);
2435 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL,
2436 mask, NL80211_MESHCONF_TTL, nla_get_u8);
2437 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
2438 mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8);
2439 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
2440 mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
2441 nla_get_u8);
2442 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time,
2443 mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32);
2444 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout,
2445 mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
2446 nla_get_u16);
2447 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
2448 mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
2449 nla_get_u32);
2450 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
2451 mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
2452 nla_get_u16);
2453 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
2454 dot11MeshHWMPnetDiameterTraversalTime,
2455 mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
2456 nla_get_u16);
2457
2458 /* Apply changes */
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002459 err = drv->ops->set_mesh_params(&drv->wiphy, dev, &cfg, mask);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002460
Jouni Malinenf3f92582009-03-20 17:57:36 +02002461 out:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002462 /* cleanup */
2463 cfg80211_put_dev(drv);
2464 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002465 out_rtnl:
2466 rtnl_unlock();
2467
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002468 return err;
2469}
2470
2471#undef FILL_IN_MESH_PARAM_IF_SET
2472
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002473static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
2474{
2475 struct sk_buff *msg;
2476 void *hdr = NULL;
2477 struct nlattr *nl_reg_rules;
2478 unsigned int i;
2479 int err = -EINVAL;
2480
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002481 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002482
2483 if (!cfg80211_regdomain)
2484 goto out;
2485
2486 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
2487 if (!msg) {
2488 err = -ENOBUFS;
2489 goto out;
2490 }
2491
2492 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
2493 NL80211_CMD_GET_REG);
2494 if (!hdr)
2495 goto nla_put_failure;
2496
2497 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2,
2498 cfg80211_regdomain->alpha2);
2499
2500 nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
2501 if (!nl_reg_rules)
2502 goto nla_put_failure;
2503
2504 for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) {
2505 struct nlattr *nl_reg_rule;
2506 const struct ieee80211_reg_rule *reg_rule;
2507 const struct ieee80211_freq_range *freq_range;
2508 const struct ieee80211_power_rule *power_rule;
2509
2510 reg_rule = &cfg80211_regdomain->reg_rules[i];
2511 freq_range = &reg_rule->freq_range;
2512 power_rule = &reg_rule->power_rule;
2513
2514 nl_reg_rule = nla_nest_start(msg, i);
2515 if (!nl_reg_rule)
2516 goto nla_put_failure;
2517
2518 NLA_PUT_U32(msg, NL80211_ATTR_REG_RULE_FLAGS,
2519 reg_rule->flags);
2520 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_START,
2521 freq_range->start_freq_khz);
2522 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_END,
2523 freq_range->end_freq_khz);
2524 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
2525 freq_range->max_bandwidth_khz);
2526 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
2527 power_rule->max_antenna_gain);
2528 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
2529 power_rule->max_eirp);
2530
2531 nla_nest_end(msg, nl_reg_rule);
2532 }
2533
2534 nla_nest_end(msg, nl_reg_rules);
2535
2536 genlmsg_end(msg, hdr);
2537 err = genlmsg_unicast(msg, info->snd_pid);
2538 goto out;
2539
2540nla_put_failure:
2541 genlmsg_cancel(msg, hdr);
2542 err = -EMSGSIZE;
2543out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002544 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08002545 return err;
2546}
2547
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002548static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
2549{
2550 struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
2551 struct nlattr *nl_reg_rule;
2552 char *alpha2 = NULL;
2553 int rem_reg_rules = 0, r = 0;
2554 u32 num_rules = 0, rule_idx = 0, size_of_regd;
2555 struct ieee80211_regdomain *rd = NULL;
2556
2557 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
2558 return -EINVAL;
2559
2560 if (!info->attrs[NL80211_ATTR_REG_RULES])
2561 return -EINVAL;
2562
2563 alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
2564
2565 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
2566 rem_reg_rules) {
2567 num_rules++;
2568 if (num_rules > NL80211_MAX_SUPP_REG_RULES)
2569 goto bad_reg;
2570 }
2571
2572 if (!reg_is_valid_request(alpha2))
2573 return -EINVAL;
2574
2575 size_of_regd = sizeof(struct ieee80211_regdomain) +
2576 (num_rules * sizeof(struct ieee80211_reg_rule));
2577
2578 rd = kzalloc(size_of_regd, GFP_KERNEL);
2579 if (!rd)
2580 return -ENOMEM;
2581
2582 rd->n_reg_rules = num_rules;
2583 rd->alpha2[0] = alpha2[0];
2584 rd->alpha2[1] = alpha2[1];
2585
2586 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
2587 rem_reg_rules) {
2588 nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
2589 nla_data(nl_reg_rule), nla_len(nl_reg_rule),
2590 reg_rule_policy);
2591 r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
2592 if (r)
2593 goto bad_reg;
2594
2595 rule_idx++;
2596
2597 if (rule_idx > NL80211_MAX_SUPP_REG_RULES)
2598 goto bad_reg;
2599 }
2600
2601 BUG_ON(rule_idx != num_rules);
2602
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002603 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002604 r = set_regdom(rd);
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05002605 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002606 return r;
2607
Johannes Bergd2372b32008-10-24 20:32:20 +02002608 bad_reg:
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002609 kfree(rd);
2610 return -EINVAL;
2611}
2612
Johannes Berg2a519312009-02-10 21:25:55 +01002613static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
2614{
2615 struct cfg80211_registered_device *drv;
2616 struct net_device *dev;
2617 struct cfg80211_scan_request *request;
2618 struct cfg80211_ssid *ssid;
2619 struct ieee80211_channel *channel;
2620 struct nlattr *attr;
2621 struct wiphy *wiphy;
2622 int err, tmp, n_ssids = 0, n_channels = 0, i;
2623 enum ieee80211_band band;
Jouni Malinen70692ad2009-02-16 19:39:13 +02002624 size_t ie_len;
Johannes Berg2a519312009-02-10 21:25:55 +01002625
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002626 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
2627 return -EINVAL;
2628
Johannes Berg3b858752009-03-12 09:55:09 +01002629 rtnl_lock();
2630
Johannes Berg2a519312009-02-10 21:25:55 +01002631 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2632 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002633 goto out_rtnl;
Johannes Berg2a519312009-02-10 21:25:55 +01002634
2635 wiphy = &drv->wiphy;
2636
2637 if (!drv->ops->scan) {
2638 err = -EOPNOTSUPP;
2639 goto out;
2640 }
2641
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002642 if (!netif_running(dev)) {
2643 err = -ENETDOWN;
2644 goto out;
2645 }
2646
Johannes Berg2a519312009-02-10 21:25:55 +01002647 if (drv->scan_req) {
2648 err = -EBUSY;
Johannes Berg3b858752009-03-12 09:55:09 +01002649 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002650 }
2651
2652 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
2653 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp)
2654 n_channels++;
2655 if (!n_channels) {
2656 err = -EINVAL;
Johannes Berg3b858752009-03-12 09:55:09 +01002657 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002658 }
2659 } else {
2660 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
2661 if (wiphy->bands[band])
2662 n_channels += wiphy->bands[band]->n_channels;
2663 }
2664
2665 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
2666 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
2667 n_ssids++;
2668
2669 if (n_ssids > wiphy->max_scan_ssids) {
2670 err = -EINVAL;
Johannes Berg3b858752009-03-12 09:55:09 +01002671 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002672 }
2673
Jouni Malinen70692ad2009-02-16 19:39:13 +02002674 if (info->attrs[NL80211_ATTR_IE])
2675 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
2676 else
2677 ie_len = 0;
2678
Johannes Berg18a83652009-03-31 12:12:05 +02002679 if (ie_len > wiphy->max_scan_ie_len) {
2680 err = -EINVAL;
2681 goto out;
2682 }
2683
Johannes Berg2a519312009-02-10 21:25:55 +01002684 request = kzalloc(sizeof(*request)
2685 + sizeof(*ssid) * n_ssids
Jouni Malinen70692ad2009-02-16 19:39:13 +02002686 + sizeof(channel) * n_channels
2687 + ie_len, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01002688 if (!request) {
2689 err = -ENOMEM;
Johannes Berg3b858752009-03-12 09:55:09 +01002690 goto out;
Johannes Berg2a519312009-02-10 21:25:55 +01002691 }
2692
2693 request->channels = (void *)((char *)request + sizeof(*request));
2694 request->n_channels = n_channels;
2695 if (n_ssids)
2696 request->ssids = (void *)(request->channels + n_channels);
2697 request->n_ssids = n_ssids;
Jouni Malinen70692ad2009-02-16 19:39:13 +02002698 if (ie_len) {
2699 if (request->ssids)
2700 request->ie = (void *)(request->ssids + n_ssids);
2701 else
2702 request->ie = (void *)(request->channels + n_channels);
2703 }
Johannes Berg2a519312009-02-10 21:25:55 +01002704
2705 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
2706 /* user specified, bail out if channel not found */
2707 request->n_channels = n_channels;
2708 i = 0;
2709 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
2710 request->channels[i] = ieee80211_get_channel(wiphy, nla_get_u32(attr));
2711 if (!request->channels[i]) {
2712 err = -EINVAL;
2713 goto out_free;
2714 }
2715 i++;
2716 }
2717 } else {
2718 /* all channels */
2719 i = 0;
2720 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
2721 int j;
2722 if (!wiphy->bands[band])
2723 continue;
2724 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
2725 request->channels[i] = &wiphy->bands[band]->channels[j];
2726 i++;
2727 }
2728 }
2729 }
2730
2731 i = 0;
2732 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
2733 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
2734 if (request->ssids[i].ssid_len > IEEE80211_MAX_SSID_LEN) {
2735 err = -EINVAL;
2736 goto out_free;
2737 }
2738 memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
2739 request->ssids[i].ssid_len = nla_len(attr);
2740 i++;
2741 }
2742 }
2743
Jouni Malinen70692ad2009-02-16 19:39:13 +02002744 if (info->attrs[NL80211_ATTR_IE]) {
2745 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Johannes Bergde95a542009-04-01 11:58:36 +02002746 memcpy((void *)request->ie,
2747 nla_data(info->attrs[NL80211_ATTR_IE]),
Jouni Malinen70692ad2009-02-16 19:39:13 +02002748 request->ie_len);
2749 }
2750
Johannes Berg2a519312009-02-10 21:25:55 +01002751 request->ifidx = dev->ifindex;
2752 request->wiphy = &drv->wiphy;
2753
2754 drv->scan_req = request;
2755 err = drv->ops->scan(&drv->wiphy, dev, request);
2756
2757 out_free:
2758 if (err) {
2759 drv->scan_req = NULL;
2760 kfree(request);
2761 }
Johannes Berg2a519312009-02-10 21:25:55 +01002762 out:
2763 cfg80211_put_dev(drv);
2764 dev_put(dev);
Johannes Berg3b858752009-03-12 09:55:09 +01002765 out_rtnl:
2766 rtnl_unlock();
2767
Johannes Berg2a519312009-02-10 21:25:55 +01002768 return err;
2769}
2770
2771static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
2772 struct cfg80211_registered_device *rdev,
2773 struct net_device *dev,
2774 struct cfg80211_bss *res)
2775{
2776 void *hdr;
2777 struct nlattr *bss;
2778
2779 hdr = nl80211hdr_put(msg, pid, seq, flags,
2780 NL80211_CMD_NEW_SCAN_RESULTS);
2781 if (!hdr)
2782 return -1;
2783
2784 NLA_PUT_U32(msg, NL80211_ATTR_SCAN_GENERATION,
2785 rdev->bss_generation);
2786 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2787
2788 bss = nla_nest_start(msg, NL80211_ATTR_BSS);
2789 if (!bss)
2790 goto nla_put_failure;
2791 if (!is_zero_ether_addr(res->bssid))
2792 NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid);
2793 if (res->information_elements && res->len_information_elements)
2794 NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS,
2795 res->len_information_elements,
2796 res->information_elements);
2797 if (res->tsf)
2798 NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf);
2799 if (res->beacon_interval)
2800 NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval);
2801 NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability);
2802 NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq);
2803
Johannes Berg77965c92009-02-18 18:45:06 +01002804 switch (rdev->wiphy.signal_type) {
Johannes Berg2a519312009-02-10 21:25:55 +01002805 case CFG80211_SIGNAL_TYPE_MBM:
2806 NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal);
2807 break;
2808 case CFG80211_SIGNAL_TYPE_UNSPEC:
2809 NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal);
2810 break;
2811 default:
2812 break;
2813 }
2814
2815 nla_nest_end(msg, bss);
2816
2817 return genlmsg_end(msg, hdr);
2818
2819 nla_put_failure:
2820 genlmsg_cancel(msg, hdr);
2821 return -EMSGSIZE;
2822}
2823
2824static int nl80211_dump_scan(struct sk_buff *skb,
2825 struct netlink_callback *cb)
2826{
2827 struct cfg80211_registered_device *dev;
2828 struct net_device *netdev;
2829 struct cfg80211_internal_bss *scan;
2830 int ifidx = cb->args[0];
2831 int start = cb->args[1], idx = 0;
2832 int err;
2833
2834 if (!ifidx) {
2835 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
2836 nl80211_fam.attrbuf, nl80211_fam.maxattr,
2837 nl80211_policy);
2838 if (err)
2839 return err;
2840
2841 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
2842 return -EINVAL;
2843
2844 ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
2845 if (!ifidx)
2846 return -EINVAL;
2847 cb->args[0] = ifidx;
2848 }
2849
2850 netdev = dev_get_by_index(&init_net, ifidx);
2851 if (!netdev)
2852 return -ENODEV;
2853
2854 dev = cfg80211_get_dev_from_ifindex(ifidx);
2855 if (IS_ERR(dev)) {
2856 err = PTR_ERR(dev);
2857 goto out_put_netdev;
2858 }
2859
2860 spin_lock_bh(&dev->bss_lock);
2861 cfg80211_bss_expire(dev);
2862
2863 list_for_each_entry(scan, &dev->bss_list, list) {
2864 if (++idx <= start)
2865 continue;
2866 if (nl80211_send_bss(skb,
2867 NETLINK_CB(cb->skb).pid,
2868 cb->nlh->nlmsg_seq, NLM_F_MULTI,
2869 dev, netdev, &scan->pub) < 0) {
2870 idx--;
2871 goto out;
2872 }
2873 }
2874
2875 out:
2876 spin_unlock_bh(&dev->bss_lock);
2877
2878 cb->args[1] = idx;
2879 err = skb->len;
2880 cfg80211_put_dev(dev);
2881 out_put_netdev:
2882 dev_put(netdev);
2883
2884 return err;
2885}
2886
Jouni Malinen255e7372009-03-20 21:21:17 +02002887static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
2888{
2889 return auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM ||
2890 auth_type == NL80211_AUTHTYPE_SHARED_KEY ||
2891 auth_type == NL80211_AUTHTYPE_FT ||
2892 auth_type == NL80211_AUTHTYPE_NETWORK_EAP;
2893}
2894
Jouni Malinen636a5d32009-03-19 13:39:22 +02002895static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
2896{
2897 struct cfg80211_registered_device *drv;
2898 struct net_device *dev;
2899 struct cfg80211_auth_request req;
2900 struct wiphy *wiphy;
2901 int err;
2902
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002903 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
2904 return -EINVAL;
2905
2906 if (!info->attrs[NL80211_ATTR_MAC])
2907 return -EINVAL;
2908
Jouni Malinen17780922009-03-27 20:52:47 +02002909 if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
2910 return -EINVAL;
2911
Jouni Malinen636a5d32009-03-19 13:39:22 +02002912 rtnl_lock();
2913
2914 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2915 if (err)
2916 goto unlock_rtnl;
2917
2918 if (!drv->ops->auth) {
2919 err = -EOPNOTSUPP;
2920 goto out;
2921 }
2922
Jouni Malineneec60b02009-03-20 21:21:19 +02002923 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
2924 err = -EOPNOTSUPP;
2925 goto out;
2926 }
2927
Jouni Malinen35a8efe2009-03-20 21:21:18 +02002928 if (!netif_running(dev)) {
2929 err = -ENETDOWN;
2930 goto out;
2931 }
2932
Jouni Malinen636a5d32009-03-19 13:39:22 +02002933 wiphy = &drv->wiphy;
2934 memset(&req, 0, sizeof(req));
2935
2936 req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2937
2938 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
2939 req.chan = ieee80211_get_channel(
2940 wiphy,
2941 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
2942 if (!req.chan) {
2943 err = -EINVAL;
2944 goto out;
2945 }
2946 }
2947
2948 if (info->attrs[NL80211_ATTR_SSID]) {
2949 req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
2950 req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
2951 }
2952
2953 if (info->attrs[NL80211_ATTR_IE]) {
2954 req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
2955 req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
2956 }
2957
Jouni Malinen17780922009-03-27 20:52:47 +02002958 req.auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
2959 if (!nl80211_valid_auth_type(req.auth_type)) {
2960 err = -EINVAL;
2961 goto out;
Jouni Malinen636a5d32009-03-19 13:39:22 +02002962 }
2963
2964 err = drv->ops->auth(&drv->wiphy, dev, &req);
2965
2966out:
2967 cfg80211_put_dev(drv);
2968 dev_put(dev);
2969unlock_rtnl:
2970 rtnl_unlock();
2971 return err;
2972}
2973
2974static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
2975{
2976 struct cfg80211_registered_device *drv;
2977 struct net_device *dev;
2978 struct cfg80211_assoc_request req;
2979 struct wiphy *wiphy;
2980 int err;
2981
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002982 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
2983 return -EINVAL;
2984
2985 if (!info->attrs[NL80211_ATTR_MAC] ||
2986 !info->attrs[NL80211_ATTR_SSID])
2987 return -EINVAL;
2988
Jouni Malinen636a5d32009-03-19 13:39:22 +02002989 rtnl_lock();
2990
2991 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
2992 if (err)
2993 goto unlock_rtnl;
2994
2995 if (!drv->ops->assoc) {
2996 err = -EOPNOTSUPP;
2997 goto out;
2998 }
2999
Jouni Malineneec60b02009-03-20 21:21:19 +02003000 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
3001 err = -EOPNOTSUPP;
3002 goto out;
3003 }
3004
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003005 if (!netif_running(dev)) {
3006 err = -ENETDOWN;
3007 goto out;
3008 }
3009
Jouni Malinen636a5d32009-03-19 13:39:22 +02003010 wiphy = &drv->wiphy;
3011 memset(&req, 0, sizeof(req));
3012
3013 req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
3014
3015 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
3016 req.chan = ieee80211_get_channel(
3017 wiphy,
3018 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
3019 if (!req.chan) {
3020 err = -EINVAL;
3021 goto out;
3022 }
3023 }
3024
Jouni Malinen636a5d32009-03-19 13:39:22 +02003025 req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3026 req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
3027
3028 if (info->attrs[NL80211_ATTR_IE]) {
3029 req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3030 req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3031 }
3032
Jouni Malinendc6382c2009-05-06 22:09:37 +03003033 if (info->attrs[NL80211_ATTR_USE_MFP]) {
3034 enum nl80211_mfp use_mfp =
3035 nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
3036 if (use_mfp == NL80211_MFP_REQUIRED)
3037 req.use_mfp = true;
3038 else if (use_mfp != NL80211_MFP_NO) {
3039 err = -EINVAL;
3040 goto out;
3041 }
3042 }
3043
Jouni Malinen3f77316c2009-05-11 21:57:57 +03003044 req.control_port = info->attrs[NL80211_ATTR_CONTROL_PORT];
3045
Jouni Malinen636a5d32009-03-19 13:39:22 +02003046 err = drv->ops->assoc(&drv->wiphy, dev, &req);
3047
3048out:
3049 cfg80211_put_dev(drv);
3050 dev_put(dev);
3051unlock_rtnl:
3052 rtnl_unlock();
3053 return err;
3054}
3055
3056static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
3057{
3058 struct cfg80211_registered_device *drv;
3059 struct net_device *dev;
3060 struct cfg80211_deauth_request req;
3061 struct wiphy *wiphy;
3062 int err;
3063
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003064 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3065 return -EINVAL;
3066
3067 if (!info->attrs[NL80211_ATTR_MAC])
3068 return -EINVAL;
3069
3070 if (!info->attrs[NL80211_ATTR_REASON_CODE])
3071 return -EINVAL;
3072
Jouni Malinen636a5d32009-03-19 13:39:22 +02003073 rtnl_lock();
3074
3075 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
3076 if (err)
3077 goto unlock_rtnl;
3078
3079 if (!drv->ops->deauth) {
3080 err = -EOPNOTSUPP;
3081 goto out;
3082 }
3083
Jouni Malineneec60b02009-03-20 21:21:19 +02003084 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
3085 err = -EOPNOTSUPP;
3086 goto out;
3087 }
3088
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003089 if (!netif_running(dev)) {
3090 err = -ENETDOWN;
3091 goto out;
3092 }
3093
Jouni Malinen636a5d32009-03-19 13:39:22 +02003094 wiphy = &drv->wiphy;
3095 memset(&req, 0, sizeof(req));
3096
3097 req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
3098
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003099 req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
3100 if (req.reason_code == 0) {
3101 /* Reason Code 0 is reserved */
3102 err = -EINVAL;
3103 goto out;
Jouni Malinen255e7372009-03-20 21:21:17 +02003104 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02003105
3106 if (info->attrs[NL80211_ATTR_IE]) {
3107 req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3108 req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3109 }
3110
3111 err = drv->ops->deauth(&drv->wiphy, dev, &req);
3112
3113out:
3114 cfg80211_put_dev(drv);
3115 dev_put(dev);
3116unlock_rtnl:
3117 rtnl_unlock();
3118 return err;
3119}
3120
3121static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
3122{
3123 struct cfg80211_registered_device *drv;
3124 struct net_device *dev;
3125 struct cfg80211_disassoc_request req;
3126 struct wiphy *wiphy;
3127 int err;
3128
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003129 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3130 return -EINVAL;
3131
3132 if (!info->attrs[NL80211_ATTR_MAC])
3133 return -EINVAL;
3134
3135 if (!info->attrs[NL80211_ATTR_REASON_CODE])
3136 return -EINVAL;
3137
Jouni Malinen636a5d32009-03-19 13:39:22 +02003138 rtnl_lock();
3139
3140 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
3141 if (err)
3142 goto unlock_rtnl;
3143
3144 if (!drv->ops->disassoc) {
3145 err = -EOPNOTSUPP;
3146 goto out;
3147 }
3148
Jouni Malineneec60b02009-03-20 21:21:19 +02003149 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
3150 err = -EOPNOTSUPP;
3151 goto out;
3152 }
3153
Jouni Malinen35a8efe2009-03-20 21:21:18 +02003154 if (!netif_running(dev)) {
3155 err = -ENETDOWN;
3156 goto out;
3157 }
3158
Jouni Malinen636a5d32009-03-19 13:39:22 +02003159 wiphy = &drv->wiphy;
3160 memset(&req, 0, sizeof(req));
3161
3162 req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
3163
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003164 req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
3165 if (req.reason_code == 0) {
3166 /* Reason Code 0 is reserved */
3167 err = -EINVAL;
3168 goto out;
Jouni Malinen255e7372009-03-20 21:21:17 +02003169 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02003170
3171 if (info->attrs[NL80211_ATTR_IE]) {
3172 req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3173 req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3174 }
3175
3176 err = drv->ops->disassoc(&drv->wiphy, dev, &req);
3177
3178out:
3179 cfg80211_put_dev(drv);
3180 dev_put(dev);
3181unlock_rtnl:
3182 rtnl_unlock();
3183 return err;
3184}
3185
Johannes Berg04a773a2009-04-19 21:24:32 +02003186static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
3187{
3188 struct cfg80211_registered_device *drv;
3189 struct net_device *dev;
3190 struct cfg80211_ibss_params ibss;
3191 struct wiphy *wiphy;
3192 int err;
3193
Johannes Berg8e30bc52009-04-22 17:45:38 +02003194 memset(&ibss, 0, sizeof(ibss));
3195
Johannes Berg04a773a2009-04-19 21:24:32 +02003196 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3197 return -EINVAL;
3198
3199 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
3200 !info->attrs[NL80211_ATTR_SSID] ||
3201 !nla_len(info->attrs[NL80211_ATTR_SSID]))
3202 return -EINVAL;
3203
Johannes Berg8e30bc52009-04-22 17:45:38 +02003204 ibss.beacon_interval = 100;
3205
3206 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
3207 ibss.beacon_interval =
3208 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
3209 if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000)
3210 return -EINVAL;
3211 }
3212
Johannes Berg04a773a2009-04-19 21:24:32 +02003213 rtnl_lock();
3214
3215 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
3216 if (err)
3217 goto unlock_rtnl;
3218
3219 if (!drv->ops->join_ibss) {
3220 err = -EOPNOTSUPP;
3221 goto out;
3222 }
3223
3224 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
3225 err = -EOPNOTSUPP;
3226 goto out;
3227 }
3228
3229 if (!netif_running(dev)) {
3230 err = -ENETDOWN;
3231 goto out;
3232 }
3233
3234 wiphy = &drv->wiphy;
Johannes Berg04a773a2009-04-19 21:24:32 +02003235
3236 if (info->attrs[NL80211_ATTR_MAC])
3237 ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
3238 ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
3239 ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
3240
3241 if (info->attrs[NL80211_ATTR_IE]) {
3242 ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
3243 ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3244 }
3245
3246 ibss.channel = ieee80211_get_channel(wiphy,
3247 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
3248 if (!ibss.channel ||
3249 ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
3250 ibss.channel->flags & IEEE80211_CHAN_DISABLED) {
3251 err = -EINVAL;
3252 goto out;
3253 }
3254
3255 ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
3256
3257 err = cfg80211_join_ibss(drv, dev, &ibss);
3258
3259out:
3260 cfg80211_put_dev(drv);
3261 dev_put(dev);
3262unlock_rtnl:
3263 rtnl_unlock();
3264 return err;
3265}
3266
3267static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
3268{
3269 struct cfg80211_registered_device *drv;
3270 struct net_device *dev;
3271 int err;
3272
3273 rtnl_lock();
3274
3275 err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
3276 if (err)
3277 goto unlock_rtnl;
3278
3279 if (!drv->ops->leave_ibss) {
3280 err = -EOPNOTSUPP;
3281 goto out;
3282 }
3283
3284 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
3285 err = -EOPNOTSUPP;
3286 goto out;
3287 }
3288
3289 if (!netif_running(dev)) {
3290 err = -ENETDOWN;
3291 goto out;
3292 }
3293
Johannes Berg9d308422009-04-20 18:43:46 +02003294 err = cfg80211_leave_ibss(drv, dev, false);
Johannes Berg04a773a2009-04-19 21:24:32 +02003295
3296out:
3297 cfg80211_put_dev(drv);
3298 dev_put(dev);
3299unlock_rtnl:
3300 rtnl_unlock();
3301 return err;
3302}
3303
Johannes Berg55682962007-09-20 13:09:35 -04003304static struct genl_ops nl80211_ops[] = {
3305 {
3306 .cmd = NL80211_CMD_GET_WIPHY,
3307 .doit = nl80211_get_wiphy,
3308 .dumpit = nl80211_dump_wiphy,
3309 .policy = nl80211_policy,
3310 /* can be retrieved by unprivileged users */
3311 },
3312 {
3313 .cmd = NL80211_CMD_SET_WIPHY,
3314 .doit = nl80211_set_wiphy,
3315 .policy = nl80211_policy,
3316 .flags = GENL_ADMIN_PERM,
3317 },
3318 {
3319 .cmd = NL80211_CMD_GET_INTERFACE,
3320 .doit = nl80211_get_interface,
3321 .dumpit = nl80211_dump_interface,
3322 .policy = nl80211_policy,
3323 /* can be retrieved by unprivileged users */
3324 },
3325 {
3326 .cmd = NL80211_CMD_SET_INTERFACE,
3327 .doit = nl80211_set_interface,
3328 .policy = nl80211_policy,
3329 .flags = GENL_ADMIN_PERM,
3330 },
3331 {
3332 .cmd = NL80211_CMD_NEW_INTERFACE,
3333 .doit = nl80211_new_interface,
3334 .policy = nl80211_policy,
3335 .flags = GENL_ADMIN_PERM,
3336 },
3337 {
3338 .cmd = NL80211_CMD_DEL_INTERFACE,
3339 .doit = nl80211_del_interface,
3340 .policy = nl80211_policy,
3341 .flags = GENL_ADMIN_PERM,
3342 },
Johannes Berg41ade002007-12-19 02:03:29 +01003343 {
3344 .cmd = NL80211_CMD_GET_KEY,
3345 .doit = nl80211_get_key,
3346 .policy = nl80211_policy,
3347 .flags = GENL_ADMIN_PERM,
3348 },
3349 {
3350 .cmd = NL80211_CMD_SET_KEY,
3351 .doit = nl80211_set_key,
3352 .policy = nl80211_policy,
3353 .flags = GENL_ADMIN_PERM,
3354 },
3355 {
3356 .cmd = NL80211_CMD_NEW_KEY,
3357 .doit = nl80211_new_key,
3358 .policy = nl80211_policy,
3359 .flags = GENL_ADMIN_PERM,
3360 },
3361 {
3362 .cmd = NL80211_CMD_DEL_KEY,
3363 .doit = nl80211_del_key,
3364 .policy = nl80211_policy,
3365 .flags = GENL_ADMIN_PERM,
3366 },
Johannes Berged1b6cc2007-12-19 02:03:32 +01003367 {
3368 .cmd = NL80211_CMD_SET_BEACON,
3369 .policy = nl80211_policy,
3370 .flags = GENL_ADMIN_PERM,
3371 .doit = nl80211_addset_beacon,
3372 },
3373 {
3374 .cmd = NL80211_CMD_NEW_BEACON,
3375 .policy = nl80211_policy,
3376 .flags = GENL_ADMIN_PERM,
3377 .doit = nl80211_addset_beacon,
3378 },
3379 {
3380 .cmd = NL80211_CMD_DEL_BEACON,
3381 .policy = nl80211_policy,
3382 .flags = GENL_ADMIN_PERM,
3383 .doit = nl80211_del_beacon,
3384 },
Johannes Berg5727ef12007-12-19 02:03:34 +01003385 {
3386 .cmd = NL80211_CMD_GET_STATION,
3387 .doit = nl80211_get_station,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003388 .dumpit = nl80211_dump_station,
Johannes Berg5727ef12007-12-19 02:03:34 +01003389 .policy = nl80211_policy,
Johannes Berg5727ef12007-12-19 02:03:34 +01003390 },
3391 {
3392 .cmd = NL80211_CMD_SET_STATION,
3393 .doit = nl80211_set_station,
3394 .policy = nl80211_policy,
3395 .flags = GENL_ADMIN_PERM,
3396 },
3397 {
3398 .cmd = NL80211_CMD_NEW_STATION,
3399 .doit = nl80211_new_station,
3400 .policy = nl80211_policy,
3401 .flags = GENL_ADMIN_PERM,
3402 },
3403 {
3404 .cmd = NL80211_CMD_DEL_STATION,
3405 .doit = nl80211_del_station,
3406 .policy = nl80211_policy,
3407 .flags = GENL_ADMIN_PERM,
3408 },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003409 {
3410 .cmd = NL80211_CMD_GET_MPATH,
3411 .doit = nl80211_get_mpath,
3412 .dumpit = nl80211_dump_mpath,
3413 .policy = nl80211_policy,
3414 .flags = GENL_ADMIN_PERM,
3415 },
3416 {
3417 .cmd = NL80211_CMD_SET_MPATH,
3418 .doit = nl80211_set_mpath,
3419 .policy = nl80211_policy,
3420 .flags = GENL_ADMIN_PERM,
3421 },
3422 {
3423 .cmd = NL80211_CMD_NEW_MPATH,
3424 .doit = nl80211_new_mpath,
3425 .policy = nl80211_policy,
3426 .flags = GENL_ADMIN_PERM,
3427 },
3428 {
3429 .cmd = NL80211_CMD_DEL_MPATH,
3430 .doit = nl80211_del_mpath,
3431 .policy = nl80211_policy,
3432 .flags = GENL_ADMIN_PERM,
3433 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003434 {
3435 .cmd = NL80211_CMD_SET_BSS,
3436 .doit = nl80211_set_bss,
3437 .policy = nl80211_policy,
3438 .flags = GENL_ADMIN_PERM,
3439 },
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003440 {
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003441 .cmd = NL80211_CMD_GET_REG,
3442 .doit = nl80211_get_reg,
3443 .policy = nl80211_policy,
3444 /* can be retrieved by unprivileged users */
3445 },
3446 {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003447 .cmd = NL80211_CMD_SET_REG,
3448 .doit = nl80211_set_reg,
3449 .policy = nl80211_policy,
3450 .flags = GENL_ADMIN_PERM,
3451 },
3452 {
3453 .cmd = NL80211_CMD_REQ_SET_REG,
3454 .doit = nl80211_req_set_reg,
3455 .policy = nl80211_policy,
3456 .flags = GENL_ADMIN_PERM,
3457 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003458 {
3459 .cmd = NL80211_CMD_GET_MESH_PARAMS,
3460 .doit = nl80211_get_mesh_params,
3461 .policy = nl80211_policy,
3462 /* can be retrieved by unprivileged users */
3463 },
3464 {
3465 .cmd = NL80211_CMD_SET_MESH_PARAMS,
3466 .doit = nl80211_set_mesh_params,
3467 .policy = nl80211_policy,
3468 .flags = GENL_ADMIN_PERM,
3469 },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +02003470 {
Johannes Berg2a519312009-02-10 21:25:55 +01003471 .cmd = NL80211_CMD_TRIGGER_SCAN,
3472 .doit = nl80211_trigger_scan,
3473 .policy = nl80211_policy,
3474 .flags = GENL_ADMIN_PERM,
3475 },
3476 {
3477 .cmd = NL80211_CMD_GET_SCAN,
3478 .policy = nl80211_policy,
3479 .dumpit = nl80211_dump_scan,
3480 },
Jouni Malinen636a5d32009-03-19 13:39:22 +02003481 {
3482 .cmd = NL80211_CMD_AUTHENTICATE,
3483 .doit = nl80211_authenticate,
3484 .policy = nl80211_policy,
3485 .flags = GENL_ADMIN_PERM,
3486 },
3487 {
3488 .cmd = NL80211_CMD_ASSOCIATE,
3489 .doit = nl80211_associate,
3490 .policy = nl80211_policy,
3491 .flags = GENL_ADMIN_PERM,
3492 },
3493 {
3494 .cmd = NL80211_CMD_DEAUTHENTICATE,
3495 .doit = nl80211_deauthenticate,
3496 .policy = nl80211_policy,
3497 .flags = GENL_ADMIN_PERM,
3498 },
3499 {
3500 .cmd = NL80211_CMD_DISASSOCIATE,
3501 .doit = nl80211_disassociate,
3502 .policy = nl80211_policy,
3503 .flags = GENL_ADMIN_PERM,
3504 },
Johannes Berg04a773a2009-04-19 21:24:32 +02003505 {
3506 .cmd = NL80211_CMD_JOIN_IBSS,
3507 .doit = nl80211_join_ibss,
3508 .policy = nl80211_policy,
3509 .flags = GENL_ADMIN_PERM,
3510 },
3511 {
3512 .cmd = NL80211_CMD_LEAVE_IBSS,
3513 .doit = nl80211_leave_ibss,
3514 .policy = nl80211_policy,
3515 .flags = GENL_ADMIN_PERM,
3516 },
Johannes Berg55682962007-09-20 13:09:35 -04003517};
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003518static struct genl_multicast_group nl80211_mlme_mcgrp = {
3519 .name = "mlme",
3520};
Johannes Berg55682962007-09-20 13:09:35 -04003521
3522/* multicast groups */
3523static struct genl_multicast_group nl80211_config_mcgrp = {
3524 .name = "config",
3525};
Johannes Berg2a519312009-02-10 21:25:55 +01003526static struct genl_multicast_group nl80211_scan_mcgrp = {
3527 .name = "scan",
3528};
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04003529static struct genl_multicast_group nl80211_regulatory_mcgrp = {
3530 .name = "regulatory",
3531};
Johannes Berg55682962007-09-20 13:09:35 -04003532
3533/* notification functions */
3534
3535void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
3536{
3537 struct sk_buff *msg;
3538
3539 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3540 if (!msg)
3541 return;
3542
3543 if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
3544 nlmsg_free(msg);
3545 return;
3546 }
3547
3548 genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL);
3549}
3550
Johannes Berg2a519312009-02-10 21:25:55 +01003551static int nl80211_send_scan_donemsg(struct sk_buff *msg,
3552 struct cfg80211_registered_device *rdev,
3553 struct net_device *netdev,
3554 u32 pid, u32 seq, int flags,
3555 u32 cmd)
3556{
3557 void *hdr;
3558
3559 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
3560 if (!hdr)
3561 return -1;
3562
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -05003563 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg2a519312009-02-10 21:25:55 +01003564 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
3565
3566 /* XXX: we should probably bounce back the request? */
3567
3568 return genlmsg_end(msg, hdr);
3569
3570 nla_put_failure:
3571 genlmsg_cancel(msg, hdr);
3572 return -EMSGSIZE;
3573}
3574
3575void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
3576 struct net_device *netdev)
3577{
3578 struct sk_buff *msg;
3579
3580 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3581 if (!msg)
3582 return;
3583
3584 if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0,
3585 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
3586 nlmsg_free(msg);
3587 return;
3588 }
3589
3590 genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL);
3591}
3592
3593void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
3594 struct net_device *netdev)
3595{
3596 struct sk_buff *msg;
3597
3598 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3599 if (!msg)
3600 return;
3601
3602 if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0,
3603 NL80211_CMD_SCAN_ABORTED) < 0) {
3604 nlmsg_free(msg);
3605 return;
3606 }
3607
3608 genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL);
3609}
3610
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04003611/*
3612 * This can happen on global regulatory changes or device specific settings
3613 * based on custom world regulatory domains.
3614 */
3615void nl80211_send_reg_change_event(struct regulatory_request *request)
3616{
3617 struct sk_buff *msg;
3618 void *hdr;
3619
3620 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3621 if (!msg)
3622 return;
3623
3624 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
3625 if (!hdr) {
3626 nlmsg_free(msg);
3627 return;
3628 }
3629
3630 /* Userspace can always count this one always being set */
3631 NLA_PUT_U8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator);
3632
3633 if (request->alpha2[0] == '0' && request->alpha2[1] == '0')
3634 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
3635 NL80211_REGDOM_TYPE_WORLD);
3636 else if (request->alpha2[0] == '9' && request->alpha2[1] == '9')
3637 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
3638 NL80211_REGDOM_TYPE_CUSTOM_WORLD);
3639 else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
3640 request->intersect)
3641 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
3642 NL80211_REGDOM_TYPE_INTERSECTION);
3643 else {
3644 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
3645 NL80211_REGDOM_TYPE_COUNTRY);
3646 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2);
3647 }
3648
3649 if (wiphy_idx_valid(request->wiphy_idx))
3650 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx);
3651
3652 if (genlmsg_end(msg, hdr) < 0) {
3653 nlmsg_free(msg);
3654 return;
3655 }
3656
3657 genlmsg_multicast(msg, 0, nl80211_regulatory_mcgrp.id, GFP_KERNEL);
3658
3659 return;
3660
3661nla_put_failure:
3662 genlmsg_cancel(msg, hdr);
3663 nlmsg_free(msg);
3664}
3665
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003666static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
3667 struct net_device *netdev,
3668 const u8 *buf, size_t len,
3669 enum nl80211_commands cmd)
3670{
3671 struct sk_buff *msg;
3672 void *hdr;
3673
Jouni Malinend91c01c2009-04-18 21:53:15 +03003674 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003675 if (!msg)
3676 return;
3677
3678 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
3679 if (!hdr) {
3680 nlmsg_free(msg);
3681 return;
3682 }
3683
3684 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
3685 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
3686 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
3687
3688 if (genlmsg_end(msg, hdr) < 0) {
3689 nlmsg_free(msg);
3690 return;
3691 }
3692
Jouni Malinend91c01c2009-04-18 21:53:15 +03003693 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_ATOMIC);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003694 return;
3695
3696 nla_put_failure:
3697 genlmsg_cancel(msg, hdr);
3698 nlmsg_free(msg);
3699}
3700
3701void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
3702 struct net_device *netdev, const u8 *buf, size_t len)
3703{
3704 nl80211_send_mlme_event(rdev, netdev, buf, len,
3705 NL80211_CMD_AUTHENTICATE);
3706}
3707
3708void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
3709 struct net_device *netdev, const u8 *buf,
3710 size_t len)
3711{
3712 nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_ASSOCIATE);
3713}
3714
Jouni Malinen53b46b82009-03-27 20:53:56 +02003715void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
3716 struct net_device *netdev, const u8 *buf, size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003717{
3718 nl80211_send_mlme_event(rdev, netdev, buf, len,
3719 NL80211_CMD_DEAUTHENTICATE);
3720}
3721
Jouni Malinen53b46b82009-03-27 20:53:56 +02003722void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
3723 struct net_device *netdev, const u8 *buf,
3724 size_t len)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003725{
3726 nl80211_send_mlme_event(rdev, netdev, buf, len,
3727 NL80211_CMD_DISASSOCIATE);
3728}
3729
Luis R. Rodriguez1b06bb42009-05-02 00:34:48 -04003730static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
3731 struct net_device *netdev, int cmd,
3732 const u8 *addr)
Jouni Malinen1965c852009-04-22 21:38:25 +03003733{
3734 struct sk_buff *msg;
3735 void *hdr;
3736
3737 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
3738 if (!msg)
3739 return;
3740
3741 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
3742 if (!hdr) {
3743 nlmsg_free(msg);
3744 return;
3745 }
3746
3747 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
3748 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
3749 NLA_PUT_FLAG(msg, NL80211_ATTR_TIMED_OUT);
3750 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
3751
3752 if (genlmsg_end(msg, hdr) < 0) {
3753 nlmsg_free(msg);
3754 return;
3755 }
3756
3757 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_ATOMIC);
3758 return;
3759
3760 nla_put_failure:
3761 genlmsg_cancel(msg, hdr);
3762 nlmsg_free(msg);
3763}
3764
3765void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
3766 struct net_device *netdev, const u8 *addr)
3767{
3768 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE,
3769 addr);
3770}
3771
3772void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
3773 struct net_device *netdev, const u8 *addr)
3774{
3775 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE, addr);
3776}
3777
Johannes Berg04a773a2009-04-19 21:24:32 +02003778void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
3779 struct net_device *netdev, const u8 *bssid,
3780 gfp_t gfp)
3781{
3782 struct sk_buff *msg;
3783 void *hdr;
3784
3785 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
3786 if (!msg)
3787 return;
3788
3789 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS);
3790 if (!hdr) {
3791 nlmsg_free(msg);
3792 return;
3793 }
3794
3795 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
3796 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
3797 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
3798
3799 if (genlmsg_end(msg, hdr) < 0) {
3800 nlmsg_free(msg);
3801 return;
3802 }
3803
3804 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
3805 return;
3806
3807 nla_put_failure:
3808 genlmsg_cancel(msg, hdr);
3809 nlmsg_free(msg);
3810}
3811
Jouni Malinena3b8b052009-03-27 21:59:49 +02003812void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
3813 struct net_device *netdev, const u8 *addr,
3814 enum nl80211_key_type key_type, int key_id,
3815 const u8 *tsc)
3816{
3817 struct sk_buff *msg;
3818 void *hdr;
3819
3820 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3821 if (!msg)
3822 return;
3823
3824 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
3825 if (!hdr) {
3826 nlmsg_free(msg);
3827 return;
3828 }
3829
3830 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
3831 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
3832 if (addr)
3833 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
3834 NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, key_type);
3835 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id);
3836 if (tsc)
3837 NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc);
3838
3839 if (genlmsg_end(msg, hdr) < 0) {
3840 nlmsg_free(msg);
3841 return;
3842 }
3843
3844 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_KERNEL);
3845 return;
3846
3847 nla_put_failure:
3848 genlmsg_cancel(msg, hdr);
3849 nlmsg_free(msg);
3850}
3851
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04003852void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
3853 struct ieee80211_channel *channel_before,
3854 struct ieee80211_channel *channel_after)
3855{
3856 struct sk_buff *msg;
3857 void *hdr;
3858 struct nlattr *nl_freq;
3859
3860 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
3861 if (!msg)
3862 return;
3863
3864 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
3865 if (!hdr) {
3866 nlmsg_free(msg);
3867 return;
3868 }
3869
3870 /*
3871 * Since we are applying the beacon hint to a wiphy we know its
3872 * wiphy_idx is valid
3873 */
3874 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy));
3875
3876 /* Before */
3877 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
3878 if (!nl_freq)
3879 goto nla_put_failure;
3880 if (nl80211_msg_put_channel(msg, channel_before))
3881 goto nla_put_failure;
3882 nla_nest_end(msg, nl_freq);
3883
3884 /* After */
3885 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
3886 if (!nl_freq)
3887 goto nla_put_failure;
3888 if (nl80211_msg_put_channel(msg, channel_after))
3889 goto nla_put_failure;
3890 nla_nest_end(msg, nl_freq);
3891
3892 if (genlmsg_end(msg, hdr) < 0) {
3893 nlmsg_free(msg);
3894 return;
3895 }
3896
3897 genlmsg_multicast(msg, 0, nl80211_regulatory_mcgrp.id, GFP_ATOMIC);
3898
3899 return;
3900
3901nla_put_failure:
3902 genlmsg_cancel(msg, hdr);
3903 nlmsg_free(msg);
3904}
3905
Johannes Berg55682962007-09-20 13:09:35 -04003906/* initialisation/exit functions */
3907
3908int nl80211_init(void)
3909{
3910 int err, i;
3911
3912 err = genl_register_family(&nl80211_fam);
3913 if (err)
3914 return err;
3915
3916 for (i = 0; i < ARRAY_SIZE(nl80211_ops); i++) {
3917 err = genl_register_ops(&nl80211_fam, &nl80211_ops[i]);
3918 if (err)
3919 goto err_out;
3920 }
3921
3922 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
3923 if (err)
3924 goto err_out;
3925
Johannes Berg2a519312009-02-10 21:25:55 +01003926 err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
3927 if (err)
3928 goto err_out;
3929
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04003930 err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
3931 if (err)
3932 goto err_out;
3933
Jouni Malinen6039f6d2009-03-19 13:39:21 +02003934 err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
3935 if (err)
3936 goto err_out;
3937
Johannes Berg55682962007-09-20 13:09:35 -04003938 return 0;
3939 err_out:
3940 genl_unregister_family(&nl80211_fam);
3941 return err;
3942}
3943
3944void nl80211_exit(void)
3945{
3946 genl_unregister_family(&nl80211_fam);
3947}