]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * mac80211 configuration hooks for cfg80211 | |
3 | * | |
4 | * Copyright 2006, 2007 Johannes Berg <johannes@sipsolutions.net> | |
5 | * | |
6 | * This file is GPLv2 as found in COPYING. | |
7 | */ | |
8 | ||
9 | #include <linux/ieee80211.h> | |
10 | #include <linux/nl80211.h> | |
11 | #include <linux/rtnetlink.h> | |
12 | #include <net/net_namespace.h> | |
13 | #include <linux/rcupdate.h> | |
14 | #include <net/cfg80211.h> | |
15 | #include "ieee80211_i.h" | |
16 | #include "cfg.h" | |
17 | #include "rate.h" | |
18 | #include "mesh.h" | |
19 | ||
20 | static bool nl80211_type_check(enum nl80211_iftype type) | |
21 | { | |
22 | switch (type) { | |
23 | case NL80211_IFTYPE_ADHOC: | |
24 | case NL80211_IFTYPE_STATION: | |
25 | case NL80211_IFTYPE_MONITOR: | |
26 | #ifdef CONFIG_MAC80211_MESH | |
27 | case NL80211_IFTYPE_MESH_POINT: | |
28 | #endif | |
29 | case NL80211_IFTYPE_AP: | |
30 | case NL80211_IFTYPE_AP_VLAN: | |
31 | case NL80211_IFTYPE_WDS: | |
32 | return true; | |
33 | default: | |
34 | return false; | |
35 | } | |
36 | } | |
37 | ||
38 | static int ieee80211_add_iface(struct wiphy *wiphy, char *name, | |
39 | enum nl80211_iftype type, u32 *flags, | |
40 | struct vif_params *params) | |
41 | { | |
42 | struct ieee80211_local *local = wiphy_priv(wiphy); | |
43 | struct net_device *dev; | |
44 | struct ieee80211_sub_if_data *sdata; | |
45 | int err; | |
46 | ||
47 | if (!nl80211_type_check(type)) | |
48 | return -EINVAL; | |
49 | ||
50 | err = ieee80211_if_add(local, name, &dev, type, params); | |
51 | if (err || type != NL80211_IFTYPE_MONITOR || !flags) | |
52 | return err; | |
53 | ||
54 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
55 | sdata->u.mntr_flags = *flags; | |
56 | return 0; | |
57 | } | |
58 | ||
59 | static int ieee80211_del_iface(struct wiphy *wiphy, int ifindex) | |
60 | { | |
61 | struct net_device *dev; | |
62 | struct ieee80211_sub_if_data *sdata; | |
63 | ||
64 | /* we're under RTNL */ | |
65 | dev = __dev_get_by_index(&init_net, ifindex); | |
66 | if (!dev) | |
67 | return -ENODEV; | |
68 | ||
69 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
70 | ||
71 | ieee80211_if_remove(sdata); | |
72 | ||
73 | return 0; | |
74 | } | |
75 | ||
76 | static int ieee80211_change_iface(struct wiphy *wiphy, int ifindex, | |
77 | enum nl80211_iftype type, u32 *flags, | |
78 | struct vif_params *params) | |
79 | { | |
80 | struct net_device *dev; | |
81 | struct ieee80211_sub_if_data *sdata; | |
82 | int ret; | |
83 | ||
84 | /* we're under RTNL */ | |
85 | dev = __dev_get_by_index(&init_net, ifindex); | |
86 | if (!dev) | |
87 | return -ENODEV; | |
88 | ||
89 | if (!nl80211_type_check(type)) | |
90 | return -EINVAL; | |
91 | ||
92 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
93 | ||
94 | ret = ieee80211_if_change_type(sdata, type); | |
95 | if (ret) | |
96 | return ret; | |
97 | ||
98 | if (netif_running(sdata->dev)) | |
99 | return -EBUSY; | |
100 | ||
101 | if (ieee80211_vif_is_mesh(&sdata->vif) && params->mesh_id_len) | |
102 | ieee80211_sdata_set_mesh_id(sdata, | |
103 | params->mesh_id_len, | |
104 | params->mesh_id); | |
105 | ||
106 | if (sdata->vif.type != NL80211_IFTYPE_MONITOR || !flags) | |
107 | return 0; | |
108 | ||
109 | sdata->u.mntr_flags = *flags; | |
110 | return 0; | |
111 | } | |
112 | ||
113 | static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, | |
114 | u8 key_idx, u8 *mac_addr, | |
115 | struct key_params *params) | |
116 | { | |
117 | struct ieee80211_sub_if_data *sdata; | |
118 | struct sta_info *sta = NULL; | |
119 | enum ieee80211_key_alg alg; | |
120 | struct ieee80211_key *key; | |
121 | int err; | |
122 | ||
123 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
124 | ||
125 | switch (params->cipher) { | |
126 | case WLAN_CIPHER_SUITE_WEP40: | |
127 | case WLAN_CIPHER_SUITE_WEP104: | |
128 | alg = ALG_WEP; | |
129 | break; | |
130 | case WLAN_CIPHER_SUITE_TKIP: | |
131 | alg = ALG_TKIP; | |
132 | break; | |
133 | case WLAN_CIPHER_SUITE_CCMP: | |
134 | alg = ALG_CCMP; | |
135 | break; | |
136 | case WLAN_CIPHER_SUITE_AES_CMAC: | |
137 | alg = ALG_AES_CMAC; | |
138 | break; | |
139 | default: | |
140 | return -EINVAL; | |
141 | } | |
142 | ||
143 | key = ieee80211_key_alloc(alg, key_idx, params->key_len, params->key); | |
144 | if (!key) | |
145 | return -ENOMEM; | |
146 | ||
147 | rcu_read_lock(); | |
148 | ||
149 | if (mac_addr) { | |
150 | sta = sta_info_get(sdata->local, mac_addr); | |
151 | if (!sta) { | |
152 | ieee80211_key_free(key); | |
153 | err = -ENOENT; | |
154 | goto out_unlock; | |
155 | } | |
156 | } | |
157 | ||
158 | ieee80211_key_link(key, sdata, sta); | |
159 | ||
160 | err = 0; | |
161 | out_unlock: | |
162 | rcu_read_unlock(); | |
163 | ||
164 | return err; | |
165 | } | |
166 | ||
167 | static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev, | |
168 | u8 key_idx, u8 *mac_addr) | |
169 | { | |
170 | struct ieee80211_sub_if_data *sdata; | |
171 | struct sta_info *sta; | |
172 | int ret; | |
173 | ||
174 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
175 | ||
176 | rcu_read_lock(); | |
177 | ||
178 | if (mac_addr) { | |
179 | ret = -ENOENT; | |
180 | ||
181 | sta = sta_info_get(sdata->local, mac_addr); | |
182 | if (!sta) | |
183 | goto out_unlock; | |
184 | ||
185 | if (sta->key) { | |
186 | ieee80211_key_free(sta->key); | |
187 | WARN_ON(sta->key); | |
188 | ret = 0; | |
189 | } | |
190 | ||
191 | goto out_unlock; | |
192 | } | |
193 | ||
194 | if (!sdata->keys[key_idx]) { | |
195 | ret = -ENOENT; | |
196 | goto out_unlock; | |
197 | } | |
198 | ||
199 | ieee80211_key_free(sdata->keys[key_idx]); | |
200 | WARN_ON(sdata->keys[key_idx]); | |
201 | ||
202 | ret = 0; | |
203 | out_unlock: | |
204 | rcu_read_unlock(); | |
205 | ||
206 | return ret; | |
207 | } | |
208 | ||
209 | static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, | |
210 | u8 key_idx, u8 *mac_addr, void *cookie, | |
211 | void (*callback)(void *cookie, | |
212 | struct key_params *params)) | |
213 | { | |
214 | struct ieee80211_sub_if_data *sdata; | |
215 | struct sta_info *sta = NULL; | |
216 | u8 seq[6] = {0}; | |
217 | struct key_params params; | |
218 | struct ieee80211_key *key; | |
219 | u32 iv32; | |
220 | u16 iv16; | |
221 | int err = -ENOENT; | |
222 | ||
223 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
224 | ||
225 | rcu_read_lock(); | |
226 | ||
227 | if (mac_addr) { | |
228 | sta = sta_info_get(sdata->local, mac_addr); | |
229 | if (!sta) | |
230 | goto out; | |
231 | ||
232 | key = sta->key; | |
233 | } else | |
234 | key = sdata->keys[key_idx]; | |
235 | ||
236 | if (!key) | |
237 | goto out; | |
238 | ||
239 | memset(¶ms, 0, sizeof(params)); | |
240 | ||
241 | switch (key->conf.alg) { | |
242 | case ALG_TKIP: | |
243 | params.cipher = WLAN_CIPHER_SUITE_TKIP; | |
244 | ||
245 | iv32 = key->u.tkip.tx.iv32; | |
246 | iv16 = key->u.tkip.tx.iv16; | |
247 | ||
248 | if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE && | |
249 | sdata->local->ops->get_tkip_seq) | |
250 | sdata->local->ops->get_tkip_seq( | |
251 | local_to_hw(sdata->local), | |
252 | key->conf.hw_key_idx, | |
253 | &iv32, &iv16); | |
254 | ||
255 | seq[0] = iv16 & 0xff; | |
256 | seq[1] = (iv16 >> 8) & 0xff; | |
257 | seq[2] = iv32 & 0xff; | |
258 | seq[3] = (iv32 >> 8) & 0xff; | |
259 | seq[4] = (iv32 >> 16) & 0xff; | |
260 | seq[5] = (iv32 >> 24) & 0xff; | |
261 | params.seq = seq; | |
262 | params.seq_len = 6; | |
263 | break; | |
264 | case ALG_CCMP: | |
265 | params.cipher = WLAN_CIPHER_SUITE_CCMP; | |
266 | seq[0] = key->u.ccmp.tx_pn[5]; | |
267 | seq[1] = key->u.ccmp.tx_pn[4]; | |
268 | seq[2] = key->u.ccmp.tx_pn[3]; | |
269 | seq[3] = key->u.ccmp.tx_pn[2]; | |
270 | seq[4] = key->u.ccmp.tx_pn[1]; | |
271 | seq[5] = key->u.ccmp.tx_pn[0]; | |
272 | params.seq = seq; | |
273 | params.seq_len = 6; | |
274 | break; | |
275 | case ALG_WEP: | |
276 | if (key->conf.keylen == 5) | |
277 | params.cipher = WLAN_CIPHER_SUITE_WEP40; | |
278 | else | |
279 | params.cipher = WLAN_CIPHER_SUITE_WEP104; | |
280 | break; | |
281 | case ALG_AES_CMAC: | |
282 | params.cipher = WLAN_CIPHER_SUITE_AES_CMAC; | |
283 | seq[0] = key->u.aes_cmac.tx_pn[5]; | |
284 | seq[1] = key->u.aes_cmac.tx_pn[4]; | |
285 | seq[2] = key->u.aes_cmac.tx_pn[3]; | |
286 | seq[3] = key->u.aes_cmac.tx_pn[2]; | |
287 | seq[4] = key->u.aes_cmac.tx_pn[1]; | |
288 | seq[5] = key->u.aes_cmac.tx_pn[0]; | |
289 | params.seq = seq; | |
290 | params.seq_len = 6; | |
291 | break; | |
292 | } | |
293 | ||
294 | params.key = key->conf.key; | |
295 | params.key_len = key->conf.keylen; | |
296 | ||
297 | callback(cookie, ¶ms); | |
298 | err = 0; | |
299 | ||
300 | out: | |
301 | rcu_read_unlock(); | |
302 | return err; | |
303 | } | |
304 | ||
305 | static int ieee80211_config_default_key(struct wiphy *wiphy, | |
306 | struct net_device *dev, | |
307 | u8 key_idx) | |
308 | { | |
309 | struct ieee80211_sub_if_data *sdata; | |
310 | ||
311 | rcu_read_lock(); | |
312 | ||
313 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
314 | ieee80211_set_default_key(sdata, key_idx); | |
315 | ||
316 | rcu_read_unlock(); | |
317 | ||
318 | return 0; | |
319 | } | |
320 | ||
321 | static int ieee80211_config_default_mgmt_key(struct wiphy *wiphy, | |
322 | struct net_device *dev, | |
323 | u8 key_idx) | |
324 | { | |
325 | struct ieee80211_sub_if_data *sdata; | |
326 | ||
327 | rcu_read_lock(); | |
328 | ||
329 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
330 | ieee80211_set_default_mgmt_key(sdata, key_idx); | |
331 | ||
332 | rcu_read_unlock(); | |
333 | ||
334 | return 0; | |
335 | } | |
336 | ||
337 | static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) | |
338 | { | |
339 | struct ieee80211_sub_if_data *sdata = sta->sdata; | |
340 | ||
341 | sinfo->filled = STATION_INFO_INACTIVE_TIME | | |
342 | STATION_INFO_RX_BYTES | | |
343 | STATION_INFO_TX_BYTES | | |
344 | STATION_INFO_TX_BITRATE; | |
345 | ||
346 | sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); | |
347 | sinfo->rx_bytes = sta->rx_bytes; | |
348 | sinfo->tx_bytes = sta->tx_bytes; | |
349 | ||
350 | if (sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) { | |
351 | sinfo->filled |= STATION_INFO_SIGNAL; | |
352 | sinfo->signal = (s8)sta->last_signal; | |
353 | } | |
354 | ||
355 | sinfo->txrate.flags = 0; | |
356 | if (sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS) | |
357 | sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS; | |
358 | if (sta->last_tx_rate.flags & IEEE80211_TX_RC_40_MHZ_WIDTH) | |
359 | sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; | |
360 | if (sta->last_tx_rate.flags & IEEE80211_TX_RC_SHORT_GI) | |
361 | sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; | |
362 | ||
363 | if (!(sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS)) { | |
364 | struct ieee80211_supported_band *sband; | |
365 | sband = sta->local->hw.wiphy->bands[ | |
366 | sta->local->hw.conf.channel->band]; | |
367 | sinfo->txrate.legacy = | |
368 | sband->bitrates[sta->last_tx_rate.idx].bitrate; | |
369 | } else | |
370 | sinfo->txrate.mcs = sta->last_tx_rate.idx; | |
371 | ||
372 | if (ieee80211_vif_is_mesh(&sdata->vif)) { | |
373 | #ifdef CONFIG_MAC80211_MESH | |
374 | sinfo->filled |= STATION_INFO_LLID | | |
375 | STATION_INFO_PLID | | |
376 | STATION_INFO_PLINK_STATE; | |
377 | ||
378 | sinfo->llid = le16_to_cpu(sta->llid); | |
379 | sinfo->plid = le16_to_cpu(sta->plid); | |
380 | sinfo->plink_state = sta->plink_state; | |
381 | #endif | |
382 | } | |
383 | } | |
384 | ||
385 | ||
386 | static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev, | |
387 | int idx, u8 *mac, struct station_info *sinfo) | |
388 | { | |
389 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | |
390 | struct sta_info *sta; | |
391 | int ret = -ENOENT; | |
392 | ||
393 | rcu_read_lock(); | |
394 | ||
395 | sta = sta_info_get_by_idx(local, idx, dev); | |
396 | if (sta) { | |
397 | ret = 0; | |
398 | memcpy(mac, sta->sta.addr, ETH_ALEN); | |
399 | sta_set_sinfo(sta, sinfo); | |
400 | } | |
401 | ||
402 | rcu_read_unlock(); | |
403 | ||
404 | return ret; | |
405 | } | |
406 | ||
407 | static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, | |
408 | u8 *mac, struct station_info *sinfo) | |
409 | { | |
410 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | |
411 | struct sta_info *sta; | |
412 | int ret = -ENOENT; | |
413 | ||
414 | rcu_read_lock(); | |
415 | ||
416 | /* XXX: verify sta->dev == dev */ | |
417 | ||
418 | sta = sta_info_get(local, mac); | |
419 | if (sta) { | |
420 | ret = 0; | |
421 | sta_set_sinfo(sta, sinfo); | |
422 | } | |
423 | ||
424 | rcu_read_unlock(); | |
425 | ||
426 | return ret; | |
427 | } | |
428 | ||
429 | /* | |
430 | * This handles both adding a beacon and setting new beacon info | |
431 | */ | |
432 | static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata, | |
433 | struct beacon_parameters *params) | |
434 | { | |
435 | struct beacon_data *new, *old; | |
436 | int new_head_len, new_tail_len; | |
437 | int size; | |
438 | int err = -EINVAL; | |
439 | ||
440 | old = sdata->u.ap.beacon; | |
441 | ||
442 | /* head must not be zero-length */ | |
443 | if (params->head && !params->head_len) | |
444 | return -EINVAL; | |
445 | ||
446 | /* | |
447 | * This is a kludge. beacon interval should really be part | |
448 | * of the beacon information. | |
449 | */ | |
450 | if (params->interval) { | |
451 | sdata->local->hw.conf.beacon_int = params->interval; | |
452 | err = ieee80211_hw_config(sdata->local, | |
453 | IEEE80211_CONF_CHANGE_BEACON_INTERVAL); | |
454 | if (err < 0) | |
455 | return err; | |
456 | /* | |
457 | * We updated some parameter so if below bails out | |
458 | * it's not an error. | |
459 | */ | |
460 | err = 0; | |
461 | } | |
462 | ||
463 | /* Need to have a beacon head if we don't have one yet */ | |
464 | if (!params->head && !old) | |
465 | return err; | |
466 | ||
467 | /* sorry, no way to start beaconing without dtim period */ | |
468 | if (!params->dtim_period && !old) | |
469 | return err; | |
470 | ||
471 | /* new or old head? */ | |
472 | if (params->head) | |
473 | new_head_len = params->head_len; | |
474 | else | |
475 | new_head_len = old->head_len; | |
476 | ||
477 | /* new or old tail? */ | |
478 | if (params->tail || !old) | |
479 | /* params->tail_len will be zero for !params->tail */ | |
480 | new_tail_len = params->tail_len; | |
481 | else | |
482 | new_tail_len = old->tail_len; | |
483 | ||
484 | size = sizeof(*new) + new_head_len + new_tail_len; | |
485 | ||
486 | new = kzalloc(size, GFP_KERNEL); | |
487 | if (!new) | |
488 | return -ENOMEM; | |
489 | ||
490 | /* start filling the new info now */ | |
491 | ||
492 | /* new or old dtim period? */ | |
493 | if (params->dtim_period) | |
494 | new->dtim_period = params->dtim_period; | |
495 | else | |
496 | new->dtim_period = old->dtim_period; | |
497 | ||
498 | /* | |
499 | * pointers go into the block we allocated, | |
500 | * memory is | beacon_data | head | tail | | |
501 | */ | |
502 | new->head = ((u8 *) new) + sizeof(*new); | |
503 | new->tail = new->head + new_head_len; | |
504 | new->head_len = new_head_len; | |
505 | new->tail_len = new_tail_len; | |
506 | ||
507 | /* copy in head */ | |
508 | if (params->head) | |
509 | memcpy(new->head, params->head, new_head_len); | |
510 | else | |
511 | memcpy(new->head, old->head, new_head_len); | |
512 | ||
513 | /* copy in optional tail */ | |
514 | if (params->tail) | |
515 | memcpy(new->tail, params->tail, new_tail_len); | |
516 | else | |
517 | if (old) | |
518 | memcpy(new->tail, old->tail, new_tail_len); | |
519 | ||
520 | rcu_assign_pointer(sdata->u.ap.beacon, new); | |
521 | ||
522 | synchronize_rcu(); | |
523 | ||
524 | kfree(old); | |
525 | ||
526 | return ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON); | |
527 | } | |
528 | ||
529 | static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev, | |
530 | struct beacon_parameters *params) | |
531 | { | |
532 | struct ieee80211_sub_if_data *sdata; | |
533 | struct beacon_data *old; | |
534 | ||
535 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
536 | ||
537 | if (sdata->vif.type != NL80211_IFTYPE_AP) | |
538 | return -EINVAL; | |
539 | ||
540 | old = sdata->u.ap.beacon; | |
541 | ||
542 | if (old) | |
543 | return -EALREADY; | |
544 | ||
545 | return ieee80211_config_beacon(sdata, params); | |
546 | } | |
547 | ||
548 | static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, | |
549 | struct beacon_parameters *params) | |
550 | { | |
551 | struct ieee80211_sub_if_data *sdata; | |
552 | struct beacon_data *old; | |
553 | ||
554 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
555 | ||
556 | if (sdata->vif.type != NL80211_IFTYPE_AP) | |
557 | return -EINVAL; | |
558 | ||
559 | old = sdata->u.ap.beacon; | |
560 | ||
561 | if (!old) | |
562 | return -ENOENT; | |
563 | ||
564 | return ieee80211_config_beacon(sdata, params); | |
565 | } | |
566 | ||
567 | static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev) | |
568 | { | |
569 | struct ieee80211_sub_if_data *sdata; | |
570 | struct beacon_data *old; | |
571 | ||
572 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
573 | ||
574 | if (sdata->vif.type != NL80211_IFTYPE_AP) | |
575 | return -EINVAL; | |
576 | ||
577 | old = sdata->u.ap.beacon; | |
578 | ||
579 | if (!old) | |
580 | return -ENOENT; | |
581 | ||
582 | rcu_assign_pointer(sdata->u.ap.beacon, NULL); | |
583 | synchronize_rcu(); | |
584 | kfree(old); | |
585 | ||
586 | return ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON); | |
587 | } | |
588 | ||
589 | /* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */ | |
590 | struct iapp_layer2_update { | |
591 | u8 da[ETH_ALEN]; /* broadcast */ | |
592 | u8 sa[ETH_ALEN]; /* STA addr */ | |
593 | __be16 len; /* 6 */ | |
594 | u8 dsap; /* 0 */ | |
595 | u8 ssap; /* 0 */ | |
596 | u8 control; | |
597 | u8 xid_info[3]; | |
598 | } __attribute__ ((packed)); | |
599 | ||
600 | static void ieee80211_send_layer2_update(struct sta_info *sta) | |
601 | { | |
602 | struct iapp_layer2_update *msg; | |
603 | struct sk_buff *skb; | |
604 | ||
605 | /* Send Level 2 Update Frame to update forwarding tables in layer 2 | |
606 | * bridge devices */ | |
607 | ||
608 | skb = dev_alloc_skb(sizeof(*msg)); | |
609 | if (!skb) | |
610 | return; | |
611 | msg = (struct iapp_layer2_update *)skb_put(skb, sizeof(*msg)); | |
612 | ||
613 | /* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID) | |
614 | * Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */ | |
615 | ||
616 | memset(msg->da, 0xff, ETH_ALEN); | |
617 | memcpy(msg->sa, sta->sta.addr, ETH_ALEN); | |
618 | msg->len = htons(6); | |
619 | msg->dsap = 0; | |
620 | msg->ssap = 0x01; /* NULL LSAP, CR Bit: Response */ | |
621 | msg->control = 0xaf; /* XID response lsb.1111F101. | |
622 | * F=0 (no poll command; unsolicited frame) */ | |
623 | msg->xid_info[0] = 0x81; /* XID format identifier */ | |
624 | msg->xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */ | |
625 | msg->xid_info[2] = 0; /* XID sender's receive window size (RW) */ | |
626 | ||
627 | skb->dev = sta->sdata->dev; | |
628 | skb->protocol = eth_type_trans(skb, sta->sdata->dev); | |
629 | memset(skb->cb, 0, sizeof(skb->cb)); | |
630 | netif_rx(skb); | |
631 | } | |
632 | ||
633 | static void sta_apply_parameters(struct ieee80211_local *local, | |
634 | struct sta_info *sta, | |
635 | struct station_parameters *params) | |
636 | { | |
637 | u32 rates; | |
638 | int i, j; | |
639 | struct ieee80211_supported_band *sband; | |
640 | struct ieee80211_sub_if_data *sdata = sta->sdata; | |
641 | ||
642 | sband = local->hw.wiphy->bands[local->oper_channel->band]; | |
643 | ||
644 | /* | |
645 | * FIXME: updating the flags is racy when this function is | |
646 | * called from ieee80211_change_station(), this will | |
647 | * be resolved in a future patch. | |
648 | */ | |
649 | ||
650 | if (params->station_flags & STATION_FLAG_CHANGED) { | |
651 | spin_lock_bh(&sta->lock); | |
652 | sta->flags &= ~WLAN_STA_AUTHORIZED; | |
653 | if (params->station_flags & STATION_FLAG_AUTHORIZED) | |
654 | sta->flags |= WLAN_STA_AUTHORIZED; | |
655 | ||
656 | sta->flags &= ~WLAN_STA_SHORT_PREAMBLE; | |
657 | if (params->station_flags & STATION_FLAG_SHORT_PREAMBLE) | |
658 | sta->flags |= WLAN_STA_SHORT_PREAMBLE; | |
659 | ||
660 | sta->flags &= ~WLAN_STA_WME; | |
661 | if (params->station_flags & STATION_FLAG_WME) | |
662 | sta->flags |= WLAN_STA_WME; | |
663 | ||
664 | sta->flags &= ~WLAN_STA_MFP; | |
665 | if (params->station_flags & STATION_FLAG_MFP) | |
666 | sta->flags |= WLAN_STA_MFP; | |
667 | spin_unlock_bh(&sta->lock); | |
668 | } | |
669 | ||
670 | /* | |
671 | * FIXME: updating the following information is racy when this | |
672 | * function is called from ieee80211_change_station(). | |
673 | * However, all this information should be static so | |
674 | * maybe we should just reject attemps to change it. | |
675 | */ | |
676 | ||
677 | if (params->aid) { | |
678 | sta->sta.aid = params->aid; | |
679 | if (sta->sta.aid > IEEE80211_MAX_AID) | |
680 | sta->sta.aid = 0; /* XXX: should this be an error? */ | |
681 | } | |
682 | ||
683 | if (params->listen_interval >= 0) | |
684 | sta->listen_interval = params->listen_interval; | |
685 | ||
686 | if (params->supported_rates) { | |
687 | rates = 0; | |
688 | ||
689 | for (i = 0; i < params->supported_rates_len; i++) { | |
690 | int rate = (params->supported_rates[i] & 0x7f) * 5; | |
691 | for (j = 0; j < sband->n_bitrates; j++) { | |
692 | if (sband->bitrates[j].bitrate == rate) | |
693 | rates |= BIT(j); | |
694 | } | |
695 | } | |
696 | sta->sta.supp_rates[local->oper_channel->band] = rates; | |
697 | } | |
698 | ||
699 | if (params->ht_capa) | |
700 | ieee80211_ht_cap_ie_to_sta_ht_cap(sband, | |
701 | params->ht_capa, | |
702 | &sta->sta.ht_cap); | |
703 | ||
704 | if (ieee80211_vif_is_mesh(&sdata->vif) && params->plink_action) { | |
705 | switch (params->plink_action) { | |
706 | case PLINK_ACTION_OPEN: | |
707 | mesh_plink_open(sta); | |
708 | break; | |
709 | case PLINK_ACTION_BLOCK: | |
710 | mesh_plink_block(sta); | |
711 | break; | |
712 | } | |
713 | } | |
714 | } | |
715 | ||
716 | static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, | |
717 | u8 *mac, struct station_parameters *params) | |
718 | { | |
719 | struct ieee80211_local *local = wiphy_priv(wiphy); | |
720 | struct sta_info *sta; | |
721 | struct ieee80211_sub_if_data *sdata; | |
722 | int err; | |
723 | int layer2_update; | |
724 | ||
725 | /* Prevent a race with changing the rate control algorithm */ | |
726 | if (!netif_running(dev)) | |
727 | return -ENETDOWN; | |
728 | ||
729 | if (params->vlan) { | |
730 | sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); | |
731 | ||
732 | if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && | |
733 | sdata->vif.type != NL80211_IFTYPE_AP) | |
734 | return -EINVAL; | |
735 | } else | |
736 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
737 | ||
738 | if (compare_ether_addr(mac, dev->dev_addr) == 0) | |
739 | return -EINVAL; | |
740 | ||
741 | if (is_multicast_ether_addr(mac)) | |
742 | return -EINVAL; | |
743 | ||
744 | sta = sta_info_alloc(sdata, mac, GFP_KERNEL); | |
745 | if (!sta) | |
746 | return -ENOMEM; | |
747 | ||
748 | sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC; | |
749 | ||
750 | sta_apply_parameters(local, sta, params); | |
751 | ||
752 | rate_control_rate_init(sta); | |
753 | ||
754 | layer2_update = sdata->vif.type == NL80211_IFTYPE_AP_VLAN || | |
755 | sdata->vif.type == NL80211_IFTYPE_AP; | |
756 | ||
757 | rcu_read_lock(); | |
758 | ||
759 | err = sta_info_insert(sta); | |
760 | if (err) { | |
761 | /* STA has been freed */ | |
762 | if (err == -EEXIST && layer2_update) { | |
763 | /* Need to update layer 2 devices on reassociation */ | |
764 | sta = sta_info_get(local, mac); | |
765 | if (sta) | |
766 | ieee80211_send_layer2_update(sta); | |
767 | } | |
768 | rcu_read_unlock(); | |
769 | return err; | |
770 | } | |
771 | ||
772 | if (layer2_update) | |
773 | ieee80211_send_layer2_update(sta); | |
774 | ||
775 | rcu_read_unlock(); | |
776 | ||
777 | return 0; | |
778 | } | |
779 | ||
780 | static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev, | |
781 | u8 *mac) | |
782 | { | |
783 | struct ieee80211_local *local = wiphy_priv(wiphy); | |
784 | struct ieee80211_sub_if_data *sdata; | |
785 | struct sta_info *sta; | |
786 | ||
787 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
788 | ||
789 | if (mac) { | |
790 | rcu_read_lock(); | |
791 | ||
792 | /* XXX: get sta belonging to dev */ | |
793 | sta = sta_info_get(local, mac); | |
794 | if (!sta) { | |
795 | rcu_read_unlock(); | |
796 | return -ENOENT; | |
797 | } | |
798 | ||
799 | sta_info_unlink(&sta); | |
800 | rcu_read_unlock(); | |
801 | ||
802 | sta_info_destroy(sta); | |
803 | } else | |
804 | sta_info_flush(local, sdata); | |
805 | ||
806 | return 0; | |
807 | } | |
808 | ||
809 | static int ieee80211_change_station(struct wiphy *wiphy, | |
810 | struct net_device *dev, | |
811 | u8 *mac, | |
812 | struct station_parameters *params) | |
813 | { | |
814 | struct ieee80211_local *local = wiphy_priv(wiphy); | |
815 | struct sta_info *sta; | |
816 | struct ieee80211_sub_if_data *vlansdata; | |
817 | ||
818 | rcu_read_lock(); | |
819 | ||
820 | /* XXX: get sta belonging to dev */ | |
821 | sta = sta_info_get(local, mac); | |
822 | if (!sta) { | |
823 | rcu_read_unlock(); | |
824 | return -ENOENT; | |
825 | } | |
826 | ||
827 | if (params->vlan && params->vlan != sta->sdata->dev) { | |
828 | vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); | |
829 | ||
830 | if (vlansdata->vif.type != NL80211_IFTYPE_AP_VLAN && | |
831 | vlansdata->vif.type != NL80211_IFTYPE_AP) { | |
832 | rcu_read_unlock(); | |
833 | return -EINVAL; | |
834 | } | |
835 | ||
836 | sta->sdata = vlansdata; | |
837 | ieee80211_send_layer2_update(sta); | |
838 | } | |
839 | ||
840 | sta_apply_parameters(local, sta, params); | |
841 | ||
842 | rcu_read_unlock(); | |
843 | ||
844 | return 0; | |
845 | } | |
846 | ||
847 | #ifdef CONFIG_MAC80211_MESH | |
848 | static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev, | |
849 | u8 *dst, u8 *next_hop) | |
850 | { | |
851 | struct ieee80211_local *local = wiphy_priv(wiphy); | |
852 | struct ieee80211_sub_if_data *sdata; | |
853 | struct mesh_path *mpath; | |
854 | struct sta_info *sta; | |
855 | int err; | |
856 | ||
857 | if (!netif_running(dev)) | |
858 | return -ENETDOWN; | |
859 | ||
860 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
861 | ||
862 | if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT) | |
863 | return -ENOTSUPP; | |
864 | ||
865 | rcu_read_lock(); | |
866 | sta = sta_info_get(local, next_hop); | |
867 | if (!sta) { | |
868 | rcu_read_unlock(); | |
869 | return -ENOENT; | |
870 | } | |
871 | ||
872 | err = mesh_path_add(dst, sdata); | |
873 | if (err) { | |
874 | rcu_read_unlock(); | |
875 | return err; | |
876 | } | |
877 | ||
878 | mpath = mesh_path_lookup(dst, sdata); | |
879 | if (!mpath) { | |
880 | rcu_read_unlock(); | |
881 | return -ENXIO; | |
882 | } | |
883 | mesh_path_fix_nexthop(mpath, sta); | |
884 | ||
885 | rcu_read_unlock(); | |
886 | return 0; | |
887 | } | |
888 | ||
889 | static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev, | |
890 | u8 *dst) | |
891 | { | |
892 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
893 | ||
894 | if (dst) | |
895 | return mesh_path_del(dst, sdata); | |
896 | ||
897 | mesh_path_flush(sdata); | |
898 | return 0; | |
899 | } | |
900 | ||
901 | static int ieee80211_change_mpath(struct wiphy *wiphy, | |
902 | struct net_device *dev, | |
903 | u8 *dst, u8 *next_hop) | |
904 | { | |
905 | struct ieee80211_local *local = wiphy_priv(wiphy); | |
906 | struct ieee80211_sub_if_data *sdata; | |
907 | struct mesh_path *mpath; | |
908 | struct sta_info *sta; | |
909 | ||
910 | if (!netif_running(dev)) | |
911 | return -ENETDOWN; | |
912 | ||
913 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
914 | ||
915 | if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT) | |
916 | return -ENOTSUPP; | |
917 | ||
918 | rcu_read_lock(); | |
919 | ||
920 | sta = sta_info_get(local, next_hop); | |
921 | if (!sta) { | |
922 | rcu_read_unlock(); | |
923 | return -ENOENT; | |
924 | } | |
925 | ||
926 | mpath = mesh_path_lookup(dst, sdata); | |
927 | if (!mpath) { | |
928 | rcu_read_unlock(); | |
929 | return -ENOENT; | |
930 | } | |
931 | ||
932 | mesh_path_fix_nexthop(mpath, sta); | |
933 | ||
934 | rcu_read_unlock(); | |
935 | return 0; | |
936 | } | |
937 | ||
938 | static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop, | |
939 | struct mpath_info *pinfo) | |
940 | { | |
941 | if (mpath->next_hop) | |
942 | memcpy(next_hop, mpath->next_hop->sta.addr, ETH_ALEN); | |
943 | else | |
944 | memset(next_hop, 0, ETH_ALEN); | |
945 | ||
946 | pinfo->filled = MPATH_INFO_FRAME_QLEN | | |
947 | MPATH_INFO_DSN | | |
948 | MPATH_INFO_METRIC | | |
949 | MPATH_INFO_EXPTIME | | |
950 | MPATH_INFO_DISCOVERY_TIMEOUT | | |
951 | MPATH_INFO_DISCOVERY_RETRIES | | |
952 | MPATH_INFO_FLAGS; | |
953 | ||
954 | pinfo->frame_qlen = mpath->frame_queue.qlen; | |
955 | pinfo->dsn = mpath->dsn; | |
956 | pinfo->metric = mpath->metric; | |
957 | if (time_before(jiffies, mpath->exp_time)) | |
958 | pinfo->exptime = jiffies_to_msecs(mpath->exp_time - jiffies); | |
959 | pinfo->discovery_timeout = | |
960 | jiffies_to_msecs(mpath->discovery_timeout); | |
961 | pinfo->discovery_retries = mpath->discovery_retries; | |
962 | pinfo->flags = 0; | |
963 | if (mpath->flags & MESH_PATH_ACTIVE) | |
964 | pinfo->flags |= NL80211_MPATH_FLAG_ACTIVE; | |
965 | if (mpath->flags & MESH_PATH_RESOLVING) | |
966 | pinfo->flags |= NL80211_MPATH_FLAG_RESOLVING; | |
967 | if (mpath->flags & MESH_PATH_DSN_VALID) | |
968 | pinfo->flags |= NL80211_MPATH_FLAG_DSN_VALID; | |
969 | if (mpath->flags & MESH_PATH_FIXED) | |
970 | pinfo->flags |= NL80211_MPATH_FLAG_FIXED; | |
971 | if (mpath->flags & MESH_PATH_RESOLVING) | |
972 | pinfo->flags |= NL80211_MPATH_FLAG_RESOLVING; | |
973 | ||
974 | pinfo->flags = mpath->flags; | |
975 | } | |
976 | ||
977 | static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev, | |
978 | u8 *dst, u8 *next_hop, struct mpath_info *pinfo) | |
979 | ||
980 | { | |
981 | struct ieee80211_sub_if_data *sdata; | |
982 | struct mesh_path *mpath; | |
983 | ||
984 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
985 | ||
986 | if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT) | |
987 | return -ENOTSUPP; | |
988 | ||
989 | rcu_read_lock(); | |
990 | mpath = mesh_path_lookup(dst, sdata); | |
991 | if (!mpath) { | |
992 | rcu_read_unlock(); | |
993 | return -ENOENT; | |
994 | } | |
995 | memcpy(dst, mpath->dst, ETH_ALEN); | |
996 | mpath_set_pinfo(mpath, next_hop, pinfo); | |
997 | rcu_read_unlock(); | |
998 | return 0; | |
999 | } | |
1000 | ||
1001 | static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev, | |
1002 | int idx, u8 *dst, u8 *next_hop, | |
1003 | struct mpath_info *pinfo) | |
1004 | { | |
1005 | struct ieee80211_sub_if_data *sdata; | |
1006 | struct mesh_path *mpath; | |
1007 | ||
1008 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
1009 | ||
1010 | if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT) | |
1011 | return -ENOTSUPP; | |
1012 | ||
1013 | rcu_read_lock(); | |
1014 | mpath = mesh_path_lookup_by_idx(idx, sdata); | |
1015 | if (!mpath) { | |
1016 | rcu_read_unlock(); | |
1017 | return -ENOENT; | |
1018 | } | |
1019 | memcpy(dst, mpath->dst, ETH_ALEN); | |
1020 | mpath_set_pinfo(mpath, next_hop, pinfo); | |
1021 | rcu_read_unlock(); | |
1022 | return 0; | |
1023 | } | |
1024 | ||
1025 | static int ieee80211_get_mesh_params(struct wiphy *wiphy, | |
1026 | struct net_device *dev, | |
1027 | struct mesh_config *conf) | |
1028 | { | |
1029 | struct ieee80211_sub_if_data *sdata; | |
1030 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
1031 | ||
1032 | if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT) | |
1033 | return -ENOTSUPP; | |
1034 | memcpy(conf, &(sdata->u.mesh.mshcfg), sizeof(struct mesh_config)); | |
1035 | return 0; | |
1036 | } | |
1037 | ||
1038 | static inline bool _chg_mesh_attr(enum nl80211_meshconf_params parm, u32 mask) | |
1039 | { | |
1040 | return (mask >> (parm-1)) & 0x1; | |
1041 | } | |
1042 | ||
1043 | static int ieee80211_set_mesh_params(struct wiphy *wiphy, | |
1044 | struct net_device *dev, | |
1045 | const struct mesh_config *nconf, u32 mask) | |
1046 | { | |
1047 | struct mesh_config *conf; | |
1048 | struct ieee80211_sub_if_data *sdata; | |
1049 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
1050 | ||
1051 | if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT) | |
1052 | return -ENOTSUPP; | |
1053 | ||
1054 | /* Set the config options which we are interested in setting */ | |
1055 | conf = &(sdata->u.mesh.mshcfg); | |
1056 | if (_chg_mesh_attr(NL80211_MESHCONF_RETRY_TIMEOUT, mask)) | |
1057 | conf->dot11MeshRetryTimeout = nconf->dot11MeshRetryTimeout; | |
1058 | if (_chg_mesh_attr(NL80211_MESHCONF_CONFIRM_TIMEOUT, mask)) | |
1059 | conf->dot11MeshConfirmTimeout = nconf->dot11MeshConfirmTimeout; | |
1060 | if (_chg_mesh_attr(NL80211_MESHCONF_HOLDING_TIMEOUT, mask)) | |
1061 | conf->dot11MeshHoldingTimeout = nconf->dot11MeshHoldingTimeout; | |
1062 | if (_chg_mesh_attr(NL80211_MESHCONF_MAX_PEER_LINKS, mask)) | |
1063 | conf->dot11MeshMaxPeerLinks = nconf->dot11MeshMaxPeerLinks; | |
1064 | if (_chg_mesh_attr(NL80211_MESHCONF_MAX_RETRIES, mask)) | |
1065 | conf->dot11MeshMaxRetries = nconf->dot11MeshMaxRetries; | |
1066 | if (_chg_mesh_attr(NL80211_MESHCONF_TTL, mask)) | |
1067 | conf->dot11MeshTTL = nconf->dot11MeshTTL; | |
1068 | if (_chg_mesh_attr(NL80211_MESHCONF_AUTO_OPEN_PLINKS, mask)) | |
1069 | conf->auto_open_plinks = nconf->auto_open_plinks; | |
1070 | if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, mask)) | |
1071 | conf->dot11MeshHWMPmaxPREQretries = | |
1072 | nconf->dot11MeshHWMPmaxPREQretries; | |
1073 | if (_chg_mesh_attr(NL80211_MESHCONF_PATH_REFRESH_TIME, mask)) | |
1074 | conf->path_refresh_time = nconf->path_refresh_time; | |
1075 | if (_chg_mesh_attr(NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, mask)) | |
1076 | conf->min_discovery_timeout = nconf->min_discovery_timeout; | |
1077 | if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, mask)) | |
1078 | conf->dot11MeshHWMPactivePathTimeout = | |
1079 | nconf->dot11MeshHWMPactivePathTimeout; | |
1080 | if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, mask)) | |
1081 | conf->dot11MeshHWMPpreqMinInterval = | |
1082 | nconf->dot11MeshHWMPpreqMinInterval; | |
1083 | if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, | |
1084 | mask)) | |
1085 | conf->dot11MeshHWMPnetDiameterTraversalTime = | |
1086 | nconf->dot11MeshHWMPnetDiameterTraversalTime; | |
1087 | return 0; | |
1088 | } | |
1089 | ||
1090 | #endif | |
1091 | ||
1092 | static int ieee80211_change_bss(struct wiphy *wiphy, | |
1093 | struct net_device *dev, | |
1094 | struct bss_parameters *params) | |
1095 | { | |
1096 | struct ieee80211_sub_if_data *sdata; | |
1097 | u32 changed = 0; | |
1098 | ||
1099 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
1100 | ||
1101 | if (sdata->vif.type != NL80211_IFTYPE_AP) | |
1102 | return -EINVAL; | |
1103 | ||
1104 | if (params->use_cts_prot >= 0) { | |
1105 | sdata->vif.bss_conf.use_cts_prot = params->use_cts_prot; | |
1106 | changed |= BSS_CHANGED_ERP_CTS_PROT; | |
1107 | } | |
1108 | if (params->use_short_preamble >= 0) { | |
1109 | sdata->vif.bss_conf.use_short_preamble = | |
1110 | params->use_short_preamble; | |
1111 | changed |= BSS_CHANGED_ERP_PREAMBLE; | |
1112 | } | |
1113 | if (params->use_short_slot_time >= 0) { | |
1114 | sdata->vif.bss_conf.use_short_slot = | |
1115 | params->use_short_slot_time; | |
1116 | changed |= BSS_CHANGED_ERP_SLOT; | |
1117 | } | |
1118 | ||
1119 | if (params->basic_rates) { | |
1120 | int i, j; | |
1121 | u32 rates = 0; | |
1122 | struct ieee80211_local *local = wiphy_priv(wiphy); | |
1123 | struct ieee80211_supported_band *sband = | |
1124 | wiphy->bands[local->oper_channel->band]; | |
1125 | ||
1126 | for (i = 0; i < params->basic_rates_len; i++) { | |
1127 | int rate = (params->basic_rates[i] & 0x7f) * 5; | |
1128 | for (j = 0; j < sband->n_bitrates; j++) { | |
1129 | if (sband->bitrates[j].bitrate == rate) | |
1130 | rates |= BIT(j); | |
1131 | } | |
1132 | } | |
1133 | sdata->vif.bss_conf.basic_rates = rates; | |
1134 | changed |= BSS_CHANGED_BASIC_RATES; | |
1135 | } | |
1136 | ||
1137 | ieee80211_bss_info_change_notify(sdata, changed); | |
1138 | ||
1139 | return 0; | |
1140 | } | |
1141 | ||
1142 | static int ieee80211_set_txq_params(struct wiphy *wiphy, | |
1143 | struct ieee80211_txq_params *params) | |
1144 | { | |
1145 | struct ieee80211_local *local = wiphy_priv(wiphy); | |
1146 | struct ieee80211_tx_queue_params p; | |
1147 | ||
1148 | if (!local->ops->conf_tx) | |
1149 | return -EOPNOTSUPP; | |
1150 | ||
1151 | memset(&p, 0, sizeof(p)); | |
1152 | p.aifs = params->aifs; | |
1153 | p.cw_max = params->cwmax; | |
1154 | p.cw_min = params->cwmin; | |
1155 | p.txop = params->txop; | |
1156 | if (local->ops->conf_tx(local_to_hw(local), params->queue, &p)) { | |
1157 | printk(KERN_DEBUG "%s: failed to set TX queue " | |
1158 | "parameters for queue %d\n", local->mdev->name, | |
1159 | params->queue); | |
1160 | return -EINVAL; | |
1161 | } | |
1162 | ||
1163 | return 0; | |
1164 | } | |
1165 | ||
1166 | static int ieee80211_set_channel(struct wiphy *wiphy, | |
1167 | struct ieee80211_channel *chan, | |
1168 | enum nl80211_channel_type channel_type) | |
1169 | { | |
1170 | struct ieee80211_local *local = wiphy_priv(wiphy); | |
1171 | ||
1172 | local->oper_channel = chan; | |
1173 | local->oper_channel_type = channel_type; | |
1174 | ||
1175 | return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); | |
1176 | } | |
1177 | ||
1178 | static int set_mgmt_extra_ie_sta(struct ieee80211_if_sta *ifsta, u8 subtype, | |
1179 | u8 *ies, size_t ies_len) | |
1180 | { | |
1181 | switch (subtype) { | |
1182 | case IEEE80211_STYPE_PROBE_REQ >> 4: | |
1183 | kfree(ifsta->ie_probereq); | |
1184 | ifsta->ie_probereq = ies; | |
1185 | ifsta->ie_probereq_len = ies_len; | |
1186 | return 0; | |
1187 | case IEEE80211_STYPE_PROBE_RESP >> 4: | |
1188 | kfree(ifsta->ie_proberesp); | |
1189 | ifsta->ie_proberesp = ies; | |
1190 | ifsta->ie_proberesp_len = ies_len; | |
1191 | return 0; | |
1192 | case IEEE80211_STYPE_AUTH >> 4: | |
1193 | kfree(ifsta->ie_auth); | |
1194 | ifsta->ie_auth = ies; | |
1195 | ifsta->ie_auth_len = ies_len; | |
1196 | return 0; | |
1197 | case IEEE80211_STYPE_ASSOC_REQ >> 4: | |
1198 | kfree(ifsta->ie_assocreq); | |
1199 | ifsta->ie_assocreq = ies; | |
1200 | ifsta->ie_assocreq_len = ies_len; | |
1201 | return 0; | |
1202 | case IEEE80211_STYPE_REASSOC_REQ >> 4: | |
1203 | kfree(ifsta->ie_reassocreq); | |
1204 | ifsta->ie_reassocreq = ies; | |
1205 | ifsta->ie_reassocreq_len = ies_len; | |
1206 | return 0; | |
1207 | case IEEE80211_STYPE_DEAUTH >> 4: | |
1208 | kfree(ifsta->ie_deauth); | |
1209 | ifsta->ie_deauth = ies; | |
1210 | ifsta->ie_deauth_len = ies_len; | |
1211 | return 0; | |
1212 | case IEEE80211_STYPE_DISASSOC >> 4: | |
1213 | kfree(ifsta->ie_disassoc); | |
1214 | ifsta->ie_disassoc = ies; | |
1215 | ifsta->ie_disassoc_len = ies_len; | |
1216 | return 0; | |
1217 | } | |
1218 | ||
1219 | return -EOPNOTSUPP; | |
1220 | } | |
1221 | ||
1222 | static int ieee80211_set_mgmt_extra_ie(struct wiphy *wiphy, | |
1223 | struct net_device *dev, | |
1224 | struct mgmt_extra_ie_params *params) | |
1225 | { | |
1226 | struct ieee80211_sub_if_data *sdata; | |
1227 | u8 *ies; | |
1228 | size_t ies_len; | |
1229 | int ret = -EOPNOTSUPP; | |
1230 | ||
1231 | if (params->ies) { | |
1232 | ies = kmemdup(params->ies, params->ies_len, GFP_KERNEL); | |
1233 | if (ies == NULL) | |
1234 | return -ENOMEM; | |
1235 | ies_len = params->ies_len; | |
1236 | } else { | |
1237 | ies = NULL; | |
1238 | ies_len = 0; | |
1239 | } | |
1240 | ||
1241 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | |
1242 | ||
1243 | switch (sdata->vif.type) { | |
1244 | case NL80211_IFTYPE_STATION: | |
1245 | case NL80211_IFTYPE_ADHOC: | |
1246 | ret = set_mgmt_extra_ie_sta(&sdata->u.sta, params->subtype, | |
1247 | ies, ies_len); | |
1248 | break; | |
1249 | default: | |
1250 | ret = -EOPNOTSUPP; | |
1251 | break; | |
1252 | } | |
1253 | ||
1254 | if (ret) | |
1255 | kfree(ies); | |
1256 | return ret; | |
1257 | } | |
1258 | ||
1259 | struct cfg80211_ops mac80211_config_ops = { | |
1260 | .add_virtual_intf = ieee80211_add_iface, | |
1261 | .del_virtual_intf = ieee80211_del_iface, | |
1262 | .change_virtual_intf = ieee80211_change_iface, | |
1263 | .add_key = ieee80211_add_key, | |
1264 | .del_key = ieee80211_del_key, | |
1265 | .get_key = ieee80211_get_key, | |
1266 | .set_default_key = ieee80211_config_default_key, | |
1267 | .set_default_mgmt_key = ieee80211_config_default_mgmt_key, | |
1268 | .add_beacon = ieee80211_add_beacon, | |
1269 | .set_beacon = ieee80211_set_beacon, | |
1270 | .del_beacon = ieee80211_del_beacon, | |
1271 | .add_station = ieee80211_add_station, | |
1272 | .del_station = ieee80211_del_station, | |
1273 | .change_station = ieee80211_change_station, | |
1274 | .get_station = ieee80211_get_station, | |
1275 | .dump_station = ieee80211_dump_station, | |
1276 | #ifdef CONFIG_MAC80211_MESH | |
1277 | .add_mpath = ieee80211_add_mpath, | |
1278 | .del_mpath = ieee80211_del_mpath, | |
1279 | .change_mpath = ieee80211_change_mpath, | |
1280 | .get_mpath = ieee80211_get_mpath, | |
1281 | .dump_mpath = ieee80211_dump_mpath, | |
1282 | .set_mesh_params = ieee80211_set_mesh_params, | |
1283 | .get_mesh_params = ieee80211_get_mesh_params, | |
1284 | #endif | |
1285 | .change_bss = ieee80211_change_bss, | |
1286 | .set_txq_params = ieee80211_set_txq_params, | |
1287 | .set_channel = ieee80211_set_channel, | |
1288 | .set_mgmt_extra_ie = ieee80211_set_mgmt_extra_ie, | |
1289 | }; |