blob: 5a4c6edd93485a968ce65f9b2cb210f5e2311c51 [file] [log] [blame]
Jiri Bencf0706e82007-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 Bencf0706e82007-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 Bencf0706e82007-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 Bencf0706e82007-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"
Jiri Bencf0706e82007-05-05 11:45:53 -070017
Johannes Berg42613db2007-09-28 21:52:27 +020018static enum ieee80211_if_types
19nl80211_type_to_mac80211_type(enum nl80211_iftype type)
20{
21 switch (type) {
22 case NL80211_IFTYPE_UNSPECIFIED:
23 return IEEE80211_IF_TYPE_STA;
24 case NL80211_IFTYPE_ADHOC:
25 return IEEE80211_IF_TYPE_IBSS;
26 case NL80211_IFTYPE_STATION:
27 return IEEE80211_IF_TYPE_STA;
28 case NL80211_IFTYPE_MONITOR:
29 return IEEE80211_IF_TYPE_MNTR;
30 default:
31 return IEEE80211_IF_TYPE_INVALID;
32 }
33}
34
Jiri Bencf0706e82007-05-05 11:45:53 -070035static int ieee80211_add_iface(struct wiphy *wiphy, char *name,
Johannes Berg55682962007-09-20 13:09:35 -040036 enum nl80211_iftype type)
Jiri Bencf0706e82007-05-05 11:45:53 -070037{
38 struct ieee80211_local *local = wiphy_priv(wiphy);
Johannes Berg42613db2007-09-28 21:52:27 +020039 enum ieee80211_if_types itype;
Jiri Bencf0706e82007-05-05 11:45:53 -070040
41 if (unlikely(local->reg_state != IEEE80211_DEV_REGISTERED))
42 return -ENODEV;
43
Johannes Berg42613db2007-09-28 21:52:27 +020044 itype = nl80211_type_to_mac80211_type(type);
45 if (itype == IEEE80211_IF_TYPE_INVALID)
Jiri Bencf0706e82007-05-05 11:45:53 -070046 return -EINVAL;
Jiri Bencf0706e82007-05-05 11:45:53 -070047
48 return ieee80211_if_add(local->mdev, name, NULL, itype);
49}
50
51static int ieee80211_del_iface(struct wiphy *wiphy, int ifindex)
52{
53 struct ieee80211_local *local = wiphy_priv(wiphy);
54 struct net_device *dev;
55 char *name;
56
57 if (unlikely(local->reg_state != IEEE80211_DEV_REGISTERED))
58 return -ENODEV;
59
Johannes Berg42613db2007-09-28 21:52:27 +020060 /* we're under RTNL */
61 dev = __dev_get_by_index(&init_net, ifindex);
Jiri Bencf0706e82007-05-05 11:45:53 -070062 if (!dev)
63 return 0;
64
65 name = dev->name;
Jiri Bencf0706e82007-05-05 11:45:53 -070066
67 return ieee80211_if_remove(local->mdev, name, -1);
68}
69
Johannes Berg42613db2007-09-28 21:52:27 +020070static int ieee80211_change_iface(struct wiphy *wiphy, int ifindex,
71 enum nl80211_iftype type)
72{
73 struct ieee80211_local *local = wiphy_priv(wiphy);
74 struct net_device *dev;
75 enum ieee80211_if_types itype;
76 struct ieee80211_sub_if_data *sdata;
77
78 if (unlikely(local->reg_state != IEEE80211_DEV_REGISTERED))
79 return -ENODEV;
80
81 /* we're under RTNL */
82 dev = __dev_get_by_index(&init_net, ifindex);
83 if (!dev)
84 return -ENODEV;
85
86 if (netif_running(dev))
87 return -EBUSY;
88
89 itype = nl80211_type_to_mac80211_type(type);
90 if (itype == IEEE80211_IF_TYPE_INVALID)
91 return -EINVAL;
92
93 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
94
Johannes Berg51fb61e2007-12-19 01:31:27 +010095 if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN)
Johannes Berg42613db2007-09-28 21:52:27 +020096 return -EOPNOTSUPP;
97
98 ieee80211_if_reinit(dev);
99 ieee80211_if_set_type(dev, itype);
100
101 return 0;
102}
103
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100104static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
105 u8 key_idx, u8 *mac_addr,
106 struct key_params *params)
107{
108 struct ieee80211_sub_if_data *sdata;
109 struct sta_info *sta = NULL;
110 enum ieee80211_key_alg alg;
111 int ret;
112
113 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
114
115 switch (params->cipher) {
116 case WLAN_CIPHER_SUITE_WEP40:
117 case WLAN_CIPHER_SUITE_WEP104:
118 alg = ALG_WEP;
119 break;
120 case WLAN_CIPHER_SUITE_TKIP:
121 alg = ALG_TKIP;
122 break;
123 case WLAN_CIPHER_SUITE_CCMP:
124 alg = ALG_CCMP;
125 break;
126 default:
127 return -EINVAL;
128 }
129
130 if (mac_addr) {
131 sta = sta_info_get(sdata->local, mac_addr);
132 if (!sta)
133 return -ENOENT;
134 }
135
136 ret = 0;
137 if (!ieee80211_key_alloc(sdata, sta, alg, key_idx,
138 params->key_len, params->key))
139 ret = -ENOMEM;
140
141 if (sta)
142 sta_info_put(sta);
143
144 return ret;
145}
146
147static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
148 u8 key_idx, u8 *mac_addr)
149{
150 struct ieee80211_sub_if_data *sdata;
151 struct sta_info *sta;
152 int ret;
153
154 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
155
156 if (mac_addr) {
157 sta = sta_info_get(sdata->local, mac_addr);
158 if (!sta)
159 return -ENOENT;
160
161 ret = 0;
162 if (sta->key)
163 ieee80211_key_free(sta->key);
164 else
165 ret = -ENOENT;
166
167 sta_info_put(sta);
168 return ret;
169 }
170
171 if (!sdata->keys[key_idx])
172 return -ENOENT;
173
174 ieee80211_key_free(sdata->keys[key_idx]);
175
176 return 0;
177}
178
Johannes Berg62da92f2007-12-19 02:03:31 +0100179static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
180 u8 key_idx, u8 *mac_addr, void *cookie,
181 void (*callback)(void *cookie,
182 struct key_params *params))
183{
184 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
185 struct sta_info *sta = NULL;
186 u8 seq[6] = {0};
187 struct key_params params;
188 struct ieee80211_key *key;
189 u32 iv32;
190 u16 iv16;
191 int err = -ENOENT;
192
193 if (mac_addr) {
194 sta = sta_info_get(sdata->local, mac_addr);
195 if (!sta)
196 goto out;
197
198 key = sta->key;
199 } else
200 key = sdata->keys[key_idx];
201
202 if (!key)
203 goto out;
204
205 memset(&params, 0, sizeof(params));
206
207 switch (key->conf.alg) {
208 case ALG_TKIP:
209 params.cipher = WLAN_CIPHER_SUITE_TKIP;
210
211 iv32 = key->u.tkip.iv32;
212 iv16 = key->u.tkip.iv16;
213
214 if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE &&
215 sdata->local->ops->get_tkip_seq)
216 sdata->local->ops->get_tkip_seq(
217 local_to_hw(sdata->local),
218 key->conf.hw_key_idx,
219 &iv32, &iv16);
220
221 seq[0] = iv16 & 0xff;
222 seq[1] = (iv16 >> 8) & 0xff;
223 seq[2] = iv32 & 0xff;
224 seq[3] = (iv32 >> 8) & 0xff;
225 seq[4] = (iv32 >> 16) & 0xff;
226 seq[5] = (iv32 >> 24) & 0xff;
227 params.seq = seq;
228 params.seq_len = 6;
229 break;
230 case ALG_CCMP:
231 params.cipher = WLAN_CIPHER_SUITE_CCMP;
232 seq[0] = key->u.ccmp.tx_pn[5];
233 seq[1] = key->u.ccmp.tx_pn[4];
234 seq[2] = key->u.ccmp.tx_pn[3];
235 seq[3] = key->u.ccmp.tx_pn[2];
236 seq[4] = key->u.ccmp.tx_pn[1];
237 seq[5] = key->u.ccmp.tx_pn[0];
238 params.seq = seq;
239 params.seq_len = 6;
240 break;
241 case ALG_WEP:
242 if (key->conf.keylen == 5)
243 params.cipher = WLAN_CIPHER_SUITE_WEP40;
244 else
245 params.cipher = WLAN_CIPHER_SUITE_WEP104;
246 break;
247 }
248
249 params.key = key->conf.key;
250 params.key_len = key->conf.keylen;
251
252 callback(cookie, &params);
253 err = 0;
254
255 out:
256 if (sta)
257 sta_info_put(sta);
258 return err;
259}
260
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100261static int ieee80211_config_default_key(struct wiphy *wiphy,
262 struct net_device *dev,
263 u8 key_idx)
264{
265 struct ieee80211_sub_if_data *sdata;
266
267 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
268 ieee80211_set_default_key(sdata, key_idx);
269
270 return 0;
271}
272
Johannes Berg7bbdd2d2007-12-19 02:03:37 +0100273static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
274 u8 *mac, struct station_stats *stats)
275{
276 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
277 struct sta_info *sta;
278
279 sta = sta_info_get(local, mac);
280 if (!sta)
281 return -ENOENT;
282
283 /* XXX: verify sta->dev == dev */
284
285 stats->filled = STATION_STAT_INACTIVE_TIME |
286 STATION_STAT_RX_BYTES |
287 STATION_STAT_TX_BYTES;
288
289 stats->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
290 stats->rx_bytes = sta->rx_bytes;
291 stats->tx_bytes = sta->tx_bytes;
292
293 sta_info_put(sta);
294
295 return 0;
296}
297
Johannes Berg5dfdaf52007-12-19 02:03:33 +0100298/*
299 * This handles both adding a beacon and setting new beacon info
300 */
301static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata,
302 struct beacon_parameters *params)
303{
304 struct beacon_data *new, *old;
305 int new_head_len, new_tail_len;
306 int size;
307 int err = -EINVAL;
308
309 old = sdata->u.ap.beacon;
310
311 /* head must not be zero-length */
312 if (params->head && !params->head_len)
313 return -EINVAL;
314
315 /*
316 * This is a kludge. beacon interval should really be part
317 * of the beacon information.
318 */
319 if (params->interval) {
320 sdata->local->hw.conf.beacon_int = params->interval;
321 if (ieee80211_hw_config(sdata->local))
322 return -EINVAL;
323 /*
324 * We updated some parameter so if below bails out
325 * it's not an error.
326 */
327 err = 0;
328 }
329
330 /* Need to have a beacon head if we don't have one yet */
331 if (!params->head && !old)
332 return err;
333
334 /* sorry, no way to start beaconing without dtim period */
335 if (!params->dtim_period && !old)
336 return err;
337
338 /* new or old head? */
339 if (params->head)
340 new_head_len = params->head_len;
341 else
342 new_head_len = old->head_len;
343
344 /* new or old tail? */
345 if (params->tail || !old)
346 /* params->tail_len will be zero for !params->tail */
347 new_tail_len = params->tail_len;
348 else
349 new_tail_len = old->tail_len;
350
351 size = sizeof(*new) + new_head_len + new_tail_len;
352
353 new = kzalloc(size, GFP_KERNEL);
354 if (!new)
355 return -ENOMEM;
356
357 /* start filling the new info now */
358
359 /* new or old dtim period? */
360 if (params->dtim_period)
361 new->dtim_period = params->dtim_period;
362 else
363 new->dtim_period = old->dtim_period;
364
365 /*
366 * pointers go into the block we allocated,
367 * memory is | beacon_data | head | tail |
368 */
369 new->head = ((u8 *) new) + sizeof(*new);
370 new->tail = new->head + new_head_len;
371 new->head_len = new_head_len;
372 new->tail_len = new_tail_len;
373
374 /* copy in head */
375 if (params->head)
376 memcpy(new->head, params->head, new_head_len);
377 else
378 memcpy(new->head, old->head, new_head_len);
379
380 /* copy in optional tail */
381 if (params->tail)
382 memcpy(new->tail, params->tail, new_tail_len);
383 else
384 if (old)
385 memcpy(new->tail, old->tail, new_tail_len);
386
387 rcu_assign_pointer(sdata->u.ap.beacon, new);
388
389 synchronize_rcu();
390
391 kfree(old);
392
393 return ieee80211_if_config_beacon(sdata->dev);
394}
395
396static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev,
397 struct beacon_parameters *params)
398{
399 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
400 struct beacon_data *old;
401
402 if (sdata->vif.type != IEEE80211_IF_TYPE_AP)
403 return -EINVAL;
404
405 old = sdata->u.ap.beacon;
406
407 if (old)
408 return -EALREADY;
409
410 return ieee80211_config_beacon(sdata, params);
411}
412
413static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev,
414 struct beacon_parameters *params)
415{
416 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
417 struct beacon_data *old;
418
419 if (sdata->vif.type != IEEE80211_IF_TYPE_AP)
420 return -EINVAL;
421
422 old = sdata->u.ap.beacon;
423
424 if (!old)
425 return -ENOENT;
426
427 return ieee80211_config_beacon(sdata, params);
428}
429
430static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev)
431{
432 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
433 struct beacon_data *old;
434
435 if (sdata->vif.type != IEEE80211_IF_TYPE_AP)
436 return -EINVAL;
437
438 old = sdata->u.ap.beacon;
439
440 if (!old)
441 return -ENOENT;
442
443 rcu_assign_pointer(sdata->u.ap.beacon, NULL);
444 synchronize_rcu();
445 kfree(old);
446
447 return ieee80211_if_config_beacon(dev);
448}
449
Jiri Bencf0706e82007-05-05 11:45:53 -0700450struct cfg80211_ops mac80211_config_ops = {
451 .add_virtual_intf = ieee80211_add_iface,
452 .del_virtual_intf = ieee80211_del_iface,
Johannes Berg42613db2007-09-28 21:52:27 +0200453 .change_virtual_intf = ieee80211_change_iface,
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100454 .add_key = ieee80211_add_key,
455 .del_key = ieee80211_del_key,
Johannes Berg62da92f2007-12-19 02:03:31 +0100456 .get_key = ieee80211_get_key,
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100457 .set_default_key = ieee80211_config_default_key,
Johannes Berg5dfdaf52007-12-19 02:03:33 +0100458 .add_beacon = ieee80211_add_beacon,
459 .set_beacon = ieee80211_set_beacon,
460 .del_beacon = ieee80211_del_beacon,
Johannes Berg7bbdd2d2007-12-19 02:03:37 +0100461 .get_station = ieee80211_get_station,
Jiri Bencf0706e82007-05-05 11:45:53 -0700462};