Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
libipw_wx.c
Go to the documentation of this file.
1 /******************************************************************************
2 
3  Copyright(c) 2004-2005 Intel Corporation. All rights reserved.
4 
5  Portions of this file are based on the WEP enablement code provided by the
6  Host AP project hostap-drivers v0.1.3
7  Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
9  Copyright (c) 2002-2003, Jouni Malinen <[email protected]>
10 
11  This program is free software; you can redistribute it and/or modify it
12  under the terms of version 2 of the GNU General Public License as
13  published by the Free Software Foundation.
14 
15  This program is distributed in the hope that it will be useful, but WITHOUT
16  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18  more details.
19 
20  You should have received a copy of the GNU General Public License along with
21  this program; if not, write to the Free Software Foundation, Inc., 59
22  Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 
24  The full GNU General Public License is included in this distribution in the
25  file called LICENSE.
26 
27  Contact Information:
28  Intel Linux Wireless <[email protected]>
29  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30 
31 ******************************************************************************/
32 
33 #include <linux/hardirq.h>
34 #include <linux/kmod.h>
35 #include <linux/slab.h>
36 #include <linux/module.h>
37 #include <linux/jiffies.h>
38 
39 #include <net/lib80211.h>
40 #include <linux/wireless.h>
41 
42 #include "libipw.h"
43 
44 static const char *libipw_modes[] = {
45  "?", "a", "b", "ab", "g", "ag", "bg", "abg"
46 };
47 
48 static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
49 {
50  unsigned long end = jiffies;
51 
52  if (end >= start)
53  return jiffies_to_msecs(end - start);
54 
55  return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
56 }
57 
58 #define MAX_CUSTOM_LEN 64
59 static char *libipw_translate_scan(struct libipw_device *ieee,
60  char *start, char *stop,
61  struct libipw_network *network,
62  struct iw_request_info *info)
63 {
64  char custom[MAX_CUSTOM_LEN];
65  char *p;
66  struct iw_event iwe;
67  int i, j;
68  char *current_val; /* For rates */
69  u8 rate;
70 
71  /* First entry *MUST* be the AP MAC address */
72  iwe.cmd = SIOCGIWAP;
73  iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
74  memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
75  start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
76 
77  /* Remaining entries will be displayed in the order we provide them */
78 
79  /* Add the ESSID */
80  iwe.cmd = SIOCGIWESSID;
81  iwe.u.data.flags = 1;
82  iwe.u.data.length = min(network->ssid_len, (u8) 32);
83  start = iwe_stream_add_point(info, start, stop,
84  &iwe, network->ssid);
85 
86  /* Add the protocol name */
87  iwe.cmd = SIOCGIWNAME;
88  snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
89  libipw_modes[network->mode]);
90  start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN);
91 
92  /* Add mode */
93  iwe.cmd = SIOCGIWMODE;
95  if (network->capability & WLAN_CAPABILITY_ESS)
96  iwe.u.mode = IW_MODE_MASTER;
97  else
98  iwe.u.mode = IW_MODE_ADHOC;
99 
100  start = iwe_stream_add_event(info, start, stop,
101  &iwe, IW_EV_UINT_LEN);
102  }
103 
104  /* Add channel and frequency */
105  /* Note : userspace automatically computes channel using iwrange */
106  iwe.cmd = SIOCGIWFREQ;
107  iwe.u.freq.m = libipw_channel_to_freq(ieee, network->channel);
108  iwe.u.freq.e = 6;
109  iwe.u.freq.i = 0;
110  start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
111 
112  /* Add encryption capability */
113  iwe.cmd = SIOCGIWENCODE;
114  if (network->capability & WLAN_CAPABILITY_PRIVACY)
115  iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
116  else
117  iwe.u.data.flags = IW_ENCODE_DISABLED;
118  iwe.u.data.length = 0;
119  start = iwe_stream_add_point(info, start, stop,
120  &iwe, network->ssid);
121 
122  /* Add basic and extended rates */
123  /* Rate : stuffing multiple values in a single event require a bit
124  * more of magic - Jean II */
125  current_val = start + iwe_stream_lcp_len(info);
126  iwe.cmd = SIOCGIWRATE;
127  /* Those two flags are ignored... */
128  iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
129 
130  for (i = 0, j = 0; i < network->rates_len;) {
131  if (j < network->rates_ex_len &&
132  ((network->rates_ex[j] & 0x7F) <
133  (network->rates[i] & 0x7F)))
134  rate = network->rates_ex[j++] & 0x7F;
135  else
136  rate = network->rates[i++] & 0x7F;
137  /* Bit rate given in 500 kb/s units (+ 0x80) */
138  iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
139  /* Add new value to event */
140  current_val = iwe_stream_add_value(info, start, current_val,
141  stop, &iwe, IW_EV_PARAM_LEN);
142  }
143  for (; j < network->rates_ex_len; j++) {
144  rate = network->rates_ex[j] & 0x7F;
145  /* Bit rate given in 500 kb/s units (+ 0x80) */
146  iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
147  /* Add new value to event */
148  current_val = iwe_stream_add_value(info, start, current_val,
149  stop, &iwe, IW_EV_PARAM_LEN);
150  }
151  /* Check if we added any rate */
152  if ((current_val - start) > iwe_stream_lcp_len(info))
153  start = current_val;
154 
155  /* Add quality statistics */
156  iwe.cmd = IWEVQUAL;
159 
160  if (!(network->stats.mask & LIBIPW_STATMASK_RSSI)) {
161  iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
163  iwe.u.qual.qual = 0;
164  } else {
165  if (ieee->perfect_rssi == ieee->worst_rssi)
166  iwe.u.qual.qual = 100;
167  else
168  iwe.u.qual.qual =
169  (100 *
170  (ieee->perfect_rssi - ieee->worst_rssi) *
171  (ieee->perfect_rssi - ieee->worst_rssi) -
172  (ieee->perfect_rssi - network->stats.rssi) *
173  (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
174  62 * (ieee->perfect_rssi -
175  network->stats.rssi))) /
176  ((ieee->perfect_rssi -
177  ieee->worst_rssi) * (ieee->perfect_rssi -
178  ieee->worst_rssi));
179  if (iwe.u.qual.qual > 100)
180  iwe.u.qual.qual = 100;
181  else if (iwe.u.qual.qual < 1)
182  iwe.u.qual.qual = 0;
183  }
184 
185  if (!(network->stats.mask & LIBIPW_STATMASK_NOISE)) {
186  iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
187  iwe.u.qual.noise = 0;
188  } else {
189  iwe.u.qual.noise = network->stats.noise;
190  }
191 
192  if (!(network->stats.mask & LIBIPW_STATMASK_SIGNAL)) {
193  iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
194  iwe.u.qual.level = 0;
195  } else {
196  iwe.u.qual.level = network->stats.signal;
197  }
198 
199  start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
200 
201  iwe.cmd = IWEVCUSTOM;
202  p = custom;
203 
204  iwe.u.data.length = p - custom;
205  if (iwe.u.data.length)
206  start = iwe_stream_add_point(info, start, stop, &iwe, custom);
207 
208  memset(&iwe, 0, sizeof(iwe));
209  if (network->wpa_ie_len) {
210  char buf[MAX_WPA_IE_LEN];
211  memcpy(buf, network->wpa_ie, network->wpa_ie_len);
212  iwe.cmd = IWEVGENIE;
213  iwe.u.data.length = network->wpa_ie_len;
214  start = iwe_stream_add_point(info, start, stop, &iwe, buf);
215  }
216 
217  memset(&iwe, 0, sizeof(iwe));
218  if (network->rsn_ie_len) {
219  char buf[MAX_WPA_IE_LEN];
220  memcpy(buf, network->rsn_ie, network->rsn_ie_len);
221  iwe.cmd = IWEVGENIE;
222  iwe.u.data.length = network->rsn_ie_len;
223  start = iwe_stream_add_point(info, start, stop, &iwe, buf);
224  }
225 
226  /* Add EXTRA: Age to display seconds since last beacon/probe response
227  * for given network. */
228  iwe.cmd = IWEVCUSTOM;
229  p = custom;
230  p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
231  " Last beacon: %ums ago",
232  elapsed_jiffies_msecs(network->last_scanned));
233  iwe.u.data.length = p - custom;
234  if (iwe.u.data.length)
235  start = iwe_stream_add_point(info, start, stop, &iwe, custom);
236 
237  /* Add spectrum management information */
238  iwe.cmd = -1;
239  p = custom;
240  p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
241 
242  if (libipw_get_channel_flags(ieee, network->channel) &
244  iwe.cmd = IWEVCUSTOM;
245  p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
246  }
247 
248  if (libipw_get_channel_flags(ieee, network->channel) &
250  iwe.cmd = IWEVCUSTOM;
251  p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS ");
252  }
253 
254  if (iwe.cmd == IWEVCUSTOM) {
255  iwe.u.data.length = p - custom;
256  start = iwe_stream_add_point(info, start, stop, &iwe, custom);
257  }
258 
259  return start;
260 }
261 
262 #define SCAN_ITEM_SIZE 128
263 
265  struct iw_request_info *info,
266  union iwreq_data *wrqu, char *extra)
267 {
268  struct libipw_network *network;
269  unsigned long flags;
270  int err = 0;
271 
272  char *ev = extra;
273  char *stop = ev + wrqu->data.length;
274  int i = 0;
276 
277  LIBIPW_DEBUG_WX("Getting scan\n");
278 
279  spin_lock_irqsave(&ieee->lock, flags);
280 
281  list_for_each_entry(network, &ieee->network_list, list) {
282  i++;
283  if (stop - ev < SCAN_ITEM_SIZE) {
284  err = -E2BIG;
285  break;
286  }
287 
288  if (ieee->scan_age == 0 ||
289  time_after(network->last_scanned + ieee->scan_age, jiffies))
290  ev = libipw_translate_scan(ieee, ev, stop, network,
291  info);
292  else {
293  LIBIPW_DEBUG_SCAN("Not showing network '%s ("
294  "%pM)' due to age (%ums).\n",
295  print_ssid(ssid, network->ssid,
296  network->ssid_len),
297  network->bssid,
298  elapsed_jiffies_msecs(
299  network->last_scanned));
300  }
301  }
302 
303  spin_unlock_irqrestore(&ieee->lock, flags);
304 
305  wrqu->data.length = ev - extra;
306  wrqu->data.flags = 0;
307 
308  LIBIPW_DEBUG_WX("exit: %d networks returned.\n", i);
309 
310  return err;
311 }
312 
314  struct iw_request_info *info,
315  union iwreq_data *wrqu, char *keybuf)
316 {
317  struct iw_point *erq = &(wrqu->encoding);
318  struct net_device *dev = ieee->dev;
319  struct libipw_security sec = {
320  .flags = 0
321  };
322  int i, key, key_provided, len;
323  struct lib80211_crypt_data **crypt;
324  int host_crypto = ieee->host_encrypt || ieee->host_decrypt;
326 
327  LIBIPW_DEBUG_WX("SET_ENCODE\n");
328 
329  key = erq->flags & IW_ENCODE_INDEX;
330  if (key) {
331  if (key > WEP_KEYS)
332  return -EINVAL;
333  key--;
334  key_provided = 1;
335  } else {
336  key_provided = 0;
337  key = ieee->crypt_info.tx_keyidx;
338  }
339 
340  LIBIPW_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
341  "provided" : "default");
342 
343  crypt = &ieee->crypt_info.crypt[key];
344 
345  if (erq->flags & IW_ENCODE_DISABLED) {
346  if (key_provided && *crypt) {
347  LIBIPW_DEBUG_WX("Disabling encryption on key %d.\n",
348  key);
350  } else
351  LIBIPW_DEBUG_WX("Disabling encryption.\n");
352 
353  /* Check all the keys to see if any are still configured,
354  * and if no key index was provided, de-init them all */
355  for (i = 0; i < WEP_KEYS; i++) {
356  if (ieee->crypt_info.crypt[i] != NULL) {
357  if (key_provided)
358  break;
360  &ieee->crypt_info.crypt[i]);
361  }
362  }
363 
364  if (i == WEP_KEYS) {
365  sec.enabled = 0;
366  sec.encrypt = 0;
367  sec.level = SEC_LEVEL_0;
369  }
370 
371  goto done;
372  }
373 
374  sec.enabled = 1;
375  sec.encrypt = 1;
376  sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
377 
378  if (*crypt != NULL && (*crypt)->ops != NULL &&
379  strcmp((*crypt)->ops->name, "WEP") != 0) {
380  /* changing to use WEP; deinit previously used algorithm
381  * on this key */
383  }
384 
385  if (*crypt == NULL && host_crypto) {
386  struct lib80211_crypt_data *new_crypt;
387 
388  /* take WEP into use */
389  new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
390  GFP_KERNEL);
391  if (new_crypt == NULL)
392  return -ENOMEM;
393  new_crypt->ops = lib80211_get_crypto_ops("WEP");
394  if (!new_crypt->ops) {
395  request_module("lib80211_crypt_wep");
396  new_crypt->ops = lib80211_get_crypto_ops("WEP");
397  }
398 
399  if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
400  new_crypt->priv = new_crypt->ops->init(key);
401 
402  if (!new_crypt->ops || !new_crypt->priv) {
403  kfree(new_crypt);
404  new_crypt = NULL;
405 
406  printk(KERN_WARNING "%s: could not initialize WEP: "
407  "load module lib80211_crypt_wep\n", dev->name);
408  return -EOPNOTSUPP;
409  }
410  *crypt = new_crypt;
411  }
412 
413  /* If a new key was provided, set it up */
414  if (erq->length > 0) {
415  len = erq->length <= 5 ? 5 : 13;
416  memcpy(sec.keys[key], keybuf, erq->length);
417  if (len > erq->length)
418  memset(sec.keys[key] + erq->length, 0,
419  len - erq->length);
420  LIBIPW_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
421  key, print_ssid(ssid, sec.keys[key], len),
422  erq->length, len);
423  sec.key_sizes[key] = len;
424  if (*crypt)
425  (*crypt)->ops->set_key(sec.keys[key], len, NULL,
426  (*crypt)->priv);
427  sec.flags |= (1 << key);
428  /* This ensures a key will be activated if no key is
429  * explicitly set */
430  if (key == sec.active_key)
431  sec.flags |= SEC_ACTIVE_KEY;
432 
433  } else {
434  if (host_crypto) {
435  len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
436  NULL, (*crypt)->priv);
437  if (len == 0) {
438  /* Set a default key of all 0 */
439  LIBIPW_DEBUG_WX("Setting key %d to all "
440  "zero.\n", key);
441  memset(sec.keys[key], 0, 13);
442  (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
443  (*crypt)->priv);
444  sec.key_sizes[key] = 13;
445  sec.flags |= (1 << key);
446  }
447  }
448  /* No key data - just set the default TX key index */
449  if (key_provided) {
450  LIBIPW_DEBUG_WX("Setting key %d to default Tx "
451  "key.\n", key);
452  ieee->crypt_info.tx_keyidx = key;
453  sec.active_key = key;
454  sec.flags |= SEC_ACTIVE_KEY;
455  }
456  }
457  if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
458  ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
459  sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
461  sec.flags |= SEC_AUTH_MODE;
462  LIBIPW_DEBUG_WX("Auth: %s\n",
463  sec.auth_mode == WLAN_AUTH_OPEN ?
464  "OPEN" : "SHARED KEY");
465  }
466 
467  /* For now we just support WEP, so only set that security level...
468  * TODO: When WPA is added this is one place that needs to change */
469  sec.flags |= SEC_LEVEL;
470  sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
471  sec.encode_alg[key] = SEC_ALG_WEP;
472 
473  done:
474  if (ieee->set_security)
475  ieee->set_security(dev, &sec);
476 
477  return 0;
478 }
479 
481  struct iw_request_info *info,
482  union iwreq_data *wrqu, char *keybuf)
483 {
484  struct iw_point *erq = &(wrqu->encoding);
485  int len, key;
486  struct lib80211_crypt_data *crypt;
487  struct libipw_security *sec = &ieee->sec;
488 
489  LIBIPW_DEBUG_WX("GET_ENCODE\n");
490 
491  key = erq->flags & IW_ENCODE_INDEX;
492  if (key) {
493  if (key > WEP_KEYS)
494  return -EINVAL;
495  key--;
496  } else
497  key = ieee->crypt_info.tx_keyidx;
498 
499  crypt = ieee->crypt_info.crypt[key];
500  erq->flags = key + 1;
501 
502  if (!sec->enabled) {
503  erq->length = 0;
504  erq->flags |= IW_ENCODE_DISABLED;
505  return 0;
506  }
507 
508  len = sec->key_sizes[key];
509  memcpy(keybuf, sec->keys[key], len);
510 
511  erq->length = len;
512  erq->flags |= IW_ENCODE_ENABLED;
513 
514  if (ieee->open_wep)
515  erq->flags |= IW_ENCODE_OPEN;
516  else
517  erq->flags |= IW_ENCODE_RESTRICTED;
518 
519  return 0;
520 }
521 
523  struct iw_request_info *info,
524  union iwreq_data *wrqu, char *extra)
525 {
526  struct net_device *dev = ieee->dev;
527  struct iw_point *encoding = &wrqu->encoding;
528  struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
529  int i, idx, ret = 0;
530  int group_key = 0;
531  const char *alg, *module;
532  struct lib80211_crypto_ops *ops;
533  struct lib80211_crypt_data **crypt;
534 
535  struct libipw_security sec = {
536  .flags = 0,
537  };
538 
539  idx = encoding->flags & IW_ENCODE_INDEX;
540  if (idx) {
541  if (idx < 1 || idx > WEP_KEYS)
542  return -EINVAL;
543  idx--;
544  } else
545  idx = ieee->crypt_info.tx_keyidx;
546 
547  if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
548  crypt = &ieee->crypt_info.crypt[idx];
549  group_key = 1;
550  } else {
551  /* some Cisco APs use idx>0 for unicast in dynamic WEP */
552  if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
553  return -EINVAL;
554  if (ieee->iw_mode == IW_MODE_INFRA)
555  crypt = &ieee->crypt_info.crypt[idx];
556  else
557  return -EINVAL;
558  }
559 
560  sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
561  if ((encoding->flags & IW_ENCODE_DISABLED) ||
562  ext->alg == IW_ENCODE_ALG_NONE) {
563  if (*crypt)
565 
566  for (i = 0; i < WEP_KEYS; i++)
567  if (ieee->crypt_info.crypt[i] != NULL)
568  break;
569 
570  if (i == WEP_KEYS) {
571  sec.enabled = 0;
572  sec.encrypt = 0;
573  sec.level = SEC_LEVEL_0;
574  sec.flags |= SEC_LEVEL;
575  }
576  goto done;
577  }
578 
579  sec.enabled = 1;
580  sec.encrypt = 1;
581 
582  if (group_key ? !ieee->host_mc_decrypt :
583  !(ieee->host_encrypt || ieee->host_decrypt ||
584  ieee->host_encrypt_msdu))
585  goto skip_host_crypt;
586 
587  switch (ext->alg) {
588  case IW_ENCODE_ALG_WEP:
589  alg = "WEP";
590  module = "lib80211_crypt_wep";
591  break;
592  case IW_ENCODE_ALG_TKIP:
593  alg = "TKIP";
594  module = "lib80211_crypt_tkip";
595  break;
596  case IW_ENCODE_ALG_CCMP:
597  alg = "CCMP";
598  module = "lib80211_crypt_ccmp";
599  break;
600  default:
601  LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
602  dev->name, ext->alg);
603  ret = -EINVAL;
604  goto done;
605  }
606 
607  ops = lib80211_get_crypto_ops(alg);
608  if (ops == NULL) {
609  request_module(module);
610  ops = lib80211_get_crypto_ops(alg);
611  }
612  if (ops == NULL) {
613  LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
614  dev->name, ext->alg);
615  ret = -EINVAL;
616  goto done;
617  }
618 
619  if (*crypt == NULL || (*crypt)->ops != ops) {
620  struct lib80211_crypt_data *new_crypt;
621 
623 
624  new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
625  if (new_crypt == NULL) {
626  ret = -ENOMEM;
627  goto done;
628  }
629  new_crypt->ops = ops;
630  if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
631  new_crypt->priv = new_crypt->ops->init(idx);
632  if (new_crypt->priv == NULL) {
633  kfree(new_crypt);
634  ret = -EINVAL;
635  goto done;
636  }
637  *crypt = new_crypt;
638  }
639 
640  if (ext->key_len > 0 && (*crypt)->ops->set_key &&
641  (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
642  (*crypt)->priv) < 0) {
643  LIBIPW_DEBUG_WX("%s: key setting failed\n", dev->name);
644  ret = -EINVAL;
645  goto done;
646  }
647 
648  skip_host_crypt:
649  if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
650  ieee->crypt_info.tx_keyidx = idx;
651  sec.active_key = idx;
652  sec.flags |= SEC_ACTIVE_KEY;
653  }
654 
655  if (ext->alg != IW_ENCODE_ALG_NONE) {
656  memcpy(sec.keys[idx], ext->key, ext->key_len);
657  sec.key_sizes[idx] = ext->key_len;
658  sec.flags |= (1 << idx);
659  if (ext->alg == IW_ENCODE_ALG_WEP) {
660  sec.encode_alg[idx] = SEC_ALG_WEP;
661  sec.flags |= SEC_LEVEL;
662  sec.level = SEC_LEVEL_1;
663  } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
664  sec.encode_alg[idx] = SEC_ALG_TKIP;
665  sec.flags |= SEC_LEVEL;
666  sec.level = SEC_LEVEL_2;
667  } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
668  sec.encode_alg[idx] = SEC_ALG_CCMP;
669  sec.flags |= SEC_LEVEL;
670  sec.level = SEC_LEVEL_3;
671  }
672  /* Don't set sec level for group keys. */
673  if (group_key)
674  sec.flags &= ~SEC_LEVEL;
675  }
676  done:
677  if (ieee->set_security)
678  ieee->set_security(dev, &sec);
679 
680  return ret;
681 }
682 
684  struct iw_request_info *info,
685  union iwreq_data *wrqu, char *extra)
686 {
687  struct iw_point *encoding = &wrqu->encoding;
688  struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
689  struct libipw_security *sec = &ieee->sec;
690  int idx, max_key_len;
691 
692  max_key_len = encoding->length - sizeof(*ext);
693  if (max_key_len < 0)
694  return -EINVAL;
695 
696  idx = encoding->flags & IW_ENCODE_INDEX;
697  if (idx) {
698  if (idx < 1 || idx > WEP_KEYS)
699  return -EINVAL;
700  idx--;
701  } else
702  idx = ieee->crypt_info.tx_keyidx;
703 
704  if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
705  ext->alg != IW_ENCODE_ALG_WEP)
706  if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
707  return -EINVAL;
708 
709  encoding->flags = idx + 1;
710  memset(ext, 0, sizeof(*ext));
711 
712  if (!sec->enabled) {
713  ext->alg = IW_ENCODE_ALG_NONE;
714  ext->key_len = 0;
715  encoding->flags |= IW_ENCODE_DISABLED;
716  } else {
717  if (sec->encode_alg[idx] == SEC_ALG_WEP)
718  ext->alg = IW_ENCODE_ALG_WEP;
719  else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
720  ext->alg = IW_ENCODE_ALG_TKIP;
721  else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
722  ext->alg = IW_ENCODE_ALG_CCMP;
723  else
724  return -EINVAL;
725 
726  ext->key_len = sec->key_sizes[idx];
727  memcpy(ext->key, sec->keys[idx], ext->key_len);
728  encoding->flags |= IW_ENCODE_ENABLED;
729  if (ext->key_len &&
730  (ext->alg == IW_ENCODE_ALG_TKIP ||
731  ext->alg == IW_ENCODE_ALG_CCMP))
733 
734  }
735 
736  return 0;
737 }
738 
741