-
Notifications
You must be signed in to change notification settings - Fork 0
/
iwlib.c
3367 lines (3064 loc) · 90.4 KB
/
iwlib.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Wireless Tools
*
* Jean II - HPLB 97->99 - HPL 99->07
*
* Common subroutines to all the wireless tools...
*
* This file is released under the GPL license.
* Copyright (c) 1997-2007 Jean Tourrilhes <jt@hpl.hp.com>
*/
/***************************** INCLUDES *****************************/
#include "iwlib.h" /* Header */
/************************ CONSTANTS & MACROS ************************/
/*
* Constants fof WE-9->15
*/
#define IW15_MAX_FREQUENCIES 16
#define IW15_MAX_BITRATES 8
#define IW15_MAX_TXPOWER 8
#define IW15_MAX_ENCODING_SIZES 8
#define IW15_MAX_SPY 8
#define IW15_MAX_AP 8
/****************************** TYPES ******************************/
/*
* Struct iw_range up to WE-15
*/
struct iw15_range
{
__u32 throughput;
__u32 min_nwid;
__u32 max_nwid;
__u16 num_channels;
__u8 num_frequency;
struct iw_freq freq[IW15_MAX_FREQUENCIES];
__s32 sensitivity;
struct iw_quality max_qual;
__u8 num_bitrates;
__s32 bitrate[IW15_MAX_BITRATES];
__s32 min_rts;
__s32 max_rts;
__s32 min_frag;
__s32 max_frag;
__s32 min_pmp;
__s32 max_pmp;
__s32 min_pmt;
__s32 max_pmt;
__u16 pmp_flags;
__u16 pmt_flags;
__u16 pm_capa;
__u16 encoding_size[IW15_MAX_ENCODING_SIZES];
__u8 num_encoding_sizes;
__u8 max_encoding_tokens;
__u16 txpower_capa;
__u8 num_txpower;
__s32 txpower[IW15_MAX_TXPOWER];
__u8 we_version_compiled;
__u8 we_version_source;
__u16 retry_capa;
__u16 retry_flags;
__u16 r_time_flags;
__s32 min_retry;
__s32 max_retry;
__s32 min_r_time;
__s32 max_r_time;
struct iw_quality avg_qual;
};
/*
* Union for all the versions of iwrange.
* Fortunately, I mostly only add fields at the end, and big-bang
* reorganisations are few.
*/
union iw_range_raw
{
struct iw15_range range15; /* WE 9->15 */
struct iw_range range; /* WE 16->current */
};
/*
* Offsets in iw_range struct
*/
#define iwr15_off(f) ( ((char *) &(((struct iw15_range *) NULL)->f)) - \
(char *) NULL)
#define iwr_off(f) ( ((char *) &(((struct iw_range *) NULL)->f)) - \
(char *) NULL)
/*
* Union to perform unaligned access when working around alignement issues
*/
union iw_align_u16
{
__u16 value;
unsigned char byte[2];
};
/**************************** VARIABLES ****************************/
/* Modes as human readable strings */
const char * const iw_operation_mode[] = { "Auto",
"Ad-Hoc",
"Managed",
"Master",
"Repeater",
"Secondary",
"Monitor",
"Unknown/bug" };
/* Modulations as human readable strings */
const struct iw_modul_descr iw_modul_list[] = {
/* Start with aggregate types, so that they display first */
{ IW_MODUL_11AG, "11ag",
"IEEE 802.11a + 802.11g (2.4 & 5 GHz, up to 54 Mb/s)" },
{ IW_MODUL_11AB, "11ab",
"IEEE 802.11a + 802.11b (2.4 & 5 GHz, up to 54 Mb/s)" },
{ IW_MODUL_11G, "11g", "IEEE 802.11g (2.4 GHz, up to 54 Mb/s)" },
{ IW_MODUL_11A, "11a", "IEEE 802.11a (5 GHz, up to 54 Mb/s)" },
{ IW_MODUL_11B, "11b", "IEEE 802.11b (2.4 GHz, up to 11 Mb/s)" },
/* Proprietary aggregates */
{ IW_MODUL_TURBO | IW_MODUL_11A, "turboa",
"Atheros turbo mode at 5 GHz (up to 108 Mb/s)" },
{ IW_MODUL_TURBO | IW_MODUL_11G, "turbog",
"Atheros turbo mode at 2.4 GHz (up to 108 Mb/s)" },
{ IW_MODUL_PBCC | IW_MODUL_11B, "11+",
"TI 802.11+ (2.4 GHz, up to 22 Mb/s)" },
/* Individual modulations */
{ IW_MODUL_OFDM_G, "OFDMg",
"802.11g higher rates, OFDM at 2.4 GHz (up to 54 Mb/s)" },
{ IW_MODUL_OFDM_A, "OFDMa", "802.11a, OFDM at 5 GHz (up to 54 Mb/s)" },
{ IW_MODUL_CCK, "CCK", "802.11b higher rates (2.4 GHz, up to 11 Mb/s)" },
{ IW_MODUL_DS, "DS", "802.11 Direct Sequence (2.4 GHz, up to 2 Mb/s)" },
{ IW_MODUL_FH, "FH", "802.11 Frequency Hopping (2,4 GHz, up to 2 Mb/s)" },
/* Proprietary modulations */
{ IW_MODUL_TURBO, "turbo",
"Atheros turbo mode, channel bonding (up to 108 Mb/s)" },
{ IW_MODUL_PBCC, "PBCC",
"TI 802.11+ higher rates (2.4 GHz, up to 22 Mb/s)" },
{ IW_MODUL_CUSTOM, "custom",
"Driver specific modulation (check driver documentation)" },
};
/* Disable runtime version warning in iw_get_range_info() */
int iw_ignore_version = 0;
/************************ SOCKET SUBROUTINES *************************/
/*------------------------------------------------------------------*/
/*
* Open a socket.
* Depending on the protocol present, open the right socket. The socket
* will allow us to talk to the driver.
*/
int
iw_sockets_open(void)
{
static const int families[] = {
AF_INET, AF_IPX, AF_AX25, AF_APPLETALK
};
unsigned int i;
int sock;
/*
* Now pick any (exisiting) useful socket family for generic queries
* Note : don't open all the socket, only returns when one matches,
* all protocols might not be valid.
* Workaround by Jim Kaba <jkaba@sarnoff.com>
* Note : in 99% of the case, we will just open the inet_sock.
* The remaining 1% case are not fully correct...
*/
/* Try all families we support */
for(i = 0; i < sizeof(families)/sizeof(int); ++i)
{
/* Try to open the socket, if success returns it */
sock = socket(families[i], SOCK_DGRAM, 0);
if(sock >= 0)
return sock;
}
return -1;
}
/*------------------------------------------------------------------*/
/*
* Extract the interface name out of /proc/net/wireless or /proc/net/dev.
*/
static inline char *
iw_get_ifname(char * name, /* Where to store the name */
int nsize, /* Size of name buffer */
char * buf) /* Current position in buffer */
{
char * end;
/* Skip leading spaces */
while(isspace(*buf))
buf++;
#ifndef IW_RESTRIC_ENUM
/* Get name up to the last ':'. Aliases may contain ':' in them,
* but the last one should be the separator */
end = strrchr(buf, ':');
#else
/* Get name up to ": "
* Note : we compare to ": " to make sure to process aliased interfaces
* properly. Doesn't work on /proc/net/dev, because it doesn't guarantee
* a ' ' after the ':'*/
end = strstr(buf, ": ");
#endif
/* Not found ??? To big ??? */
if((end == NULL) || (((end - buf) + 1) > nsize))
return(NULL);
/* Copy */
memcpy(name, buf, (end - buf));
name[end - buf] = '\0';
/* Return value currently unused, just make sure it's non-NULL */
return(end);
}
/*------------------------------------------------------------------*/
/*
* Enumerate devices and call specified routine
* The new way just use /proc/net/wireless, so get all wireless interfaces,
* whether configured or not. This is the default if available.
* The old way use SIOCGIFCONF, so get only configured interfaces (wireless
* or not).
*/
void
iw_enum_devices(int skfd,
iw_enum_handler fn,
char * args[],
int count)
{
char buff[1024];
FILE * fh;
struct ifconf ifc;
struct ifreq *ifr;
int i;
#ifndef IW_RESTRIC_ENUM
/* Check if /proc/net/dev is available */
fh = fopen(PROC_NET_DEV, "r");
#else
/* Check if /proc/net/wireless is available */
fh = fopen(PROC_NET_WIRELESS, "r");
#endif
if(fh != NULL)
{
/* Success : use data from /proc/net/wireless */
/* Eat 2 lines of header */
fgets(buff, sizeof(buff), fh);
fgets(buff, sizeof(buff), fh);
/* Read each device line */
while(fgets(buff, sizeof(buff), fh))
{
char name[IFNAMSIZ + 1];
char *s;
/* Skip empty or almost empty lines. It seems that in some
* cases fgets return a line with only a newline. */
if((buff[0] == '\0') || (buff[1] == '\0'))
continue;
/* Extract interface name */
s = iw_get_ifname(name, sizeof(name), buff);
if(!s)
{
/* Failed to parse, complain and continue */
#ifndef IW_RESTRIC_ENUM
fprintf(stderr, "Cannot parse " PROC_NET_DEV "\n");
#else
fprintf(stderr, "Cannot parse " PROC_NET_WIRELESS "\n");
#endif
}
else
/* Got it, print info about this interface */
(*fn)(skfd, name, args, count);
}
fclose(fh);
}
else
{
/* Get list of configured devices using "traditional" way */
ifc.ifc_len = sizeof(buff);
ifc.ifc_buf = buff;
if(ioctl(skfd, SIOCGIFCONF, &ifc) < 0)
{
fprintf(stderr, "SIOCGIFCONF: %s\n", strerror(errno));
return;
}
ifr = ifc.ifc_req;
/* Print them */
for(i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; ifr++)
(*fn)(skfd, ifr->ifr_name, args, count);
}
}
/*********************** WIRELESS SUBROUTINES ************************/
/*------------------------------------------------------------------*/
/*
* Extract WE version number from /proc/net/wireless
* In most cases, you really want to get version information from
* the range info (range->we_version_compiled), see below...
*
* If we have WE-16 and later, the WE version is available at the
* end of the header line of the file.
* For version prior to that, we can only detect the change from
* v11 to v12, so we do an approximate job. Fortunately, v12 to v15
* are highly binary compatible (on the struct level).
*/
int
iw_get_kernel_we_version(void)
{
char buff[1024];
FILE * fh;
char * p;
int v;
/* Check if /proc/net/wireless is available */
fh = fopen(PROC_NET_WIRELESS, "r");
if(fh == NULL)
{
fprintf(stderr, "Cannot read " PROC_NET_WIRELESS "\n");
return(-1);
}
/* Read the first line of buffer */
fgets(buff, sizeof(buff), fh);
if(strstr(buff, "| WE") == NULL)
{
/* Prior to WE16, so explicit version not present */
/* Black magic */
if(strstr(buff, "| Missed") == NULL)
v = 11;
else
v = 15;
fclose(fh);
return(v);
}
/* Read the second line of buffer */
fgets(buff, sizeof(buff), fh);
/* Get to the last separator, to get the version */
p = strrchr(buff, '|');
if((p == NULL) || (sscanf(p + 1, "%d", &v) != 1))
{
fprintf(stderr, "Cannot parse " PROC_NET_WIRELESS "\n");
fclose(fh);
return(-1);
}
fclose(fh);
return(v);
}
/*------------------------------------------------------------------*/
/*
* Print the WE versions of the interface.
*/
static int
print_iface_version_info(int skfd,
char * ifname,
char * args[], /* Command line args */
int count) /* Args count */
{
struct iwreq wrq;
char buffer[sizeof(iwrange) * 2]; /* Large enough */
struct iw_range * range;
/* Avoid "Unused parameter" warning */
args = args; count = count;
/* If no wireless name : no wireless extensions.
* This enable us to treat the SIOCGIWRANGE failure below properly. */
if(iw_get_ext(skfd, ifname, SIOCGIWNAME, &wrq) < 0)
return(-1);
/* Cleanup */
memset(buffer, 0, sizeof(buffer));
wrq.u.data.pointer = (caddr_t) buffer;
wrq.u.data.length = sizeof(buffer);
wrq.u.data.flags = 0;
if(iw_get_ext(skfd, ifname, SIOCGIWRANGE, &wrq) < 0)
{
/* Interface support WE (see above), but not IWRANGE */
fprintf(stderr, "%-8.16s Driver has no Wireless Extension version information.\n\n", ifname);
return(0);
}
/* Copy stuff at the right place, ignore extra */
range = (struct iw_range *) buffer;
/* For new versions, we can check the version directly, for old versions
* we use magic. 300 bytes is a also magic number, don't touch... */
if(wrq.u.data.length >= 300)
{
/* Version is always at the same offset, so it's ok */
printf("%-8.16s Recommend Wireless Extension v%d or later,\n",
ifname, range->we_version_source);
printf(" Currently compiled with Wireless Extension v%d.\n\n",
range->we_version_compiled);
}
else
{
fprintf(stderr, "%-8.16s Wireless Extension version too old.\n\n",
ifname);
}
return(0);
}
/*------------------------------------------------------------------*/
/*
* Print the WE versions of the tools.
*/
int
iw_print_version_info(const char * toolname)
{
int skfd; /* generic raw socket desc. */
int we_kernel_version;
/* Create a channel to the NET kernel. */
if((skfd = iw_sockets_open()) < 0)
{
perror("socket");
return -1;
}
/* Information about the tools themselves */
if(toolname != NULL)
printf("%-8.16s Wireless-Tools version %d\n", toolname, WT_VERSION);
printf(" Compatible with Wireless Extension v11 to v%d.\n\n",
WE_MAX_VERSION);
/* Get version from kernel */
we_kernel_version = iw_get_kernel_we_version();
/* Only version >= 16 can be verified, other are guessed */
if(we_kernel_version > 15)
printf("Kernel Currently compiled with Wireless Extension v%d.\n\n",
we_kernel_version);
/* Version for each device */
iw_enum_devices(skfd, &print_iface_version_info, NULL, 0);
iw_sockets_close(skfd);
return 0;
}
/*------------------------------------------------------------------*/
/*
* Get the range information out of the driver
*/
int
iw_get_range_info(int skfd,
const char * ifname,
iwrange * range)
{
struct iwreq wrq;
char buffer[sizeof(iwrange) * 2]; /* Large enough */
union iw_range_raw * range_raw;
/* Cleanup */
bzero(buffer, sizeof(buffer));
wrq.u.data.pointer = (caddr_t) buffer;
wrq.u.data.length = sizeof(buffer);
wrq.u.data.flags = 0;
if(iw_get_ext(skfd, ifname, SIOCGIWRANGE, &wrq) < 0)
return(-1);
/* Point to the buffer */
range_raw = (union iw_range_raw *) buffer;
/* For new versions, we can check the version directly, for old versions
* we use magic. 300 bytes is a also magic number, don't touch... */
if(wrq.u.data.length < 300)
{
/* That's v10 or earlier. Ouch ! Let's make a guess...*/
range_raw->range.we_version_compiled = 9;
}
/* Check how it needs to be processed */
if(range_raw->range.we_version_compiled > 15)
{
/* This is our native format, that's easy... */
/* Copy stuff at the right place, ignore extra */
memcpy((char *) range, buffer, sizeof(iwrange));
}
else
{
/* Zero unknown fields */
bzero((char *) range, sizeof(struct iw_range));
/* Initial part unmoved */
memcpy((char *) range,
buffer,
iwr15_off(num_channels));
/* Frequencies pushed futher down towards the end */
memcpy((char *) range + iwr_off(num_channels),
buffer + iwr15_off(num_channels),
iwr15_off(sensitivity) - iwr15_off(num_channels));
/* This one moved up */
memcpy((char *) range + iwr_off(sensitivity),
buffer + iwr15_off(sensitivity),
iwr15_off(num_bitrates) - iwr15_off(sensitivity));
/* This one goes after avg_qual */
memcpy((char *) range + iwr_off(num_bitrates),
buffer + iwr15_off(num_bitrates),
iwr15_off(min_rts) - iwr15_off(num_bitrates));
/* Number of bitrates has changed, put it after */
memcpy((char *) range + iwr_off(min_rts),
buffer + iwr15_off(min_rts),
iwr15_off(txpower_capa) - iwr15_off(min_rts));
/* Added encoding_login_index, put it after */
memcpy((char *) range + iwr_off(txpower_capa),
buffer + iwr15_off(txpower_capa),
iwr15_off(txpower) - iwr15_off(txpower_capa));
/* Hum... That's an unexpected glitch. Bummer. */
memcpy((char *) range + iwr_off(txpower),
buffer + iwr15_off(txpower),
iwr15_off(avg_qual) - iwr15_off(txpower));
/* Avg qual moved up next to max_qual */
memcpy((char *) range + iwr_off(avg_qual),
buffer + iwr15_off(avg_qual),
sizeof(struct iw_quality));
}
/* We are now checking much less than we used to do, because we can
* accomodate more WE version. But, there are still cases where things
* will break... */
if(!iw_ignore_version)
{
/* We don't like very old version (unfortunately kernel 2.2.X) */
if(range->we_version_compiled <= 10)
{
fprintf(stderr, "Warning: Driver for device %s has been compiled with an ancient version\n", ifname);
fprintf(stderr, "of Wireless Extension, while this program support version 11 and later.\n");
fprintf(stderr, "Some things may be broken...\n\n");
}
/* We don't like future versions of WE, because we can't cope with
* the unknown */
if(range->we_version_compiled > WE_MAX_VERSION)
{
fprintf(stderr, "Warning: Driver for device %s has been compiled with version %d\n", ifname, range->we_version_compiled);
fprintf(stderr, "of Wireless Extension, while this program supports up to version %d.\n", WE_MAX_VERSION);
fprintf(stderr, "Some things may be broken...\n\n");
}
/* Driver version verification */
if((range->we_version_compiled > 10) &&
(range->we_version_compiled < range->we_version_source))
{
fprintf(stderr, "Warning: Driver for device %s recommend version %d of Wireless Extension,\n", ifname, range->we_version_source);
fprintf(stderr, "but has been compiled with version %d, therefore some driver features\n", range->we_version_compiled);
fprintf(stderr, "may not be available...\n\n");
}
/* Note : we are only trying to catch compile difference, not source.
* If the driver source has not been updated to the latest, it doesn't
* matter because the new fields are set to zero */
}
/* Don't complain twice.
* In theory, the test apply to each individual driver, but usually
* all drivers are compiled from the same kernel. */
iw_ignore_version = 1;
return(0);
}
/*------------------------------------------------------------------*/
/*
* Get information about what private ioctls are supported by the driver
*
* Note : there is one danger using this function. If it return 0, you
* still need to free() the buffer. Beware.
*/
int
iw_get_priv_info(int skfd,
const char * ifname,
iwprivargs ** ppriv)
{
struct iwreq wrq;
iwprivargs * priv = NULL; /* Not allocated yet */
int maxpriv = 16; /* Minimum for compatibility WE<13 */
iwprivargs * newpriv;
/* Some driver may return a very large number of ioctls. Some
* others a very small number. We now use a dynamic allocation
* of the array to satisfy everybody. Of course, as we don't know
* in advance the size of the array, we try various increasing
* sizes. Jean II */
do
{
/* (Re)allocate the buffer */
newpriv = realloc(priv, maxpriv * sizeof(priv[0]));
if(newpriv == NULL)
{
fprintf(stderr, "%s: Allocation failed\n", __FUNCTION__);
break;
}
priv = newpriv;
/* Ask the driver if it's large enough */
wrq.u.data.pointer = (caddr_t) priv;
wrq.u.data.length = maxpriv;
wrq.u.data.flags = 0;
if(iw_get_ext(skfd, ifname, SIOCGIWPRIV, &wrq) >= 0)
{
/* Success. Pass the buffer by pointer */
*ppriv = priv;
/* Return the number of ioctls */
return(wrq.u.data.length);
}
/* Only E2BIG means the buffer was too small, abort on other errors */
if(errno != E2BIG)
{
/* Most likely "not supported". Don't barf. */
break;
}
/* Failed. We probably need a bigger buffer. Check if the kernel
* gave us any hints. */
if(wrq.u.data.length > maxpriv)
maxpriv = wrq.u.data.length;
else
maxpriv *= 2;
}
while(maxpriv < 1000);
/* Cleanup */
if(priv)
free(priv);
*ppriv = NULL;
return(-1);
}
/*------------------------------------------------------------------*/
/*
* Get essential wireless config from the device driver
* We will call all the classical wireless ioctl on the driver through
* the socket to know what is supported and to get the settings...
* Note : compare to the version in iwconfig, we extract only
* what's *really* needed to configure a device...
*/
int
iw_get_basic_config(int skfd,
const char * ifname,
wireless_config * info)
{
struct iwreq wrq;
memset((char *) &wrq, 0, sizeof(struct iwreq));
memset((char *) info, 0, sizeof(struct wireless_config));
/* Get wireless name */
if(iw_get_ext(skfd, ifname, SIOCGIWNAME, &wrq) < 0)
/* If no wireless name : no wireless extensions */
return(-1);
else
{
strncpy(info->name, wrq.u.name, IFNAMSIZ);
info->name[IFNAMSIZ] = '\0';
}
/* Get network ID */
if(iw_get_ext(skfd, ifname, SIOCGIWNWID, &wrq) >= 0)
{
info->has_nwid = 1;
memcpy(&(info->nwid), &(wrq.u.nwid), sizeof(iwparam));
}
/* Get frequency / channel */
if(iw_get_ext(skfd, ifname, SIOCGIWFREQ, &wrq) >= 0)
{
info->has_freq = 1;
info->freq = iw_freq2float(&(wrq.u.freq));
info->freq_flags = wrq.u.freq.flags;
}
/* Get encryption information */
wrq.u.data.pointer = (caddr_t) info->key;
wrq.u.data.length = IW_ENCODING_TOKEN_MAX;
wrq.u.data.flags = 0;
if(iw_get_ext(skfd, ifname, SIOCGIWENCODE, &wrq) >= 0)
{
info->has_key = 1;
info->key_size = wrq.u.data.length;
info->key_flags = wrq.u.data.flags;
}
/* Get ESSID */
wrq.u.essid.pointer = (caddr_t) info->essid;
wrq.u.essid.length = IW_ESSID_MAX_SIZE + 2;
wrq.u.essid.flags = 0;
if(iw_get_ext(skfd, ifname, SIOCGIWESSID, &wrq) >= 0)
{
info->has_essid = 1;
info->essid_on = wrq.u.data.flags;
info->essid_len = wrq.u.essid.length;
}
/* Get operation mode */
if(iw_get_ext(skfd, ifname, SIOCGIWMODE, &wrq) >= 0)
{
info->has_mode = 1;
/* Note : event->u.mode is unsigned, no need to check <= 0 */
if(wrq.u.mode < IW_NUM_OPER_MODE)
info->mode = wrq.u.mode;
else
info->mode = IW_NUM_OPER_MODE; /* Unknown/bug */
}
return(0);
}
/*------------------------------------------------------------------*/
/*
* Set essential wireless config in the device driver
* We will call all the classical wireless ioctl on the driver through
* the socket to know what is supported and to set the settings...
* We support only the restricted set as above...
*/
int
iw_set_basic_config(int skfd,
const char * ifname,
wireless_config * info)
{
struct iwreq wrq;
int ret = 0;
/* Get wireless name (check if interface is valid) */
if(iw_get_ext(skfd, ifname, SIOCGIWNAME, &wrq) < 0)
/* If no wireless name : no wireless extensions */
return(-2);
/* Set the current mode of operation
* Mode need to be first : some settings apply only in a specific mode
* (such as frequency).
*/
if(info->has_mode)
{
strncpy(wrq.ifr_name, ifname, IFNAMSIZ);
wrq.u.mode = info->mode;
if(iw_get_ext(skfd, ifname, SIOCSIWMODE, &wrq) < 0)
{
fprintf(stderr, "SIOCSIWMODE: %s\n", strerror(errno));
ret = -1;
}
}
/* Set frequency / channel */
if(info->has_freq)
{
iw_float2freq(info->freq, &(wrq.u.freq));
if(iw_set_ext(skfd, ifname, SIOCSIWFREQ, &wrq) < 0)
{
fprintf(stderr, "SIOCSIWFREQ: %s\n", strerror(errno));
ret = -1;
}
}
/* Set encryption information */
if(info->has_key)
{
int flags = info->key_flags;
/* Check if there is a key index */
if((flags & IW_ENCODE_INDEX) > 0)
{
/* Set the index */
wrq.u.data.pointer = (caddr_t) NULL;
wrq.u.data.flags = (flags & (IW_ENCODE_INDEX)) | IW_ENCODE_NOKEY;
wrq.u.data.length = 0;
if(iw_set_ext(skfd, ifname, SIOCSIWENCODE, &wrq) < 0)
{
fprintf(stderr, "SIOCSIWENCODE(%d): %s\n",
errno, strerror(errno));
ret = -1;
}
}
/* Mask out index to minimise probability of reject when setting key */
flags = flags & (~IW_ENCODE_INDEX);
/* Set the key itself (set current key in this case) */
wrq.u.data.pointer = (caddr_t) info->key;
wrq.u.data.length = info->key_size;
wrq.u.data.flags = flags;
/* Compatibility with WE<13 */
if(flags & IW_ENCODE_NOKEY)
wrq.u.data.pointer = NULL;
if(iw_set_ext(skfd, ifname, SIOCSIWENCODE, &wrq) < 0)
{
fprintf(stderr, "SIOCSIWENCODE(%d): %s\n",
errno, strerror(errno));
ret = -1;
}
}
/* Set Network ID, if available (this is for non-802.11 cards) */
if(info->has_nwid)
{
memcpy(&(wrq.u.nwid), &(info->nwid), sizeof(iwparam));
wrq.u.nwid.fixed = 1; /* Hum... When in Rome... */
if(iw_set_ext(skfd, ifname, SIOCSIWNWID, &wrq) < 0)
{
fprintf(stderr, "SIOCSIWNWID: %s\n", strerror(errno));
ret = -1;
}
}
/* Set ESSID (extended network), if available.
* ESSID need to be last : most device re-perform the scanning/discovery
* when this is set, and things like encryption keys are better be
* defined if we want to discover the right set of APs/nodes.
*/
if(info->has_essid)
{
int we_kernel_version;
we_kernel_version = iw_get_kernel_we_version();
wrq.u.essid.pointer = (caddr_t) info->essid;
wrq.u.essid.length = strlen(info->essid);
wrq.u.data.flags = info->essid_on;
if(we_kernel_version < 21)
wrq.u.essid.length++;
if(iw_set_ext(skfd, ifname, SIOCSIWESSID, &wrq) < 0)
{
fprintf(stderr, "SIOCSIWESSID: %s\n", strerror(errno));
ret = -1;
}
}
return(ret);
}
/*********************** PROTOCOL SUBROUTINES ***********************/
/*
* Fun stuff with protocol identifiers (SIOCGIWNAME).
* We assume that drivers are returning sensible values in there,
* which is not always the case :-(
*/
/*------------------------------------------------------------------*/
/*
* Compare protocol identifiers.
* We don't want to know if the two protocols are the exactly same,
* but if they interoperate at some level, and also if they accept the
* same type of config (ESSID vs NWID, freq...).
* This is supposed to work around the alphabet soup.
* Return 1 if protocols are compatible, 0 otherwise
*/
int
iw_protocol_compare(const char * protocol1,
const char * protocol2)
{
const char * dot11 = "IEEE 802.11";
const char * dot11_ds = "Dbg";
const char * dot11_5g = "a";
/* If the strings are the same -> easy */
if(!strncmp(protocol1, protocol2, IFNAMSIZ))
return(1);
/* Are we dealing with one of the 802.11 variant ? */
if( (!strncmp(protocol1, dot11, strlen(dot11))) &&
(!strncmp(protocol2, dot11, strlen(dot11))) )
{
const char * sub1 = protocol1 + strlen(dot11);
const char * sub2 = protocol2 + strlen(dot11);
unsigned int i;
int isds1 = 0;
int isds2 = 0;
int is5g1 = 0;
int is5g2 = 0;
/* Check if we find the magic letters telling it's DS compatible */
for(i = 0; i < strlen(dot11_ds); i++)
{
if(strchr(sub1, dot11_ds[i]) != NULL)
isds1 = 1;
if(strchr(sub2, dot11_ds[i]) != NULL)
isds2 = 1;
}
if(isds1 && isds2)
return(1);
/* Check if we find the magic letters telling it's 5GHz compatible */
for(i = 0; i < strlen(dot11_5g); i++)
{
if(strchr(sub1, dot11_5g[i]) != NULL)
is5g1 = 1;
if(strchr(sub2, dot11_5g[i]) != NULL)
is5g2 = 1;
}
if(is5g1 && is5g2)
return(1);
}
/* Not compatible */
return(0);
}
/************************ ESSID SUBROUTINES ************************/
/*
* The ESSID identify 802.11 networks, and is an array if 32 bytes.
* Most people use it as an ASCII string, and are happy with it.
* However, any byte is valid, including the NUL character. Characters
* beyond the ASCII range are interpreted according to the locale and
* the OS, which is somethign we don't control (network of other
* people).
* Routines in here try to deal with that in asafe way.
*/
/*------------------------------------------------------------------*/
/*
* Escape non-ASCII characters from ESSID.
* This allow us to display those weirds characters to the user.
*
* Source is 32 bytes max.
* Destination buffer needs to be at least 129 bytes, will be NUL
* terminated.
*/
void
iw_essid_escape(char * dest,
const char * src,
const int slen)
{
const unsigned char * s = (const unsigned char *) src;
const unsigned char * e = s + slen;
char * d = dest;
/* Look every character of the string */
while(s < e)
{
int isescape;
/* Escape the escape to avoid ambiguity.
* We do a fast path test for performance reason. Compiler will
* optimise all that ;-) */
if(*s == '\\')
{
/* Check if we would confuse it with an escape sequence */
if((e-s) > 4 && (s[1] == 'x')
&& (isxdigit(s[2])) && (isxdigit(s[3])))
{
isescape = 1;
}
else
isescape = 0;
}
else
isescape = 0;
/* Is it a non-ASCII character ??? */
if(isescape || !isascii(*s) || iscntrl(*s))
{
/* Escape */
sprintf(d, "\\x%02X", *s);
d += 4;
}
else
{
/* Plain ASCII, just copy */
*d = *s;
d++;