blob: 89a183c2327ad0938fa6d30f346bceef5ac2152f [file] [log] [blame]
Jiri Bencf0706e822007-05-05 11:45:53 -07001/*
2 * mac80211 configuration hooks for cfg80211
3 *
Johannes Berg62da92f2007-12-19 02:03:31 +01004 * Copyright 2006, 2007 Johannes Berg <johannes@sipsolutions.net>
Jiri Bencf0706e822007-05-05 11:45:53 -07005 *
6 * This file is GPLv2 as found in COPYING.
7 */
8
Johannes Berge8cbb4c2007-12-19 02:03:30 +01009#include <linux/ieee80211.h>
Jiri Bencf0706e822007-05-05 11:45:53 -070010#include <linux/nl80211.h>
11#include <linux/rtnetlink.h>
Eric W. Biederman881d9662007-09-17 11:56:21 -070012#include <net/net_namespace.h>
Johannes Berg5dfdaf52007-12-19 02:03:33 +010013#include <linux/rcupdate.h>
Jiri Bencf0706e822007-05-05 11:45:53 -070014#include <net/cfg80211.h>
15#include "ieee80211_i.h"
Michael Wue0eb6852007-09-18 17:29:21 -040016#include "cfg.h"
Johannes Berg2c8dccc2008-04-08 15:14:40 -040017#include "rate.h"
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +010018#include "mesh.h"
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +010019
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -070020struct ieee80211_hw *wiphy_to_hw(struct wiphy *wiphy)
21{
22 struct ieee80211_local *local = wiphy_priv(wiphy);
23 return &local->hw;
24}
25EXPORT_SYMBOL(wiphy_to_hw);
26
Johannes Berg05c914f2008-09-11 00:01:58 +020027static bool nl80211_type_check(enum nl80211_iftype type)
Johannes Berg42613db2007-09-28 21:52:27 +020028{
29 switch (type) {
Johannes Berg42613db2007-09-28 21:52:27 +020030 case NL80211_IFTYPE_ADHOC:
Johannes Berg42613db2007-09-28 21:52:27 +020031 case NL80211_IFTYPE_STATION:
Johannes Berg42613db2007-09-28 21:52:27 +020032 case NL80211_IFTYPE_MONITOR:
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +010033#ifdef CONFIG_MAC80211_MESH
34 case NL80211_IFTYPE_MESH_POINT:
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +010035#endif
Johannes Bergb4540482008-04-14 15:37:03 +020036 case NL80211_IFTYPE_WDS:
Johannes Berg05c914f2008-09-11 00:01:58 +020037 return true;
Johannes Berg42613db2007-09-28 21:52:27 +020038 default:
Johannes Berg05c914f2008-09-11 00:01:58 +020039 return false;
Johannes Berg42613db2007-09-28 21:52:27 +020040 }
41}
42
Jiri Bencf0706e822007-05-05 11:45:53 -070043static int ieee80211_add_iface(struct wiphy *wiphy, char *name,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +010044 enum nl80211_iftype type, u32 *flags,
45 struct vif_params *params)
Jiri Bencf0706e822007-05-05 11:45:53 -070046{
47 struct ieee80211_local *local = wiphy_priv(wiphy);
Michael Wu8cc9a732008-01-31 19:48:23 +010048 struct net_device *dev;
49 struct ieee80211_sub_if_data *sdata;
50 int err;
Jiri Bencf0706e822007-05-05 11:45:53 -070051
Johannes Berg05c914f2008-09-11 00:01:58 +020052 if (!nl80211_type_check(type))
Jiri Bencf0706e822007-05-05 11:45:53 -070053 return -EINVAL;
Jiri Bencf0706e822007-05-05 11:45:53 -070054
Johannes Berg05c914f2008-09-11 00:01:58 +020055 err = ieee80211_if_add(local, name, &dev, type, params);
56 if (err || type != NL80211_IFTYPE_MONITOR || !flags)
Michael Wu8cc9a732008-01-31 19:48:23 +010057 return err;
58
59 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
60 sdata->u.mntr_flags = *flags;
61 return 0;
Jiri Bencf0706e822007-05-05 11:45:53 -070062}
63
64static int ieee80211_del_iface(struct wiphy *wiphy, int ifindex)
65{
Jiri Bencf0706e822007-05-05 11:45:53 -070066 struct net_device *dev;
Jasper Bryant-Greenef698d852008-08-03 12:04:37 +120067 struct ieee80211_sub_if_data *sdata;
Jiri Bencf0706e822007-05-05 11:45:53 -070068
Johannes Berg42613db2007-09-28 21:52:27 +020069 /* we're under RTNL */
70 dev = __dev_get_by_index(&init_net, ifindex);
Jiri Bencf0706e822007-05-05 11:45:53 -070071 if (!dev)
Johannes Berg75636522008-07-09 14:40:35 +020072 return -ENODEV;
Jiri Bencf0706e822007-05-05 11:45:53 -070073
Jasper Bryant-Greenef698d852008-08-03 12:04:37 +120074 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
75
76 ieee80211_if_remove(sdata);
Jiri Bencf0706e822007-05-05 11:45:53 -070077
Johannes Berg75636522008-07-09 14:40:35 +020078 return 0;
Jiri Bencf0706e822007-05-05 11:45:53 -070079}
80
Johannes Berg42613db2007-09-28 21:52:27 +020081static int ieee80211_change_iface(struct wiphy *wiphy, int ifindex,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +010082 enum nl80211_iftype type, u32 *flags,
83 struct vif_params *params)
Johannes Berg42613db2007-09-28 21:52:27 +020084{
Johannes Berg42613db2007-09-28 21:52:27 +020085 struct net_device *dev;
Johannes Berg42613db2007-09-28 21:52:27 +020086 struct ieee80211_sub_if_data *sdata;
Johannes Bergf3947e22008-07-09 14:40:36 +020087 int ret;
Johannes Berg42613db2007-09-28 21:52:27 +020088
Johannes Berg42613db2007-09-28 21:52:27 +020089 /* we're under RTNL */
90 dev = __dev_get_by_index(&init_net, ifindex);
91 if (!dev)
92 return -ENODEV;
93
Johannes Berg05c914f2008-09-11 00:01:58 +020094 if (!nl80211_type_check(type))
Johannes Berg42613db2007-09-28 21:52:27 +020095 return -EINVAL;
96
97 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
98
Johannes Berg05c914f2008-09-11 00:01:58 +020099 ret = ieee80211_if_change_type(sdata, type);
Johannes Bergf3947e22008-07-09 14:40:36 +0200100 if (ret)
101 return ret;
Johannes Berg42613db2007-09-28 21:52:27 +0200102
Johannes Bergf8b25cd2008-09-16 20:22:21 +0200103 if (netif_running(sdata->dev))
104 return -EBUSY;
105
Johannes Berg902acc72008-02-23 15:17:19 +0100106 if (ieee80211_vif_is_mesh(&sdata->vif) && params->mesh_id_len)
Johannes Berg472dbc42008-09-11 00:01:49 +0200107 ieee80211_sdata_set_mesh_id(sdata,
108 params->mesh_id_len,
109 params->mesh_id);
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100110
Johannes Berg05c914f2008-09-11 00:01:58 +0200111 if (sdata->vif.type != NL80211_IFTYPE_MONITOR || !flags)
Michael Wu8cc9a732008-01-31 19:48:23 +0100112 return 0;
113
114 sdata->u.mntr_flags = *flags;
Johannes Berg42613db2007-09-28 21:52:27 +0200115 return 0;
116}
117
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100118static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
119 u8 key_idx, u8 *mac_addr,
120 struct key_params *params)
121{
122 struct ieee80211_sub_if_data *sdata;
123 struct sta_info *sta = NULL;
124 enum ieee80211_key_alg alg;
Johannes Bergdb4d1162008-02-25 16:27:45 +0100125 struct ieee80211_key *key;
Johannes Berg3b967662008-04-08 17:56:52 +0200126 int err;
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100127
128 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
129
130 switch (params->cipher) {
131 case WLAN_CIPHER_SUITE_WEP40:
132 case WLAN_CIPHER_SUITE_WEP104:
133 alg = ALG_WEP;
134 break;
135 case WLAN_CIPHER_SUITE_TKIP:
136 alg = ALG_TKIP;
137 break;
138 case WLAN_CIPHER_SUITE_CCMP:
139 alg = ALG_CCMP;
140 break;
141 default:
142 return -EINVAL;
143 }
144
Johannes Bergdb4d1162008-02-25 16:27:45 +0100145 key = ieee80211_key_alloc(alg, key_idx, params->key_len, params->key);
146 if (!key)
147 return -ENOMEM;
148
Johannes Berg3b967662008-04-08 17:56:52 +0200149 rcu_read_lock();
150
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100151 if (mac_addr) {
152 sta = sta_info_get(sdata->local, mac_addr);
Johannes Bergdb4d1162008-02-25 16:27:45 +0100153 if (!sta) {
154 ieee80211_key_free(key);
Johannes Berg3b967662008-04-08 17:56:52 +0200155 err = -ENOENT;
156 goto out_unlock;
Johannes Bergdb4d1162008-02-25 16:27:45 +0100157 }
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100158 }
159
Johannes Bergdb4d1162008-02-25 16:27:45 +0100160 ieee80211_key_link(key, sdata, sta);
161
Johannes Berg3b967662008-04-08 17:56:52 +0200162 err = 0;
163 out_unlock:
164 rcu_read_unlock();
165
166 return err;
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100167}
168
169static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
170 u8 key_idx, u8 *mac_addr)
171{
172 struct ieee80211_sub_if_data *sdata;
173 struct sta_info *sta;
174 int ret;
175
176 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
177
Johannes Berg3b967662008-04-08 17:56:52 +0200178 rcu_read_lock();
179
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100180 if (mac_addr) {
Johannes Berg3b967662008-04-08 17:56:52 +0200181 ret = -ENOENT;
182
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100183 sta = sta_info_get(sdata->local, mac_addr);
184 if (!sta)
Johannes Berg3b967662008-04-08 17:56:52 +0200185 goto out_unlock;
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100186
Johannes Bergdb4d1162008-02-25 16:27:45 +0100187 if (sta->key) {
Johannes Bergd0709a62008-02-25 16:27:46 +0100188 ieee80211_key_free(sta->key);
Johannes Bergdb4d1162008-02-25 16:27:45 +0100189 WARN_ON(sta->key);
Johannes Berg3b967662008-04-08 17:56:52 +0200190 ret = 0;
191 }
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100192
Johannes Berg3b967662008-04-08 17:56:52 +0200193 goto out_unlock;
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100194 }
195
Johannes Berg3b967662008-04-08 17:56:52 +0200196 if (!sdata->keys[key_idx]) {
197 ret = -ENOENT;
198 goto out_unlock;
199 }
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100200
Johannes Bergd0709a62008-02-25 16:27:46 +0100201 ieee80211_key_free(sdata->keys[key_idx]);
Johannes Bergdb4d1162008-02-25 16:27:45 +0100202 WARN_ON(sdata->keys[key_idx]);
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100203
Johannes Berg3b967662008-04-08 17:56:52 +0200204 ret = 0;
205 out_unlock:
206 rcu_read_unlock();
207
208 return ret;
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100209}
210
Johannes Berg62da92f2007-12-19 02:03:31 +0100211static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
212 u8 key_idx, u8 *mac_addr, void *cookie,
213 void (*callback)(void *cookie,
214 struct key_params *params))
215{
Johannes Berg14db74b2008-07-29 13:22:52 +0200216 struct ieee80211_sub_if_data *sdata;
Johannes Berg62da92f2007-12-19 02:03:31 +0100217 struct sta_info *sta = NULL;
218 u8 seq[6] = {0};
219 struct key_params params;
220 struct ieee80211_key *key;
221 u32 iv32;
222 u16 iv16;
223 int err = -ENOENT;
224
Johannes Berg14db74b2008-07-29 13:22:52 +0200225 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
226
Johannes Berg3b967662008-04-08 17:56:52 +0200227 rcu_read_lock();
228
Johannes Berg62da92f2007-12-19 02:03:31 +0100229 if (mac_addr) {
230 sta = sta_info_get(sdata->local, mac_addr);
231 if (!sta)
232 goto out;
233
234 key = sta->key;
235 } else
236 key = sdata->keys[key_idx];
237
238 if (!key)
239 goto out;
240
241 memset(&params, 0, sizeof(params));
242
243 switch (key->conf.alg) {
244 case ALG_TKIP:
245 params.cipher = WLAN_CIPHER_SUITE_TKIP;
246
Harvey Harrisonb0f76b32008-05-14 16:26:19 -0700247 iv32 = key->u.tkip.tx.iv32;
248 iv16 = key->u.tkip.tx.iv16;
Johannes Berg62da92f2007-12-19 02:03:31 +0100249
250 if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE &&
251 sdata->local->ops->get_tkip_seq)
252 sdata->local->ops->get_tkip_seq(
253 local_to_hw(sdata->local),
254 key->conf.hw_key_idx,
255 &iv32, &iv16);
256
257 seq[0] = iv16 & 0xff;
258 seq[1] = (iv16 >> 8) & 0xff;
259 seq[2] = iv32 & 0xff;
260 seq[3] = (iv32 >> 8) & 0xff;
261 seq[4] = (iv32 >> 16) & 0xff;
262 seq[5] = (iv32 >> 24) & 0xff;
263 params.seq = seq;
264 params.seq_len = 6;
265 break;
266 case ALG_CCMP:
267 params.cipher = WLAN_CIPHER_SUITE_CCMP;
268 seq[0] = key->u.ccmp.tx_pn[5];
269 seq[1] = key->u.ccmp.tx_pn[4];
270 seq[2] = key->u.ccmp.tx_pn[3];
271 seq[3] = key->u.ccmp.tx_pn[2];
272 seq[4] = key->u.ccmp.tx_pn[1];
273 seq[5] = key->u.ccmp.tx_pn[0];
274 params.seq = seq;
275 params.seq_len = 6;
276 break;
277 case ALG_WEP:
278 if (key->conf.keylen == 5)
279 params.cipher = WLAN_CIPHER_SUITE_WEP40;
280 else
281 params.cipher = WLAN_CIPHER_SUITE_WEP104;
282 break;
283 }
284
285 params.key = key->conf.key;
286 params.key_len = key->conf.keylen;
287
288 callback(cookie, &params);
289 err = 0;
290
291 out:
Johannes Berg3b967662008-04-08 17:56:52 +0200292 rcu_read_unlock();
Johannes Berg62da92f2007-12-19 02:03:31 +0100293 return err;
294}
295
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100296static int ieee80211_config_default_key(struct wiphy *wiphy,
297 struct net_device *dev,
298 u8 key_idx)
299{
300 struct ieee80211_sub_if_data *sdata;
301
Johannes Berg3b967662008-04-08 17:56:52 +0200302 rcu_read_lock();
303
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100304 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
305 ieee80211_set_default_key(sdata, key_idx);
306
Johannes Berg3b967662008-04-08 17:56:52 +0200307 rcu_read_unlock();
308
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100309 return 0;
310}
311
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100312static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
313{
Johannes Bergd0709a62008-02-25 16:27:46 +0100314 struct ieee80211_sub_if_data *sdata = sta->sdata;
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100315
316 sinfo->filled = STATION_INFO_INACTIVE_TIME |
317 STATION_INFO_RX_BYTES |
318 STATION_INFO_TX_BYTES;
319
320 sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
321 sinfo->rx_bytes = sta->rx_bytes;
322 sinfo->tx_bytes = sta->tx_bytes;
323
Johannes Berg902acc72008-02-23 15:17:19 +0100324 if (ieee80211_vif_is_mesh(&sdata->vif)) {
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100325#ifdef CONFIG_MAC80211_MESH
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100326 sinfo->filled |= STATION_INFO_LLID |
327 STATION_INFO_PLID |
328 STATION_INFO_PLINK_STATE;
329
330 sinfo->llid = le16_to_cpu(sta->llid);
331 sinfo->plid = le16_to_cpu(sta->plid);
332 sinfo->plink_state = sta->plink_state;
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100333#endif
Johannes Berg902acc72008-02-23 15:17:19 +0100334 }
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100335}
336
337
338static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
339 int idx, u8 *mac, struct station_info *sinfo)
340{
341 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
342 struct sta_info *sta;
Johannes Bergd0709a62008-02-25 16:27:46 +0100343 int ret = -ENOENT;
344
345 rcu_read_lock();
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100346
347 sta = sta_info_get_by_idx(local, idx, dev);
Johannes Bergd0709a62008-02-25 16:27:46 +0100348 if (sta) {
349 ret = 0;
Johannes Berg17741cd2008-09-11 00:02:02 +0200350 memcpy(mac, sta->sta.addr, ETH_ALEN);
Johannes Bergd0709a62008-02-25 16:27:46 +0100351 sta_set_sinfo(sta, sinfo);
352 }
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100353
Johannes Bergd0709a62008-02-25 16:27:46 +0100354 rcu_read_unlock();
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100355
Johannes Bergd0709a62008-02-25 16:27:46 +0100356 return ret;
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100357}
358
Johannes Berg7bbdd2d2007-12-19 02:03:37 +0100359static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100360 u8 *mac, struct station_info *sinfo)
Johannes Berg7bbdd2d2007-12-19 02:03:37 +0100361{
362 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
363 struct sta_info *sta;
Johannes Bergd0709a62008-02-25 16:27:46 +0100364 int ret = -ENOENT;
Johannes Berg7bbdd2d2007-12-19 02:03:37 +0100365
Johannes Bergd0709a62008-02-25 16:27:46 +0100366 rcu_read_lock();
Johannes Berg7bbdd2d2007-12-19 02:03:37 +0100367
368 /* XXX: verify sta->dev == dev */
Johannes Berg7bbdd2d2007-12-19 02:03:37 +0100369
Johannes Bergd0709a62008-02-25 16:27:46 +0100370 sta = sta_info_get(local, mac);
371 if (sta) {
372 ret = 0;
373 sta_set_sinfo(sta, sinfo);
374 }
375
376 rcu_read_unlock();
377
378 return ret;
Johannes Berg7bbdd2d2007-12-19 02:03:37 +0100379}
380
Johannes Berg5dfdaf52007-12-19 02:03:33 +0100381/*
382 * This handles both adding a beacon and setting new beacon info
383 */
384static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata,
385 struct beacon_parameters *params)
386{
387 struct beacon_data *new, *old;
388 int new_head_len, new_tail_len;
389 int size;
390 int err = -EINVAL;
391
392 old = sdata->u.ap.beacon;
393
394 /* head must not be zero-length */
395 if (params->head && !params->head_len)
396 return -EINVAL;
397
398 /*
399 * This is a kludge. beacon interval should really be part
400 * of the beacon information.
401 */
402 if (params->interval) {
403 sdata->local->hw.conf.beacon_int = params->interval;
404 if (ieee80211_hw_config(sdata->local))
405 return -EINVAL;
406 /*
407 * We updated some parameter so if below bails out
408 * it's not an error.
409 */
410 err = 0;
411 }
412
413 /* Need to have a beacon head if we don't have one yet */
414 if (!params->head && !old)
415 return err;
416
417 /* sorry, no way to start beaconing without dtim period */
418 if (!params->dtim_period && !old)
419 return err;
420
421 /* new or old head? */
422 if (params->head)
423 new_head_len = params->head_len;
424 else
425 new_head_len = old->head_len;
426
427 /* new or old tail? */
428 if (params->tail || !old)
429 /* params->tail_len will be zero for !params->tail */
430 new_tail_len = params->tail_len;
431 else
432 new_tail_len = old->tail_len;
433
434 size = sizeof(*new) + new_head_len + new_tail_len;
435
436 new = kzalloc(size, GFP_KERNEL);
437 if (!new)
438 return -ENOMEM;
439
440 /* start filling the new info now */
441
442 /* new or old dtim period? */
443 if (params->dtim_period)
444 new->dtim_period = params->dtim_period;
445 else
446 new->dtim_period = old->dtim_period;
447
448 /*
449 * pointers go into the block we allocated,
450 * memory is | beacon_data | head | tail |
451 */
452 new->head = ((u8 *) new) + sizeof(*new);
453 new->tail = new->head + new_head_len;
454 new->head_len = new_head_len;
455 new->tail_len = new_tail_len;
456
457 /* copy in head */
458 if (params->head)
459 memcpy(new->head, params->head, new_head_len);
460 else
461 memcpy(new->head, old->head, new_head_len);
462
463 /* copy in optional tail */
464 if (params->tail)
465 memcpy(new->tail, params->tail, new_tail_len);
466 else
467 if (old)
468 memcpy(new->tail, old->tail, new_tail_len);
469
470 rcu_assign_pointer(sdata->u.ap.beacon, new);
471
472 synchronize_rcu();
473
474 kfree(old);
475
Johannes Berg9d139c82008-07-09 14:40:37 +0200476 return ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
Johannes Berg5dfdaf52007-12-19 02:03:33 +0100477}
478
479static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev,
480 struct beacon_parameters *params)
481{
Johannes Berg14db74b2008-07-29 13:22:52 +0200482 struct ieee80211_sub_if_data *sdata;
Johannes Berg5dfdaf52007-12-19 02:03:33 +0100483 struct beacon_data *old;
484
Johannes Berg14db74b2008-07-29 13:22:52 +0200485 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
486
Johannes Berg05c914f2008-09-11 00:01:58 +0200487 if (sdata->vif.type != NL80211_IFTYPE_AP)
Johannes Berg5dfdaf52007-12-19 02:03:33 +0100488 return -EINVAL;
489
490 old = sdata->u.ap.beacon;
491
492 if (old)
493 return -EALREADY;
494
495 return ieee80211_config_beacon(sdata, params);
496}
497
498static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev,
499 struct beacon_parameters *params)
500{
Johannes Berg14db74b2008-07-29 13:22:52 +0200501 struct ieee80211_sub_if_data *sdata;
Johannes Berg5dfdaf52007-12-19 02:03:33 +0100502 struct beacon_data *old;
503
Johannes Berg14db74b2008-07-29 13:22:52 +0200504 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
505
Johannes Berg05c914f2008-09-11 00:01:58 +0200506 if (sdata->vif.type != NL80211_IFTYPE_AP)
Johannes Berg5dfdaf52007-12-19 02:03:33 +0100507 return -EINVAL;
508
509 old = sdata->u.ap.beacon;
510
511 if (!old)
512 return -ENOENT;
513
514 return ieee80211_config_beacon(sdata, params);
515}
516
517static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev)
518{
Johannes Berg14db74b2008-07-29 13:22:52 +0200519 struct ieee80211_sub_if_data *sdata;
Johannes Berg5dfdaf52007-12-19 02:03:33 +0100520 struct beacon_data *old;
521
Johannes Berg14db74b2008-07-29 13:22:52 +0200522 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
523
Johannes Berg05c914f2008-09-11 00:01:58 +0200524 if (sdata->vif.type != NL80211_IFTYPE_AP)
Johannes Berg5dfdaf52007-12-19 02:03:33 +0100525 return -EINVAL;
526
527 old = sdata->u.ap.beacon;
528
529 if (!old)
530 return -ENOENT;
531
532 rcu_assign_pointer(sdata->u.ap.beacon, NULL);
533 synchronize_rcu();
534 kfree(old);
535
Johannes Berg9d139c82008-07-09 14:40:37 +0200536 return ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
Johannes Berg5dfdaf52007-12-19 02:03:33 +0100537}
538
Johannes Berg4fd69312007-12-19 02:03:35 +0100539/* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */
540struct iapp_layer2_update {
541 u8 da[ETH_ALEN]; /* broadcast */
542 u8 sa[ETH_ALEN]; /* STA addr */
543 __be16 len; /* 6 */
544 u8 dsap; /* 0 */
545 u8 ssap; /* 0 */
546 u8 control;
547 u8 xid_info[3];
548} __attribute__ ((packed));
549
550static void ieee80211_send_layer2_update(struct sta_info *sta)
551{
552 struct iapp_layer2_update *msg;
553 struct sk_buff *skb;
554
555 /* Send Level 2 Update Frame to update forwarding tables in layer 2
556 * bridge devices */
557
558 skb = dev_alloc_skb(sizeof(*msg));
559 if (!skb)
560 return;
561 msg = (struct iapp_layer2_update *)skb_put(skb, sizeof(*msg));
562
563 /* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID)
564 * Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */
565
566 memset(msg->da, 0xff, ETH_ALEN);
Johannes Berg17741cd2008-09-11 00:02:02 +0200567 memcpy(msg->sa, sta->sta.addr, ETH_ALEN);
Johannes Berg4fd69312007-12-19 02:03:35 +0100568 msg->len = htons(6);
569 msg->dsap = 0;
570 msg->ssap = 0x01; /* NULL LSAP, CR Bit: Response */
571 msg->control = 0xaf; /* XID response lsb.1111F101.
572 * F=0 (no poll command; unsolicited frame) */
573 msg->xid_info[0] = 0x81; /* XID format identifier */
574 msg->xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */
575 msg->xid_info[2] = 0; /* XID sender's receive window size (RW) */
576
Johannes Bergd0709a62008-02-25 16:27:46 +0100577 skb->dev = sta->sdata->dev;
578 skb->protocol = eth_type_trans(skb, sta->sdata->dev);
Johannes Berg4fd69312007-12-19 02:03:35 +0100579 memset(skb->cb, 0, sizeof(skb->cb));
580 netif_rx(skb);
581}
582
583static void sta_apply_parameters(struct ieee80211_local *local,
584 struct sta_info *sta,
585 struct station_parameters *params)
586{
587 u32 rates;
588 int i, j;
Johannes Berg8318d782008-01-24 19:38:38 +0100589 struct ieee80211_supported_band *sband;
Johannes Bergd0709a62008-02-25 16:27:46 +0100590 struct ieee80211_sub_if_data *sdata = sta->sdata;
Johannes Berg4fd69312007-12-19 02:03:35 +0100591
Johannes Berg73651ee2008-02-25 16:27:47 +0100592 /*
593 * FIXME: updating the flags is racy when this function is
594 * called from ieee80211_change_station(), this will
595 * be resolved in a future patch.
596 */
597
Johannes Berg4fd69312007-12-19 02:03:35 +0100598 if (params->station_flags & STATION_FLAG_CHANGED) {
Johannes Berg07346f812008-05-03 01:02:02 +0200599 spin_lock_bh(&sta->lock);
Johannes Berg4fd69312007-12-19 02:03:35 +0100600 sta->flags &= ~WLAN_STA_AUTHORIZED;
601 if (params->station_flags & STATION_FLAG_AUTHORIZED)
602 sta->flags |= WLAN_STA_AUTHORIZED;
603
604 sta->flags &= ~WLAN_STA_SHORT_PREAMBLE;
605 if (params->station_flags & STATION_FLAG_SHORT_PREAMBLE)
606 sta->flags |= WLAN_STA_SHORT_PREAMBLE;
607
608 sta->flags &= ~WLAN_STA_WME;
609 if (params->station_flags & STATION_FLAG_WME)
610 sta->flags |= WLAN_STA_WME;
Johannes Berg07346f812008-05-03 01:02:02 +0200611 spin_unlock_bh(&sta->lock);
Johannes Berg4fd69312007-12-19 02:03:35 +0100612 }
613
Johannes Berg73651ee2008-02-25 16:27:47 +0100614 /*
615 * FIXME: updating the following information is racy when this
616 * function is called from ieee80211_change_station().
617 * However, all this information should be static so
618 * maybe we should just reject attemps to change it.
619 */
620
Johannes Berg4fd69312007-12-19 02:03:35 +0100621 if (params->aid) {
Johannes Berg17741cd2008-09-11 00:02:02 +0200622 sta->sta.aid = params->aid;
623 if (sta->sta.aid > IEEE80211_MAX_AID)
624 sta->sta.aid = 0; /* XXX: should this be an error? */
Johannes Berg4fd69312007-12-19 02:03:35 +0100625 }
626
627 if (params->listen_interval >= 0)
628 sta->listen_interval = params->listen_interval;
629
630 if (params->supported_rates) {
631 rates = 0;
Johannes Berg8318d782008-01-24 19:38:38 +0100632 sband = local->hw.wiphy->bands[local->oper_channel->band];
633
Johannes Berg4fd69312007-12-19 02:03:35 +0100634 for (i = 0; i < params->supported_rates_len; i++) {
635 int rate = (params->supported_rates[i] & 0x7f) * 5;
Johannes Berg8318d782008-01-24 19:38:38 +0100636 for (j = 0; j < sband->n_bitrates; j++) {
637 if (sband->bitrates[j].bitrate == rate)
Johannes Berg4fd69312007-12-19 02:03:35 +0100638 rates |= BIT(j);
639 }
640 }
Johannes Berg323ce792008-09-11 02:45:11 +0200641 sta->sta.supp_rates[local->oper_channel->band] = rates;
Johannes Berg4fd69312007-12-19 02:03:35 +0100642 }
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100643
Jouni Malinen36aedc92008-08-25 11:58:58 +0300644 if (params->ht_capa) {
645 ieee80211_ht_cap_ie_to_ht_info(params->ht_capa,
Johannes Berg687c7c02008-09-11 03:14:11 +0200646 &sta->sta.ht_info);
Jouni Malinen36aedc92008-08-25 11:58:58 +0300647 }
648
Johannes Berg902acc72008-02-23 15:17:19 +0100649 if (ieee80211_vif_is_mesh(&sdata->vif) && params->plink_action) {
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100650 switch (params->plink_action) {
651 case PLINK_ACTION_OPEN:
652 mesh_plink_open(sta);
653 break;
654 case PLINK_ACTION_BLOCK:
655 mesh_plink_block(sta);
656 break;
657 }
Johannes Berg902acc72008-02-23 15:17:19 +0100658 }
Johannes Berg4fd69312007-12-19 02:03:35 +0100659}
660
661static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
662 u8 *mac, struct station_parameters *params)
663{
Johannes Berg14db74b2008-07-29 13:22:52 +0200664 struct ieee80211_local *local = wiphy_priv(wiphy);
Johannes Berg4fd69312007-12-19 02:03:35 +0100665 struct sta_info *sta;
666 struct ieee80211_sub_if_data *sdata;
Johannes Berg73651ee2008-02-25 16:27:47 +0100667 int err;
Johannes Berg4fd69312007-12-19 02:03:35 +0100668
669 /* Prevent a race with changing the rate control algorithm */
670 if (!netif_running(dev))
671 return -ENETDOWN;
672
Johannes Berg4fd69312007-12-19 02:03:35 +0100673 if (params->vlan) {
674 sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
675
Johannes Berg05c914f2008-09-11 00:01:58 +0200676 if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
677 sdata->vif.type != NL80211_IFTYPE_AP)
Johannes Berg4fd69312007-12-19 02:03:35 +0100678 return -EINVAL;
679 } else
680 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
681
Johannes Berg03e44972008-02-27 09:56:40 +0100682 if (compare_ether_addr(mac, dev->dev_addr) == 0)
683 return -EINVAL;
684
685 if (is_multicast_ether_addr(mac))
686 return -EINVAL;
687
688 sta = sta_info_alloc(sdata, mac, GFP_KERNEL);
Johannes Berg73651ee2008-02-25 16:27:47 +0100689 if (!sta)
690 return -ENOMEM;
Johannes Berg4fd69312007-12-19 02:03:35 +0100691
692 sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC;
693
694 sta_apply_parameters(local, sta, params);
695
696 rate_control_rate_init(sta, local);
697
Johannes Berg73651ee2008-02-25 16:27:47 +0100698 rcu_read_lock();
699
700 err = sta_info_insert(sta);
701 if (err) {
Johannes Berg93e5deb2008-04-01 15:21:00 +0200702 /* STA has been freed */
Johannes Berg73651ee2008-02-25 16:27:47 +0100703 rcu_read_unlock();
704 return err;
705 }
706
Johannes Berg05c914f2008-09-11 00:01:58 +0200707 if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
708 sdata->vif.type == NL80211_IFTYPE_AP)
Johannes Berg73651ee2008-02-25 16:27:47 +0100709 ieee80211_send_layer2_update(sta);
710
711 rcu_read_unlock();
712
Johannes Berg4fd69312007-12-19 02:03:35 +0100713 return 0;
714}
715
716static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
717 u8 *mac)
718{
Johannes Berg14db74b2008-07-29 13:22:52 +0200719 struct ieee80211_local *local = wiphy_priv(wiphy);
720 struct ieee80211_sub_if_data *sdata;
Johannes Berg4fd69312007-12-19 02:03:35 +0100721 struct sta_info *sta;
722
Johannes Berg14db74b2008-07-29 13:22:52 +0200723 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
724
Johannes Berg4fd69312007-12-19 02:03:35 +0100725 if (mac) {
Johannes Berg98dd6a52008-04-10 15:36:09 +0200726 rcu_read_lock();
727
Johannes Berg4fd69312007-12-19 02:03:35 +0100728 /* XXX: get sta belonging to dev */
729 sta = sta_info_get(local, mac);
Johannes Berg98dd6a52008-04-10 15:36:09 +0200730 if (!sta) {
731 rcu_read_unlock();
Johannes Berg4fd69312007-12-19 02:03:35 +0100732 return -ENOENT;
Johannes Berg98dd6a52008-04-10 15:36:09 +0200733 }
Johannes Berg4fd69312007-12-19 02:03:35 +0100734
Johannes Bergd0709a62008-02-25 16:27:46 +0100735 sta_info_unlink(&sta);
Johannes Berg98dd6a52008-04-10 15:36:09 +0200736 rcu_read_unlock();
737
Johannes Berg4f6fab42008-03-31 19:23:02 +0200738 sta_info_destroy(sta);
Johannes Berg4fd69312007-12-19 02:03:35 +0100739 } else
Johannes Bergd0709a62008-02-25 16:27:46 +0100740 sta_info_flush(local, sdata);
Johannes Berg4fd69312007-12-19 02:03:35 +0100741
742 return 0;
743}
744
745static int ieee80211_change_station(struct wiphy *wiphy,
746 struct net_device *dev,
747 u8 *mac,
748 struct station_parameters *params)
749{
Johannes Berg14db74b2008-07-29 13:22:52 +0200750 struct ieee80211_local *local = wiphy_priv(wiphy);
Johannes Berg4fd69312007-12-19 02:03:35 +0100751 struct sta_info *sta;
752 struct ieee80211_sub_if_data *vlansdata;
753
Johannes Berg98dd6a52008-04-10 15:36:09 +0200754 rcu_read_lock();
755
Johannes Berg4fd69312007-12-19 02:03:35 +0100756 /* XXX: get sta belonging to dev */
757 sta = sta_info_get(local, mac);
Johannes Berg98dd6a52008-04-10 15:36:09 +0200758 if (!sta) {
759 rcu_read_unlock();
Johannes Berg4fd69312007-12-19 02:03:35 +0100760 return -ENOENT;
Johannes Berg98dd6a52008-04-10 15:36:09 +0200761 }
Johannes Berg4fd69312007-12-19 02:03:35 +0100762
Johannes Bergd0709a62008-02-25 16:27:46 +0100763 if (params->vlan && params->vlan != sta->sdata->dev) {
Johannes Berg4fd69312007-12-19 02:03:35 +0100764 vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
765
Johannes Berg05c914f2008-09-11 00:01:58 +0200766 if (vlansdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
767 vlansdata->vif.type != NL80211_IFTYPE_AP) {
Johannes Berg98dd6a52008-04-10 15:36:09 +0200768 rcu_read_unlock();
Johannes Berg4fd69312007-12-19 02:03:35 +0100769 return -EINVAL;
Johannes Berg98dd6a52008-04-10 15:36:09 +0200770 }
Johannes Berg4fd69312007-12-19 02:03:35 +0100771
Johannes Berg14db74b2008-07-29 13:22:52 +0200772 sta->sdata = vlansdata;
Johannes Berg4fd69312007-12-19 02:03:35 +0100773 ieee80211_send_layer2_update(sta);
774 }
775
776 sta_apply_parameters(local, sta, params);
777
Johannes Berg98dd6a52008-04-10 15:36:09 +0200778 rcu_read_unlock();
779
Johannes Berg4fd69312007-12-19 02:03:35 +0100780 return 0;
781}
782
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100783#ifdef CONFIG_MAC80211_MESH
784static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
785 u8 *dst, u8 *next_hop)
786{
Johannes Berg14db74b2008-07-29 13:22:52 +0200787 struct ieee80211_local *local = wiphy_priv(wiphy);
788 struct ieee80211_sub_if_data *sdata;
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100789 struct mesh_path *mpath;
790 struct sta_info *sta;
791 int err;
792
793 if (!netif_running(dev))
794 return -ENETDOWN;
795
Johannes Berg14db74b2008-07-29 13:22:52 +0200796 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
797
Johannes Berg05c914f2008-09-11 00:01:58 +0200798 if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100799 return -ENOTSUPP;
800
Johannes Bergd0709a62008-02-25 16:27:46 +0100801 rcu_read_lock();
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100802 sta = sta_info_get(local, next_hop);
Johannes Bergd0709a62008-02-25 16:27:46 +0100803 if (!sta) {
804 rcu_read_unlock();
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100805 return -ENOENT;
Johannes Bergd0709a62008-02-25 16:27:46 +0100806 }
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100807
Jasper Bryant-Greenef698d852008-08-03 12:04:37 +1200808 err = mesh_path_add(dst, sdata);
Johannes Bergd0709a62008-02-25 16:27:46 +0100809 if (err) {
810 rcu_read_unlock();
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100811 return err;
Johannes Bergd0709a62008-02-25 16:27:46 +0100812 }
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100813
Jasper Bryant-Greenef698d852008-08-03 12:04:37 +1200814 mpath = mesh_path_lookup(dst, sdata);
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100815 if (!mpath) {
816 rcu_read_unlock();
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100817 return -ENXIO;
818 }
819 mesh_path_fix_nexthop(mpath, sta);
Johannes Bergd0709a62008-02-25 16:27:46 +0100820
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100821 rcu_read_unlock();
822 return 0;
823}
824
825static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev,
826 u8 *dst)
827{
Jasper Bryant-Greenef698d852008-08-03 12:04:37 +1200828 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100829
Jasper Bryant-Greenef698d852008-08-03 12:04:37 +1200830 if (dst)
831 return mesh_path_del(dst, sdata);
832
833 mesh_path_flush(sdata);
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100834 return 0;
835}
836
837static int ieee80211_change_mpath(struct wiphy *wiphy,
838 struct net_device *dev,
839 u8 *dst, u8 *next_hop)
840{
Johannes Berg14db74b2008-07-29 13:22:52 +0200841 struct ieee80211_local *local = wiphy_priv(wiphy);
842 struct ieee80211_sub_if_data *sdata;
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100843 struct mesh_path *mpath;
844 struct sta_info *sta;
845
846 if (!netif_running(dev))
847 return -ENETDOWN;
848
Johannes Berg14db74b2008-07-29 13:22:52 +0200849 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
850
Johannes Berg05c914f2008-09-11 00:01:58 +0200851 if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100852 return -ENOTSUPP;
853
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100854 rcu_read_lock();
Johannes Bergd0709a62008-02-25 16:27:46 +0100855
856 sta = sta_info_get(local, next_hop);
857 if (!sta) {
858 rcu_read_unlock();
859 return -ENOENT;
860 }
861
Jasper Bryant-Greenef698d852008-08-03 12:04:37 +1200862 mpath = mesh_path_lookup(dst, sdata);
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100863 if (!mpath) {
864 rcu_read_unlock();
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100865 return -ENOENT;
866 }
867
868 mesh_path_fix_nexthop(mpath, sta);
Johannes Bergd0709a62008-02-25 16:27:46 +0100869
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100870 rcu_read_unlock();
871 return 0;
872}
873
874static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,
875 struct mpath_info *pinfo)
876{
877 if (mpath->next_hop)
Johannes Berg17741cd2008-09-11 00:02:02 +0200878 memcpy(next_hop, mpath->next_hop->sta.addr, ETH_ALEN);
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100879 else
880 memset(next_hop, 0, ETH_ALEN);
881
882 pinfo->filled = MPATH_INFO_FRAME_QLEN |
883 MPATH_INFO_DSN |
884 MPATH_INFO_METRIC |
885 MPATH_INFO_EXPTIME |
886 MPATH_INFO_DISCOVERY_TIMEOUT |
887 MPATH_INFO_DISCOVERY_RETRIES |
888 MPATH_INFO_FLAGS;
889
890 pinfo->frame_qlen = mpath->frame_queue.qlen;
891 pinfo->dsn = mpath->dsn;
892 pinfo->metric = mpath->metric;
893 if (time_before(jiffies, mpath->exp_time))
894 pinfo->exptime = jiffies_to_msecs(mpath->exp_time - jiffies);
895 pinfo->discovery_timeout =
896 jiffies_to_msecs(mpath->discovery_timeout);
897 pinfo->discovery_retries = mpath->discovery_retries;
898 pinfo->flags = 0;
899 if (mpath->flags & MESH_PATH_ACTIVE)
900 pinfo->flags |= NL80211_MPATH_FLAG_ACTIVE;
901 if (mpath->flags & MESH_PATH_RESOLVING)
902 pinfo->flags |= NL80211_MPATH_FLAG_RESOLVING;
903 if (mpath->flags & MESH_PATH_DSN_VALID)
904 pinfo->flags |= NL80211_MPATH_FLAG_DSN_VALID;
905 if (mpath->flags & MESH_PATH_FIXED)
906 pinfo->flags |= NL80211_MPATH_FLAG_FIXED;
907 if (mpath->flags & MESH_PATH_RESOLVING)
908 pinfo->flags |= NL80211_MPATH_FLAG_RESOLVING;
909
910 pinfo->flags = mpath->flags;
911}
912
913static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,
914 u8 *dst, u8 *next_hop, struct mpath_info *pinfo)
915
916{
Johannes Berg14db74b2008-07-29 13:22:52 +0200917 struct ieee80211_sub_if_data *sdata;
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100918 struct mesh_path *mpath;
919
Johannes Berg14db74b2008-07-29 13:22:52 +0200920 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
921
Johannes Berg05c914f2008-09-11 00:01:58 +0200922 if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100923 return -ENOTSUPP;
924
925 rcu_read_lock();
Jasper Bryant-Greenef698d852008-08-03 12:04:37 +1200926 mpath = mesh_path_lookup(dst, sdata);
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100927 if (!mpath) {
928 rcu_read_unlock();
929 return -ENOENT;
930 }
931 memcpy(dst, mpath->dst, ETH_ALEN);
932 mpath_set_pinfo(mpath, next_hop, pinfo);
933 rcu_read_unlock();
934 return 0;
935}
936
937static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
938 int idx, u8 *dst, u8 *next_hop,
939 struct mpath_info *pinfo)
940{
Johannes Berg14db74b2008-07-29 13:22:52 +0200941 struct ieee80211_sub_if_data *sdata;
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100942 struct mesh_path *mpath;
943
Johannes Berg14db74b2008-07-29 13:22:52 +0200944 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
945
Johannes Berg05c914f2008-09-11 00:01:58 +0200946 if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100947 return -ENOTSUPP;
948
949 rcu_read_lock();
Jasper Bryant-Greenef698d852008-08-03 12:04:37 +1200950 mpath = mesh_path_lookup_by_idx(idx, sdata);
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100951 if (!mpath) {
952 rcu_read_unlock();
953 return -ENOENT;
954 }
955 memcpy(dst, mpath->dst, ETH_ALEN);
956 mpath_set_pinfo(mpath, next_hop, pinfo);
957 rcu_read_unlock();
958 return 0;
959}
960#endif
961
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300962static int ieee80211_change_bss(struct wiphy *wiphy,
963 struct net_device *dev,
964 struct bss_parameters *params)
965{
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300966 struct ieee80211_sub_if_data *sdata;
967 u32 changed = 0;
968
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300969 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
970
Johannes Berg05c914f2008-09-11 00:01:58 +0200971 if (sdata->vif.type != NL80211_IFTYPE_AP)
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300972 return -EINVAL;
973
974 if (params->use_cts_prot >= 0) {
975 sdata->bss_conf.use_cts_prot = params->use_cts_prot;
976 changed |= BSS_CHANGED_ERP_CTS_PROT;
977 }
978 if (params->use_short_preamble >= 0) {
979 sdata->bss_conf.use_short_preamble =
980 params->use_short_preamble;
981 changed |= BSS_CHANGED_ERP_PREAMBLE;
982 }
983 if (params->use_short_slot_time >= 0) {
984 sdata->bss_conf.use_short_slot =
985 params->use_short_slot_time;
986 changed |= BSS_CHANGED_ERP_SLOT;
987 }
988
989 ieee80211_bss_info_change_notify(sdata, changed);
990
991 return 0;
992}
993
Jiri Bencf0706e822007-05-05 11:45:53 -0700994struct cfg80211_ops mac80211_config_ops = {
995 .add_virtual_intf = ieee80211_add_iface,
996 .del_virtual_intf = ieee80211_del_iface,
Johannes Berg42613db2007-09-28 21:52:27 +0200997 .change_virtual_intf = ieee80211_change_iface,
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100998 .add_key = ieee80211_add_key,
999 .del_key = ieee80211_del_key,
Johannes Berg62da92f2007-12-19 02:03:31 +01001000 .get_key = ieee80211_get_key,
Johannes Berge8cbb4c2007-12-19 02:03:30 +01001001 .set_default_key = ieee80211_config_default_key,
Johannes Berg5dfdaf52007-12-19 02:03:33 +01001002 .add_beacon = ieee80211_add_beacon,
1003 .set_beacon = ieee80211_set_beacon,
1004 .del_beacon = ieee80211_del_beacon,
Johannes Berg4fd69312007-12-19 02:03:35 +01001005 .add_station = ieee80211_add_station,
1006 .del_station = ieee80211_del_station,
1007 .change_station = ieee80211_change_station,
Johannes Berg7bbdd2d2007-12-19 02:03:37 +01001008 .get_station = ieee80211_get_station,
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +01001009 .dump_station = ieee80211_dump_station,
1010#ifdef CONFIG_MAC80211_MESH
1011 .add_mpath = ieee80211_add_mpath,
1012 .del_mpath = ieee80211_del_mpath,
1013 .change_mpath = ieee80211_change_mpath,
1014 .get_mpath = ieee80211_get_mpath,
1015 .dump_mpath = ieee80211_dump_mpath,
1016#endif
Jouni Malinen9f1ba902008-08-07 20:07:01 +03001017 .change_bss = ieee80211_change_bss,
Jiri Bencf0706e822007-05-05 11:45:53 -07001018};