]> err.no Git - linux-2.6/blob - net/mac80211/cfg.c
mac80211: add beacon configuration via cfg80211
[linux-2.6] / net / mac80211 / cfg.c
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
18 static enum ieee80211_if_types
19 nl80211_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
35 static int ieee80211_add_iface(struct wiphy *wiphy, char *name,
36                                enum nl80211_iftype type)
37 {
38         struct ieee80211_local *local = wiphy_priv(wiphy);
39         enum ieee80211_if_types itype;
40
41         if (unlikely(local->reg_state != IEEE80211_DEV_REGISTERED))
42                 return -ENODEV;
43
44         itype = nl80211_type_to_mac80211_type(type);
45         if (itype == IEEE80211_IF_TYPE_INVALID)
46                 return -EINVAL;
47
48         return ieee80211_if_add(local->mdev, name, NULL, itype);
49 }
50
51 static 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
60         /* we're under RTNL */
61         dev = __dev_get_by_index(&init_net, ifindex);
62         if (!dev)
63                 return 0;
64
65         name = dev->name;
66
67         return ieee80211_if_remove(local->mdev, name, -1);
68 }
69
70 static 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
95         if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN)
96                 return -EOPNOTSUPP;
97
98         ieee80211_if_reinit(dev);
99         ieee80211_if_set_type(dev, itype);
100
101         return 0;
102 }
103
104 static 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
147 static 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
179 static 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
261 static 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
273 static 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
298 /*
299  * This handles both adding a beacon and setting new beacon info
300  */
301 static 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
396 static 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
413 static 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
430 static 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
450 struct cfg80211_ops mac80211_config_ops = {
451         .add_virtual_intf = ieee80211_add_iface,
452         .del_virtual_intf = ieee80211_del_iface,
453         .change_virtual_intf = ieee80211_change_iface,
454         .add_key = ieee80211_add_key,
455         .del_key = ieee80211_del_key,
456         .get_key = ieee80211_get_key,
457         .set_default_key = ieee80211_config_default_key,
458         .add_beacon = ieee80211_add_beacon,
459         .set_beacon = ieee80211_set_beacon,
460         .del_beacon = ieee80211_del_beacon,
461         .get_station = ieee80211_get_station,
462 };