nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* |
2 | * Wireless Tools |
||
3 | * |
||
4 | * Jean II - HPLB 97->99 - HPL 99->07 |
||
5 | * |
||
6 | * Common subroutines to all the wireless tools... |
||
7 | * |
||
8 | * This file is released under the GPL license. |
||
9 | * Copyright (c) 1997-2007 Jean Tourrilhes <jt@hpl.hp.com> |
||
10 | */ |
||
11 | |||
12 | /***************************** INCLUDES *****************************/ |
||
13 | |||
14 | #include "iwlib.h" /* Header */ |
||
15 | |||
16 | /************************ CONSTANTS & MACROS ************************/ |
||
17 | |||
18 | /* |
||
19 | * Constants fof WE-9->15 |
||
20 | */ |
||
21 | #define IW15_MAX_FREQUENCIES 16 |
||
22 | #define IW15_MAX_BITRATES 8 |
||
23 | #define IW15_MAX_TXPOWER 8 |
||
24 | #define IW15_MAX_ENCODING_SIZES 8 |
||
25 | #define IW15_MAX_SPY 8 |
||
26 | #define IW15_MAX_AP 8 |
||
27 | |||
28 | /****************************** TYPES ******************************/ |
||
29 | |||
30 | /* |
||
31 | * Struct iw_range up to WE-15 |
||
32 | */ |
||
33 | struct iw15_range |
||
34 | { |
||
35 | __u32 throughput; |
||
36 | __u32 min_nwid; |
||
37 | __u32 max_nwid; |
||
38 | __u16 num_channels; |
||
39 | __u8 num_frequency; |
||
40 | struct iw_freq freq[IW15_MAX_FREQUENCIES]; |
||
41 | __s32 sensitivity; |
||
42 | struct iw_quality max_qual; |
||
43 | __u8 num_bitrates; |
||
44 | __s32 bitrate[IW15_MAX_BITRATES]; |
||
45 | __s32 min_rts; |
||
46 | __s32 max_rts; |
||
47 | __s32 min_frag; |
||
48 | __s32 max_frag; |
||
49 | __s32 min_pmp; |
||
50 | __s32 max_pmp; |
||
51 | __s32 min_pmt; |
||
52 | __s32 max_pmt; |
||
53 | __u16 pmp_flags; |
||
54 | __u16 pmt_flags; |
||
55 | __u16 pm_capa; |
||
56 | __u16 encoding_size[IW15_MAX_ENCODING_SIZES]; |
||
57 | __u8 num_encoding_sizes; |
||
58 | __u8 max_encoding_tokens; |
||
59 | __u16 txpower_capa; |
||
60 | __u8 num_txpower; |
||
61 | __s32 txpower[IW15_MAX_TXPOWER]; |
||
62 | __u8 we_version_compiled; |
||
63 | __u8 we_version_source; |
||
64 | __u16 retry_capa; |
||
65 | __u16 retry_flags; |
||
66 | __u16 r_time_flags; |
||
67 | __s32 min_retry; |
||
68 | __s32 max_retry; |
||
69 | __s32 min_r_time; |
||
70 | __s32 max_r_time; |
||
71 | struct iw_quality avg_qual; |
||
72 | }; |
||
73 | |||
74 | /* |
||
75 | * Union for all the versions of iwrange. |
||
76 | * Fortunately, I mostly only add fields at the end, and big-bang |
||
77 | * reorganisations are few. |
||
78 | */ |
||
79 | union iw_range_raw |
||
80 | { |
||
81 | struct iw15_range range15; /* WE 9->15 */ |
||
82 | struct iw_range range; /* WE 16->current */ |
||
83 | }; |
||
84 | |||
85 | /* |
||
86 | * Offsets in iw_range struct |
||
87 | */ |
||
88 | #define iwr15_off(f) ( ((char *) &(((struct iw15_range *) NULL)->f)) - \ |
||
89 | (char *) NULL) |
||
90 | #define iwr_off(f) ( ((char *) &(((struct iw_range *) NULL)->f)) - \ |
||
91 | (char *) NULL) |
||
92 | |||
93 | /**************************** VARIABLES ****************************/ |
||
94 | |||
95 | /* Modes as human readable strings */ |
||
96 | const char * const iw_operation_mode[] = { "Auto", |
||
97 | "Ad-Hoc", |
||
98 | "Managed", |
||
99 | "Master", |
||
100 | "Repeater", |
||
101 | "Secondary", |
||
102 | "Monitor", |
||
103 | "Unknown/bug" }; |
||
104 | |||
105 | /* Modulations as human readable strings */ |
||
106 | const struct iw_modul_descr iw_modul_list[] = { |
||
107 | /* Start with aggregate types, so that they display first */ |
||
108 | { IW_MODUL_11AG, "11ag", |
||
109 | "IEEE 802.11a + 802.11g (2.4 & 5 GHz, up to 54 Mb/s)" }, |
||
110 | { IW_MODUL_11AB, "11ab", |
||
111 | "IEEE 802.11a + 802.11b (2.4 & 5 GHz, up to 54 Mb/s)" }, |
||
112 | { IW_MODUL_11G, "11g", "IEEE 802.11g (2.4 GHz, up to 54 Mb/s)" }, |
||
113 | { IW_MODUL_11A, "11a", "IEEE 802.11a (5 GHz, up to 54 Mb/s)" }, |
||
114 | { IW_MODUL_11B, "11b", "IEEE 802.11b (2.4 GHz, up to 11 Mb/s)" }, |
||
115 | |||
116 | /* Proprietary aggregates */ |
||
117 | { IW_MODUL_TURBO | IW_MODUL_11A, "turboa", |
||
118 | "Atheros turbo mode at 5 GHz (up to 108 Mb/s)" }, |
||
119 | { IW_MODUL_TURBO | IW_MODUL_11G, "turbog", |
||
120 | "Atheros turbo mode at 2.4 GHz (up to 108 Mb/s)" }, |
||
121 | { IW_MODUL_PBCC | IW_MODUL_11B, "11+", |
||
122 | "TI 802.11+ (2.4 GHz, up to 22 Mb/s)" }, |
||
123 | |||
124 | /* Individual modulations */ |
||
125 | { IW_MODUL_OFDM_G, "OFDMg", |
||
126 | "802.11g higher rates, OFDM at 2.4 GHz (up to 54 Mb/s)" }, |
||
127 | { IW_MODUL_OFDM_A, "OFDMa", "802.11a, OFDM at 5 GHz (up to 54 Mb/s)" }, |
||
128 | { IW_MODUL_CCK, "CCK", "802.11b higher rates (2.4 GHz, up to 11 Mb/s)" }, |
||
129 | { IW_MODUL_DS, "DS", "802.11 Direct Sequence (2.4 GHz, up to 2 Mb/s)" }, |
||
130 | { IW_MODUL_FH, "FH", "802.11 Frequency Hopping (2,4 GHz, up to 2 Mb/s)" }, |
||
131 | |||
132 | /* Proprietary modulations */ |
||
133 | { IW_MODUL_TURBO, "turbo", |
||
134 | "Atheros turbo mode, channel bonding (up to 108 Mb/s)" }, |
||
135 | { IW_MODUL_PBCC, "PBCC", |
||
136 | "TI 802.11+ higher rates (2.4 GHz, up to 22 Mb/s)" }, |
||
137 | { IW_MODUL_CUSTOM, "custom", |
||
138 | "Driver specific modulation (check driver documentation)" }, |
||
139 | }; |
||
140 | |||
141 | /* Disable runtime version warning in iw_get_range_info() */ |
||
142 | int iw_ignore_version = 0; |
||
143 | |||
144 | /************************ SOCKET SUBROUTINES *************************/ |
||
145 | |||
146 | /*------------------------------------------------------------------*/ |
||
147 | /* |
||
148 | * Open a socket. |
||
149 | * Depending on the protocol present, open the right socket. The socket |
||
150 | * will allow us to talk to the driver. |
||
151 | */ |
||
152 | int |
||
153 | iw_sockets_open(void) |
||
154 | { |
||
155 | static const int families[] = { |
||
156 | AF_INET, AF_IPX, AF_AX25, AF_APPLETALK |
||
157 | }; |
||
158 | unsigned int i; |
||
159 | int sock; |
||
160 | |||
161 | /* |
||
162 | * Now pick any (exisiting) useful socket family for generic queries |
||
163 | * Note : don't open all the socket, only returns when one matches, |
||
164 | * all protocols might not be valid. |
||
165 | * Workaround by Jim Kaba <jkaba@sarnoff.com> |
||
166 | * Note : in 99% of the case, we will just open the inet_sock. |
||
167 | * The remaining 1% case are not fully correct... |
||
168 | */ |
||
169 | |||
170 | /* Try all families we support */ |
||
171 | for(i = 0; i < sizeof(families)/sizeof(int); ++i) |
||
172 | { |
||
173 | /* Try to open the socket, if success returns it */ |
||
174 | sock = socket(families[i], SOCK_DGRAM, 0); |
||
175 | if(sock >= 0) |
||
176 | return sock; |
||
177 | } |
||
178 | |||
179 | return -1; |
||
180 | } |
||
181 | |||
182 | /*------------------------------------------------------------------*/ |
||
183 | /* |
||
184 | * Extract the interface name out of /proc/net/wireless or /proc/net/dev. |
||
185 | */ |
||
186 | static inline char * |
||
187 | iw_get_ifname(char * name, /* Where to store the name */ |
||
188 | int nsize, /* Size of name buffer */ |
||
189 | char * buf) /* Current position in buffer */ |
||
190 | { |
||
191 | char * end; |
||
192 | |||
193 | /* Skip leading spaces */ |
||
194 | while(isspace(*buf)) |
||
195 | buf++; |
||
196 | |||
197 | #ifndef IW_RESTRIC_ENUM |
||
198 | /* Get name up to the last ':'. Aliases may contain ':' in them, |
||
199 | * but the last one should be the separator */ |
||
200 | end = strrchr(buf, ':'); |
||
201 | #else |
||
202 | /* Get name up to ": " |
||
203 | * Note : we compare to ": " to make sure to process aliased interfaces |
||
204 | * properly. Doesn't work on /proc/net/dev, because it doesn't guarantee |
||
205 | * a ' ' after the ':'*/ |
||
206 | end = strstr(buf, ": "); |
||
207 | #endif |
||
208 | |||
209 | /* Not found ??? To big ??? */ |
||
210 | if((end == NULL) || (((end - buf) + 1) > nsize)) |
||
211 | return(NULL); |
||
212 | |||
213 | /* Copy */ |
||
214 | memcpy(name, buf, (end - buf)); |
||
215 | name[end - buf] = '\0'; |
||
216 | |||
217 | /* Return value currently unused, just make sure it's non-NULL */ |
||
218 | return(end); |
||
219 | } |
||
220 | |||
221 | /*------------------------------------------------------------------*/ |
||
222 | /* |
||
223 | * Enumerate devices and call specified routine |
||
224 | * The new way just use /proc/net/wireless, so get all wireless interfaces, |
||
225 | * whether configured or not. This is the default if available. |
||
226 | * The old way use SIOCGIFCONF, so get only configured interfaces (wireless |
||
227 | * or not). |
||
228 | */ |
||
229 | void |
||
230 | iw_enum_devices(int skfd, |
||
231 | iw_enum_handler fn, |
||
232 | char * args[], |
||
233 | int count) |
||
234 | { |
||
235 | char buff[1024]; |
||
236 | FILE * fh; |
||
237 | struct ifconf ifc; |
||
238 | struct ifreq *ifr; |
||
239 | int i; |
||
240 | |||
241 | #ifndef IW_RESTRIC_ENUM |
||
242 | /* Check if /proc/net/dev is available */ |
||
243 | fh = fopen(PROC_NET_DEV, "r"); |
||
244 | #else |
||
245 | /* Check if /proc/net/wireless is available */ |
||
246 | fh = fopen(PROC_NET_WIRELESS, "r"); |
||
247 | #endif |
||
248 | |||
249 | if(fh != NULL) |
||
250 | { |
||
251 | /* Success : use data from /proc/net/wireless */ |
||
252 | |||
253 | /* Eat 2 lines of header */ |
||
254 | fgets(buff, sizeof(buff), fh); |
||
255 | fgets(buff, sizeof(buff), fh); |
||
256 | |||
257 | /* Read each device line */ |
||
258 | while(fgets(buff, sizeof(buff), fh)) |
||
259 | { |
||
260 | char name[IFNAMSIZ + 1]; |
||
261 | char *s; |
||
262 | |||
263 | /* Skip empty or almost empty lines. It seems that in some |
||
264 | * cases fgets return a line with only a newline. */ |
||
265 | if((buff[0] == '\0') || (buff[1] == '\0')) |
||
266 | continue; |
||
267 | |||
268 | /* Extract interface name */ |
||
269 | s = iw_get_ifname(name, sizeof(name), buff); |
||
270 | |||
271 | if(!s) |
||
272 | { |
||
273 | /* Failed to parse, complain and continue */ |
||
274 | #ifndef IW_RESTRIC_ENUM |
||
275 | fprintf(stderr, "Cannot parse " PROC_NET_DEV "\n"); |
||
276 | #else |
||
277 | fprintf(stderr, "Cannot parse " PROC_NET_WIRELESS "\n"); |
||
278 | #endif |
||
279 | } |
||
280 | else |
||
281 | /* Got it, print info about this interface */ |
||
282 | (*fn)(skfd, name, args, count); |
||
283 | } |
||
284 | |||
285 | fclose(fh); |
||
286 | } |
||
287 | else |
||
288 | { |
||
289 | /* Get list of configured devices using "traditional" way */ |
||
290 | ifc.ifc_len = sizeof(buff); |
||
291 | ifc.ifc_buf = buff; |
||
292 | if(ioctl(skfd, SIOCGIFCONF, &ifc) < 0) |
||
293 | { |
||
294 | fprintf(stderr, "SIOCGIFCONF: %s\n", strerror(errno)); |
||
295 | return; |
||
296 | } |
||
297 | ifr = ifc.ifc_req; |
||
298 | |||
299 | /* Print them */ |
||
300 | for(i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; ifr++) |
||
301 | (*fn)(skfd, ifr->ifr_name, args, count); |
||
302 | } |
||
303 | } |
||
304 | |||
305 | /*********************** WIRELESS SUBROUTINES ************************/ |
||
306 | |||
307 | /*------------------------------------------------------------------*/ |
||
308 | /* |
||
309 | * Extract WE version number from /proc/net/wireless |
||
310 | * In most cases, you really want to get version information from |
||
311 | * the range info (range->we_version_compiled), see below... |
||
312 | * |
||
313 | * If we have WE-16 and later, the WE version is available at the |
||
314 | * end of the header line of the file. |
||
315 | * For version prior to that, we can only detect the change from |
||
316 | * v11 to v12, so we do an approximate job. Fortunately, v12 to v15 |
||
317 | * are highly binary compatible (on the struct level). |
||
318 | */ |
||
319 | int |
||
320 | iw_get_kernel_we_version(void) |
||
321 | { |
||
322 | char buff[1024]; |
||
323 | FILE * fh; |
||
324 | char * p; |
||
325 | int v; |
||
326 | |||
327 | /* Check if /proc/net/wireless is available */ |
||
328 | fh = fopen(PROC_NET_WIRELESS, "r"); |
||
329 | |||
330 | if(fh == NULL) |
||
331 | { |
||
332 | fprintf(stderr, "Cannot read " PROC_NET_WIRELESS "\n"); |
||
333 | return(-1); |
||
334 | } |
||
335 | |||
336 | /* Read the first line of buffer */ |
||
337 | fgets(buff, sizeof(buff), fh); |
||
338 | |||
339 | if(strstr(buff, "| WE") == NULL) |
||
340 | { |
||
341 | /* Prior to WE16, so explicit version not present */ |
||
342 | |||
343 | /* Black magic */ |
||
344 | if(strstr(buff, "| Missed") == NULL) |
||
345 | v = 11; |
||
346 | else |
||
347 | v = 15; |
||
348 | fclose(fh); |
||
349 | return(v); |
||
350 | } |
||
351 | |||
352 | /* Read the second line of buffer */ |
||
353 | fgets(buff, sizeof(buff), fh); |
||
354 | |||
355 | /* Get to the last separator, to get the version */ |
||
356 | p = strrchr(buff, '|'); |
||
357 | if((p == NULL) || (sscanf(p + 1, "%d", &v) != 1)) |
||
358 | { |
||
359 | fprintf(stderr, "Cannot parse " PROC_NET_WIRELESS "\n"); |
||
360 | fclose(fh); |
||
361 | return(-1); |
||
362 | } |
||
363 | |||
364 | fclose(fh); |
||
365 | return(v); |
||
366 | } |
||
367 | |||
368 | /*------------------------------------------------------------------*/ |
||
369 | /* |
||
370 | * Print the WE versions of the interface. |
||
371 | */ |
||
372 | static int |
||
373 | print_iface_version_info(int skfd, |
||
374 | char * ifname, |
||
375 | char * args[], /* Command line args */ |
||
376 | int count) /* Args count */ |
||
377 | { |
||
378 | struct iwreq wrq; |
||
379 | char buffer[sizeof(iwrange) * 2]; /* Large enough */ |
||
380 | struct iw_range * range; |
||
381 | |||
382 | /* Avoid "Unused parameter" warning */ |
||
383 | args = args; count = count; |
||
384 | |||
385 | /* If no wireless name : no wireless extensions. |
||
386 | * This enable us to treat the SIOCGIWRANGE failure below properly. */ |
||
387 | if(iw_get_ext(skfd, ifname, SIOCGIWNAME, &wrq) < 0) |
||
388 | return(-1); |
||
389 | |||
390 | /* Cleanup */ |
||
391 | memset(buffer, 0, sizeof(buffer)); |
||
392 | |||
393 | wrq.u.data.pointer = (caddr_t) buffer; |
||
394 | wrq.u.data.length = sizeof(buffer); |
||
395 | wrq.u.data.flags = 0; |
||
396 | if(iw_get_ext(skfd, ifname, SIOCGIWRANGE, &wrq) < 0) |
||
397 | { |
||
398 | /* Interface support WE (see above), but not IWRANGE */ |
||
399 | fprintf(stderr, "%-8.16s Driver has no Wireless Extension version information.\n\n", ifname); |
||
400 | return(0); |
||
401 | } |
||
402 | |||
403 | /* Copy stuff at the right place, ignore extra */ |
||
404 | range = (struct iw_range *) buffer; |
||
405 | |||
406 | /* For new versions, we can check the version directly, for old versions |
||
407 | * we use magic. 300 bytes is a also magic number, don't touch... */ |
||
408 | if(wrq.u.data.length >= 300) |
||
409 | { |
||
410 | /* Version is always at the same offset, so it's ok */ |
||
411 | printf("%-8.16s Recommend Wireless Extension v%d or later,\n", |
||
412 | ifname, range->we_version_source); |
||
413 | printf(" Currently compiled with Wireless Extension v%d.\n\n", |
||
414 | range->we_version_compiled); |
||
415 | } |
||
416 | else |
||
417 | { |
||
418 | fprintf(stderr, "%-8.16s Wireless Extension version too old.\n\n", |
||
419 | ifname); |
||
420 | } |
||
421 | |||
422 | |||
423 | return(0); |
||
424 | } |
||
425 | |||
426 | /*------------------------------------------------------------------*/ |
||
427 | /* |
||
428 | * Print the WE versions of the tools. |
||
429 | */ |
||
430 | int |
||
431 | iw_print_version_info(const char * toolname) |
||
432 | { |
||
433 | int skfd; /* generic raw socket desc. */ |
||
434 | int we_kernel_version; |
||
435 | |||
436 | /* Create a channel to the NET kernel. */ |
||
437 | if((skfd = iw_sockets_open()) < 0) |
||
438 | { |
||
439 | perror("socket"); |
||
440 | return -1; |
||
441 | } |
||
442 | |||
443 | /* Information about the tools themselves */ |
||
444 | if(toolname != NULL) |
||
445 | printf("%-8.16s Wireless-Tools version %d\n", toolname, WT_VERSION); |
||
446 | printf(" Compatible with Wireless Extension v11 to v%d.\n\n", |
||
447 | WE_MAX_VERSION); |
||
448 | |||
449 | /* Get version from kernel */ |
||
450 | we_kernel_version = iw_get_kernel_we_version(); |
||
451 | /* Only version >= 16 can be verified, other are guessed */ |
||
452 | if(we_kernel_version > 15) |
||
453 | printf("Kernel Currently compiled with Wireless Extension v%d.\n\n", |
||
454 | we_kernel_version); |
||
455 | |||
456 | /* Version for each device */ |
||
457 | iw_enum_devices(skfd, &print_iface_version_info, NULL, 0); |
||
458 | |||
459 | iw_sockets_close(skfd); |
||
460 | |||
461 | return 0; |
||
462 | } |
||
463 | |||
464 | /*------------------------------------------------------------------*/ |
||
465 | /* |
||
466 | * Get the range information out of the driver |
||
467 | */ |
||
468 | int |
||
469 | iw_get_range_info(int skfd, |
||
470 | const char * ifname, |
||
471 | iwrange * range) |
||
472 | { |
||
473 | struct iwreq wrq; |
||
474 | char buffer[sizeof(iwrange) * 2]; /* Large enough */ |
||
475 | union iw_range_raw * range_raw; |
||
476 | |||
477 | /* Cleanup */ |
||
478 | bzero(buffer, sizeof(buffer)); |
||
479 | |||
480 | wrq.u.data.pointer = (caddr_t) buffer; |
||
481 | wrq.u.data.length = sizeof(buffer); |
||
482 | wrq.u.data.flags = 0; |
||
483 | if(iw_get_ext(skfd, ifname, SIOCGIWRANGE, &wrq) < 0) |
||
484 | return(-1); |
||
485 | |||
486 | /* Point to the buffer */ |
||
487 | range_raw = (union iw_range_raw *) buffer; |
||
488 | |||
489 | /* For new versions, we can check the version directly, for old versions |
||
490 | * we use magic. 300 bytes is a also magic number, don't touch... */ |
||
491 | if(wrq.u.data.length < 300) |
||
492 | { |
||
493 | /* That's v10 or earlier. Ouch ! Let's make a guess...*/ |
||
494 | range_raw->range.we_version_compiled = 9; |
||
495 | } |
||
496 | |||
497 | /* Check how it needs to be processed */ |
||
498 | if(range_raw->range.we_version_compiled > 15) |
||
499 | { |
||
500 | /* This is our native format, that's easy... */ |
||
501 | /* Copy stuff at the right place, ignore extra */ |
||
502 | memcpy((char *) range, buffer, sizeof(iwrange)); |
||
503 | } |
||
504 | else |
||
505 | { |
||
506 | /* Zero unknown fields */ |
||
507 | bzero((char *) range, sizeof(struct iw_range)); |
||
508 | |||
509 | /* Initial part unmoved */ |
||
510 | memcpy((char *) range, |
||
511 | buffer, |
||
512 | iwr15_off(num_channels)); |
||
513 | /* Frequencies pushed futher down towards the end */ |
||
514 | memcpy((char *) range + iwr_off(num_channels), |
||
515 | buffer + iwr15_off(num_channels), |
||
516 | iwr15_off(sensitivity) - iwr15_off(num_channels)); |
||
517 | /* This one moved up */ |
||
518 | memcpy((char *) range + iwr_off(sensitivity), |
||
519 | buffer + iwr15_off(sensitivity), |
||
520 | iwr15_off(num_bitrates) - iwr15_off(sensitivity)); |
||
521 | /* This one goes after avg_qual */ |
||
522 | memcpy((char *) range + iwr_off(num_bitrates), |
||
523 | buffer + iwr15_off(num_bitrates), |
||
524 | iwr15_off(min_rts) - iwr15_off(num_bitrates)); |
||
525 | /* Number of bitrates has changed, put it after */ |
||
526 | memcpy((char *) range + iwr_off(min_rts), |
||
527 | buffer + iwr15_off(min_rts), |
||
528 | iwr15_off(txpower_capa) - iwr15_off(min_rts)); |
||
529 | /* Added encoding_login_index, put it after */ |
||
530 | memcpy((char *) range + iwr_off(txpower_capa), |
||
531 | buffer + iwr15_off(txpower_capa), |
||
532 | iwr15_off(txpower) - iwr15_off(txpower_capa)); |
||
533 | /* Hum... That's an unexpected glitch. Bummer. */ |
||
534 | memcpy((char *) range + iwr_off(txpower), |
||
535 | buffer + iwr15_off(txpower), |
||
536 | iwr15_off(avg_qual) - iwr15_off(txpower)); |
||
537 | /* Avg qual moved up next to max_qual */ |
||
538 | memcpy((char *) range + iwr_off(avg_qual), |
||
539 | buffer + iwr15_off(avg_qual), |
||
540 | sizeof(struct iw_quality)); |
||
541 | } |
||
542 | |||
543 | /* We are now checking much less than we used to do, because we can |
||
544 | * accomodate more WE version. But, there are still cases where things |
||
545 | * will break... */ |
||
546 | if(!iw_ignore_version) |
||
547 | { |
||
548 | /* We don't like very old version (unfortunately kernel 2.2.X) */ |
||
549 | if(range->we_version_compiled <= 10) |
||
550 | { |
||
551 | fprintf(stderr, "Warning: Driver for device %s has been compiled with an ancient version\n", ifname); |
||
552 | fprintf(stderr, "of Wireless Extension, while this program support version 11 and later.\n"); |
||
553 | fprintf(stderr, "Some things may be broken...\n\n"); |
||
554 | } |
||
555 | |||
556 | /* We don't like future versions of WE, because we can't cope with |
||
557 | * the unknown */ |
||
558 | if(range->we_version_compiled > WE_MAX_VERSION) |
||
559 | { |
||
560 | fprintf(stderr, "Warning: Driver for device %s has been compiled with version %d\n", ifname, range->we_version_compiled); |
||
561 | fprintf(stderr, "of Wireless Extension, while this program supports up to version %d.\n", WE_MAX_VERSION); |
||
562 | fprintf(stderr, "Some things may be broken...\n\n"); |
||
563 | } |
||
564 | |||
565 | /* Driver version verification */ |
||
566 | if((range->we_version_compiled > 10) && |
||
567 | (range->we_version_compiled < range->we_version_source)) |
||
568 | { |
||
569 | fprintf(stderr, "Warning: Driver for device %s recommend version %d of Wireless Extension,\n", ifname, range->we_version_source); |
||
570 | fprintf(stderr, "but has been compiled with version %d, therefore some driver features\n", range->we_version_compiled); |
||
571 | fprintf(stderr, "may not be available...\n\n"); |
||
572 | } |
||
573 | /* Note : we are only trying to catch compile difference, not source. |
||
574 | * If the driver source has not been updated to the latest, it doesn't |
||
575 | * matter because the new fields are set to zero */ |
||
576 | } |
||
577 | |||
578 | /* Don't complain twice. |
||
579 | * In theory, the test apply to each individual driver, but usually |
||
580 | * all drivers are compiled from the same kernel. */ |
||
581 | iw_ignore_version = 1; |
||
582 | |||
583 | return(0); |
||
584 | } |
||
585 | |||
586 | /*------------------------------------------------------------------*/ |
||
587 | /* |
||
588 | * Get information about what private ioctls are supported by the driver |
||
589 | * |
||
590 | * Note : there is one danger using this function. If it return 0, you |
||
591 | * still need to free() the buffer. Beware. |
||
592 | */ |
||
593 | int |
||
594 | iw_get_priv_info(int skfd, |
||
595 | const char * ifname, |
||
596 | iwprivargs ** ppriv) |
||
597 | { |
||
598 | struct iwreq wrq; |
||
599 | iwprivargs * priv = NULL; /* Not allocated yet */ |
||
600 | int maxpriv = 16; /* Minimum for compatibility WE<13 */ |
||
601 | iwprivargs * newpriv; |
||
602 | |||
603 | /* Some driver may return a very large number of ioctls. Some |
||
604 | * others a very small number. We now use a dynamic allocation |
||
605 | * of the array to satisfy everybody. Of course, as we don't know |
||
606 | * in advance the size of the array, we try various increasing |
||
607 | * sizes. Jean II */ |
||
608 | do |
||
609 | { |
||
610 | /* (Re)allocate the buffer */ |
||
611 | newpriv = realloc(priv, maxpriv * sizeof(priv[0])); |
||
612 | if(newpriv == NULL) |
||
613 | { |
||
614 | fprintf(stderr, "%s: Allocation failed\n", __FUNCTION__); |
||
615 | break; |
||
616 | } |
||
617 | priv = newpriv; |
||
618 | |||
619 | /* Ask the driver if it's large enough */ |
||
620 | wrq.u.data.pointer = (caddr_t) priv; |
||
621 | wrq.u.data.length = maxpriv; |
||
622 | wrq.u.data.flags = 0; |
||
623 | if(iw_get_ext(skfd, ifname, SIOCGIWPRIV, &wrq) >= 0) |
||
624 | { |
||
625 | /* Success. Pass the buffer by pointer */ |
||
626 | *ppriv = priv; |
||
627 | /* Return the number of ioctls */ |
||
628 | return(wrq.u.data.length); |
||
629 | } |
||
630 | |||
631 | /* Only E2BIG means the buffer was too small, abort on other errors */ |
||
632 | if(errno != E2BIG) |
||
633 | { |
||
634 | /* Most likely "not supported". Don't barf. */ |
||
635 | break; |
||
636 | } |
||
637 | |||
638 | /* Failed. We probably need a bigger buffer. Check if the kernel |
||
639 | * gave us any hints. */ |
||
640 | if(wrq.u.data.length > maxpriv) |
||
641 | maxpriv = wrq.u.data.length; |
||
642 | else |
||
643 | maxpriv *= 2; |
||
644 | } |
||
645 | while(maxpriv < 1000); |
||
646 | |||
647 | /* Cleanup */ |
||
648 | if(priv) |
||
649 | free(priv); |
||
650 | *ppriv = NULL; |
||
651 | |||
652 | return(-1); |
||
653 | } |
||
654 | |||
655 | /*------------------------------------------------------------------*/ |
||
656 | /* |
||
657 | * Get essential wireless config from the device driver |
||
658 | * We will call all the classical wireless ioctl on the driver through |
||
659 | * the socket to know what is supported and to get the settings... |
||
660 | * Note : compare to the version in iwconfig, we extract only |
||
661 | * what's *really* needed to configure a device... |
||
662 | */ |
||
663 | int |
||
664 | iw_get_basic_config(int skfd, |
||
665 | const char * ifname, |
||
666 | wireless_config * info) |
||
667 | { |
||
668 | struct iwreq wrq; |
||
669 | |||
670 | memset((char *) info, 0, sizeof(struct wireless_config)); |
||
671 | |||
672 | /* Get wireless name */ |
||
673 | if(iw_get_ext(skfd, ifname, SIOCGIWNAME, &wrq) < 0) |
||
674 | /* If no wireless name : no wireless extensions */ |
||
675 | return(-1); |
||
676 | else |
||
677 | { |
||
678 | strncpy(info->name, wrq.u.name, IFNAMSIZ); |
||
679 | info->name[IFNAMSIZ] = '\0'; |
||
680 | } |
||
681 | |||
682 | /* Get network ID */ |
||
683 | if(iw_get_ext(skfd, ifname, SIOCGIWNWID, &wrq) >= 0) |
||
684 | { |
||
685 | info->has_nwid = 1; |
||
686 | memcpy(&(info->nwid), &(wrq.u.nwid), sizeof(iwparam)); |
||
687 | } |
||
688 | |||
689 | /* Get frequency / channel */ |
||
690 | if(iw_get_ext(skfd, ifname, SIOCGIWFREQ, &wrq) >= 0) |
||
691 | { |
||
692 | info->has_freq = 1; |
||
693 | info->freq = iw_freq2float(&(wrq.u.freq)); |
||
694 | info->freq_flags = wrq.u.freq.flags; |
||
695 | } |
||
696 | |||
697 | /* Get encryption information */ |
||
698 | wrq.u.data.pointer = (caddr_t) info->key; |
||
699 | wrq.u.data.length = IW_ENCODING_TOKEN_MAX; |
||
700 | wrq.u.data.flags = 0; |
||
701 | if(iw_get_ext(skfd, ifname, SIOCGIWENCODE, &wrq) >= 0) |
||
702 | { |
||
703 | info->has_key = 1; |
||
704 | info->key_size = wrq.u.data.length; |
||
705 | info->key_flags = wrq.u.data.flags; |
||
706 | } |
||
707 | |||
708 | /* Get ESSID */ |
||
709 | wrq.u.essid.pointer = (caddr_t) info->essid; |
||
710 | wrq.u.essid.length = IW_ESSID_MAX_SIZE + 1; |
||
711 | wrq.u.essid.flags = 0; |
||
712 | if(iw_get_ext(skfd, ifname, SIOCGIWESSID, &wrq) >= 0) |
||
713 | { |
||
714 | info->has_essid = 1; |
||
715 | info->essid_on = wrq.u.data.flags; |
||
716 | } |
||
717 | |||
718 | /* Get operation mode */ |
||
719 | if(iw_get_ext(skfd, ifname, SIOCGIWMODE, &wrq) >= 0) |
||
720 | { |
||
721 | info->has_mode = 1; |
||
722 | /* Note : event->u.mode is unsigned, no need to check <= 0 */ |
||
723 | if(wrq.u.mode < IW_NUM_OPER_MODE) |
||
724 | info->mode = wrq.u.mode; |
||
725 | else |
||
726 | info->mode = IW_NUM_OPER_MODE; /* Unknown/bug */ |
||
727 | } |
||
728 | |||
729 | return(0); |
||
730 | } |
||
731 | |||
732 | /*------------------------------------------------------------------*/ |
||
733 | /* |
||
734 | * Set essential wireless config in the device driver |
||
735 | * We will call all the classical wireless ioctl on the driver through |
||
736 | * the socket to know what is supported and to set the settings... |
||
737 | * We support only the restricted set as above... |
||
738 | */ |
||
739 | int |
||
740 | iw_set_basic_config(int skfd, |
||
741 | const char * ifname, |
||
742 | wireless_config * info) |
||
743 | { |
||
744 | struct iwreq wrq; |
||
745 | int ret = 0; |
||
746 | |||
747 | /* Get wireless name (check if interface is valid) */ |
||
748 | if(iw_get_ext(skfd, ifname, SIOCGIWNAME, &wrq) < 0) |
||
749 | /* If no wireless name : no wireless extensions */ |
||
750 | return(-2); |
||
751 | |||
752 | /* Set the current mode of operation |
||
753 | * Mode need to be first : some settings apply only in a specific mode |
||
754 | * (such as frequency). |
||
755 | */ |
||
756 | if(info->has_mode) |
||
757 | { |
||
758 | strncpy(wrq.ifr_name, ifname, IFNAMSIZ); |
||
759 | wrq.u.mode = info->mode; |
||
760 | |||
761 | if(iw_get_ext(skfd, ifname, SIOCSIWMODE, &wrq) < 0) |
||
762 | { |
||
763 | fprintf(stderr, "SIOCSIWMODE: %s\n", strerror(errno)); |
||
764 | ret = -1; |
||
765 | } |
||
766 | } |
||
767 | |||
768 | /* Set frequency / channel */ |
||
769 | if(info->has_freq) |
||
770 | { |
||
771 | iw_float2freq(info->freq, &(wrq.u.freq)); |
||
772 | |||
773 | if(iw_set_ext(skfd, ifname, SIOCSIWFREQ, &wrq) < 0) |
||
774 | { |
||
775 | fprintf(stderr, "SIOCSIWFREQ: %s\n", strerror(errno)); |
||
776 | ret = -1; |
||
777 | } |
||
778 | } |
||
779 | |||
780 | /* Set encryption information */ |
||
781 | if(info->has_key) |
||
782 | { |
||
783 | int flags = info->key_flags; |
||
784 | |||
785 | /* Check if there is a key index */ |
||
786 | if((flags & IW_ENCODE_INDEX) > 0) |
||
787 | { |
||
788 | /* Set the index */ |
||
789 | wrq.u.data.pointer = (caddr_t) NULL; |
||
790 | wrq.u.data.flags = (flags & (IW_ENCODE_INDEX)) | IW_ENCODE_NOKEY; |
||
791 | wrq.u.data.length = 0; |
||
792 | |||
793 | if(iw_set_ext(skfd, ifname, SIOCSIWENCODE, &wrq) < 0) |
||
794 | { |
||
795 | fprintf(stderr, "SIOCSIWENCODE(%d): %s\n", |
||
796 | errno, strerror(errno)); |
||
797 | ret = -1; |
||
798 | } |
||
799 | } |
||
800 | |||
801 | /* Mask out index to minimise probability of reject when setting key */ |
||
802 | flags = flags & (~IW_ENCODE_INDEX); |
||
803 | |||
804 | /* Set the key itself (set current key in this case) */ |
||
805 | wrq.u.data.pointer = (caddr_t) info->key; |
||
806 | wrq.u.data.length = info->key_size; |
||
807 | wrq.u.data.flags = flags; |
||
808 | |||
809 | /* Compatibility with WE<13 */ |
||
810 | if(flags & IW_ENCODE_NOKEY) |
||
811 | wrq.u.data.pointer = NULL; |
||
812 | |||
813 | if(iw_set_ext(skfd, ifname, SIOCSIWENCODE, &wrq) < 0) |
||
814 | { |
||
815 | fprintf(stderr, "SIOCSIWENCODE(%d): %s\n", |
||
816 | errno, strerror(errno)); |
||
817 | ret = -1; |
||
818 | } |
||
819 | } |
||
820 | |||
821 | /* Set Network ID, if available (this is for non-802.11 cards) */ |
||
822 | if(info->has_nwid) |
||
823 | { |
||
824 | memcpy(&(wrq.u.nwid), &(info->nwid), sizeof(iwparam)); |
||
825 | wrq.u.nwid.fixed = 1; /* Hum... When in Rome... */ |
||
826 | |||
827 | if(iw_set_ext(skfd, ifname, SIOCSIWNWID, &wrq) < 0) |
||
828 | { |
||
829 | fprintf(stderr, "SIOCSIWNWID: %s\n", strerror(errno)); |
||
830 | ret = -1; |
||
831 | } |
||
832 | } |
||
833 | |||
834 | /* Set ESSID (extended network), if available. |
||
835 | * ESSID need to be last : most device re-perform the scanning/discovery |
||
836 | * when this is set, and things like encryption keys are better be |
||
837 | * defined if we want to discover the right set of APs/nodes. |
||
838 | */ |
||
839 | if(info->has_essid) |
||
840 | { |
||
841 | int we_kernel_version; |
||
842 | we_kernel_version = iw_get_kernel_we_version(); |
||
843 | |||
844 | wrq.u.essid.pointer = (caddr_t) info->essid; |
||
845 | wrq.u.essid.length = strlen(info->essid); |
||
846 | wrq.u.data.flags = info->essid_on; |
||
847 | if(we_kernel_version < 21) |
||
848 | wrq.u.essid.length++; |
||
849 | |||
850 | if(iw_set_ext(skfd, ifname, SIOCSIWESSID, &wrq) < 0) |
||
851 | { |
||
852 | fprintf(stderr, "SIOCSIWESSID: %s\n", strerror(errno)); |
||
853 | ret = -1; |
||
854 | } |
||
855 | } |
||
856 | |||
857 | return(ret); |
||
858 | } |
||
859 | |||
860 | /*********************** PROTOCOL SUBROUTINES ***********************/ |
||
861 | /* |
||
862 | * Fun stuff with protocol identifiers (SIOCGIWNAME). |
||
863 | * We assume that drivers are returning sensible values in there, |
||
864 | * which is not always the case :-( |
||
865 | */ |
||
866 | |||
867 | /*------------------------------------------------------------------*/ |
||
868 | /* |
||
869 | * Compare protocol identifiers. |
||
870 | * We don't want to know if the two protocols are the exactly same, |
||
871 | * but if they interoperate at some level, and also if they accept the |
||
872 | * same type of config (ESSID vs NWID, freq...). |
||
873 | * This is supposed to work around the alphabet soup. |
||
874 | * Return 1 if protocols are compatible, 0 otherwise |
||
875 | */ |
||
876 | int |
||
877 | iw_protocol_compare(const char * protocol1, |
||
878 | const char * protocol2) |
||
879 | { |
||
880 | const char * dot11 = "IEEE 802.11"; |
||
881 | const char * dot11_ds = "Dbg"; |
||
882 | const char * dot11_5g = "a"; |
||
883 | |||
884 | /* If the strings are the same -> easy */ |
||
885 | if(!strncmp(protocol1, protocol2, IFNAMSIZ)) |
||
886 | return(1); |
||
887 | |||
888 | /* Are we dealing with one of the 802.11 variant ? */ |
||
889 | if( (!strncmp(protocol1, dot11, strlen(dot11))) && |
||
890 | (!strncmp(protocol2, dot11, strlen(dot11))) ) |
||
891 | { |
||
892 | const char * sub1 = protocol1 + strlen(dot11); |
||
893 | const char * sub2 = protocol2 + strlen(dot11); |
||
894 | unsigned int i; |
||
895 | int isds1 = 0; |
||
896 | int isds2 = 0; |
||
897 | int is5g1 = 0; |
||
898 | int is5g2 = 0; |
||
899 | |||
900 | /* Check if we find the magic letters telling it's DS compatible */ |
||
901 | for(i = 0; i < strlen(dot11_ds); i++) |
||
902 | { |
||
903 | if(strchr(sub1, dot11_ds[i]) != NULL) |
||
904 | isds1 = 1; |
||
905 | if(strchr(sub2, dot11_ds[i]) != NULL) |
||
906 | isds2 = 1; |
||
907 | } |
||
908 | if(isds1 && isds2) |
||
909 | return(1); |
||
910 | |||
911 | /* Check if we find the magic letters telling it's 5GHz compatible */ |
||
912 | for(i = 0; i < strlen(dot11_5g); i++) |
||
913 | { |
||
914 | if(strchr(sub1, dot11_5g[i]) != NULL) |
||
915 | is5g1 = 1; |
||
916 | if(strchr(sub2, dot11_5g[i]) != NULL) |
||
917 | is5g2 = 1; |
||
918 | } |
||
919 | if(is5g1 && is5g2) |
||
920 | return(1); |
||
921 | } |
||
922 | /* Not compatible */ |
||
923 | return(0); |
||
924 | } |
||
925 | |||
926 | /********************** FREQUENCY SUBROUTINES ***********************/ |
||
927 | /* |
||
928 | * Note : the two functions below are the cause of troubles on |
||
929 | * various embeeded platforms, as they are the reason we require |
||
930 | * libm (math library). |
||
931 | * In this case, please use enable BUILD_NOLIBM in the makefile |
||
932 | * |
||
933 | * FIXME : check negative mantissa and exponent |
||
934 | */ |
||
935 | |||
936 | /*------------------------------------------------------------------*/ |
||
937 | /* |
||
938 | * Convert a floating point the our internal representation of |
||
939 | * frequencies. |
||
940 | * The kernel doesn't want to hear about floating point, so we use |
||
941 | * this custom format instead. |
||
942 | */ |
||
943 | void |
||
944 | iw_float2freq(double in, |
||
945 | iwfreq * out) |
||
946 | { |
||
947 | #ifdef WE_NOLIBM |
||
948 | /* Version without libm : slower */ |
||
949 | out->e = 0; |
||
950 | while(in > 1e9) |
||
951 | { |
||
952 | in /= 10; |
||
953 | out->e++; |
||
954 | } |
||
955 | out->m = (long) in; |
||
956 | #else /* WE_NOLIBM */ |
||
957 | /* Version with libm : faster */ |
||
958 | out->e = (short) (floor(log10(in))); |
||
959 | if(out->e > 8) |
||
960 | { |
||
961 | out->m = ((long) (floor(in / pow(10,out->e - 6)))) * 100; |
||
962 | out->e -= 8; |
||
963 | } |
||
964 | else |
||
965 | { |
||
966 | out->m = (long) in; |
||
967 | out->e = 0; |
||
968 | } |
||
969 | #endif /* WE_NOLIBM */ |
||
970 | } |
||
971 | |||
972 | /*------------------------------------------------------------------*/ |
||
973 | /* |
||
974 | * Convert our internal representation of frequencies to a floating point. |
||
975 | */ |
||
976 | double |
||
977 | iw_freq2float(const iwfreq * in) |
||
978 | { |
||
979 | #ifdef WE_NOLIBM |
||
980 | /* Version without libm : slower */ |
||
981 | int i; |
||
982 | double res = (double) in->m; |
||
983 | for(i = 0; i < in->e; i++) |
||
984 | res *= 10; |
||
985 | return(res); |
||
986 | #else /* WE_NOLIBM */ |
||
987 | /* Version with libm : faster */ |
||
988 | return ((double) in->m) * pow(10,in->e); |
||
989 | #endif /* WE_NOLIBM */ |
||
990 | } |
||
991 | |||
992 | /*------------------------------------------------------------------*/ |
||
993 | /* |
||
994 | * Output a frequency with proper scaling |
||
995 | */ |
||
996 | void |
||
997 | iw_print_freq_value(char * buffer, |
||
998 | int buflen, |
||
999 | double freq) |
||
1000 | { |
||
1001 | if(freq < KILO) |
||
1002 | snprintf(buffer, buflen, "%g", freq); |
||
1003 | else |
||
1004 | { |
||
1005 | char scale; |
||
1006 | int divisor; |
||
1007 | |||
1008 | if(freq >= GIGA) |
||
1009 | { |
||
1010 | scale = 'G'; |
||
1011 | divisor = GIGA; |
||
1012 | } |
||
1013 | else |
||
1014 | { |
||
1015 | if(freq >= MEGA) |
||
1016 | { |
||
1017 | scale = 'M'; |
||
1018 | divisor = MEGA; |
||
1019 | } |
||
1020 | else |
||
1021 | { |
||
1022 | scale = 'k'; |
||
1023 | divisor = KILO; |
||
1024 | } |
||
1025 | } |
||
1026 | snprintf(buffer, buflen, "%g %cHz", freq / divisor, scale); |
||
1027 | } |
||
1028 | } |
||
1029 | |||
1030 | /*------------------------------------------------------------------*/ |
||
1031 | /* |
||
1032 | * Output a frequency with proper scaling |
||
1033 | */ |
||
1034 | void |
||
1035 | iw_print_freq(char * buffer, |
||
1036 | int buflen, |
||
1037 | double freq, |
||
1038 | int channel, |
||
1039 | int freq_flags) |
||
1040 | { |
||
1041 | char sep = ((freq_flags & IW_FREQ_FIXED) ? '=' : ':'); |
||
1042 | char vbuf[16]; |
||
1043 | |||
1044 | /* Print the frequency/channel value */ |
||
1045 | iw_print_freq_value(vbuf, sizeof(vbuf), freq); |
||
1046 | |||
1047 | /* Check if channel only */ |
||
1048 | if(freq < KILO) |
||
1049 | snprintf(buffer, buflen, "Channel%c%s", sep, vbuf); |
||
1050 | else |
||
1051 | { |
||
1052 | /* Frequency. Check if we have a channel as well */ |
||
1053 | if(channel >= 0) |
||
1054 | snprintf(buffer, buflen, "Frequency%c%s (Channel %d)", |
||
1055 | sep, vbuf, channel); |
||
1056 | else |
||
1057 | snprintf(buffer, buflen, "Frequency%c%s", sep, vbuf); |
||
1058 | } |
||
1059 | } |
||
1060 | |||
1061 | /*------------------------------------------------------------------*/ |
||
1062 | /* |
||
1063 | * Convert a frequency to a channel (negative -> error) |
||
1064 | */ |
||
1065 | int |
||
1066 | iw_freq_to_channel(double freq, |
||
1067 | const struct iw_range * range) |
||
1068 | { |
||
1069 | double ref_freq; |
||
1070 | int k; |
||
1071 | |||
1072 | /* Check if it's a frequency or not already a channel */ |
||
1073 | if(freq < KILO) |
||
1074 | return(-1); |
||
1075 | |||
1076 | /* We compare the frequencies as double to ignore differences |
||
1077 | * in encoding. Slower, but safer... */ |
||
1078 | for(k = 0; k < range->num_frequency; k++) |
||
1079 | { |
||
1080 | ref_freq = iw_freq2float(&(range->freq[k])); |
||
1081 | if(freq == ref_freq) |
||
1082 | return(range->freq[k].i); |
||
1083 | } |
||
1084 | /* Not found */ |
||
1085 | return(-2); |
||
1086 | } |
||
1087 | |||
1088 | /*------------------------------------------------------------------*/ |
||
1089 | /* |
||
1090 | * Convert a channel to a frequency (negative -> error) |
||
1091 | * Return the channel on success |
||
1092 | */ |
||
1093 | int |
||
1094 | iw_channel_to_freq(int channel, |
||
1095 | double * pfreq, |
||
1096 | const struct iw_range * range) |
||
1097 | { |
||
1098 | int has_freq = 0; |
||
1099 | int k; |
||
1100 | |||
1101 | /* Check if the driver support only channels or if it has frequencies */ |
||
1102 | for(k = 0; k < range->num_frequency; k++) |
||
1103 | { |
||
1104 | if((range->freq[k].e != 0) || (range->freq[k].m > (int) KILO)) |
||
1105 | has_freq = 1; |
||
1106 | } |
||
1107 | if(!has_freq) |
||
1108 | return(-1); |
||
1109 | |||
1110 | /* Find the correct frequency in the list */ |
||
1111 | for(k = 0; k < range->num_frequency; k++) |
||
1112 | { |
||
1113 | if(range->freq[k].i == channel) |
||
1114 | { |
||
1115 | *pfreq = iw_freq2float(&(range->freq[k])); |
||
1116 | return(channel); |
||
1117 | } |
||
1118 | } |
||
1119 | /* Not found */ |
||
1120 | return(-2); |
||
1121 | } |
||
1122 | |||
1123 | /*********************** BITRATE SUBROUTINES ***********************/ |
||
1124 | |||
1125 | /*------------------------------------------------------------------*/ |
||
1126 | /* |
||
1127 | * Output a bitrate with proper scaling |
||
1128 | */ |
||
1129 | void |
||
1130 | iw_print_bitrate(char * buffer, |
||
1131 | int buflen, |
||
1132 | int bitrate) |
||
1133 | { |
||
1134 | double rate = bitrate; |
||
1135 | char scale; |
||
1136 | int divisor; |
||
1137 | |||
1138 | if(rate >= GIGA) |
||
1139 | { |
||
1140 | scale = 'G'; |
||
1141 | divisor = GIGA; |
||
1142 | } |
||
1143 | else |
||
1144 | { |
||
1145 | if(rate >= MEGA) |
||
1146 | { |
||
1147 | scale = 'M'; |
||
1148 | divisor = MEGA; |
||
1149 | } |
||
1150 | else |
||
1151 | { |
||
1152 | scale = 'k'; |
||
1153 | divisor = KILO; |
||
1154 | } |
||
1155 | } |
||
1156 | snprintf(buffer, buflen, "%g %cb/s", rate / divisor, scale); |
||
1157 | } |
||
1158 | |||
1159 | /************************ POWER SUBROUTINES *************************/ |
||
1160 | |||
1161 | /*------------------------------------------------------------------*/ |
||
1162 | /* |
||
1163 | * Convert a value in dBm to a value in milliWatt. |
||
1164 | */ |
||
1165 | int |
||
1166 | iw_dbm2mwatt(int in) |
||
1167 | { |
||
1168 | #ifdef WE_NOLIBM |
||
1169 | /* Version without libm : slower */ |
||
1170 | int ip = in / 10; |
||
1171 | int fp = in % 10; |
||
1172 | int k; |
||
1173 | double res = 1.0; |
||
1174 | |||
1175 | /* Split integral and floating part to avoid accumulating rounding errors */ |
||
1176 | for(k = 0; k < ip; k++) |
||
1177 | res *= 10; |
||
1178 | for(k = 0; k < fp; k++) |
||
1179 | res *= LOG10_MAGIC; |
||
1180 | return((int) res); |
||
1181 | #else /* WE_NOLIBM */ |
||
1182 | /* Version with libm : faster */ |
||
1183 | return((int) (floor(pow(10.0, (((double) in) / 10.0))))); |
||
1184 | #endif /* WE_NOLIBM */ |
||
1185 | } |
||
1186 | |||
1187 | /*------------------------------------------------------------------*/ |
||
1188 | /* |
||
1189 | * Convert a value in milliWatt to a value in dBm. |
||
1190 | */ |
||
1191 | int |
||
1192 | iw_mwatt2dbm(int in) |
||
1193 | { |
||
1194 | #ifdef WE_NOLIBM |
||
1195 | /* Version without libm : slower */ |
||
1196 | double fin = (double) in; |
||
1197 | int res = 0; |
||
1198 | |||
1199 | /* Split integral and floating part to avoid accumulating rounding errors */ |
||
1200 | while(fin > 10.0) |
||
1201 | { |
||
1202 | res += 10; |
||
1203 | fin /= 10.0; |
||
1204 | } |
||
1205 | while(fin > 1.000001) /* Eliminate rounding errors, take ceil */ |
||
1206 | { |
||
1207 | res += 1; |
||
1208 | fin /= LOG10_MAGIC; |
||
1209 | } |
||
1210 | return(res); |
||
1211 | #else /* WE_NOLIBM */ |
||
1212 | /* Version with libm : faster */ |
||
1213 | return((int) (ceil(10.0 * log10((double) in)))); |
||
1214 | #endif /* WE_NOLIBM */ |
||
1215 | } |
||
1216 | |||
1217 | /*------------------------------------------------------------------*/ |
||
1218 | /* |
||
1219 | * Output a txpower with proper conversion |
||
1220 | */ |
||
1221 | void |
||
1222 | iw_print_txpower(char * buffer, |
||
1223 | int buflen, |
||
1224 | struct iw_param * txpower) |
||
1225 | { |
||
1226 | int dbm; |
||
1227 | |||
1228 | /* Check if disabled */ |
||
1229 | if(txpower->disabled) |
||
1230 | { |
||
1231 | snprintf(buffer, buflen, "off"); |
||
1232 | } |
||
1233 | else |
||
1234 | { |
||
1235 | /* Check for relative values */ |
||
1236 | if(txpower->flags & IW_TXPOW_RELATIVE) |
||
1237 | { |
||
1238 | snprintf(buffer, buflen, "%d", txpower->value); |
||
1239 | } |
||
1240 | else |
||
1241 | { |
||
1242 | /* Convert everything to dBm */ |
||
1243 | if(txpower->flags & IW_TXPOW_MWATT) |
||
1244 | dbm = iw_mwatt2dbm(txpower->value); |
||
1245 | else |
||
1246 | dbm = txpower->value; |
||
1247 | |||
1248 | /* Display */ |
||
1249 | snprintf(buffer, buflen, "%d dBm", dbm); |
||
1250 | } |
||
1251 | } |
||
1252 | } |
||
1253 | |||
1254 | /********************** STATISTICS SUBROUTINES **********************/ |
||
1255 | |||
1256 | /*------------------------------------------------------------------*/ |
||
1257 | /* |
||
1258 | * Read /proc/net/wireless to get the latest statistics |
||
1259 | * Note : strtok not thread safe, not used in WE-12 and later. |
||
1260 | */ |
||
1261 | int |
||
1262 | iw_get_stats(int skfd, |
||
1263 | const char * ifname, |
||
1264 | iwstats * stats, |
||
1265 | const iwrange * range, |
||
1266 | int has_range) |
||
1267 | { |
||
1268 | /* Fortunately, we can always detect this condition properly */ |
||
1269 | if((has_range) && (range->we_version_compiled > 11)) |
||
1270 | { |
||
1271 | struct iwreq wrq; |
||
1272 | wrq.u.data.pointer = (caddr_t) stats; |
||
1273 | wrq.u.data.length = sizeof(struct iw_statistics); |
||
1274 | wrq.u.data.flags = 1; /* Clear updated flag */ |
||
1275 | strncpy(wrq.ifr_name, ifname, IFNAMSIZ); |
||
1276 | if(iw_get_ext(skfd, ifname, SIOCGIWSTATS, &wrq) < 0) |
||
1277 | return(-1); |
||
1278 | |||
1279 | /* Format has not changed since WE-12, no conversion */ |
||
1280 | return(0); |
||
1281 | } |
||
1282 | else |
||
1283 | { |
||
1284 | FILE * f = fopen(PROC_NET_WIRELESS, "r"); |
||
1285 | char buf[256]; |
||
1286 | char * bp; |
||
1287 | int t; |
||
1288 | |||
1289 | if(f==NULL) |
||
1290 | return -1; |
||
1291 | /* Loop on all devices */ |
||
1292 | while(fgets(buf,255,f)) |
||
1293 | { |
||
1294 | bp=buf; |
||
1295 | while(*bp&&isspace(*bp)) |
||
1296 | bp++; |
||
1297 | /* Is it the good device ? */ |
||
1298 | if(strncmp(bp,ifname,strlen(ifname))==0 && bp[strlen(ifname)]==':') |
||
1299 | { |
||
1300 | /* Skip ethX: */ |
||
1301 | bp=strchr(bp,':'); |
||
1302 | bp++; |
||
1303 | /* -- status -- */ |
||
1304 | bp = strtok(bp, " "); |
||
1305 | sscanf(bp, "%X", &t); |
||
1306 | stats->status = (unsigned short) t; |
||
1307 | /* -- link quality -- */ |
||
1308 | bp = strtok(NULL, " "); |
||
1309 | if(strchr(bp,'.') != NULL) |
||
1310 | stats->qual.updated |= 1; |
||
1311 | sscanf(bp, "%d", &t); |
||
1312 | stats->qual.qual = (unsigned char) t; |
||
1313 | /* -- signal level -- */ |
||
1314 | bp = strtok(NULL, " "); |
||
1315 | if(strchr(bp,'.') != NULL) |
||
1316 | stats->qual.updated |= 2; |
||
1317 | sscanf(bp, "%d", &t); |
||
1318 | stats->qual.level = (unsigned char) t; |
||
1319 | /* -- noise level -- */ |
||
1320 | bp = strtok(NULL, " "); |
||
1321 | if(strchr(bp,'.') != NULL) |
||
1322 | stats->qual.updated += 4; |
||
1323 | sscanf(bp, "%d", &t); |
||
1324 | stats->qual.noise = (unsigned char) t; |
||
1325 | /* -- discarded packets -- */ |
||
1326 | bp = strtok(NULL, " "); |
||
1327 | sscanf(bp, "%d", &stats->discard.nwid); |
||
1328 | bp = strtok(NULL, " "); |
||
1329 | sscanf(bp, "%d", &stats->discard.code); |
||
1330 | bp = strtok(NULL, " "); |
||
1331 | sscanf(bp, "%d", &stats->discard.misc); |
||
1332 | fclose(f); |
||
1333 | /* No conversion needed */ |
||
1334 | return 0; |
||
1335 | } |
||
1336 | } |
||
1337 | fclose(f); |
||
1338 | return -1; |
||
1339 | } |
||
1340 | } |
||
1341 | |||
1342 | /*------------------------------------------------------------------*/ |
||
1343 | /* |
||
1344 | * Output the link statistics, taking care of formating |
||
1345 | */ |
||
1346 | void |
||
1347 | iw_print_stats(char * buffer, |
||
1348 | int buflen, |
||
1349 | const iwqual * qual, |
||
1350 | const iwrange * range, |
||
1351 | int has_range) |
||
1352 | { |
||
1353 | int len; |
||
1354 | |||
1355 | /* People are very often confused by the 8 bit arithmetic happening |
||
1356 | * here. |
||
1357 | * All the values here are encoded in a 8 bit integer. 8 bit integers |
||
1358 | * are either unsigned [0 ; 255], signed [-128 ; +127] or |
||
1359 | * negative [-255 ; 0]. |
||
1360 | * Further, on 8 bits, 0x100 == 256 == 0. |
||
1361 | * |
||
1362 | * Relative/percent values are always encoded unsigned, between 0 and 255. |
||
1363 | * Absolute/dBm values are always encoded between -192 and 63. |
||
1364 | * (Note that up to version 28 of Wireless Tools, dBm used to be |
||
1365 | * encoded always negative, between -256 and -1). |
||
1366 | * |
||
1367 | * How do we separate relative from absolute values ? |
||
1368 | * The old way is to use the range to do that. As of WE-19, we have |
||
1369 | * an explicit IW_QUAL_DBM flag in updated... |
||
1370 | * The range allow to specify the real min/max of the value. As the |
||
1371 | * range struct only specify one bound of the value, we assume that |
||
1372 | * the other bound is 0 (zero). |
||
1373 | * For relative values, range is [0 ; range->max]. |
||
1374 | * For absolute values, range is [range->max ; 63]. |
||
1375 | * |
||
1376 | * Let's take two example : |
||
1377 | * 1) value is 75%. qual->value = 75 ; range->max_qual.value = 100 |
||
1378 | * 2) value is -54dBm. noise floor of the radio is -104dBm. |
||
1379 | * qual->value = -54 = 202 ; range->max_qual.value = -104 = 152 |
||
1380 | * |
||
1381 | * Jean II |
||
1382 | */ |
||
1383 | |||
1384 | /* Just do it... |
||
1385 | * The old way to detect dBm require both the range and a non-null |
||
1386 | * level (which confuse the test). The new way can deal with level of 0 |
||
1387 | * because it does an explicit test on the flag. */ |
||
1388 | if(has_range && ((qual->level != 0) |
||
1389 | || (qual->updated & (IW_QUAL_DBM | IW_QUAL_RCPI)))) |
||
1390 | { |
||
1391 | /* Deal with quality : always a relative value */ |
||
1392 | if(!(qual->updated & IW_QUAL_QUAL_INVALID)) |
||
1393 | { |
||
1394 | len = snprintf(buffer, buflen, "Quality%c%d/%d ", |
||
1395 | qual->updated & IW_QUAL_QUAL_UPDATED ? '=' : ':', |
||
1396 | qual->qual, range->max_qual.qual); |
||
1397 | buffer += len; |
||
1398 | buflen -= len; |
||
1399 | } |
||
1400 | |||
1401 | /* Check if the statistics are in RCPI (IEEE 802.11k) */ |
||
1402 | if(qual->updated & IW_QUAL_RCPI) |
||
1403 | { |
||
1404 | /* Deal with signal level in RCPI */ |
||
1405 | /* RCPI = int{(Power in dBm +110)*2} for 0dbm > Power > -110dBm */ |
||
1406 | if(!(qual->updated & IW_QUAL_LEVEL_INVALID)) |
||
1407 | { |
||
1408 | double rcpilevel = (qual->level / 2.0) - 110.0; |
||
1409 | len = snprintf(buffer, buflen, "Signal level%c%g dBm ", |
||
1410 | qual->updated & IW_QUAL_LEVEL_UPDATED ? '=' : ':', |
||
1411 | rcpilevel); |
||
1412 | buffer += len; |
||
1413 | buflen -= len; |
||
1414 | } |
||
1415 | |||
1416 | /* Deal with noise level in dBm (absolute power measurement) */ |
||
1417 | if(!(qual->updated & IW_QUAL_NOISE_INVALID)) |
||
1418 | { |
||
1419 | double rcpinoise = (qual->noise / 2.0) - 110.0; |
||
1420 | len = snprintf(buffer, buflen, "Noise level%c%g dBm", |
||
1421 | qual->updated & IW_QUAL_NOISE_UPDATED ? '=' : ':', |
||
1422 | rcpinoise); |
||
1423 | } |
||
1424 | } |
||
1425 | else |
||
1426 | { |
||
1427 | /* Check if the statistics are in dBm */ |
||
1428 | if((qual->updated & IW_QUAL_DBM) |
||
1429 | || (qual->level > range->max_qual.level)) |
||
1430 | { |
||
1431 | /* Deal with signal level in dBm (absolute power measurement) */ |
||
1432 | if(!(qual->updated & IW_QUAL_LEVEL_INVALID)) |
||
1433 | { |
||
1434 | int dblevel = qual->level; |
||
1435 | /* Implement a range for dBm [-192; 63] */ |
||
1436 | if(qual->level >= 64) |
||
1437 | dblevel -= 0x100; |
||
1438 | len = snprintf(buffer, buflen, "Signal level%c%d dBm ", |
||
1439 | qual->updated & IW_QUAL_LEVEL_UPDATED ? '=' : ':', |
||
1440 | dblevel); |
||
1441 | buffer += len; |
||
1442 | buflen -= len; |
||
1443 | } |
||
1444 | |||
1445 | /* Deal with noise level in dBm (absolute power measurement) */ |
||
1446 | if(!(qual->updated & IW_QUAL_NOISE_INVALID)) |
||
1447 | { |
||
1448 | int dbnoise = qual->noise; |
||
1449 | /* Implement a range for dBm [-192; 63] */ |
||
1450 | if(qual->noise >= 64) |
||
1451 | dbnoise -= 0x100; |
||
1452 | len = snprintf(buffer, buflen, "Noise level%c%d dBm", |
||
1453 | qual->updated & IW_QUAL_NOISE_UPDATED ? '=' : ':', |
||
1454 | dbnoise); |
||
1455 | } |
||
1456 | } |
||
1457 | else |
||
1458 | { |
||
1459 | /* Deal with signal level as relative value (0 -> max) */ |
||
1460 | if(!(qual->updated & IW_QUAL_LEVEL_INVALID)) |
||
1461 | { |
||
1462 | len = snprintf(buffer, buflen, "Signal level%c%d/%d ", |
||
1463 | qual->updated & IW_QUAL_LEVEL_UPDATED ? '=' : ':', |
||
1464 | qual->level, range->max_qual.level); |
||
1465 | buffer += len; |
||
1466 | buflen -= len; |
||
1467 | } |
||
1468 | |||
1469 | /* Deal with noise level as relative value (0 -> max) */ |
||
1470 | if(!(qual->updated & IW_QUAL_NOISE_INVALID)) |
||
1471 | { |
||
1472 | len = snprintf(buffer, buflen, "Noise level%c%d/%d", |
||
1473 | qual->updated & IW_QUAL_NOISE_UPDATED ? '=' : ':', |
||
1474 | qual->noise, range->max_qual.noise); |
||
1475 | } |
||
1476 | } |
||
1477 | } |
||
1478 | } |
||
1479 | else |
||
1480 | { |
||
1481 | /* We can't read the range, so we don't know... */ |
||
1482 | snprintf(buffer, buflen, |
||
1483 | "Quality:%d Signal level:%d Noise level:%d", |
||
1484 | qual->qual, qual->level, qual->noise); |
||
1485 | } |
||
1486 | } |
||
1487 | |||
1488 | /*********************** ENCODING SUBROUTINES ***********************/ |
||
1489 | |||
1490 | /*------------------------------------------------------------------*/ |
||
1491 | /* |
||
1492 | * Output the encoding key, with a nice formating |
||
1493 | */ |
||
1494 | void |
||
1495 | iw_print_key(char * buffer, |
||
1496 | int buflen, |
||
1497 | const unsigned char * key, /* Must be unsigned */ |
||
1498 | int key_size, |
||
1499 | int key_flags) |
||
1500 | { |
||
1501 | int i; |
||
1502 | |||
1503 | /* Check buffer size -> 1 bytes => 2 digits + 1/2 separator */ |
||
1504 | if((key_size * 3) > buflen) |
||
1505 | { |
||
1506 | snprintf(buffer, buflen, "<too big>"); |
||
1507 | return; |
||
1508 | } |
||
1509 | |||
1510 | /* Is the key present ??? */ |
||
1511 | if(key_flags & IW_ENCODE_NOKEY) |
||
1512 | { |
||
1513 | /* Nope : print on or dummy */ |
||
1514 | if(key_size <= 0) |
||
1515 | strcpy(buffer, "on"); /* Size checked */ |
||
1516 | else |
||
1517 | { |
||
1518 | strcpy(buffer, "**"); /* Size checked */ |
||
1519 | buffer +=2; |
||
1520 | for(i = 1; i < key_size; i++) |
||
1521 | { |
||
1522 | if((i & 0x1) == 0) |
||
1523 | strcpy(buffer++, "-"); /* Size checked */ |
||
1524 | strcpy(buffer, "**"); /* Size checked */ |
||
1525 | buffer +=2; |
||
1526 | } |
||
1527 | } |
||
1528 | } |
||
1529 | else |
||
1530 | { |
||
1531 | /* Yes : print the key */ |
||
1532 | sprintf(buffer, "%.2X", key[0]); /* Size checked */ |
||
1533 | buffer +=2; |
||
1534 | for(i = 1; i < key_size; i++) |
||
1535 | { |
||
1536 | if((i & 0x1) == 0) |
||
1537 | strcpy(buffer++, "-"); /* Size checked */ |
||
1538 | sprintf(buffer, "%.2X", key[i]); /* Size checked */ |
||
1539 | buffer +=2; |
||
1540 | } |
||
1541 | } |
||
1542 | } |
||
1543 | |||
1544 | /*------------------------------------------------------------------*/ |
||
1545 | /* |
||
1546 | * Convert a passphrase into a key |
||
1547 | * ### NOT IMPLEMENTED ### |
||
1548 | * Return size of the key, or 0 (no key) or -1 (error) |
||
1549 | */ |
||
1550 | static int |
||
1551 | iw_pass_key(const char * input, |
||
1552 | unsigned char * key) |
||
1553 | { |
||
1554 | input = input; key = key; |
||
1555 | fprintf(stderr, "Error: Passphrase not implemented\n"); |
||
1556 | return(-1); |
||
1557 | } |
||
1558 | |||
1559 | /*------------------------------------------------------------------*/ |
||
1560 | /* |
||
1561 | * Parse a key from the command line. |
||
1562 | * Return size of the key, or 0 (no key) or -1 (error) |
||
1563 | * If the key is too long, it's simply truncated... |
||
1564 | */ |
||
1565 | int |
||
1566 | iw_in_key(const char * input, |
||
1567 | unsigned char * key) |
||
1568 | { |
||
1569 | int keylen = 0; |
||
1570 | |||
1571 | /* Check the type of key */ |
||
1572 | if(!strncmp(input, "s:", 2)) |
||
1573 | { |
||
1574 | /* First case : as an ASCII string (Lucent/Agere cards) */ |
||
1575 | keylen = strlen(input + 2); /* skip "s:" */ |
||
1576 | if(keylen > IW_ENCODING_TOKEN_MAX) |
||
1577 | keylen = IW_ENCODING_TOKEN_MAX; |
||
1578 | memcpy(key, input + 2, keylen); |
||
1579 | } |
||
1580 | else |
||
1581 | if(!strncmp(input, "p:", 2)) |
||
1582 | { |
||
1583 | /* Second case : as a passphrase (PrismII cards) */ |
||
1584 | return(iw_pass_key(input + 2, key)); /* skip "p:" */ |
||
1585 | } |
||
1586 | else |
||
1587 | { |
||
1588 | const char * p; |
||
1589 | int dlen; /* Digits sequence length */ |
||
1590 | unsigned char out[IW_ENCODING_TOKEN_MAX]; |
||
1591 | |||
1592 | /* Third case : as hexadecimal digits */ |
||
1593 | p = input; |
||
1594 | dlen = -1; |
||
1595 | |||
1596 | /* Loop until we run out of chars in input or overflow the output */ |
||
1597 | while(*p != '\0') |
||
1598 | { |
||
1599 | int temph; |
||
1600 | int templ; |
||
1601 | int count; |
||
1602 | /* No more chars in this sequence */ |
||
1603 | if(dlen <= 0) |
||
1604 | { |
||
1605 | /* Skip separator */ |
||
1606 | if(dlen == 0) |
||
1607 | p++; |
||
1608 | /* Calculate num of char to next separator */ |
||
1609 | dlen = strcspn(p, "-:;.,"); |
||
1610 | } |
||
1611 | /* Get each char separatly (and not by two) so that we don't |
||
1612 | * get confused by 'enc' (=> '0E'+'0C') and similar */ |
||
1613 | count = sscanf(p, "%1X%1X", &temph, &templ); |
||
1614 | if(count < 1) |
||
1615 | return(-1); /* Error -> non-hex char */ |
||
1616 | /* Fixup odd strings such as '123' is '01'+'23' and not '12'+'03'*/ |
||
1617 | if(dlen % 2) |
||
1618 | count = 1; |
||
1619 | /* Put back two chars as one byte and output */ |
||
1620 | if(count == 2) |
||
1621 | templ |= temph << 4; |
||
1622 | else |
||
1623 | templ = temph; |
||
1624 | out[keylen++] = (unsigned char) (templ & 0xFF); |
||
1625 | /* Check overflow in output */ |
||
1626 | if(keylen >= IW_ENCODING_TOKEN_MAX) |
||
1627 | break; |
||
1628 | /* Move on to next chars */ |
||
1629 | p += count; |
||
1630 | dlen -= count; |
||
1631 | } |
||
1632 | /* We use a temporary output buffer 'out' so that if there is |
||
1633 | * an error, we don't overwrite the original key buffer. |
||
1634 | * Because of the way iwconfig loop on multiple key/enc arguments |
||
1635 | * until it finds an error in here, this is necessary to avoid |
||
1636 | * silently corrupting the encryption key... */ |
||
1637 | memcpy(key, out, keylen); |
||
1638 | } |
||
1639 | |||
1640 | #ifdef DEBUG |
||
1641 | { |
||
1642 | char buf[IW_ENCODING_TOKEN_MAX * 3]; |
||
1643 | iw_print_key(buf, sizeof(buf), key, keylen, 0); |
||
1644 | printf("Got key : %d [%s]\n", keylen, buf); |
||
1645 | } |
||
1646 | #endif |
||
1647 | |||
1648 | return(keylen); |
||
1649 | } |
||
1650 | |||
1651 | /*------------------------------------------------------------------*/ |
||
1652 | /* |
||
1653 | * Parse a key from the command line. |
||
1654 | * Return size of the key, or 0 (no key) or -1 (error) |
||
1655 | */ |
||
1656 | int |
||
1657 | iw_in_key_full(int skfd, |
||
1658 | const char * ifname, |
||
1659 | const char * input, |
||
1660 | unsigned char * key, |
||
1661 | __u16 * flags) |
||
1662 | { |
||
1663 | int keylen = 0; |
||
1664 | char * p; |
||
1665 | |||
1666 | if(!strncmp(input, "l:", 2)) |
||
1667 | { |
||
1668 | struct iw_range range; |
||
1669 | |||
1670 | /* Extra case : as a login (user:passwd - Cisco LEAP) */ |
||
1671 | keylen = strlen(input + 2) + 1; /* skip "l:", add '\0' */ |
||
1672 | /* Most user/password is 8 char, so 18 char total, < 32 */ |
||
1673 | if(keylen > IW_ENCODING_TOKEN_MAX) |
||
1674 | keylen = IW_ENCODING_TOKEN_MAX; |
||
1675 | memcpy(key, input + 2, keylen); |
||
1676 | |||
1677 | /* Separate the two strings */ |
||
1678 | p = strchr((char *) key, ':'); |
||
1679 | if(p == NULL) |
||
1680 | { |
||
1681 | fprintf(stderr, "Error: Invalid login format\n"); |
||
1682 | return(-1); |
||
1683 | } |
||
1684 | *p = '\0'; |
||
1685 | |||
1686 | /* Extract range info */ |
||
1687 | if(iw_get_range_info(skfd, ifname, &range) < 0) |
||
1688 | /* Hum... Maybe we should return an error ??? */ |
||
1689 | memset(&range, 0, sizeof(range)); |
||
1690 | |||
1691 | if(range.we_version_compiled > 15) |
||
1692 | { |
||
1693 | |||
1694 | printf("flags = %X, index = %X\n", |
||
1695 | *flags, range.encoding_login_index); |
||
1696 | if((*flags & IW_ENCODE_INDEX) == 0) |
||
1697 | { |
||
1698 | /* Extract range info */ |
||
1699 | if(iw_get_range_info(skfd, ifname, &range) < 0) |
||
1700 | memset(&range, 0, sizeof(range)); |
||
1701 | printf("flags = %X, index = %X\n", *flags, range.encoding_login_index); |
||
1702 | /* Set the index the driver expects */ |
||
1703 | *flags |= range.encoding_login_index & IW_ENCODE_INDEX; |
||
1704 | } |
||
1705 | printf("flags = %X, index = %X\n", *flags, range.encoding_login_index); |
||
1706 | } |
||
1707 | } |
||
1708 | else |
||
1709 | /* Simpler routine above */ |
||
1710 | keylen = iw_in_key(input, key); |
||
1711 | |||
1712 | return(keylen); |
||
1713 | } |
||
1714 | |||
1715 | /******************* POWER MANAGEMENT SUBROUTINES *******************/ |
||
1716 | |||
1717 | /*------------------------------------------------------------------*/ |
||
1718 | /* |
||
1719 | * Output a power management value with all attributes... |
||
1720 | */ |
||
1721 | void |
||
1722 | iw_print_pm_value(char * buffer, |
||
1723 | int buflen, |
||
1724 | int value, |
||
1725 | int flags, |
||
1726 | int we_version) |
||
1727 | { |
||
1728 | /* Check size */ |
||
1729 | if(buflen < 25) |
||
1730 | { |
||
1731 | snprintf(buffer, buflen, "<too big>"); |
||
1732 | return; |
||
1733 | } |
||
1734 | buflen -= 25; |
||
1735 | |||
1736 | /* Modifiers */ |
||
1737 | if(flags & IW_POWER_MIN) |
||
1738 | { |
||
1739 | strcpy(buffer, " min"); /* Size checked */ |
||
1740 | buffer += 4; |
||
1741 | } |
||
1742 | if(flags & IW_POWER_MAX) |
||
1743 | { |
||
1744 | strcpy(buffer, " max"); /* Size checked */ |
||
1745 | buffer += 4; |
||
1746 | } |
||
1747 | |||
1748 | /* Type */ |
||
1749 | if(flags & IW_POWER_TIMEOUT) |
||
1750 | { |
||
1751 | strcpy(buffer, " timeout:"); /* Size checked */ |
||
1752 | buffer += 9; |
||
1753 | } |
||
1754 | else |
||
1755 | { |
||
1756 | if(flags & IW_POWER_SAVING) |
||
1757 | { |
||
1758 | strcpy(buffer, " saving:"); /* Size checked */ |
||
1759 | buffer += 8; |
||
1760 | } |
||
1761 | else |
||
1762 | { |
||
1763 | strcpy(buffer, " period:"); /* Size checked */ |
||
1764 | buffer += 8; |
||
1765 | } |
||
1766 | } |
||
1767 | |||
1768 | /* Display value without units */ |
||
1769 | if(flags & IW_POWER_RELATIVE) |
||
1770 | { |
||
1771 | if(we_version < 21) |
||
1772 | value /= MEGA; |
||
1773 | snprintf(buffer, buflen, "%d", value); |
||
1774 | } |
||
1775 | else |
||
1776 | { |
||
1777 | /* Display value with units */ |
||
1778 | if(value >= (int) MEGA) |
||
1779 | snprintf(buffer, buflen, "%gs", ((double) value) / MEGA); |
||
1780 | else |
||
1781 | if(value >= (int) KILO) |
||
1782 | snprintf(buffer, buflen, "%gms", ((double) value) / KILO); |
||
1783 | else |
||
1784 | snprintf(buffer, buflen, "%dus", value); |
||
1785 | } |
||
1786 | } |
||
1787 | |||
1788 | /*------------------------------------------------------------------*/ |
||
1789 | /* |
||
1790 | * Output a power management mode |
||
1791 | */ |
||
1792 | void |
||
1793 | iw_print_pm_mode(char * buffer, |
||
1794 | int buflen, |
||
1795 | int flags) |
||
1796 | { |
||
1797 | /* Check size */ |
||
1798 | if(buflen < 28) |
||
1799 | { |
||
1800 | snprintf(buffer, buflen, "<too big>"); |
||
1801 | return; |
||
1802 | } |
||
1803 | |||
1804 | /* Print the proper mode... */ |
||
1805 | switch(flags & IW_POWER_MODE) |
||
1806 | { |
||
1807 | case IW_POWER_UNICAST_R: |
||
1808 | strcpy(buffer, "mode:Unicast only received"); /* Size checked */ |
||
1809 | break; |
||
1810 | case IW_POWER_MULTICAST_R: |
||
1811 | strcpy(buffer, "mode:Multicast only received"); /* Size checked */ |
||
1812 | break; |
||
1813 | case IW_POWER_ALL_R: |
||
1814 | strcpy(buffer, "mode:All packets received"); /* Size checked */ |
||
1815 | break; |
||
1816 | case IW_POWER_FORCE_S: |
||
1817 | strcpy(buffer, "mode:Force sending"); /* Size checked */ |
||
1818 | break; |
||
1819 | case IW_POWER_REPEATER: |
||
1820 | strcpy(buffer, "mode:Repeat multicasts"); /* Size checked */ |
||
1821 | break; |
||
1822 | default: |
||
1823 | strcpy(buffer, ""); /* Size checked */ |
||
1824 | break; |
||
1825 | } |
||
1826 | } |
||
1827 | |||
1828 | /***************** RETRY LIMIT/LIFETIME SUBROUTINES *****************/ |
||
1829 | |||
1830 | /*------------------------------------------------------------------*/ |
||
1831 | /* |
||
1832 | * Output a retry value with all attributes... |
||
1833 | */ |
||
1834 | void |
||
1835 | iw_print_retry_value(char * buffer, |
||
1836 | int buflen, |
||
1837 | int value, |
||
1838 | int flags, |
||
1839 | int we_version) |
||
1840 | { |
||
1841 | /* Check buffer size */ |
||
1842 | if(buflen < 20) |
||
1843 | { |
||
1844 | snprintf(buffer, buflen, "<too big>"); |
||
1845 | return; |
||
1846 | } |
||
1847 | buflen -= 20; |
||
1848 | |||
1849 | /* Modifiers */ |
||
1850 | if(flags & IW_RETRY_MIN) |
||
1851 | { |
||
1852 | strcpy(buffer, " min"); /* Size checked */ |
||
1853 | buffer += 4; |
||
1854 | } |
||
1855 | if(flags & IW_RETRY_MAX) |
||
1856 | { |
||
1857 | strcpy(buffer, " max"); /* Size checked */ |
||
1858 | buffer += 4; |
||
1859 | } |
||
1860 | if(flags & IW_RETRY_SHORT) |
||
1861 | { |
||
1862 | strcpy(buffer, " short"); /* Size checked */ |
||
1863 | buffer += 6; |
||
1864 | } |
||
1865 | if(flags & IW_RETRY_LONG) |
||
1866 | { |
||
1867 | strcpy(buffer, " long"); /* Size checked */ |
||
1868 | buffer += 6; |
||
1869 | } |
||
1870 | |||
1871 | /* Type lifetime of limit */ |
||
1872 | if(flags & IW_RETRY_LIFETIME) |
||
1873 | { |
||
1874 | strcpy(buffer, " lifetime:"); /* Size checked */ |
||
1875 | buffer += 10; |
||
1876 | |||
1877 | /* Display value without units */ |
||
1878 | if(flags & IW_RETRY_RELATIVE) |
||
1879 | { |
||
1880 | if(we_version < 21) |
||
1881 | value /= MEGA; |
||
1882 | snprintf(buffer, buflen, "%d", value); |
||
1883 | } |
||
1884 | else |
||
1885 | { |
||
1886 | /* Display value with units */ |
||
1887 | if(value >= (int) MEGA) |
||
1888 | snprintf(buffer, buflen, "%gs", ((double) value) / MEGA); |
||
1889 | else |
||
1890 | if(value >= (int) KILO) |
||
1891 | snprintf(buffer, buflen, "%gms", ((double) value) / KILO); |
||
1892 | else |
||
1893 | snprintf(buffer, buflen, "%dus", value); |
||
1894 | } |
||
1895 | } |
||
1896 | else |
||
1897 | snprintf(buffer, buflen, " limit:%d", value); |
||
1898 | } |
||
1899 | |||
1900 | /************************* TIME SUBROUTINES *************************/ |
||
1901 | |||
1902 | /*------------------------------------------------------------------*/ |
||
1903 | /* |
||
1904 | * Print timestamps |
||
1905 | * Inspired from irdadump... |
||
1906 | */ |
||
1907 | void |
||
1908 | iw_print_timeval(char * buffer, |
||
1909 | int buflen, |
||
1910 | const struct timeval * timev, |
||
1911 | const struct timezone * tz) |
||
1912 | { |
||
1913 | int s; |
||
1914 | |||
1915 | s = (timev->tv_sec - tz->tz_minuteswest * 60) % 86400; |
||
1916 | snprintf(buffer, buflen, "%02d:%02d:%02d.%06u", |
||
1917 | s / 3600, (s % 3600) / 60, |
||
1918 | s % 60, (u_int32_t) timev->tv_usec); |
||
1919 | } |
||
1920 | |||
1921 | /*********************** ADDRESS SUBROUTINES ************************/ |
||
1922 | /* |
||
1923 | * This section is mostly a cut & past from net-tools-1.2.0 |
||
1924 | * (Well... This has evolved over the years) |
||
1925 | * manage address display and input... |
||
1926 | */ |
||
1927 | |||
1928 | /*------------------------------------------------------------------*/ |
||
1929 | /* |
||
1930 | * Check if interface support the right MAC address type... |
||
1931 | */ |
||
1932 | int |
||
1933 | iw_check_mac_addr_type(int skfd, |
||
1934 | const char * ifname) |
||
1935 | { |
||
1936 | struct ifreq ifr; |
||
1937 | |||
1938 | /* Get the type of hardware address */ |
||
1939 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ); |
||
1940 | if((ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0) || |
||
1941 | ((ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) |
||
1942 | && (ifr.ifr_hwaddr.sa_family != ARPHRD_IEEE80211))) |
||
1943 | { |
||
1944 | /* Deep trouble... */ |
||
1945 | fprintf(stderr, "Interface %s doesn't support MAC addresses\n", |
||
1946 | ifname); |
||
1947 | return(-1); |
||
1948 | } |
||
1949 | |||
1950 | #ifdef DEBUG |
||
1951 | { |
||
1952 | char buf[20]; |
||
1953 | printf("Hardware : %d - %s\n", ifr.ifr_hwaddr.sa_family, |
||
1954 | iw_saether_ntop(&ifr.ifr_hwaddr, buf)); |
||
1955 | } |
||
1956 | #endif |
||
1957 | |||
1958 | return(0); |
||
1959 | } |
||
1960 | |||
1961 | |||
1962 | /*------------------------------------------------------------------*/ |
||
1963 | /* |
||
1964 | * Check if interface support the right interface address type... |
||
1965 | */ |
||
1966 | int |
||
1967 | iw_check_if_addr_type(int skfd, |
||
1968 | const char * ifname) |
||
1969 | { |
||
1970 | struct ifreq ifr; |
||
1971 | |||
1972 | /* Get the type of interface address */ |
||
1973 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ); |
||
1974 | if((ioctl(skfd, SIOCGIFADDR, &ifr) < 0) || |
||
1975 | (ifr.ifr_addr.sa_family != AF_INET)) |
||
1976 | { |
||
1977 | /* Deep trouble... */ |
||
1978 | fprintf(stderr, "Interface %s doesn't support IP addresses\n", ifname); |
||
1979 | return(-1); |
||
1980 | } |
||
1981 | |||
1982 | #ifdef DEBUG |
||
1983 | printf("Interface : %d - 0x%lX\n", ifr.ifr_addr.sa_family, |
||
1984 | *((unsigned long *) ifr.ifr_addr.sa_data)); |
||
1985 | #endif |
||
1986 | |||
1987 | return(0); |
||
1988 | } |
||
1989 | |||
1990 | #if 0 |
||
1991 | /*------------------------------------------------------------------*/ |
||
1992 | /* |
||
1993 | * Check if interface support the right address types... |
||
1994 | */ |
||
1995 | int |
||
1996 | iw_check_addr_type(int skfd, |
||
1997 | char * ifname) |
||
1998 | { |
||
1999 | /* Check the interface address type */ |
||
2000 | if(iw_check_if_addr_type(skfd, ifname) < 0) |
||
2001 | return(-1); |
||
2002 | |||
2003 | /* Check the interface address type */ |
||
2004 | if(iw_check_mac_addr_type(skfd, ifname) < 0) |
||
2005 | return(-1); |
||
2006 | |||
2007 | return(0); |
||
2008 | } |
||
2009 | #endif |
||
2010 | |||
2011 | #if 0 |
||
2012 | /*------------------------------------------------------------------*/ |
||
2013 | /* |
||
2014 | * Ask the kernel for the MAC address of an interface. |
||
2015 | */ |
||
2016 | int |
||
2017 | iw_get_mac_addr(int skfd, |
||
2018 | const char * ifname, |
||
2019 | struct ether_addr * eth, |
||
2020 | unsigned short * ptype) |
||
2021 | { |
||
2022 | struct ifreq ifr; |
||
2023 | int ret; |
||
2024 | |||
2025 | /* Prepare request */ |
||
2026 | bzero(&ifr, sizeof(struct ifreq)); |
||
2027 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ); |
||
2028 | |||
2029 | /* Do it */ |
||
2030 | ret = ioctl(skfd, SIOCGIFHWADDR, &ifr); |
||
2031 | |||
2032 | memcpy(eth->ether_addr_octet, ifr.ifr_hwaddr.sa_data, 6); |
||
2033 | *ptype = ifr.ifr_hwaddr.sa_family; |
||
2034 | return(ret); |
||
2035 | } |
||
2036 | #endif |
||
2037 | |||
2038 | /*------------------------------------------------------------------*/ |
||
2039 | /* |
||
2040 | * Display an arbitrary length MAC address in readable format. |
||
2041 | */ |
||
2042 | char * |
||
2043 | iw_mac_ntop(const unsigned char * mac, |
||
2044 | int maclen, |
||
2045 | char * buf, |
||
2046 | int buflen) |
||
2047 | { |
||
2048 | int i; |
||
2049 | |||
2050 | /* Overflow check (don't forget '\0') */ |
||
2051 | if(buflen < (maclen * 3 - 1 + 1)) |
||
2052 | return(NULL); |
||
2053 | |||
2054 | /* First byte */ |
||
2055 | sprintf(buf, "%02X", mac[0]); |
||
2056 | |||
2057 | /* Other bytes */ |
||
2058 | for(i = 1; i < maclen; i++) |
||
2059 | sprintf(buf + (i * 3) - 1, ":%02X", mac[i]); |
||
2060 | return(buf); |
||
2061 | } |
||
2062 | |||
2063 | /*------------------------------------------------------------------*/ |
||
2064 | /* |
||
2065 | * Display an Ethernet address in readable format. |
||
2066 | */ |
||
2067 | void |
||
2068 | iw_ether_ntop(const struct ether_addr * eth, |
||
2069 | char * buf) |
||
2070 | { |
||
2071 | sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X", |
||
2072 | eth->ether_addr_octet[0], eth->ether_addr_octet[1], |
||
2073 | eth->ether_addr_octet[2], eth->ether_addr_octet[3], |
||
2074 | eth->ether_addr_octet[4], eth->ether_addr_octet[5]); |
||
2075 | } |
||
2076 | |||
2077 | /*------------------------------------------------------------------*/ |
||
2078 | /* |
||
2079 | * Display an Wireless Access Point Socket Address in readable format. |
||
2080 | * Note : 0x44 is an accident of history, that's what the Orinoco/PrismII |
||
2081 | * chipset report, and the driver doesn't filter it. |
||
2082 | */ |
||
2083 | char * |
||
2084 | iw_sawap_ntop(const struct sockaddr * sap, |
||
2085 | char * buf) |
||
2086 | { |
||
2087 | const struct ether_addr ether_zero = {{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}; |
||
2088 | const struct ether_addr ether_bcast = {{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }}; |
||
2089 | const struct ether_addr ether_hack = {{ 0x44, 0x44, 0x44, 0x44, 0x44, 0x44 }}; |
||
2090 | const struct ether_addr * ether_wap = (const struct ether_addr *) sap->sa_data; |
||
2091 | |||
2092 | if(!iw_ether_cmp(ether_wap, ðer_zero)) |
||
2093 | sprintf(buf, "Not-Associated"); |
||
2094 | else |
||
2095 | if(!iw_ether_cmp(ether_wap, ðer_bcast)) |
||
2096 | sprintf(buf, "Invalid"); |
||
2097 | else |
||
2098 | if(!iw_ether_cmp(ether_wap, ðer_hack)) |
||
2099 | sprintf(buf, "None"); |
||
2100 | else |
||
2101 | iw_ether_ntop(ether_wap, buf); |
||
2102 | return(buf); |
||
2103 | } |
||
2104 | |||
2105 | /*------------------------------------------------------------------*/ |
||
2106 | /* |
||
2107 | * Input an arbitrary length MAC address and convert to binary. |
||
2108 | * Return address size. |
||
2109 | */ |
||
2110 | int |
||
2111 | iw_mac_aton(const char * orig, |
||
2112 | unsigned char * mac, |
||
2113 | int macmax) |
||
2114 | { |
||
2115 | const char * p = orig; |
||
2116 | int maclen = 0; |
||
2117 | |||
2118 | /* Loop on all bytes of the string */ |
||
2119 | while(*p != '\0') |
||
2120 | { |
||
2121 | int temph; |
||
2122 | int templ; |
||
2123 | int count; |
||
2124 | /* Extract one byte as two chars */ |
||
2125 | count = sscanf(p, "%1X%1X", &temph, &templ); |
||
2126 | if(count != 2) |
||
2127 | break; /* Error -> non-hex chars */ |
||
2128 | /* Output two chars as one byte */ |
||
2129 | templ |= temph << 4; |
||
2130 | mac[maclen++] = (unsigned char) (templ & 0xFF); |
||
2131 | |||
2132 | /* Check end of string */ |
||
2133 | p += 2; |
||
2134 | if(*p == '\0') |
||
2135 | { |
||
2136 | #ifdef DEBUG |
||
2137 | char buf[20]; |
||
2138 | iw_ether_ntop((const struct ether_addr *) mac, buf); |
||
2139 | fprintf(stderr, "iw_mac_aton(%s): %s\n", orig, buf); |
||
2140 | #endif |
||
2141 | return(maclen); /* Normal exit */ |
||
2142 | } |
||
2143 | |||
2144 | /* Check overflow */ |
||
2145 | if(maclen >= macmax) |
||
2146 | { |
||
2147 | #ifdef DEBUG |
||
2148 | fprintf(stderr, "iw_mac_aton(%s): trailing junk!\n", orig); |
||
2149 | #endif |
||
2150 | errno = E2BIG; |
||
2151 | return(0); /* Error -> overflow */ |
||
2152 | } |
||
2153 | |||
2154 | /* Check separator */ |
||
2155 | if(*p != ':') |
||
2156 | break; |
||
2157 | p++; |
||
2158 | } |
||
2159 | |||
2160 | /* Error... */ |
||
2161 | #ifdef DEBUG |
||
2162 | fprintf(stderr, "iw_mac_aton(%s): invalid ether address!\n", orig); |
||
2163 | #endif |
||
2164 | errno = EINVAL; |
||
2165 | return(0); |
||
2166 | } |
||
2167 | |||
2168 | /*------------------------------------------------------------------*/ |
||
2169 | /* |
||
2170 | * Input an Ethernet address and convert to binary. |
||
2171 | */ |
||
2172 | int |
||
2173 | iw_ether_aton(const char *orig, struct ether_addr *eth) |
||
2174 | { |
||
2175 | int maclen; |
||
2176 | maclen = iw_mac_aton(orig, (unsigned char *) eth, ETH_ALEN); |
||
2177 | if((maclen > 0) && (maclen < ETH_ALEN)) |
||
2178 | { |
||
2179 | errno = EINVAL; |
||
2180 | maclen = 0; |
||
2181 | } |
||
2182 | return(maclen); |
||
2183 | } |
||
2184 | |||
2185 | /*------------------------------------------------------------------*/ |
||
2186 | /* |
||
2187 | * Input an Internet address and convert to binary. |
||
2188 | */ |
||
2189 | int |
||
2190 | iw_in_inet(char *name, struct sockaddr *sap) |
||
2191 | { |
||
2192 | struct hostent *hp; |
||
2193 | struct netent *np; |
||
2194 | struct sockaddr_in *sain = (struct sockaddr_in *) sap; |
||
2195 | |||
2196 | /* Grmpf. -FvK */ |
||
2197 | sain->sin_family = AF_INET; |
||
2198 | sain->sin_port = 0; |
||
2199 | |||
2200 | /* Default is special, meaning 0.0.0.0. */ |
||
2201 | if (!strcmp(name, "default")) { |
||
2202 | sain->sin_addr.s_addr = INADDR_ANY; |
||
2203 | return(1); |
||
2204 | } |
||
2205 | |||
2206 | /* Try the NETWORKS database to see if this is a known network. */ |
||
2207 | if ((np = getnetbyname(name)) != (struct netent *)NULL) { |
||
2208 | sain->sin_addr.s_addr = htonl(np->n_net); |
||
2209 | strcpy(name, np->n_name); |
||
2210 | return(1); |
||
2211 | } |
||
2212 | |||
2213 | /* Always use the resolver (DNS name + IP addresses) */ |
||
2214 | if ((hp = gethostbyname(name)) == (struct hostent *)NULL) { |
||
2215 | errno = h_errno; |
||
2216 | return(-1); |
||
2217 | } |
||
2218 | memcpy((char *) &sain->sin_addr, (char *) hp->h_addr_list[0], hp->h_length); |
||
2219 | strcpy(name, hp->h_name); |
||
2220 | return(0); |
||
2221 | } |
||
2222 | |||
2223 | /*------------------------------------------------------------------*/ |
||
2224 | /* |
||
2225 | * Input an address and convert to binary. |
||
2226 | */ |
||
2227 | int |
||
2228 | iw_in_addr(int skfd, |
||
2229 | const char * ifname, |
||
2230 | char * bufp, |
||
2231 | struct sockaddr *sap) |
||
2232 | { |
||
2233 | /* Check if it is a hardware or IP address */ |
||
2234 | if(strchr(bufp, ':') == NULL) |
||
2235 | { |
||
2236 | struct sockaddr if_address; |
||
2237 | struct arpreq arp_query; |
||
2238 | |||
2239 | /* Check if we have valid interface address type */ |
||
2240 | if(iw_check_if_addr_type(skfd, ifname) < 0) |
||
2241 | { |
||
2242 | fprintf(stderr, "%-8.16s Interface doesn't support IP addresses\n", ifname); |
||
2243 | return(-1); |
||
2244 | } |
||
2245 | |||
2246 | /* Read interface address */ |
||
2247 | if(iw_in_inet(bufp, &if_address) < 0) |
||
2248 | { |
||
2249 | fprintf(stderr, "Invalid interface address %s\n", bufp); |
||
2250 | return(-1); |
||
2251 | } |
||
2252 | |||
2253 | /* Translate IP addresses to MAC addresses */ |
||
2254 | memcpy((char *) &(arp_query.arp_pa), |
||
2255 | (char *) &if_address, |
||
2256 | sizeof(struct sockaddr)); |
||
2257 | arp_query.arp_ha.sa_family = 0; |
||
2258 | arp_query.arp_flags = 0; |
||
2259 | /* The following restrict the search to the interface only */ |
||
2260 | /* For old kernels which complain, just comment it... */ |
||
2261 | strncpy(arp_query.arp_dev, ifname, IFNAMSIZ); |
||
2262 | if((ioctl(skfd, SIOCGARP, &arp_query) < 0) || |
||
2263 | !(arp_query.arp_flags & ATF_COM)) |
||
2264 | { |
||
2265 | fprintf(stderr, "Arp failed for %s on %s... (%d)\nTry to ping the address before setting it.\n", |
||
2266 | bufp, ifname, errno); |
||
2267 | return(-1); |
||
2268 | } |
||
2269 | |||
2270 | /* Store new MAC address */ |
||
2271 | memcpy((char *) sap, |
||
2272 | (char *) &(arp_query.arp_ha), |
||
2273 | sizeof(struct sockaddr)); |
||
2274 | |||
2275 | #ifdef DEBUG |
||
2276 | { |
||
2277 | char buf[20]; |
||
2278 | printf("IP Address %s => Hw Address = %s\n", |
||
2279 | bufp, iw_saether_ntop(sap, buf)); |
||
2280 | } |
||
2281 | #endif |
||
2282 | } |
||
2283 | else /* If it's an hardware address */ |
||
2284 | { |
||
2285 | /* Check if we have valid mac address type */ |
||
2286 | if(iw_check_mac_addr_type(skfd, ifname) < 0) |
||
2287 | { |
||
2288 | fprintf(stderr, "%-8.16s Interface doesn't support MAC addresses\n", ifname); |
||
2289 | return(-1); |
||
2290 | } |
||
2291 | |||
2292 | /* Get the hardware address */ |
||
2293 | if(iw_saether_aton(bufp, sap) == 0) |
||
2294 | { |
||
2295 | fprintf(stderr, "Invalid hardware address %s\n", bufp); |
||
2296 | return(-1); |
||
2297 | } |
||
2298 | } |
||
2299 | |||
2300 | #ifdef DEBUG |
||
2301 | { |
||
2302 | char buf[20]; |
||
2303 | printf("Hw Address = %s\n", iw_saether_ntop(sap, buf)); |
||
2304 | } |
||
2305 | #endif |
||
2306 | |||
2307 | return(0); |
||
2308 | } |
||
2309 | |||
2310 | /************************* MISC SUBROUTINES **************************/ |
||
2311 | |||
2312 | /* Size (in bytes) of various events */ |
||
2313 | static const int priv_type_size[] = { |
||
2314 | 0, /* IW_PRIV_TYPE_NONE */ |
||
2315 | 1, /* IW_PRIV_TYPE_BYTE */ |
||
2316 | 1, /* IW_PRIV_TYPE_CHAR */ |
||
2317 | 0, /* Not defined */ |
||
2318 | sizeof(__u32), /* IW_PRIV_TYPE_INT */ |
||
2319 | sizeof(struct iw_freq), /* IW_PRIV_TYPE_FLOAT */ |
||
2320 | sizeof(struct sockaddr), /* IW_PRIV_TYPE_ADDR */ |
||
2321 | 0, /* Not defined */ |
||
2322 | }; |
||
2323 | |||
2324 | /*------------------------------------------------------------------*/ |
||
2325 | /* |
||
2326 | * Max size in bytes of an private argument. |
||
2327 | */ |
||
2328 | int |
||
2329 | iw_get_priv_size(int args) |
||
2330 | { |
||
2331 | int num = args & IW_PRIV_SIZE_MASK; |
||
2332 | int type = (args & IW_PRIV_TYPE_MASK) >> 12; |
||
2333 | |||
2334 | return(num * priv_type_size[type]); |
||
2335 | } |
||
2336 | |||
2337 | /************************ EVENT SUBROUTINES ************************/ |
||
2338 | /* |
||
2339 | * The Wireless Extension API 14 and greater define Wireless Events, |
||
2340 | * that are used for various events and scanning. |
||
2341 | * Those functions help the decoding of events, so are needed only in |
||
2342 | * this case. |
||
2343 | */ |
||
2344 | |||
2345 | /* -------------------------- CONSTANTS -------------------------- */ |
||
2346 | |||
2347 | /* Type of headers we know about (basically union iwreq_data) */ |
||
2348 | #define IW_HEADER_TYPE_NULL 0 /* Not available */ |
||
2349 | #define IW_HEADER_TYPE_CHAR 2 /* char [IFNAMSIZ] */ |
||
2350 | #define IW_HEADER_TYPE_UINT 4 /* __u32 */ |
||
2351 | #define IW_HEADER_TYPE_FREQ 5 /* struct iw_freq */ |
||
2352 | #define IW_HEADER_TYPE_ADDR 6 /* struct sockaddr */ |
||
2353 | #define IW_HEADER_TYPE_POINT 8 /* struct iw_point */ |
||
2354 | #define IW_HEADER_TYPE_PARAM 9 /* struct iw_param */ |
||
2355 | #define IW_HEADER_TYPE_QUAL 10 /* struct iw_quality */ |
||
2356 | |||
2357 | /* Handling flags */ |
||
2358 | /* Most are not implemented. I just use them as a reminder of some |
||
2359 | * cool features we might need one day ;-) */ |
||
2360 | #define IW_DESCR_FLAG_NONE 0x0000 /* Obvious */ |
||
2361 | /* Wrapper level flags */ |
||
2362 | #define IW_DESCR_FLAG_DUMP 0x0001 /* Not part of the dump command */ |
||
2363 | #define IW_DESCR_FLAG_EVENT 0x0002 /* Generate an event on SET */ |
||
2364 | #define IW_DESCR_FLAG_RESTRICT 0x0004 /* GET : request is ROOT only */ |
||
2365 | /* SET : Omit payload from generated iwevent */ |
||
2366 | #define IW_DESCR_FLAG_NOMAX 0x0008 /* GET : no limit on request size */ |
||
2367 | /* Driver level flags */ |
||
2368 | #define IW_DESCR_FLAG_WAIT 0x0100 /* Wait for driver event */ |
||
2369 | |||
2370 | /* ---------------------------- TYPES ---------------------------- */ |
||
2371 | |||
2372 | /* |
||
2373 | * Describe how a standard IOCTL looks like. |
||
2374 | */ |
||
2375 | struct iw_ioctl_description |
||
2376 | { |
||
2377 | __u8 header_type; /* NULL, iw_point or other */ |
||
2378 | __u8 token_type; /* Future */ |
||
2379 | __u16 token_size; /* Granularity of payload */ |
||
2380 | __u16 min_tokens; /* Min acceptable token number */ |
||
2381 | __u16 max_tokens; /* Max acceptable token number */ |
||
2382 | __u32 flags; /* Special handling of the request */ |
||
2383 | }; |
||
2384 | |||
2385 | /* -------------------------- VARIABLES -------------------------- */ |
||
2386 | |||
2387 | /* |
||
2388 | * Meta-data about all the standard Wireless Extension request we |
||
2389 | * know about. |
||
2390 | */ |
||
2391 | static const struct iw_ioctl_description standard_ioctl_descr[] = { |
||
2392 | [SIOCSIWCOMMIT - SIOCIWFIRST] = { |
||
2393 | .header_type = IW_HEADER_TYPE_NULL, |
||
2394 | }, |
||
2395 | [SIOCGIWNAME - SIOCIWFIRST] = { |
||
2396 | .header_type = IW_HEADER_TYPE_CHAR, |
||
2397 | .flags = IW_DESCR_FLAG_DUMP, |
||
2398 | }, |
||
2399 | [SIOCSIWNWID - SIOCIWFIRST] = { |
||
2400 | .header_type = IW_HEADER_TYPE_PARAM, |
||
2401 | .flags = IW_DESCR_FLAG_EVENT, |
||
2402 | }, |
||
2403 | [SIOCGIWNWID - SIOCIWFIRST] = { |
||
2404 | .header_type = IW_HEADER_TYPE_PARAM, |
||
2405 | .flags = IW_DESCR_FLAG_DUMP, |
||
2406 | }, |
||
2407 | [SIOCSIWFREQ - SIOCIWFIRST] = { |
||
2408 | .header_type = IW_HEADER_TYPE_FREQ, |
||
2409 | .flags = IW_DESCR_FLAG_EVENT, |
||
2410 | }, |
||
2411 | [SIOCGIWFREQ - SIOCIWFIRST] = { |
||
2412 | .header_type = IW_HEADER_TYPE_FREQ, |
||
2413 | .flags = IW_DESCR_FLAG_DUMP, |
||
2414 | }, |
||
2415 | [SIOCSIWMODE - SIOCIWFIRST] = { |
||
2416 | .header_type = IW_HEADER_TYPE_UINT, |
||
2417 | .flags = IW_DESCR_FLAG_EVENT, |
||
2418 | }, |
||
2419 | [SIOCGIWMODE - SIOCIWFIRST] = { |
||
2420 | .header_type = IW_HEADER_TYPE_UINT, |
||
2421 | .flags = IW_DESCR_FLAG_DUMP, |
||
2422 | }, |
||
2423 | [SIOCSIWSENS - SIOCIWFIRST] = { |
||
2424 | .header_type = IW_HEADER_TYPE_PARAM, |
||
2425 | }, |
||
2426 | [SIOCGIWSENS - SIOCIWFIRST] = { |
||
2427 | .header_type = IW_HEADER_TYPE_PARAM, |
||
2428 | }, |
||
2429 | [SIOCSIWRANGE - SIOCIWFIRST] = { |
||
2430 | .header_type = IW_HEADER_TYPE_NULL, |
||
2431 | }, |
||
2432 | [SIOCGIWRANGE - SIOCIWFIRST] = { |
||
2433 | .header_type = IW_HEADER_TYPE_POINT, |
||
2434 | .token_size = 1, |
||
2435 | .max_tokens = sizeof(struct iw_range), |
||
2436 | .flags = IW_DESCR_FLAG_DUMP, |
||
2437 | }, |
||
2438 | [SIOCSIWPRIV - SIOCIWFIRST] = { |
||
2439 | .header_type = IW_HEADER_TYPE_NULL, |
||
2440 | }, |
||
2441 | [SIOCGIWPRIV - SIOCIWFIRST] = { /* (handled directly by us) */ |
||
2442 | .header_type = IW_HEADER_TYPE_NULL, |
||
2443 | }, |
||
2444 | [SIOCSIWSTATS - SIOCIWFIRST] = { |
||
2445 | .header_type = IW_HEADER_TYPE_NULL, |
||
2446 | }, |
||
2447 | [SIOCGIWSTATS - SIOCIWFIRST] = { /* (handled directly by us) */ |
||
2448 | .header_type = IW_HEADER_TYPE_NULL, |
||
2449 | .flags = IW_DESCR_FLAG_DUMP, |
||
2450 | }, |
||
2451 | [SIOCSIWSPY - SIOCIWFIRST] = { |
||
2452 | .header_type = IW_HEADER_TYPE_POINT, |
||
2453 | .token_size = sizeof(struct sockaddr), |
||
2454 | .max_tokens = IW_MAX_SPY, |
||
2455 | }, |
||
2456 | [SIOCGIWSPY - SIOCIWFIRST] = { |
||
2457 | .header_type = IW_HEADER_TYPE_POINT, |
||
2458 | .token_size = sizeof(struct sockaddr) + |
||
2459 | sizeof(struct iw_quality), |
||
2460 | .max_tokens = IW_MAX_SPY, |
||
2461 | }, |
||
2462 | [SIOCSIWTHRSPY - SIOCIWFIRST] = { |
||
2463 | .header_type = IW_HEADER_TYPE_POINT, |
||
2464 | .token_size = sizeof(struct iw_thrspy), |
||
2465 | .min_tokens = 1, |
||
2466 | .max_tokens = 1, |
||
2467 | }, |
||
2468 | [SIOCGIWTHRSPY - SIOCIWFIRST] = { |
||
2469 | .header_type = IW_HEADER_TYPE_POINT, |
||
2470 | .token_size = sizeof(struct iw_thrspy), |
||
2471 | .min_tokens = 1, |
||
2472 | .max_tokens = 1, |
||
2473 | }, |
||
2474 | [SIOCSIWAP - SIOCIWFIRST] = { |
||
2475 | .header_type = IW_HEADER_TYPE_ADDR, |
||
2476 | }, |
||
2477 | [SIOCGIWAP - SIOCIWFIRST] = { |
||
2478 | .header_type = IW_HEADER_TYPE_ADDR, |
||
2479 | .flags = IW_DESCR_FLAG_DUMP, |
||
2480 | }, |
||
2481 | [SIOCSIWMLME - SIOCIWFIRST] = { |
||
2482 | .header_type = IW_HEADER_TYPE_POINT, |
||
2483 | .token_size = 1, |
||
2484 | .min_tokens = sizeof(struct iw_mlme), |
||
2485 | .max_tokens = sizeof(struct iw_mlme), |
||
2486 | }, |
||
2487 | [SIOCGIWAPLIST - SIOCIWFIRST] = { |
||
2488 | .header_type = IW_HEADER_TYPE_POINT, |
||
2489 | .token_size = sizeof(struct sockaddr) + |
||
2490 | sizeof(struct iw_quality), |
||
2491 | .max_tokens = IW_MAX_AP, |
||
2492 | .flags = IW_DESCR_FLAG_NOMAX, |
||
2493 | }, |
||
2494 | [SIOCSIWSCAN - SIOCIWFIRST] = { |
||
2495 | .header_type = IW_HEADER_TYPE_POINT, |
||
2496 | .token_size = 1, |
||
2497 | .min_tokens = 0, |
||
2498 | .max_tokens = sizeof(struct iw_scan_req), |
||
2499 | }, |
||
2500 | [SIOCGIWSCAN - SIOCIWFIRST] = { |
||
2501 | .header_type = IW_HEADER_TYPE_POINT, |
||
2502 | .token_size = 1, |
||
2503 | .max_tokens = IW_SCAN_MAX_DATA, |
||
2504 | .flags = IW_DESCR_FLAG_NOMAX, |
||
2505 | }, |
||
2506 | [SIOCSIWESSID - SIOCIWFIRST] = { |
||
2507 | .header_type = IW_HEADER_TYPE_POINT, |
||
2508 | .token_size = 1, |
||
2509 | .max_tokens = IW_ESSID_MAX_SIZE + 1, |
||
2510 | .flags = IW_DESCR_FLAG_EVENT, |
||
2511 | }, |
||
2512 | [SIOCGIWESSID - SIOCIWFIRST] = { |
||
2513 | .header_type = IW_HEADER_TYPE_POINT, |
||
2514 | .token_size = 1, |
||
2515 | .max_tokens = IW_ESSID_MAX_SIZE + 1, |
||
2516 | .flags = IW_DESCR_FLAG_DUMP, |
||
2517 | }, |
||
2518 | [SIOCSIWNICKN - SIOCIWFIRST] = { |
||
2519 | .header_type = IW_HEADER_TYPE_POINT, |
||
2520 | .token_size = 1, |
||
2521 | .max_tokens = IW_ESSID_MAX_SIZE + 1, |
||
2522 | }, |
||
2523 | [SIOCGIWNICKN - SIOCIWFIRST] = { |
||
2524 | .header_type = IW_HEADER_TYPE_POINT, |
||
2525 | .token_size = 1, |
||
2526 | .max_tokens = IW_ESSID_MAX_SIZE + 1, |
||
2527 | }, |
||
2528 | [SIOCSIWRATE - SIOCIWFIRST] = { |
||
2529 | .header_type = IW_HEADER_TYPE_PARAM, |
||
2530 | }, |
||
2531 | [SIOCGIWRATE - SIOCIWFIRST] = { |
||
2532 | .header_type = IW_HEADER_TYPE_PARAM, |
||
2533 | }, |
||
2534 | [SIOCSIWRTS - SIOCIWFIRST] = { |
||
2535 | .header_type = IW_HEADER_TYPE_PARAM, |
||
2536 | }, |
||
2537 | [SIOCGIWRTS - SIOCIWFIRST] = { |
||
2538 | .header_type = IW_HEADER_TYPE_PARAM, |
||
2539 | }, |
||
2540 | [SIOCSIWFRAG - SIOCIWFIRST] = { |
||
2541 | .header_type = IW_HEADER_TYPE_PARAM, |
||
2542 | }, |
||
2543 | [SIOCGIWFRAG - SIOCIWFIRST] = { |
||
2544 | .header_type = IW_HEADER_TYPE_PARAM, |
||
2545 | }, |
||
2546 | [SIOCSIWTXPOW - SIOCIWFIRST] = { |
||
2547 | .header_type = IW_HEADER_TYPE_PARAM, |
||
2548 | }, |
||
2549 | [SIOCGIWTXPOW - SIOCIWFIRST] = { |
||
2550 | .header_type = IW_HEADER_TYPE_PARAM, |
||
2551 | }, |
||
2552 | [SIOCSIWRETRY - SIOCIWFIRST] = { |
||
2553 | .header_type = IW_HEADER_TYPE_PARAM, |
||
2554 | }, |
||
2555 | [SIOCGIWRETRY - SIOCIWFIRST] = { |
||
2556 | .header_type = IW_HEADER_TYPE_PARAM, |
||
2557 | }, |
||
2558 | [SIOCSIWENCODE - SIOCIWFIRST] = { |
||
2559 | .header_type = IW_HEADER_TYPE_POINT, |
||
2560 | .token_size = 1, |
||
2561 | .max_tokens = IW_ENCODING_TOKEN_MAX, |
||
2562 | .flags = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT, |
||
2563 | }, |
||
2564 | [SIOCGIWENCODE - SIOCIWFIRST] = { |
||
2565 | .header_type = IW_HEADER_TYPE_POINT, |
||
2566 | .token_size = 1, |
||
2567 | .max_tokens = IW_ENCODING_TOKEN_MAX, |
||
2568 | .flags = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT, |
||
2569 | }, |
||
2570 | [SIOCSIWPOWER - SIOCIWFIRST] = { |
||
2571 | .header_type = IW_HEADER_TYPE_PARAM, |
||
2572 | }, |
||
2573 | [SIOCGIWPOWER - SIOCIWFIRST] = { |
||
2574 | .header_type = IW_HEADER_TYPE_PARAM, |
||
2575 | }, |
||
2576 | [SIOCSIWMODUL - SIOCIWFIRST] = { |
||
2577 | .header_type = IW_HEADER_TYPE_PARAM, |
||
2578 | }, |
||
2579 | [SIOCGIWMODUL - SIOCIWFIRST] = { |
||
2580 | .header_type = IW_HEADER_TYPE_PARAM, |
||
2581 | }, |
||
2582 | [SIOCSIWGENIE - SIOCIWFIRST] = { |
||
2583 | .header_type = IW_HEADER_TYPE_POINT, |
||
2584 | .token_size = 1, |
||
2585 | .max_tokens = IW_GENERIC_IE_MAX, |
||
2586 | }, |
||
2587 | [SIOCGIWGENIE - SIOCIWFIRST] = { |
||
2588 | .header_type = IW_HEADER_TYPE_POINT, |
||
2589 | .token_size = 1, |
||
2590 | .max_tokens = IW_GENERIC_IE_MAX, |
||
2591 | }, |
||
2592 | [SIOCSIWAUTH - SIOCIWFIRST] = { |
||
2593 | .header_type = IW_HEADER_TYPE_PARAM, |
||
2594 | }, |
||
2595 | [SIOCGIWAUTH - SIOCIWFIRST] = { |
||
2596 | .header_type = IW_HEADER_TYPE_PARAM, |
||
2597 | }, |
||
2598 | [SIOCSIWENCODEEXT - SIOCIWFIRST] = { |
||
2599 | .header_type = IW_HEADER_TYPE_POINT, |
||
2600 | .token_size = 1, |
||
2601 | .min_tokens = sizeof(struct iw_encode_ext), |
||
2602 | .max_tokens = sizeof(struct iw_encode_ext) + |
||
2603 | IW_ENCODING_TOKEN_MAX, |
||
2604 | }, |
||
2605 | [SIOCGIWENCODEEXT - SIOCIWFIRST] = { |
||
2606 | .header_type = IW_HEADER_TYPE_POINT, |
||
2607 | .token_size = 1, |
||
2608 | .min_tokens = sizeof(struct iw_encode_ext), |
||
2609 | .max_tokens = sizeof(struct iw_encode_ext) + |
||
2610 | IW_ENCODING_TOKEN_MAX, |
||
2611 | }, |
||
2612 | [SIOCSIWPMKSA - SIOCIWFIRST] = { |
||
2613 | .header_type = IW_HEADER_TYPE_POINT, |
||
2614 | .token_size = 1, |
||
2615 | .min_tokens = sizeof(struct iw_pmksa), |
||
2616 | .max_tokens = sizeof(struct iw_pmksa), |
||
2617 | }, |
||
2618 | }; |
||
2619 | static const unsigned int standard_ioctl_num = (sizeof(standard_ioctl_descr) / |
||
2620 | sizeof(struct iw_ioctl_description)); |
||
2621 | |||
2622 | /* |
||
2623 | * Meta-data about all the additional standard Wireless Extension events |
||
2624 | * we know about. |
||
2625 | */ |
||
2626 | static const struct iw_ioctl_description standard_event_descr[] = { |
||
2627 | [IWEVTXDROP - IWEVFIRST] = { |
||
2628 | .header_type = IW_HEADER_TYPE_ADDR, |
||
2629 | }, |
||
2630 | [IWEVQUAL - IWEVFIRST] = { |
||
2631 | .header_type = IW_HEADER_TYPE_QUAL, |
||
2632 | }, |
||
2633 | [IWEVCUSTOM - IWEVFIRST] = { |
||
2634 | .header_type = IW_HEADER_TYPE_POINT, |
||
2635 | .token_size = 1, |
||
2636 | .max_tokens = IW_CUSTOM_MAX, |
||
2637 | }, |
||
2638 | [IWEVREGISTERED - IWEVFIRST] = { |
||
2639 | .header_type = IW_HEADER_TYPE_ADDR, |
||
2640 | }, |
||
2641 | [IWEVEXPIRED - IWEVFIRST] = { |
||
2642 | .header_type = IW_HEADER_TYPE_ADDR, |
||
2643 | }, |
||
2644 | [IWEVGENIE - IWEVFIRST] = { |
||
2645 | .header_type = IW_HEADER_TYPE_POINT, |
||
2646 | .token_size = 1, |
||
2647 | .max_tokens = IW_GENERIC_IE_MAX, |
||
2648 | }, |
||
2649 | [IWEVMICHAELMICFAILURE - IWEVFIRST] = { |
||
2650 | .header_type = IW_HEADER_TYPE_POINT, |
||
2651 | .token_size = 1, |
||
2652 | .max_tokens = sizeof(struct iw_michaelmicfailure), |
||
2653 | }, |
||
2654 | [IWEVASSOCREQIE - IWEVFIRST] = { |
||
2655 | .header_type = IW_HEADER_TYPE_POINT, |
||
2656 | .token_size = 1, |
||
2657 | .max_tokens = IW_GENERIC_IE_MAX, |
||
2658 | }, |
||
2659 | [IWEVASSOCRESPIE - IWEVFIRST] = { |
||
2660 | .header_type = IW_HEADER_TYPE_POINT, |
||
2661 | .token_size = 1, |
||
2662 | .max_tokens = IW_GENERIC_IE_MAX, |
||
2663 | }, |
||
2664 | [IWEVPMKIDCAND - IWEVFIRST] = { |
||
2665 | .header_type = IW_HEADER_TYPE_POINT, |
||
2666 | .token_size = 1, |
||
2667 | .max_tokens = sizeof(struct iw_pmkid_cand), |
||
2668 | }, |
||
2669 | }; |
||
2670 | static const unsigned int standard_event_num = (sizeof(standard_event_descr) / |
||
2671 | sizeof(struct iw_ioctl_description)); |
||
2672 | |||
2673 | /* Size (in bytes) of various events */ |
||
2674 | static const int event_type_size[] = { |
||
2675 | IW_EV_LCP_PK_LEN, /* IW_HEADER_TYPE_NULL */ |
||
2676 | 0, |
||
2677 | IW_EV_CHAR_PK_LEN, /* IW_HEADER_TYPE_CHAR */ |
||
2678 | 0, |
||
2679 | IW_EV_UINT_PK_LEN, /* IW_HEADER_TYPE_UINT */ |
||
2680 | IW_EV_FREQ_PK_LEN, /* IW_HEADER_TYPE_FREQ */ |
||
2681 | IW_EV_ADDR_PK_LEN, /* IW_HEADER_TYPE_ADDR */ |
||
2682 | 0, |
||
2683 | IW_EV_POINT_PK_LEN, /* Without variable payload */ |
||
2684 | IW_EV_PARAM_PK_LEN, /* IW_HEADER_TYPE_PARAM */ |
||
2685 | IW_EV_QUAL_PK_LEN, /* IW_HEADER_TYPE_QUAL */ |
||
2686 | }; |
||
2687 | |||
2688 | /*------------------------------------------------------------------*/ |
||
2689 | /* |
||
2690 | * Initialise the struct stream_descr so that we can extract |
||
2691 | * individual events from the event stream. |
||
2692 | */ |
||
2693 | void |
||
2694 | iw_init_event_stream(struct stream_descr * stream, /* Stream of events */ |
||
2695 | char * data, |
||
2696 | int len) |
||
2697 | { |
||
2698 | /* Cleanup */ |
||
2699 | memset((char *) stream, '\0', sizeof(struct stream_descr)); |
||
2700 | |||
2701 | /* Set things up */ |
||
2702 | stream->current = data; |
||
2703 | stream->end = data + len; |
||
2704 | } |
||
2705 | |||
2706 | /*------------------------------------------------------------------*/ |
||
2707 | /* |
||
2708 | * Extract the next event from the event stream. |
||
2709 | */ |
||
2710 | int |
||
2711 | iw_extract_event_stream(struct stream_descr * stream, /* Stream of events */ |
||
2712 | struct iw_event * iwe, /* Extracted event */ |
||
2713 | int we_version) |
||
2714 | { |
||
2715 | const struct iw_ioctl_description * descr = NULL; |
||
2716 | int event_type = 0; |
||
2717 | unsigned int event_len = 1; /* Invalid */ |
||
2718 | char * pointer; |
||
2719 | /* Don't "optimise" the following variable, it will crash */ |
||
2720 | unsigned cmd_index; /* *MUST* be unsigned */ |
||
2721 | |||
2722 | /* Check for end of stream */ |
||
2723 | if((stream->current + IW_EV_LCP_PK_LEN) > stream->end) |
||
2724 | return(0); |
||
2725 | |||
2726 | #ifdef DEBUG |
||
2727 | printf("DBG - stream->current = %p, stream->value = %p, stream->end = %p\n", |
||
2728 | stream->current, stream->value, stream->end); |
||
2729 | #endif |
||
2730 | |||
2731 | /* Extract the event header (to get the event id). |
||
2732 | * Note : the event may be unaligned, therefore copy... */ |
||
2733 | memcpy((char *) iwe, stream->current, IW_EV_LCP_PK_LEN); |
||
2734 | |||
2735 | #ifdef DEBUG |
||
2736 | printf("DBG - iwe->cmd = 0x%X, iwe->len = %d\n", |
||
2737 | iwe->cmd, iwe->len); |
||
2738 | #endif |
||
2739 | |||
2740 | /* Check invalid events */ |
||
2741 | if(iwe->len <= IW_EV_LCP_PK_LEN) |
||
2742 | return(-1); |
||
2743 | |||
2744 | /* Get the type and length of that event */ |
||
2745 | if(iwe->cmd <= SIOCIWLAST) |
||
2746 | { |
||
2747 | cmd_index = iwe->cmd - SIOCIWFIRST; |
||
2748 | if(cmd_index < standard_ioctl_num) |
||
2749 | descr = &(standard_ioctl_descr[cmd_index]); |
||
2750 | } |
||
2751 | else |
||
2752 | { |
||
2753 | cmd_index = iwe->cmd - IWEVFIRST; |
||
2754 | if(cmd_index < standard_event_num) |
||
2755 | descr = &(standard_event_descr[cmd_index]); |
||
2756 | } |
||
2757 | if(descr != NULL) |
||
2758 | event_type = descr->header_type; |
||
2759 | /* Unknown events -> event_type=0 => IW_EV_LCP_PK_LEN */ |
||
2760 | event_len = event_type_size[event_type]; |
||
2761 | /* Fixup for earlier version of WE */ |
||
2762 | if((we_version <= 18) && (event_type == IW_HEADER_TYPE_POINT)) |
||
2763 | event_len += IW_EV_POINT_OFF; |
||
2764 | |||
2765 | /* Check if we know about this event */ |
||
2766 | if(event_len <= IW_EV_LCP_PK_LEN) |
||
2767 | { |
||
2768 | /* Skip to next event */ |
||
2769 | stream->current += iwe->len; |
||
2770 | return(2); |
||
2771 | } |
||
2772 | event_len -= IW_EV_LCP_PK_LEN; |
||
2773 | |||
2774 | /* Set pointer on data */ |
||
2775 | if(stream->value != NULL) |
||
2776 | pointer = stream->value; /* Next value in event */ |
||
2777 | else |
||
2778 | pointer = stream->current + IW_EV_LCP_PK_LEN; /* First value in event */ |
||
2779 | |||
2780 | #ifdef DEBUG |
||
2781 | printf("DBG - event_type = %d, event_len = %d, pointer = %p\n", |
||
2782 | event_type, event_len, pointer); |
||
2783 | #endif |
||
2784 | |||
2785 | /* Copy the rest of the event (at least, fixed part) */ |
||
2786 | if((pointer + event_len) > stream->end) |
||
2787 | { |
||
2788 | /* Go to next event */ |
||
2789 | stream->current += iwe->len; |
||
2790 | return(-2); |
||
2791 | } |
||
2792 | /* Fixup for WE-19 and later : pointer no longer in the stream */ |
||
2793 | /* Beware of alignement. Dest has local alignement, not packed */ |
||
2794 | if((we_version > 18) && (event_type == IW_HEADER_TYPE_POINT)) |
||
2795 | memcpy((char *) iwe + IW_EV_LCP_LEN + IW_EV_POINT_OFF, |
||
2796 | pointer, event_len); |
||
2797 | else |
||
2798 | memcpy((char *) iwe + IW_EV_LCP_LEN, pointer, event_len); |
||
2799 | |||
2800 | /* Skip event in the stream */ |
||
2801 | pointer += event_len; |
||
2802 | |||
2803 | /* Special processing for iw_point events */ |
||
2804 | if(event_type == IW_HEADER_TYPE_POINT) |
||
2805 | { |
||
2806 | /* Check the length of the payload */ |
||
2807 | unsigned int extra_len = iwe->len - (event_len + IW_EV_LCP_PK_LEN); |
||
2808 | if(extra_len > 0) |
||
2809 | { |
||
2810 | /* Set pointer on variable part (warning : non aligned) */ |
||
2811 | iwe->u.data.pointer = pointer; |
||
2812 | |||
2813 | /* Check that we have a descriptor for the command */ |
||
2814 | if(descr == NULL) |
||
2815 | /* Can't check payload -> unsafe... */ |
||
2816 | iwe->u.data.pointer = NULL; /* Discard paylod */ |
||
2817 | else |
||
2818 | { |
||
2819 | /* Those checks are actually pretty hard to trigger, |
||
2820 | * because of the checks done in the kernel... */ |
||
2821 | |||
2822 | unsigned int token_len = iwe->u.data.length * descr->token_size; |
||
2823 | |||
2824 | /* Ugly fixup for alignement issues. |
||
2825 | * If the kernel is 64 bits and userspace 32 bits, |
||
2826 | * we have an extra 4+4 bytes. |
||
2827 | * Fixing that in the kernel would break 64 bits userspace. */ |
||
2828 | if((token_len != extra_len) && (extra_len >= 4)) |
||
2829 | { |
||
2830 | __u16 alt_dlen = *((__u16 *) pointer); |
||
2831 | unsigned int alt_token_len = alt_dlen * descr->token_size; |
||
2832 | if((alt_token_len + 8) == extra_len) |
||
2833 | { |
||
2834 | #ifdef DEBUG |
||
2835 | printf("DBG - alt_token_len = %d\n", alt_token_len); |
||
2836 | #endif |
||
2837 | /* Ok, let's redo everything */ |
||
2838 | pointer -= event_len; |
||
2839 | pointer += 4; |
||
2840 | /* Dest has local alignement, not packed */ |
||
2841 | memcpy((char *) iwe + IW_EV_LCP_LEN + IW_EV_POINT_OFF, |
||
2842 | pointer, event_len); |
||
2843 | pointer += event_len + 4; |
||
2844 | iwe->u.data.pointer = pointer; |
||
2845 | token_len = alt_token_len; |
||
2846 | } |
||
2847 | } |
||
2848 | |||
2849 | /* Discard bogus events which advertise more tokens than |
||
2850 | * what they carry... */ |
||
2851 | if(token_len > extra_len) |
||
2852 | iwe->u.data.pointer = NULL; /* Discard paylod */ |
||
2853 | /* Check that the advertised token size is not going to |
||
2854 | * produce buffer overflow to our caller... */ |
||
2855 | if((iwe->u.data.length > descr->max_tokens) |
||
2856 | && !(descr->flags & IW_DESCR_FLAG_NOMAX)) |
||
2857 | iwe->u.data.pointer = NULL; /* Discard paylod */ |
||
2858 | /* Same for underflows... */ |
||
2859 | if(iwe->u.data.length < descr->min_tokens) |
||
2860 | iwe->u.data.pointer = NULL; /* Discard paylod */ |
||
2861 | #ifdef DEBUG |
||
2862 | printf("DBG - extra_len = %d, token_len = %d, token = %d, max = %d, min = %d\n", |
||
2863 | extra_len, token_len, iwe->u.data.length, descr->max_tokens, descr->min_tokens); |
||
2864 | #endif |
||
2865 | } |
||
2866 | } |
||
2867 | else |
||
2868 | /* No data */ |
||
2869 | iwe->u.data.pointer = NULL; |
||
2870 | |||
2871 | /* Go to next event */ |
||
2872 | stream->current += iwe->len; |
||
2873 | } |
||
2874 | else |
||
2875 | { |
||
2876 | /* Ugly fixup for alignement issues. |
||
2877 | * If the kernel is 64 bits and userspace 32 bits, |
||
2878 | * we have an extra 4 bytes. |
||
2879 | * Fixing that in the kernel would break 64 bits userspace. */ |
||
2880 | if((stream->value == NULL) |
||
2881 | && ((((iwe->len - IW_EV_LCP_PK_LEN) % event_len) == 4) |
||
2882 | || ((iwe->len == 12) && ((event_type == IW_HEADER_TYPE_UINT) || |
||
2883 | (event_type == IW_HEADER_TYPE_QUAL))) )) |
||
2884 | { |
||
2885 | #ifdef DEBUG |
||
2886 | printf("DBG - alt iwe->len = %d\n", iwe->len - 4); |
||
2887 | #endif |
||
2888 | pointer -= event_len; |
||
2889 | pointer += 4; |
||
2890 | /* Beware of alignement. Dest has local alignement, not packed */ |
||
2891 | memcpy((char *) iwe + IW_EV_LCP_LEN, pointer, event_len); |
||
2892 | pointer += event_len; |
||
2893 | } |
||
2894 | |||
2895 | /* Is there more value in the event ? */ |
||
2896 | if((pointer + event_len) <= (stream->current + iwe->len)) |
||
2897 | /* Go to next value */ |
||
2898 | stream->value = pointer; |
||
2899 | else |
||
2900 | { |
||
2901 | /* Go to next event */ |
||
2902 | stream->value = NULL; |
||
2903 | stream->current += iwe->len; |
||
2904 | } |
||
2905 | } |
||
2906 | return(1); |
||
2907 | } |
||
2908 | |||
2909 | /*********************** SCANNING SUBROUTINES ***********************/ |
||
2910 | /* |
||
2911 | * The Wireless Extension API 14 and greater define Wireless Scanning. |
||
2912 | * The normal API is complex, this is an easy API that return |
||
2913 | * a subset of the scanning results. This should be enough for most |
||
2914 | * applications that want to use Scanning. |
||
2915 | * If you want to have use the full/normal API, check iwlist.c... |
||
2916 | * |
||
2917 | * Precaution when using scanning : |
||
2918 | * The scanning operation disable normal network traffic, and therefore |
||
2919 | * you should not abuse of scan. |
||
2920 | * The scan need to check the presence of network on other frequencies. |
||
2921 | * While you are checking those other frequencies, you can *NOT* be on |
||
2922 | * your normal frequency to listen to normal traffic in the cell. |
||
2923 | * You need typically in the order of one second to actively probe all |
||
2924 | * 802.11b channels (do the maths). Some cards may do that in background, |
||
2925 | * to reply to scan commands faster, but they still have to do it. |
||
2926 | * Leaving the cell for such an extended period of time is pretty bad. |
||
2927 | * Any kind of streaming/low latency traffic will be impacted, and the |
||
2928 | * user will perceive it (easily checked with telnet). People trying to |
||
2929 | * send traffic to you will retry packets and waste bandwidth. Some |
||
2930 | * applications may be sensitive to those packet losses in weird ways, |
||
2931 | * and tracing those weird behavior back to scanning may take time. |
||
2932 | * If you are in ad-hoc mode, if two nodes scan approx at the same |
||
2933 | * time, they won't see each other, which may create associations issues. |
||
2934 | * For those reasons, the scanning activity should be limited to |
||
2935 | * what's really needed, and continuous scanning is a bad idea. |
||
2936 | * Jean II |
||
2937 | */ |
||
2938 | |||
2939 | /*------------------------------------------------------------------*/ |
||
2940 | /* |
||
2941 | * Process/store one element from the scanning results in wireless_scan |
||
2942 | */ |
||
2943 | static inline struct wireless_scan * |
||
2944 | iw_process_scanning_token(struct iw_event * event, |
||
2945 | struct wireless_scan * wscan) |
||
2946 | { |
||
2947 | struct wireless_scan * oldwscan; |
||
2948 | |||
2949 | /* Now, let's decode the event */ |
||
2950 | switch(event->cmd) |
||
2951 | { |
||
2952 | case SIOCGIWAP: |
||
2953 | /* New cell description. Allocate new cell descriptor, zero it. */ |
||
2954 | oldwscan = wscan; |
||
2955 | wscan = (struct wireless_scan *) malloc(sizeof(struct wireless_scan)); |
||
2956 | if(wscan == NULL) |
||
2957 | return(wscan); |
||
2958 | /* Link at the end of the list */ |
||
2959 | if(oldwscan != NULL) |
||
2960 | oldwscan->next = wscan; |
||
2961 | |||
2962 | /* Reset it */ |
||
2963 | bzero(wscan, sizeof(struct wireless_scan)); |
||
2964 | |||
2965 | /* Save cell identifier */ |
||
2966 | wscan->has_ap_addr = 1; |
||
2967 | memcpy(&(wscan->ap_addr), &(event->u.ap_addr), sizeof (sockaddr)); |
||
2968 | break; |
||
2969 | case SIOCGIWNWID: |
||
2970 | wscan->b.has_nwid = 1; |
||
2971 | memcpy(&(wscan->b.nwid), &(event->u.nwid), sizeof(iwparam)); |
||
2972 | break; |
||
2973 | case SIOCGIWFREQ: |
||
2974 | wscan->b.has_freq = 1; |
||
2975 | wscan->b.freq = iw_freq2float(&(event->u.freq)); |
||
2976 | wscan->b.freq_flags = event->u.freq.flags; |
||
2977 | break; |
||
2978 | case SIOCGIWMODE: |
||
2979 | wscan->b.mode = event->u.mode; |
||
2980 | if((wscan->b.mode < IW_NUM_OPER_MODE) && (wscan->b.mode >= 0)) |
||
2981 | wscan->b.has_mode = 1; |
||
2982 | break; |
||
2983 | case SIOCGIWESSID: |
||
2984 | wscan->b.has_essid = 1; |
||
2985 | wscan->b.essid_on = event->u.data.flags; |
||
2986 | memset(wscan->b.essid, '\0', IW_ESSID_MAX_SIZE+1); |
||
2987 | if((event->u.essid.pointer) && (event->u.essid.length)) |
||
2988 | memcpy(wscan->b.essid, event->u.essid.pointer, event->u.essid.length); |
||
2989 | break; |
||
2990 | case SIOCGIWENCODE: |
||
2991 | wscan->b.has_key = 1; |
||
2992 | wscan->b.key_size = event->u.data.length; |
||
2993 | wscan->b.key_flags = event->u.data.flags; |
||
2994 | if(event->u.data.pointer) |
||
2995 | memcpy(wscan->b.key, event->u.essid.pointer, event->u.data.length); |
||
2996 | else |
||
2997 | wscan->b.key_flags |= IW_ENCODE_NOKEY; |
||
2998 | break; |
||
2999 | case IWEVQUAL: |
||
3000 | /* We don't get complete stats, only qual */ |
||
3001 | wscan->has_stats = 1; |
||
3002 | memcpy(&wscan->stats.qual, &event->u.qual, sizeof(struct iw_quality)); |
||
3003 | break; |
||
3004 | case SIOCGIWRATE: |
||
3005 | /* Scan may return a list of bitrates. As we have space for only |
||
3006 | * a single bitrate, we only keep the largest one. */ |
||
3007 | if((!wscan->has_maxbitrate) || |
||
3008 | (event->u.bitrate.value > wscan->maxbitrate.value)) |
||
3009 | { |
||
3010 | wscan->has_maxbitrate = 1; |
||
3011 | memcpy(&(wscan->maxbitrate), &(event->u.bitrate), sizeof(iwparam)); |
||
3012 | } |
||
3013 | case IWEVCUSTOM: |
||
3014 | /* How can we deal with those sanely ? Jean II */ |
||
3015 | default: |
||
3016 | break; |
||
3017 | } /* switch(event->cmd) */ |
||
3018 | |||
3019 | return(wscan); |
||
3020 | } |
||
3021 | |||
3022 | /*------------------------------------------------------------------*/ |
||
3023 | /* |
||
3024 | * Initiate the scan procedure, and process results. |
||
3025 | * This is a non-blocking procedure and it will return each time |
||
3026 | * it would block, returning the amount of time the caller should wait |
||
3027 | * before calling again. |
||
3028 | * Return -1 for error, delay to wait for (in ms), or 0 for success. |
||
3029 | * Error code is in errno |
||
3030 | */ |
||
3031 | int |
||
3032 | iw_process_scan(int skfd, |
||
3033 | char * ifname, |
||
3034 | int we_version, |
||
3035 | wireless_scan_head * context) |
||
3036 | { |
||
3037 | struct iwreq wrq; |
||
3038 | unsigned char * buffer = NULL; /* Results */ |
||
3039 | int buflen = IW_SCAN_MAX_DATA; /* Min for compat WE<17 */ |
||
3040 | unsigned char * newbuf; |
||
3041 | |||
3042 | /* Don't waste too much time on interfaces (150 * 100 = 15s) */ |
||
3043 | context->retry++; |
||
3044 | if(context->retry > 150) |
||
3045 | { |
||
3046 | errno = ETIME; |
||
3047 | return(-1); |
||
3048 | } |
||
3049 | |||
3050 | /* If we have not yet initiated scanning on the interface */ |
||
3051 | if(context->retry == 1) |
||
3052 | { |
||
3053 | /* Initiate Scan */ |
||
3054 | wrq.u.data.pointer = NULL; /* Later */ |
||
3055 | wrq.u.data.flags = 0; |
||
3056 | wrq.u.data.length = 0; |
||
3057 | /* Remember that as non-root, we will get an EPERM here */ |
||
3058 | if((iw_set_ext(skfd, ifname, SIOCSIWSCAN, &wrq) < 0) |
||
3059 | && (errno != EPERM)) |
||
3060 | return(-1); |
||
3061 | /* Success : now, just wait for event or results */ |
||
3062 | return(250); /* Wait 250 ms */ |
||
3063 | } |
||
3064 | |||
3065 | realloc: |
||
3066 | /* (Re)allocate the buffer - realloc(NULL, len) == malloc(len) */ |
||
3067 | newbuf = realloc(buffer, buflen); |
||
3068 | if(newbuf == NULL) |
||
3069 | { |
||
3070 | /* man says : If realloc() fails the original block is left untouched */ |
||
3071 | if(buffer) |
||
3072 | free(buffer); |
||
3073 | errno = ENOMEM; |
||
3074 | return(-1); |
||
3075 | } |
||
3076 | buffer = newbuf; |
||
3077 | |||
3078 | /* Try to read the results */ |
||
3079 | wrq.u.data.pointer = buffer; |
||
3080 | wrq.u.data.flags = 0; |
||
3081 | wrq.u.data.length = buflen; |
||
3082 | if(iw_get_ext(skfd, ifname, SIOCGIWSCAN, &wrq) < 0) |
||
3083 | { |
||
3084 | /* Check if buffer was too small (WE-17 only) */ |
||
3085 | if((errno == E2BIG) && (we_version > 16)) |
||
3086 | { |
||
3087 | /* Some driver may return very large scan results, either |
||
3088 | * because there are many cells, or because they have many |
||
3089 | * large elements in cells (like IWEVCUSTOM). Most will |
||
3090 | * only need the regular sized buffer. We now use a dynamic |
||
3091 | * allocation of the buffer to satisfy everybody. Of course, |
||
3092 | * as we don't know in advance the size of the array, we try |
||
3093 | * various increasing sizes. Jean II */ |
||
3094 | |||
3095 | /* Check if the driver gave us any hints. */ |
||
3096 | if(wrq.u.data.length > buflen) |
||
3097 | buflen = wrq.u.data.length; |
||
3098 | else |
||
3099 | buflen *= 2; |
||
3100 | |||
3101 | /* Try again */ |
||
3102 | goto realloc; |
||
3103 | } |
||
3104 | |||
3105 | /* Check if results not available yet */ |
||
3106 | if(errno == EAGAIN) |
||
3107 | { |
||
3108 | free(buffer); |
||
3109 | /* Wait for only 100ms from now on */ |
||
3110 | return(100); /* Wait 100 ms */ |
||
3111 | } |
||
3112 | |||
3113 | free(buffer); |
||
3114 | /* Bad error, please don't come back... */ |
||
3115 | return(-1); |
||
3116 | } |
||
3117 | |||
3118 | /* We have the results, process them */ |
||
3119 | if(wrq.u.data.length) |
||
3120 | { |
||
3121 | struct iw_event iwe; |
||
3122 | struct stream_descr stream; |
||
3123 | struct wireless_scan * wscan = NULL; |
||
3124 | int ret; |
||
3125 | #ifdef DEBUG |
||
3126 | /* Debugging code. In theory useless, because it's debugged ;-) */ |
||
3127 | int i; |
||
3128 | printf("Scan result [%02X", buffer[0]); |
||
3129 | for(i = 1; i < wrq.u.data.length; i++) |
||
3130 | printf(":%02X", buffer[i]); |
||
3131 | printf("]\n"); |
||
3132 | #endif |
||
3133 | |||
3134 | /* Init */ |
||
3135 | iw_init_event_stream(&stream, (char *) buffer, wrq.u.data.length); |
||
3136 | /* This is dangerous, we may leak user data... */ |
||
3137 | context->result = NULL; |
||
3138 | |||
3139 | /* Look every token */ |
||
3140 | do |
||
3141 | { |
||
3142 | /* Extract an event and print it */ |
||
3143 | ret = iw_extract_event_stream(&stream, &iwe, we_version); |
||
3144 | if(ret > 0) |
||
3145 | { |
||
3146 | /* Convert to wireless_scan struct */ |
||
3147 | wscan = iw_process_scanning_token(&iwe, wscan); |
||
3148 | /* Check problems */ |
||
3149 | if(wscan == NULL) |
||
3150 | { |
||
3151 | free(buffer); |
||
3152 | errno = ENOMEM; |
||
3153 | return(-1); |
||
3154 | } |
||
3155 | /* Save head of list */ |
||
3156 | if(context->result == NULL) |
||
3157 | context->result = wscan; |
||
3158 | } |
||
3159 | } |
||
3160 | while(ret > 0); |
||
3161 | } |
||
3162 | |||
3163 | /* Done with this interface - return success */ |
||
3164 | free(buffer); |
||
3165 | return(0); |
||
3166 | } |
||
3167 | |||
3168 | /*------------------------------------------------------------------*/ |
||
3169 | /* |
||
3170 | * Perform a wireless scan on the specified interface. |
||
3171 | * This is a blocking procedure and it will when the scan is completed |
||
3172 | * or when an error occur. |
||
3173 | * |
||
3174 | * The scan results are given in a linked list of wireless_scan objects. |
||
3175 | * The caller *must* free the result himself (by walking the list). |
||
3176 | * If there is an error, -1 is returned and the error code is available |
||
3177 | * in errno. |
||
3178 | * |
||
3179 | * The parameter we_version can be extracted from the range structure |
||
3180 | * (range.we_version_compiled - see iw_get_range_info()), or using |
||
3181 | * iw_get_kernel_we_version(). For performance reason, you should |
||
3182 | * cache this parameter when possible rather than querying it every time. |
||
3183 | * |
||
3184 | * Return -1 for error and 0 for success. |
||
3185 | */ |
||
3186 | int |
||
3187 | iw_scan(int skfd, |
||
3188 | char * ifname, |
||
3189 | int we_version, |
||
3190 | wireless_scan_head * context) |
||
3191 | { |
||
3192 | int delay; /* in ms */ |
||
3193 | |||
3194 | /* Clean up context. Potential memory leak if(context.result != NULL) */ |
||
3195 | context->result = NULL; |
||
3196 | context->retry = 0; |
||
3197 | |||
3198 | /* Wait until we get results or error */ |
||
3199 | while(1) |
||
3200 | { |
||
3201 | /* Try to get scan results */ |
||
3202 | delay = iw_process_scan(skfd, ifname, we_version, context); |
||
3203 | |||
3204 | /* Check termination */ |
||
3205 | if(delay <= 0) |
||
3206 | break; |
||
3207 | |||
3208 | /* Wait a bit */ |
||
3209 | usleep(delay * 1000); |
||
3210 | } |
||
3211 | |||
3212 | /* End - return -1 or 0 */ |
||
3213 | return(delay); |
||
3214 | } |