blob: b1bf7d3b29dd2b37d89ff1e1e2a4f98e98e7f24a [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001/*
2 * Wireless Tools
3 *
4 * Jean II - HPLB 97->99 - HPL 99->01
5 *
6 * Common subroutines to all the wireless tools...
7 *
8 * This file is released under the GPL license.
9 * Copyright (c) 1997-2002 Jean Tourrilhes <jt@hpl.hp.com>
10 */
11
12/***************************** INCLUDES *****************************/
13
14#include "iwlib.h" /* Header */
15
16#ifdef __ANDROID__
17#include <sys/endian.h>
18#endif
19
20/************************ CONSTANTS & MACROS ************************/
21
22/* Various versions information */
23/* Recommended Wireless Extension version */
24#define WE_VERSION 15
25/* Version of Wireless Tools */
26#define WT_VERSION 25
27
28/*
29 * Verify a few things about Wireless Extensions.
30 * I try to maximise backward and forward compatibility, but things are
31 * tricky because I'm fixing bugs and adding new features.
32 * Wireless Tools *must* be compiled with the same version of WE
33 * as the driver. Sometime, the size or layout of some structure changes,
34 * and might produce interesting results.
35 * Wireless Tools will usually compile properly against different
36 * versions of WE, thanks to the zillions of #ifdefs in my code.
37 * Jean II
38 */
39#if WIRELESS_EXT < 9
40#error "Wireless Extension v9 or newer required :-("
41#error "Use Wireless Tools v19 or update your kernel headers !"
42#endif
43#if WIRELESS_EXT < WE_VERSION && !defined(WEXT_HEADER)
44#warning "Wireless Extension earlier than v15 detected,"
45#warning "Not all tools features will be compiled in !"
46#warning "No worry, I'll try to make the best of it ;-)"
47#endif
48#if WIRELESS_EXT > WE_VERSION && !defined(WEXT_HEADER)
49#warning "Wireless Extension later than v15 detected,"
50#warning "Maybe you should get a more recent version"
51#warning "of the Wireless Tools package !"
52#endif
53
54/**************************** VARIABLES ****************************/
55
56const char * const iw_operation_mode[] = { "Auto",
57 "Ad-Hoc",
58 "Managed",
59 "Master",
60 "Repeater",
61 "Secondary",
62 "Monitor" };
63
64/* Disable runtime version warning in iw_get_range_info() */
65int iw_ignore_version = 0;
66
67/************************ SOCKET SUBROUTINES *************************/
68
69/*------------------------------------------------------------------*/
70/*
71 * Open a socket.
72 * Depending on the protocol present, open the right socket. The socket
73 * will allow us to talk to the driver.
74 */
75int
76iw_sockets_open(void)
77{
78 static const int families[] = {
79 AF_INET, AF_IPX, AF_AX25, AF_APPLETALK
80 };
81 unsigned int i;
82 int sock;
83
84 /*
85 * Now pick any (exisiting) useful socket family for generic queries
86 * Note : don't open all the socket, only returns when one matches,
87 * all protocols might not be valid.
88 * Workaround by Jim Kaba <jkaba@sarnoff.com>
89 * Note : in 99% of the case, we will just open the inet_sock.
90 * The remaining 1% case are not fully correct...
91 */
92
93 /* Try all families we support */
94 for(i = 0; i < sizeof(families)/sizeof(int); ++i)
95 {
96 /* Try to open the socket, if success returns it */
97 sock = socket(families[i], SOCK_DGRAM, 0);
98 if(sock >= 0)
99 return sock;
100 }
101
102 return -1;
103}
104
105/*------------------------------------------------------------------*/
106/*
107 * Extract the interface name out of /proc/net/wireless or /proc/net/dev.
108 */
109static inline char *
110iw_get_ifname(char * name, /* Where to store the name */
111 int nsize, /* Size of name buffer */
112 char * buf) /* Current position in buffer */
113{
114 char * end;
115
116 /* Skip leading spaces */
117 while(isspace(*buf))
118 buf++;
119
120#ifndef IW_RESTRIC_ENUM
121 /* Get name up to the last ':'. Aliases may contain ':' in them,
122 * but the last one should be the separator */
123 end = strrchr(buf, ':');
124#else
125 /* Get name up to ": "
126 * Note : we compare to ": " to make sure to process aliased interfaces
127 * properly. Doesn't work on /proc/net/dev, because it doesn't guarantee
128 * a ' ' after the ':'*/
129 end = strstr(buf, ": ");
130#endif
131
132 /* Not found ??? To big ??? */
133 if((end == NULL) || (((end - buf) + 1) > nsize))
134 return(NULL);
135
136 /* Copy */
137 memcpy(name, buf, (end - buf));
138 name[end - buf] = '\0';
139
140 return(end + 2);
141}
142
143/*------------------------------------------------------------------*/
144/*
145 * Enumerate devices and call specified routine
146 * The new way just use /proc/net/wireless, so get all wireless interfaces,
147 * whether configured or not. This is the default if available.
148 * The old way use SIOCGIFCONF, so get only configured interfaces (wireless
149 * or not).
150 */
151void
152iw_enum_devices(int skfd,
153 iw_enum_handler fn,
154 char * args[],
155 int count)
156{
157 char buff[1024];
158 FILE * fh;
159 struct ifconf ifc;
160 struct ifreq *ifr;
161 int i;
162
163#ifndef IW_RESTRIC_ENUM
164 /* Check if /proc/net/wireless is available */
165 fh = fopen(PROC_NET_DEV, "r");
166#else
167 /* Check if /proc/net/wireless is available */
168 fh = fopen(PROC_NET_WIRELESS, "r");
169#endif
170
171 if(fh != NULL)
172 {
173 /* Success : use data from /proc/net/wireless */
174
175 /* Eat 2 lines of header */
176 fgets(buff, sizeof(buff), fh);
177 fgets(buff, sizeof(buff), fh);
178
179 /* Read each device line */
180 while(fgets(buff, sizeof(buff), fh))
181 {
182 char name[IFNAMSIZ + 1];
183 char *s;
184
185 /* Extract interface name */
186 s = iw_get_ifname(name, sizeof(name), buff);
187
188 if(!s)
189 /* Failed to parse, complain and continue */
190 fprintf(stderr, "Cannot parse " PROC_NET_WIRELESS "\n");
191 else
192 /* Got it, print info about this interface */
193 (*fn)(skfd, name, args, count);
194 }
195
196 fclose(fh);
197 }
198 else
199 {
200 /* Get list of configured devices using "traditional" way */
201 ifc.ifc_len = sizeof(buff);
202 ifc.ifc_buf = buff;
203 if(ioctl(skfd, SIOCGIFCONF, &ifc) < 0)
204 {
205 fprintf(stderr, "SIOCGIFCONF: %s\n", strerror(errno));
206 return;
207 }
208 ifr = ifc.ifc_req;
209
210 /* Print them */
211 for(i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; ifr++)
212 (*fn)(skfd, ifr->ifr_name, args, count);
213 }
214}
215
216/*********************** WIRELESS SUBROUTINES ************************/
217
218/*------------------------------------------------------------------*/
219/*
220 * Get the range information out of the driver
221 */
222int
223iw_get_range_info(int skfd,
224 char * ifname,
225 iwrange * range)
226{
227 struct iwreq wrq;
228 char buffer[sizeof(iwrange) * 2]; /* Large enough */
229
230 /* Cleanup */
231 memset(buffer, 0, sizeof(buffer));
232
233 wrq.u.data.pointer = (caddr_t) buffer;
234 wrq.u.data.length = sizeof(buffer);
235 wrq.u.data.flags = 0;
236 if(iw_get_ext(skfd, ifname, SIOCGIWRANGE, &wrq) < 0)
237 return(-1);
238
239 /* Copy stuff at the right place, ignore extra */
240 memcpy((char *) range, buffer, sizeof(iwrange));
241
242 /* Lots of people have driver and tools out of sync as far as Wireless
243 * Extensions are concerned. It's because /usr/include/linux/wireless.h
244 * and /usr/src/linux/include/linux/wireless.h are different.
245 * We try to catch this stuff here... */
246 if(!iw_ignore_version)
247 {
248 /* For new versions, we can check the version directly, for old versions
249 * we use magic. 300 bytes is a also magic number, don't touch... */
250 if((WIRELESS_EXT > 10) && (wrq.u.data.length >= 300))
251 {
252#if WIRELESS_EXT > 10
253 /* Version verification - for new versions */
254 if(range->we_version_compiled != WIRELESS_EXT)
255 {
256 fprintf(stderr, "Warning: Driver for device %s has been compiled with version %d\n", ifname, range->we_version_compiled);
257 fprintf(stderr, "of Wireless Extension, while this program is using version %d.\n", WIRELESS_EXT);
258 fprintf(stderr, "Some things may be broken...\n\n");
259 }
260 /* Driver version verification */
261 if(range->we_version_compiled < range->we_version_source)
262 {
263 fprintf(stderr, "Warning: Driver for device %s recommend version %d of Wireless Extension,\n", ifname, range->we_version_source);
264 fprintf(stderr, "but has been compiled with version %d, therefore some driver features\n", range->we_version_compiled);
265 fprintf(stderr, "may not be available...\n\n");
266 }
267#endif /* WIRELESS_EXT > 10 */
268 }
269 else
270 {
271 /* Version verification - for old versions */
272 if(wrq.u.data.length != sizeof(iwrange))
273 {
274 fprintf(stderr, "Warning: Driver for device %s has been compiled with an ancient version\n", ifname);
275 fprintf(stderr, "of Wireless Extension, while this program is using version %d.\n", WIRELESS_EXT);
276 fprintf(stderr, "Some things may be broken...\n\n");
277 }
278 }
279 }
280 /* Don't complain twice.
281 * In theory, the test apply to each individual driver, but usually
282 * all drivers are compiled from the same kernel, and most often
283 * problem is the system/glibc headers. */
284 iw_ignore_version = 1;
285
286 /* Note : we are only trying to catch compile difference, not source.
287 * If the driver source has not been updated to the latest, it doesn't
288 * matter because the new fields are set to zero */
289
290 return(0);
291}
292
293/*------------------------------------------------------------------*/
294/*
295 * Print the WE versions of the interface.
296 */
297static int
298print_iface_version_info(int skfd,
299 char * ifname,
300 char * args[], /* Command line args */
301 int count) /* Args count */
302{
303 struct iwreq wrq;
304 char buffer[sizeof(iwrange) * 2]; /* Large enough */
305 struct iw_range * range;
306
307 /* Avoid "Unused parameter" warning */
308 args = args; count = count;
309
310 /* Cleanup */
311 memset(buffer, 0, sizeof(buffer));
312
313 wrq.u.data.pointer = (caddr_t) buffer;
314 wrq.u.data.length = sizeof(buffer);
315 wrq.u.data.flags = 0;
316 if(iw_get_ext(skfd, ifname, SIOCGIWRANGE, &wrq) < 0)
317 return(-1);
318
319 /* Copy stuff at the right place, ignore extra */
320 range = (struct iw_range *) buffer;
321
322 /* For new versions, we can check the version directly, for old versions
323 * we use magic. 300 bytes is a also magic number, don't touch... */
324 if((WIRELESS_EXT > 10) && (wrq.u.data.length >= 300))
325 {
326#if WIRELESS_EXT > 10
327 printf("%-8.8s Recommend Wireless Extension v%d or later,\n",
328 ifname, range->we_version_source);
329 printf(" Currently compiled with Wireless Extension v%d.\n\n",
330 range->we_version_compiled);
331#endif /* WIRELESS_EXT > 10 */
332 }
333 else
334 {
335#if 0
336 fprintf(stderr, "%-8.8s no Wireless Extension version information.\n\n",
337 ifname);
338#endif
339 }
340
341
342 return(0);
343}
344
345/*------------------------------------------------------------------*/
346/*
347 * Print the WE versions of the tools.
348 */
349int
350iw_print_version_info(char * toolname)
351{
352 int skfd; /* generic raw socket desc. */
353
354 /* Create a channel to the NET kernel. */
355 if((skfd = iw_sockets_open()) < 0)
356 {
357 perror("socket");
358 return -1;
359 }
360
361 /* Information about the tools themselves */
362 if(toolname != NULL)
363 printf("%-8.8s Version %d\n", toolname, WT_VERSION);
364 printf(" Compatible with Wireless Extension v%d or earlier,\n",
365 WE_VERSION);
366 printf(" Currently compiled with Wireless Extension v%d.\n\n",
367 WIRELESS_EXT);
368
369 /* Version for each device */
370 iw_enum_devices(skfd, &print_iface_version_info, NULL, 0);
371
372 close(skfd);
373
374 return 0;
375}
376
377/*------------------------------------------------------------------*/
378/*
379 * Get information about what private ioctls are supported by the driver
380 */
381int
382iw_get_priv_info(int skfd,
383 char * ifname,
384 iwprivargs * priv,
385 int maxpriv)
386{
387 struct iwreq wrq;
388
389 /* Ask the driver */
390 wrq.u.data.pointer = (caddr_t) priv;
391 wrq.u.data.length = maxpriv;
392 wrq.u.data.flags = 0;
393
394 //printf("iw_get_priv_info pointer=%08x length=%d\n",wrq.u.data.pointer,wrq.u.data.length);
395
396 if(iw_get_ext(skfd, ifname, SIOCGIWPRIV, &wrq) < 0)
397 return(-1);
398
399 //printf("iw_get_priv_info wrq.u.data.length=%d\n",wrq.u.data.length);
400
401 /* Return the number of ioctls */
402 return(wrq.u.data.length);
403}
404
405/*------------------------------------------------------------------*/
406/*
407 * Get essential wireless config from the device driver
408 * We will call all the classical wireless ioctl on the driver through
409 * the socket to know what is supported and to get the settings...
410 * Note : compare to the version in iwconfig, we extract only
411 * what's *really* needed to configure a device...
412 */
413int
414iw_get_basic_config(int skfd,
415 char * ifname,
416 wireless_config * info)
417{
418 struct iwreq wrq;
419
420 memset((char *) info, 0, sizeof(struct wireless_config));
421
422 /* Get wireless name */
423 if(iw_get_ext(skfd, ifname, SIOCGIWNAME, &wrq) < 0)
424 /* If no wireless name : no wireless extensions */
425 return(-1);
426 else
427 {
428 strncpy(info->name, wrq.u.name, IFNAMSIZ);
429 info->name[IFNAMSIZ] = '\0';
430 }
431
432 /* Get network ID */
433 if(iw_get_ext(skfd, ifname, SIOCGIWNWID, &wrq) >= 0)
434 {
435 info->has_nwid = 1;
436 memcpy(&(info->nwid), &(wrq.u.nwid), sizeof(iwparam));
437 }
438
439 /* Get frequency / channel */
440 if(iw_get_ext(skfd, ifname, SIOCGIWFREQ, &wrq) >= 0)
441 {
442 info->has_freq = 1;
443 info->freq = iw_freq2float(&(wrq.u.freq));
444 }
445
446 /* Get encryption information */
447 wrq.u.data.pointer = (caddr_t) info->key;
448 wrq.u.data.length = IW_ENCODING_TOKEN_MAX;
449 wrq.u.data.flags = 0;
450 if(iw_get_ext(skfd, ifname, SIOCGIWENCODE, &wrq) >= 0)
451 {
452 info->has_key = 1;
453 info->key_size = wrq.u.data.length;
454 info->key_flags = wrq.u.data.flags;
455 }
456
457 /* Get ESSID */
458 wrq.u.essid.pointer = (caddr_t) info->essid;
459 wrq.u.essid.length = IW_ESSID_MAX_SIZE + 1;
460 wrq.u.essid.flags = 0;
461 if(iw_get_ext(skfd, ifname, SIOCGIWESSID, &wrq) >= 0)
462 {
463 info->has_essid = 1;
464 info->essid_on = wrq.u.data.flags;
465 }
466
467 /* Get operation mode */
468 if(iw_get_ext(skfd, ifname, SIOCGIWMODE, &wrq) >= 0)
469 {
470 info->mode = wrq.u.mode;
471 if((info->mode < 6) && (info->mode >= 0))
472 info->has_mode = 1;
473 }
474
475 return(0);
476}
477
478/*------------------------------------------------------------------*/
479/*
480 * Set essential wireless config in the device driver
481 * We will call all the classical wireless ioctl on the driver through
482 * the socket to know what is supported and to set the settings...
483 * We support only the restricted set as above...
484 */
485int
486iw_set_basic_config(int skfd,
487 char * ifname,
488 wireless_config * info)
489{
490 struct iwreq wrq;
491 int ret = 0;
492
493 /* Get wireless name (check if interface is valid) */
494 if(iw_get_ext(skfd, ifname, SIOCGIWNAME, &wrq) < 0)
495 /* If no wireless name : no wireless extensions */
496 return(-2);
497
498 /* Set Network ID, if available (this is for non-802.11 cards) */
499 if(info->has_nwid)
500 {
501 memcpy(&(wrq.u.nwid), &(info->nwid), sizeof(iwparam));
502 wrq.u.nwid.fixed = 1; /* Hum... When in Rome... */
503
504 if(iw_set_ext(skfd, ifname, SIOCSIWNWID, &wrq) < 0)
505 {
506 fprintf(stderr, "SIOCSIWNWID: %s\n", strerror(errno));
507 ret = -1;
508 }
509 }
510
511 /* Set frequency / channel */
512 if(info->has_freq)
513 {
514 iw_float2freq(info->freq, &(wrq.u.freq));
515
516 if(iw_set_ext(skfd, ifname, SIOCSIWFREQ, &wrq) < 0)
517 {
518 fprintf(stderr, "SIOCSIWFREQ: %s\n", strerror(errno));
519 ret = -1;
520 }
521 }
522
523 /* Set encryption information */
524 if(info->has_key)
525 {
526 int flags = info->key_flags;
527
528 /* Check if there is a key index */
529 if((flags & IW_ENCODE_INDEX) > 0)
530 {
531 /* Set the index */
532 wrq.u.data.pointer = (caddr_t) NULL;
533 wrq.u.data.flags = (flags & (IW_ENCODE_INDEX)) | IW_ENCODE_NOKEY;
534 wrq.u.data.length = 0;
535
536 if(iw_set_ext(skfd, ifname, SIOCSIWENCODE, &wrq) < 0)
537 {
538 fprintf(stderr, "SIOCSIWENCODE(%d): %s\n",
539 errno, strerror(errno));
540 ret = -1;
541 }
542 }
543
544 /* Mask out index to minimise probability of reject when setting key */
545 flags = flags & (~IW_ENCODE_INDEX);
546
547 /* Set the key itself (set current key in this case) */
548 wrq.u.data.pointer = (caddr_t) info->key;
549 wrq.u.data.length = info->key_size;
550 wrq.u.data.flags = flags;
551
552 if(iw_set_ext(skfd, ifname, SIOCSIWENCODE, &wrq) < 0)
553 {
554 fprintf(stderr, "SIOCSIWENCODE(%d): %s\n",
555 errno, strerror(errno));
556 ret = -1;
557 }
558 }
559
560 /* Set ESSID (extended network), if available */
561 if(info->has_essid)
562 {
563 wrq.u.essid.pointer = (caddr_t) info->essid;
564 wrq.u.essid.length = strlen(info->essid) + 1;
565 wrq.u.data.flags = info->essid_on;
566
567 if(iw_set_ext(skfd, ifname, SIOCSIWESSID, &wrq) < 0)
568 {
569 fprintf(stderr, "SIOCSIWESSID: %s\n", strerror(errno));
570 ret = -1;
571 }
572 }
573
574 /* Set the current mode of operation */
575 if(info->has_mode)
576 {
577 strncpy(wrq.ifr_name, ifname, IFNAMSIZ-1);
578 wrq.ifr_name[IFNAMSIZ-1] = '\0';
579 wrq.u.mode = info->mode;
580
581 if(iw_get_ext(skfd, ifname, SIOCSIWMODE, &wrq) < 0)
582 {
583 fprintf(stderr, "SIOCSIWMODE: %s\n", strerror(errno));
584 ret = -1;
585 }
586 }
587
588 return(ret);
589}
590
591/*********************** PROTOCOL SUBROUTINES ***********************/
592/*
593 * Fun stuff with protocol identifiers (SIOCGIWNAME).
594 * We assume that drivers are returning sensible values in there,
595 * which is not always the case :-(
596 */
597
598/*------------------------------------------------------------------*/
599/*
600 * Compare protocol identifiers.
601 * We don't want to know if the two protocols are the exactly same,
602 * but if they interoperate at some level, and also if they accept the
603 * same type of config (ESSID vs NWID, freq...).
604 * This is supposed to work around the alphabet soup.
605 * Return 1 if protocols are compatible
606 */
607int
608iw_protocol_compare(char * protocol1,
609 char * protocol2)
610{
611 char * dot11 = "IEEE 802.11";
612 char * dot11_ds = "Dbg";
613
614 /* If the strings are the same -> easy */
615 if(!strncmp(protocol1, protocol2, IFNAMSIZ))
616 return(1);
617
618 /* Are we dealing with one of the 802.11 variant ? */
619 if( (!strncmp(protocol1, dot11, strlen(dot11))) &&
620 (!strncmp(protocol2, dot11, strlen(dot11))) )
621 {
622 char * sub1 = protocol1 + strlen(dot11);
623 char * sub2 = protocol2 + strlen(dot11);
624
625 /* Skip optional separator */
626 if(*sub1 == '-')
627 sub1++;
628 if(*sub2 == '-')
629 sub2++;
630
631 /* Check if they are both 2.4 GHz Direct Sequence compatible */
632 if( (strchr(dot11_ds, *sub1) != NULL) &&
633 (strchr(dot11_ds, *sub2) != NULL) )
634 return(1);
635 }
636 /* Not compatible */
637 return(0);
638}
639
640/********************** FREQUENCY SUBROUTINES ***********************/
641/*
642 * Note : the two functions below are the cause of troubles on
643 * various embeeded platforms, as they are the reason we require
644 * libm (math library).
645 * In this case, please use enable BUILD_NOLIBM in the makefile
646 *
647 * FIXME : check negative mantissa and exponent
648 */
649
650/*------------------------------------------------------------------*/
651/*
652 * Convert a floating point the our internal representation of
653 * frequencies.
654 * The kernel doesn't want to hear about floating point, so we use
655 * this custom format instead.
656 */
657void
658iw_float2freq(double in,
659 iwfreq * out)
660{
661 /* Version without libm : slower */
662 out->e = 0;
663 while(in > 1e9)
664 {
665 in /= 10;
666 out->e++;
667 }
668 out->m = (long) in;
669}
670
671/*------------------------------------------------------------------*/
672/*
673 * Convert our internal representation of frequencies to a floating point.
674 */
675double
676iw_freq2float(iwfreq * in)
677{
678 /* Version without libm : slower */
679 int i;
680 double res = (double) in->m;
681 for(i = 0; i < in->e; i++)
682 res *= 10;
683 return(res);
684}
685
686/*------------------------------------------------------------------*/
687/*
688 * Output a frequency with proper scaling
689 */
690void
691iw_print_freq(char * buffer,
692 double freq)
693{
694 if(freq < KILO)
695 sprintf(buffer, "Channel:%g", freq);
696 else
697 {
698 if(freq >= GIGA)
699 sprintf(buffer, "Frequency:%gGHz", freq / GIGA);
700 else
701 {
702 if(freq >= MEGA)
703 sprintf(buffer, "Frequency:%gMHz", freq / MEGA);
704 else
705 sprintf(buffer, "Frequency:%gkHz", freq / KILO);
706 }
707 }
708}
709
710/*------------------------------------------------------------------*/
711/*
712 * Convert a frequency to a channel (negative -> error)
713 */
714int
715iw_freq_to_channel(double freq,
716 struct iw_range * range)
717{
718 double ref_freq;
719 int k;
720
721 /* Check if it's a frequency or not already a channel */
722 if(freq < KILO)
723 return(-1);
724
725 /* We compare the frequencies as double to ignore differences
726 * in encoding. Slower, but safer... */
727 for(k = 0; k < range->num_frequency; k++)
728 {
729 ref_freq = iw_freq2float(&(range->freq[k]));
730 if(freq == ref_freq)
731 return(range->freq[k].i);
732 }
733 /* Not found */
734 return(-2);
735}
736
737/*********************** BITRATE SUBROUTINES ***********************/
738
739/*------------------------------------------------------------------*/
740/*
741 * Output a bitrate with proper scaling
742 */
743void
744iw_print_bitrate(char * buffer,
745 int bitrate)
746{
747 double rate = bitrate;
748
749 if(rate >= GIGA)
750 sprintf(buffer, "%gGb/s", rate / GIGA);
751 else
752 if(rate >= MEGA)
753 sprintf(buffer, "%gMb/s", rate / MEGA);
754 else
755 sprintf(buffer, "%gkb/s", rate / KILO);
756}
757
758/************************ POWER SUBROUTINES *************************/
759
760/*------------------------------------------------------------------*/
761#ifndef __ANDROID__
762/*
763 * Convert a value in dBm to a value in milliWatt.
764 */
765int
766iw_dbm2mwatt(int in)
767{
768 //return((int) (floor(pow(10.0, (((double) in) / 10.0)))));
769}
770
771/*------------------------------------------------------------------*/
772/*
773 * Convert a value in milliWatt to a value in dBm.
774 */
775int
776iw_mwatt2dbm(int in)
777{
778// return((int) (ceil(10.0 * log10((double) in))));
779}
780#endif
781/********************** STATISTICS SUBROUTINES **********************/
782
783/*------------------------------------------------------------------*/
784/*
785 * Read /proc/net/wireless to get the latest statistics
786 */
787int
788iw_get_stats(int skfd,
789 char * ifname,
790 iwstats * stats)
791{
792#if WIRELESS_EXT > 11
793 struct iwreq wrq;
794 wrq.u.data.pointer = (caddr_t) stats;
795 wrq.u.data.length = 0;
796 wrq.u.data.flags = 1; /* Clear updated flag */
797 strncpy(wrq.ifr_name, ifname, IFNAMSIZ-1);
798 wrq.ifr_name[IFNAMSIZ-1] = '\0';
799 if(iw_get_ext(skfd, ifname, SIOCGIWSTATS, &wrq) < 0)
800 return(-1);
801
802 return(0);
803#else /* WIRELESS_EXT > 11 */
804 FILE * f = fopen(PROC_NET_WIRELESS, "r");
805 char buf[256];
806 char * bp;
807 int t;
808 if(f==NULL)
809 return -1;
810 /* Loop on all devices */
811 while(fgets(buf,255,f))
812 {
813 bp=buf;
814 while(*bp&&isspace(*bp))
815 bp++;
816 /* Is it the good device ? */
817 if(strncmp(bp,ifname,strlen(ifname))==0 && bp[strlen(ifname)]==':')
818 {
819 /* Skip ethX: */
820 bp=strchr(bp,':');
821 bp++;
822 /* -- status -- */
823 bp = strtok(bp, " ");
824 sscanf(bp, "%X", &t);
825 stats->status = (unsigned short) t;
826 /* -- link quality -- */
827 bp = strtok(NULL, " ");
828 if(strchr(bp,'.') != NULL)
829 stats->qual.updated |= 1;
830 sscanf(bp, "%d", &t);
831 stats->qual.qual = (unsigned char) t;
832 /* -- signal level -- */
833 bp = strtok(NULL, " ");
834 if(strchr(bp,'.') != NULL)
835 stats->qual.updated |= 2;
836 sscanf(bp, "%d", &t);
837 stats->qual.level = (unsigned char) t;
838 /* -- noise level -- */
839 bp = strtok(NULL, " ");
840 if(strchr(bp,'.') != NULL)
841 stats->qual.updated += 4;
842 sscanf(bp, "%d", &t);
843 stats->qual.noise = (unsigned char) t;
844 /* -- discarded packets -- */
845 bp = strtok(NULL, " ");
846 sscanf(bp, "%d", &stats->discard.nwid);
847 bp = strtok(NULL, " ");
848 sscanf(bp, "%d", &stats->discard.code);
849 bp = strtok(NULL, " ");
850 sscanf(bp, "%d", &stats->discard.misc);
851 fclose(f);
852 return 0;
853 }
854 }
855 fclose(f);
856 return -1;
857#endif /* WIRELESS_EXT > 11 */
858}
859
860/*------------------------------------------------------------------*/
861/*
862 * Output the link statistics, taking care of formating
863 */
864void
865iw_print_stats(char * buffer,
866 iwqual * qual,
867 iwrange * range,
868 int has_range)
869{
870 /* Just do it */
871 if(has_range && (qual->level != 0))
872 {
873 /* If the statistics are in dBm */
874 if(qual->level > range->max_qual.level)
875 {
876 /* Statistics are in dBm (absolute power measurement) */
877 sprintf(buffer,
878 "Quality:%d/%d Signal level:%d dBm Noise level:%d dBm%s",
879 qual->qual, range->max_qual.qual,
880 qual->level - 0x100, qual->noise - 0x100,
881 (qual->updated & 0x7) ? " (updated)" : "");
882 }
883 else
884 {
885 /* Statistics are relative values (0 -> max) */
886 sprintf(buffer,
887 "Quality:%d/%d Signal level:%d/%d Noise level:%d/%d%s",
888 qual->qual, range->max_qual.qual,
889 qual->level, range->max_qual.level,
890 qual->noise, range->max_qual.noise,
891 (qual->updated & 0x7) ? " (updated)" : "");
892 }
893 }
894 else
895 {
896 /* We can't read the range, so we don't know... */
897 sprintf(buffer, "Quality:%d Signal level:%d Noise level:%d%s",
898 qual->qual, qual->level, qual->noise,
899 (qual->updated & 0x7) ? " (updated)" : "");
900 }
901}
902
903/*********************** ENCODING SUBROUTINES ***********************/
904
905/*------------------------------------------------------------------*/
906/*
907 * Output the encoding key, with a nice formating
908 */
909void
910iw_print_key(char * buffer,
911 unsigned char * key,
912 int key_size,
913 int key_flags)
914{
915 int i;
916
917 /* Is the key present ??? */
918 if(key_flags & IW_ENCODE_NOKEY)
919 {
920 /* Nope : print on or dummy */
921 if(key_size <= 0)
922 strcpy(buffer, "on");
923 else
924 {
925 strcpy(buffer, "**");
926 buffer +=2;
927 for(i = 1; i < key_size; i++)
928 {
929 if((i & 0x1) == 0)
930 strcpy(buffer++, "-");
931 strcpy(buffer, "**");
932 buffer +=2;
933 }
934 }
935 }
936 else
937 {
938 /* Yes : print the key */
939 sprintf(buffer, "%.2X", key[0]);
940 buffer +=2;
941 for(i = 1; i < key_size; i++)
942 {
943 if((i & 0x1) == 0)
944 strcpy(buffer++, "-");
945 sprintf(buffer, "%.2X", key[i]);
946 buffer +=2;
947 }
948 }
949}
950
951/*------------------------------------------------------------------*/
952/*
953 * Convert a passphrase into a key
954 * ### NOT IMPLEMENTED ###
955 * Return size of the key, or 0 (no key) or -1 (error)
956 */
957int
958iw_pass_key(char * input,
959 unsigned char * key)
960{
961 input = input; key = key;
962 fprintf(stderr, "Error: Passphrase not implemented\n");
963 return(-1);
964}
965
966/*------------------------------------------------------------------*/
967/*
968 * Parse a key from the command line.
969 * Return size of the key, or 0 (no key) or -1 (error)
970 */
971int
972iw_in_key(char * input,
973 unsigned char * key)
974{
975 int keylen = 0;
976 char * buff;
977 char * p;
978 int temp;
979
980 /* Check the type of key */
981 if(!strncmp(input, "s:", 2))
982 {
983 /* First case : as an ASCII string (Lucent/Agere cards) */
984 keylen = strlen(input + 2); /* skip "s:" */
985 if(keylen > IW_ENCODING_TOKEN_MAX)
986 keylen = IW_ENCODING_TOKEN_MAX;
987 strncpy((char *)key, input + 2, keylen);
988 }
989 else
990 if(!strncmp(input, "p:", 2))
991 {
992 /* Second case : as a passphrase (PrismII cards) */
993 return(iw_pass_key(input + 2, key)); /* skip "p:" */
994 }
995 else
996 {
997 /* Third case : as hexadecimal digits */
998 buff = malloc(strlen(input) + 1);
999 if(buff == NULL)
1000 {
1001 fprintf(stderr, "Malloc failed (string too long ?)\n");
1002 return(-1);
1003 }
1004 /* Preserve original buffer */
1005 strcpy(buff, input);
1006
1007 /* Parse */
1008 p = strtok(buff, "-:;.,");
1009 while((p != (char *) NULL) && (keylen < IW_ENCODING_TOKEN_MAX))
1010 {
1011 if(sscanf(p, "%2X", &temp) != 1)
1012 return(-1); /* Error */
1013 key[keylen++] = (unsigned char) (temp & 0xFF);
1014 if(strlen(p) > 2) /* Token not finished yet */
1015 p += 2;
1016 else
1017 p = strtok((char *) NULL, "-:;.,");
1018 }
1019 free(buff);
1020 }
1021
1022 return(keylen);
1023}
1024
1025/******************* POWER MANAGEMENT SUBROUTINES *******************/
1026
1027/*------------------------------------------------------------------*/
1028/*
1029 * Output a power management value with all attributes...
1030 */
1031void
1032iw_print_pm_value(char * buffer,
1033 int value,
1034 int flags)
1035{
1036 /* Modifiers */
1037 if(flags & IW_POWER_MIN)
1038 {
1039 strcpy(buffer, " min");
1040 buffer += 4;
1041 }
1042 if(flags & IW_POWER_MAX)
1043 {
1044 strcpy(buffer, " max");
1045 buffer += 4;
1046 }
1047
1048 /* Type */
1049 if(flags & IW_POWER_TIMEOUT)
1050 {
1051 strcpy(buffer, " timeout:");
1052 buffer += 9;
1053 }
1054 else
1055 {
1056 strcpy(buffer, " period:");
1057 buffer += 8;
1058 }
1059
1060 /* Display value without units */
1061 if(flags & IW_POWER_RELATIVE)
1062 sprintf(buffer, "%g", ((double) value) / MEGA);
1063 else
1064 {
1065 /* Display value with units */
1066 if(value >= (int) MEGA)
1067 sprintf(buffer, "%gs", ((double) value) / MEGA);
1068 else
1069 if(value >= (int) KILO)
1070 sprintf(buffer, "%gms", ((double) value) / KILO);
1071 else
1072 sprintf(buffer, "%dus", value);
1073 }
1074}
1075
1076/*------------------------------------------------------------------*/
1077/*
1078 * Output a power management mode
1079 */
1080void
1081iw_print_pm_mode(char * buffer,
1082 int flags)
1083{
1084 /* Print the proper mode... */
1085 switch(flags & IW_POWER_MODE)
1086 {
1087 case IW_POWER_UNICAST_R:
1088 strcpy(buffer, "mode:Unicast only received");
1089 break;
1090 case IW_POWER_MULTICAST_R:
1091 strcpy(buffer, "mode:Multicast only received");
1092 break;
1093 case IW_POWER_ALL_R:
1094 strcpy(buffer, "mode:All packets received");
1095 break;
1096 case IW_POWER_FORCE_S:
1097 strcpy(buffer, "mode:Force sending");
1098 break;
1099 case IW_POWER_REPEATER:
1100 strcpy(buffer, "mode:Repeat multicasts");
1101 break;
1102 default:
1103 break;
1104 }
1105}
1106
1107/***************** RETRY LIMIT/LIFETIME SUBROUTINES *****************/
1108
1109#if WIRELESS_EXT > 10
1110/*------------------------------------------------------------------*/
1111/*
1112 * Output a retry value with all attributes...
1113 */
1114void
1115iw_print_retry_value(char * buffer,
1116 int value,
1117 int flags)
1118{
1119 /* Modifiers */
1120 if(flags & IW_RETRY_MIN)
1121 {
1122 strcpy(buffer, " min");
1123 buffer += 4;
1124 }
1125 if(flags & IW_RETRY_MAX)
1126 {
1127 strcpy(buffer, " max");
1128 buffer += 4;
1129 }
1130
1131 /* Type lifetime of limit */
1132 if(flags & IW_RETRY_LIFETIME)
1133 {
1134 strcpy(buffer, " lifetime:");
1135 buffer += 10;
1136
1137 /* Display value without units */
1138 if(flags & IW_POWER_RELATIVE)
1139 sprintf(buffer, "%g", ((double) value) / MEGA);
1140 else
1141 {
1142 /* Display value with units */
1143 if(value >= (int) MEGA)
1144 sprintf(buffer, "%gs", ((double) value) / MEGA);
1145 else
1146 if(value >= (int) KILO)
1147 sprintf(buffer, "%gms", ((double) value) / KILO);
1148 else
1149 sprintf(buffer, "%dus", value);
1150 }
1151 }
1152 else
1153 sprintf(buffer, " limit:%d", value);
1154}
1155#endif /* WIRELESS_EXT > 10 */
1156
1157/************************* TIME SUBROUTINES *************************/
1158
1159/*------------------------------------------------------------------*/
1160/*
1161 * Print timestamps
1162 * Inspired from irdadump...
1163 */
1164void
1165iw_print_timeval(char * buffer,
1166 const struct timeval * time)
1167{
1168 int s;
1169
1170 s = (time->tv_sec) % 86400;
1171 sprintf(buffer, "%02d:%02d:%02d.%06u ",
1172 s / 3600, (s % 3600) / 60,
1173 s % 60, (u_int32_t) time->tv_usec);
1174}
1175
1176/*********************** ADDRESS SUBROUTINES ************************/
1177/*
1178 * This section is mostly a cut & past from net-tools-1.2.0
1179 * manage address display and input...
1180 */
1181
1182/*------------------------------------------------------------------*/
1183/*
1184 * Check if interface support the right MAC address type...
1185 */
1186int
1187iw_check_mac_addr_type(int skfd,
1188 char * ifname)
1189{
1190 struct ifreq ifr;
1191
1192 /* Get the type of hardware address */
1193 strncpy(ifr.ifr_name, ifname, IFNAMSIZ-1);
1194 ifr.ifr_name[IFNAMSIZ-1] = '\0';
1195 if((ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0) ||
1196 (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER))
1197 {
1198 /* Deep trouble... */
1199 fprintf(stderr, "Interface %s doesn't support MAC addresses\n",
1200 ifname);
1201 return(-1);
1202 }
1203
1204#ifdef DEBUG
1205 printf("Hardware : %d - %s\n", ifr.ifr_hwaddr.sa_family,
1206 iw_ether_ntoa((struct ether_addr *) ifr.ifr_hwaddr.sa_data));
1207#endif
1208
1209 return(0);
1210}
1211
1212
1213/*------------------------------------------------------------------*/
1214/*
1215 * Check if interface support the right interface address type...
1216 */
1217int
1218iw_check_if_addr_type(int skfd,
1219 char * ifname)
1220{
1221 struct ifreq ifr;
1222
1223 /* Get the type of interface address */
1224 strncpy(ifr.ifr_name, ifname, IFNAMSIZ-1);
1225 ifr.ifr_name[IFNAMSIZ-1] = '\0';
1226 if((ioctl(skfd, SIOCGIFADDR, &ifr) < 0) ||
1227 (ifr.ifr_addr.sa_family != AF_INET))
1228 {
1229 /* Deep trouble... */
1230 fprintf(stderr, "Interface %s doesn't support IP addresses\n", ifname);
1231 return(-1);
1232 }
1233
1234#ifdef DEBUG
1235 printf("Interface : %d - 0x%lX\n", ifr.ifr_addr.sa_family,
1236 *((unsigned long *) ifr.ifr_addr.sa_data));
1237#endif
1238
1239 return(0);
1240}
1241
1242#if 0
1243/*------------------------------------------------------------------*/
1244/*
1245 * Check if interface support the right address types...
1246 */
1247int
1248iw_check_addr_type(int skfd,
1249 char * ifname)
1250{
1251 /* Check the interface address type */
1252 if(iw_check_if_addr_type(skfd, ifname) < 0)
1253 return(-1);
1254
1255 /* Check the interface address type */
1256 if(iw_check_mac_addr_type(skfd, ifname) < 0)
1257 return(-1);
1258
1259 return(0);
1260}
1261#endif
1262
1263/*------------------------------------------------------------------*/
1264/*
1265 * Display an Ethernet address in readable format.
1266 */
1267void
1268iw_ether_ntop(const struct ether_addr* eth, char* buf)
1269{
1270 sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X",
1271 eth->ether_addr_octet[0], eth->ether_addr_octet[1],
1272 eth->ether_addr_octet[2], eth->ether_addr_octet[3],
1273 eth->ether_addr_octet[4], eth->ether_addr_octet[5]);
1274}
1275
1276/*------------------------------------------------------------------*/
1277/*
1278 * Display an Ethernet address in readable format.
1279 * Same with a static buffer
1280 */
1281char *
1282iw_ether_ntoa(const struct ether_addr* eth)
1283{
1284 static char buf[20];
1285 iw_ether_ntop(eth, buf);
1286 return buf;
1287}
1288
1289/*------------------------------------------------------------------*/
1290/*
1291 * Input an Ethernet address and convert to binary.
1292 */
1293int
1294iw_ether_aton(const char *orig, struct ether_addr *eth)
1295{
1296 const char *bufp;
1297 int i;
1298
1299 i = 0;
1300 for(bufp = orig; *bufp != '\0'; ++bufp) {
1301 unsigned int val;
1302 unsigned char c = *bufp++;
1303 if (isdigit(c)) val = c - '0';
1304 else if (c >= 'a' && c <= 'f') val = c - 'a' + 10;
1305 else if (c >= 'A' && c <= 'F') val = c - 'A' + 10;
1306 else break;
1307
1308 val <<= 4;
1309 c = *bufp++;
1310 if (isdigit(c)) val |= c - '0';
1311 else if (c >= 'a' && c <= 'f') val |= c - 'a' + 10;
1312 else if (c >= 'A' && c <= 'F') val |= c - 'A' + 10;
1313 else break;
1314
1315 eth->ether_addr_octet[i] = (unsigned char) (val & 0377);
1316 if(++i == ETH_ALEN) {
1317 /* That's it. Any trailing junk? */
1318 if (*bufp != '\0') {
1319#ifdef DEBUG
1320 fprintf(stderr, "iw_ether_aton(%s): trailing junk!\n", orig);
1321 errno = EINVAL;
1322 return(0);
1323#endif
1324 }
1325#ifdef DEBUG
1326 fprintf(stderr, "iw_ether_aton(%s): %s\n",
1327 orig, ether_ntoa(eth));
1328#endif
1329 return(1);
1330 }
1331 if (*bufp != ':')
1332 break;
1333 }
1334
1335#ifdef DEBUG
1336 fprintf(stderr, "iw_ether_aton(%s): invalid ether address!\n", orig);
1337#endif
1338 errno = EINVAL;
1339 return(0);
1340}
1341
1342
1343/*------------------------------------------------------------------*/
1344/*
1345 * Input an Internet address and convert to binary.
1346 */
1347int
1348iw_in_inet(char *name, struct sockaddr *sap)
1349{
1350 struct hostent *hp;
1351 struct netent *np;
1352 struct sockaddr_in *sin = (struct sockaddr_in *) sap;
1353
1354 /* Grmpf. -FvK */
1355 sin->sin_family = AF_INET;
1356 sin->sin_port = 0;
1357
1358 /* Default is special, meaning 0.0.0.0. */
1359 if (!strcmp(name, "default")) {
1360 sin->sin_addr.s_addr = INADDR_ANY;
1361 return(1);
1362 }
1363
1364 /* Try the NETWORKS database to see if this is a known network. */
1365 if ((np = getnetbyname(name)) != (struct netent *)NULL) {
1366 sin->sin_addr.s_addr = htonl(np->n_net);
1367 strcpy(name, np->n_name);
1368 return(1);
1369 }
1370
1371 /* Always use the resolver (DNS name + IP addresses) */
1372 if ((hp = gethostbyname(name)) == (struct hostent *)NULL) {
1373 errno = h_errno;
1374 return(-1);
1375 }
1376 memcpy((char *) &sin->sin_addr, (char *) hp->h_addr_list[0], hp->h_length);
1377 strcpy(name, hp->h_name);
1378 return(0);
1379}
1380
1381/*------------------------------------------------------------------*/
1382/*
1383 * Input an address and convert to binary.
1384 */
1385int
1386iw_in_addr(int skfd,
1387 char * ifname,
1388 char * bufp,
1389 struct sockaddr *sap)
1390{
1391 /* Check if it is a hardware or IP address */
1392 if(index(bufp, ':') == NULL)
1393 {
1394 struct sockaddr if_address;
1395 struct arpreq arp_query;
1396
1397 /* Check if we have valid interface address type */
1398 if(iw_check_if_addr_type(skfd, ifname) < 0)
1399 {
1400 fprintf(stderr, "%-8.8s Interface doesn't support IP addresses\n", ifname);
1401 return(-1);
1402 }
1403
1404 /* Read interface address */
1405 if(iw_in_inet(bufp, &if_address) < 0)
1406 {
1407 fprintf(stderr, "Invalid interface address %s\n", bufp);
1408 return(-1);
1409 }
1410
1411 /* Translate IP addresses to MAC addresses */
1412 memcpy((char *) &(arp_query.arp_pa),
1413 (char *) &if_address,
1414 sizeof(struct sockaddr));
1415 arp_query.arp_ha.sa_family = 0;
1416 arp_query.arp_flags = 0;
1417 /* The following restrict the search to the interface only */
1418 /* For old kernels which complain, just comment it... */
1419 strncpy(arp_query.arp_dev, ifname, IFNAMSIZ-1);
1420 arp_query.arp_dev[IFNAMSIZ-1] = '\0';
1421 if((ioctl(skfd, SIOCGARP, &arp_query) < 0) ||
1422 !(arp_query.arp_flags & ATF_COM))
1423 {
1424 fprintf(stderr, "Arp failed for %s on %s... (%d)\nTry to ping the address before setting it.\n",
1425 bufp, ifname, errno);
1426 return(-1);
1427 }
1428
1429 /* Store new MAC address */
1430 memcpy((char *) sap,
1431 (char *) &(arp_query.arp_ha),
1432 sizeof(struct sockaddr));
1433
1434#ifdef DEBUG
1435 printf("IP Address %s => Hw Address = %s\n",
1436 bufp, iw_ether_ntoa((struct ether_addr *) sap->sa_data));
1437#endif
1438 }
1439 else /* If it's an hardware address */
1440 {
1441 /* Check if we have valid mac address type */
1442 if(iw_check_mac_addr_type(skfd, ifname) < 0)
1443 {
1444 fprintf(stderr, "%-8.8s Interface doesn't support MAC addresses\n", ifname);
1445 return(-1);
1446 }
1447
1448 /* Get the hardware address */
1449 if(iw_in_ether(bufp, sap) < 0)
1450 {
1451 fprintf(stderr, "Invalid hardware address %s\n", bufp);
1452 return(-1);
1453 }
1454 }
1455
1456#ifdef DEBUG
1457 printf("Hw Address = %s\n", iw_ether_ntoa((struct ether_addr *) sap->sa_data));
1458#endif
1459
1460 return(0);
1461}
1462
1463/************************* MISC SUBROUTINES **************************/
1464
1465/* Size (in bytes) of various events */
1466static const int priv_type_size[] = {
1467 0, /* IW_PRIV_TYPE_NONE */
1468 1, /* IW_PRIV_TYPE_BYTE */
1469 1, /* IW_PRIV_TYPE_CHAR */
1470 0, /* Not defined */
1471 sizeof(__u32), /* IW_PRIV_TYPE_INT */
1472 sizeof(struct iw_freq), /* IW_PRIV_TYPE_FLOAT */
1473 sizeof(struct sockaddr), /* IW_PRIV_TYPE_ADDR */
1474 0, /* Not defined */
1475};
1476
1477/*------------------------------------------------------------------*/
1478/*
1479 * Max size in bytes of an private argument.
1480 */
1481int
1482iw_get_priv_size(int args)
1483{
1484 int num = args & IW_PRIV_SIZE_MASK;
1485 int type = (args & IW_PRIV_TYPE_MASK) >> 12;
1486
1487 return(num * priv_type_size[type]);
1488}
1489
1490/************************ EVENT SUBROUTINES ************************/
1491/*
1492 * The Wireless Extension API 14 and greater define Wireless Events,
1493 * that are used for various events and scanning.
1494 * Those functions help the decoding of events, so are needed only in
1495 * this case.
1496 */
1497#if WIRELESS_EXT > 13
1498
1499/* Type of headers we know about (basically union iwreq_data) */
1500#define IW_HEADER_TYPE_NULL 0 /* Not available */
1501#define IW_HEADER_TYPE_CHAR 2 /* char [IFNAMSIZ] */
1502#define IW_HEADER_TYPE_UINT 4 /* __u32 */
1503#define IW_HEADER_TYPE_FREQ 5 /* struct iw_freq */
1504#define IW_HEADER_TYPE_ADDR 6 /* struct sockaddr */
1505#define IW_HEADER_TYPE_POINT 8 /* struct iw_point */
1506#define IW_HEADER_TYPE_PARAM 9 /* struct iw_param */
1507#define IW_HEADER_TYPE_QUAL 10 /* struct iw_quality */
1508
1509/* Headers for the various requests */
1510static const char standard_ioctl_hdr[] = {
1511 IW_HEADER_TYPE_NULL, /* SIOCSIWCOMMIT */
1512 IW_HEADER_TYPE_CHAR, /* SIOCGIWNAME */
1513 IW_HEADER_TYPE_PARAM, /* SIOCSIWNWID */
1514 IW_HEADER_TYPE_PARAM, /* SIOCGIWNWID */
1515 IW_HEADER_TYPE_FREQ, /* SIOCSIWFREQ */
1516 IW_HEADER_TYPE_FREQ, /* SIOCGIWFREQ */
1517 IW_HEADER_TYPE_UINT, /* SIOCSIWMODE */
1518 IW_HEADER_TYPE_UINT, /* SIOCGIWMODE */
1519 IW_HEADER_TYPE_PARAM, /* SIOCSIWSENS */
1520 IW_HEADER_TYPE_PARAM, /* SIOCGIWSENS */
1521 IW_HEADER_TYPE_NULL, /* SIOCSIWRANGE */
1522 IW_HEADER_TYPE_POINT, /* SIOCGIWRANGE */
1523 IW_HEADER_TYPE_NULL, /* SIOCSIWPRIV */
1524 IW_HEADER_TYPE_POINT, /* SIOCGIWPRIV */
1525 IW_HEADER_TYPE_NULL, /* SIOCSIWSTATS */
1526 IW_HEADER_TYPE_POINT, /* SIOCGIWSTATS */
1527 IW_HEADER_TYPE_POINT, /* SIOCSIWSPY */
1528 IW_HEADER_TYPE_POINT, /* SIOCGIWSPY */
1529 IW_HEADER_TYPE_NULL, /* -- hole -- */
1530 IW_HEADER_TYPE_NULL, /* -- hole -- */
1531 IW_HEADER_TYPE_ADDR, /* SIOCSIWAP */
1532 IW_HEADER_TYPE_ADDR, /* SIOCGIWAP */
1533 IW_HEADER_TYPE_NULL, /* -- hole -- */
1534 IW_HEADER_TYPE_POINT, /* SIOCGIWAPLIST */
1535 IW_HEADER_TYPE_PARAM, /* SIOCSIWSCAN */
1536 IW_HEADER_TYPE_POINT, /* SIOCGIWSCAN */
1537 IW_HEADER_TYPE_POINT, /* SIOCSIWESSID */
1538 IW_HEADER_TYPE_POINT, /* SIOCGIWESSID */
1539 IW_HEADER_TYPE_POINT, /* SIOCSIWNICKN */
1540 IW_HEADER_TYPE_POINT, /* SIOCGIWNICKN */
1541 IW_HEADER_TYPE_NULL, /* -- hole -- */
1542 IW_HEADER_TYPE_NULL, /* -- hole -- */
1543 IW_HEADER_TYPE_PARAM, /* SIOCSIWRATE */
1544 IW_HEADER_TYPE_PARAM, /* SIOCGIWRATE */
1545 IW_HEADER_TYPE_PARAM, /* SIOCSIWRTS */
1546 IW_HEADER_TYPE_PARAM, /* SIOCGIWRTS */
1547 IW_HEADER_TYPE_PARAM, /* SIOCSIWFRAG */
1548 IW_HEADER_TYPE_PARAM, /* SIOCGIWFRAG */
1549 IW_HEADER_TYPE_PARAM, /* SIOCSIWTXPOW */
1550 IW_HEADER_TYPE_PARAM, /* SIOCGIWTXPOW */
1551 IW_HEADER_TYPE_PARAM, /* SIOCSIWRETRY */
1552 IW_HEADER_TYPE_PARAM, /* SIOCGIWRETRY */
1553 IW_HEADER_TYPE_POINT, /* SIOCSIWENCODE */
1554 IW_HEADER_TYPE_POINT, /* SIOCGIWENCODE */
1555 IW_HEADER_TYPE_PARAM, /* SIOCSIWPOWER */
1556 IW_HEADER_TYPE_PARAM, /* SIOCGIWPOWER */
1557};
1558static const unsigned int standard_ioctl_num = sizeof(standard_ioctl_hdr);
1559
1560/*
1561 * Meta-data about all the additional standard Wireless Extension events
1562 * we know about.
1563 */
1564static const char standard_event_hdr[] = {
1565 IW_HEADER_TYPE_ADDR, /* IWEVTXDROP */
1566 IW_HEADER_TYPE_QUAL, /* IWEVQUAL */
1567 IW_HEADER_TYPE_POINT, /* IWEVCUSTOM */
1568 IW_HEADER_TYPE_ADDR, /* IWEVREGISTERED */
1569 IW_HEADER_TYPE_ADDR, /* IWEVEXPIRED */
1570};
1571static const unsigned int standard_event_num = sizeof(standard_event_hdr);
1572
1573/* Size (in bytes) of various events */
1574static const int event_type_size[] = {
1575 IW_EV_LCP_LEN, /* IW_HEADER_TYPE_NULL */
1576 0,
1577 IW_EV_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */
1578 0,
1579 IW_EV_UINT_LEN, /* IW_HEADER_TYPE_UINT */
1580 IW_EV_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */
1581 IW_EV_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */
1582 0,
1583 IW_EV_POINT_LEN, /* Without variable payload */
1584 IW_EV_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */
1585 IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */
1586};
1587
1588/*------------------------------------------------------------------*/
1589/*
1590 * Initialise the struct stream_descr so that we can extract
1591 * individual events from the event stream.
1592 */
1593void
1594iw_init_event_stream(struct stream_descr * stream, /* Stream of events */
1595 char * data,
1596 int len)
1597{
1598 /* Cleanup */
1599 memset((char *) stream, '\0', sizeof(struct stream_descr));
1600
1601 /* Set things up */
1602 stream->current = data;
1603 stream->end = data + len;
1604}
1605
1606/*------------------------------------------------------------------*/
1607/*
1608 * Extract the next event from the event stream.
1609 */
1610int
1611iw_extract_event_stream(struct stream_descr * stream, /* Stream of events */
1612 struct iw_event * iwe) /* Extracted event */
1613{
1614 int event_type = 0;
1615 unsigned int event_len = 1; /* Invalid */
1616 char * pointer;
1617 /* Don't "optimise" the following variable, it will crash */
1618 unsigned cmd_index; /* *MUST* be unsigned */
1619
1620 /* Check for end of stream */
1621 if((stream->current + IW_EV_LCP_LEN) > stream->end)
1622 return(0);
1623
1624#if 0
1625 printf("DBG - stream->current = %p, stream->value = %p, stream->end = %p\n",
1626 stream->current, stream->value, stream->end);
1627#endif
1628
1629 /* Extract the event header (to get the event id).
1630 * Note : the event may be unaligned, therefore copy... */
1631 memcpy((char *) iwe, stream->current, IW_EV_LCP_LEN);
1632
1633#if 0
1634 printf("DBG - iwe->cmd = 0x%X, iwe->len = %d\n",
1635 iwe->cmd, iwe->len);
1636#endif
1637
1638 /* Check invalid events */
1639 if(iwe->len <= IW_EV_LCP_LEN)
1640 return(-1);
1641
1642 /* Get the type and length of that event */
1643 if(iwe->cmd <= SIOCIWLAST)
1644 {
1645 cmd_index = iwe->cmd - SIOCIWFIRST;
1646 if(cmd_index < standard_ioctl_num)
1647 event_type = standard_ioctl_hdr[cmd_index];
1648 }
1649 else
1650 {
1651 cmd_index = iwe->cmd - IWEVFIRST;
1652 if(cmd_index < standard_event_num)
1653 event_type = standard_event_hdr[cmd_index];
1654 }
1655 /* Unknown events -> event_type=0 => IW_EV_LCP_LEN */
1656 event_len = event_type_size[event_type];
1657
1658 /* Check if we know about this event */
1659 if(event_len <= IW_EV_LCP_LEN)
1660 {
1661 /* Skip to next event */
1662 stream->current += iwe->len;
1663 return(2);
1664 }
1665 event_len -= IW_EV_LCP_LEN;
1666
1667 /* Set pointer on data */
1668 if(stream->value != NULL)
1669 pointer = stream->value; /* Next value in event */
1670 else
1671 pointer = stream->current + IW_EV_LCP_LEN; /* First value in event */
1672
1673#if 0
1674 printf("DBG - event_type = %d, event_len = %d, pointer = %p\n",
1675 event_type, event_len, pointer);
1676#endif
1677
1678 /* Copy the rest of the event (at least, fixed part) */
1679 if((pointer + event_len) > stream->end)
1680 {
1681 /* Go to next event */
1682 stream->current += iwe->len;
1683 return(-2);
1684 }
1685 memcpy((char *) iwe + IW_EV_LCP_LEN, pointer, event_len);
1686
1687 /* Skip event in the stream */
1688 pointer += event_len;
1689
1690 /* Special processing for iw_point events */
1691 if(event_type == IW_HEADER_TYPE_POINT)
1692 {
1693 /* Check the length of the payload */
1694 if((iwe->len - (event_len + IW_EV_LCP_LEN)) > 0)
1695 /* Set pointer on variable part (warning : non aligned) */
1696 iwe->u.data.pointer = pointer;
1697 else
1698 /* No data */
1699 iwe->u.data.pointer = NULL;
1700
1701 /* Go to next event */
1702 stream->current += iwe->len;
1703 }
1704 else
1705 {
1706 /* Is there more value in the event ? */
1707 if((pointer + event_len) <= (stream->current + iwe->len))
1708 /* Go to next value */
1709 stream->value = pointer;
1710 else
1711 {
1712 /* Go to next event */
1713 stream->value = NULL;
1714 stream->current += iwe->len;
1715 }
1716 }
1717 return(1);
1718}
1719
1720#endif /* WIRELESS_EXT > 13 */