2 * Licensed under the Apache License, Version 2.0 (the "License");
3 * you may not use this file except in compliance with the License.
4 * You may obtain a copy of the License at
6 * http://www.apache.org/licenses/LICENSE-2.0
8 * Unless required by applicable law or agreed to in writing, software
9 * distributed under the License is distributed on an "AS IS" BASIS,
10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 * See the License for the specific language governing permissions and
12 * limitations under the License.
14 * Copyright (C) 2008 John Crispin <blogic@openwrt.org>
15 * Copyright (C) 2008 Steven Barth <steven@midlink.org>
19 #include <net/if_arp.h>
20 #include <net/route.h>
24 #include <linux/sockios.h>
33 static int sock_iwconfig
= 0;
35 typedef struct iwscan_state
38 int ap_num
; /* Access Point number 1->N */
39 int val_index
; /* Value in table 0->(N-1) */
45 sock_iwconfig
= iw_sockets_open();
49 void iwc_shutdown(void)
53 iw_sockets_close(sock_iwconfig
);
57 /* taken from wireless tools */
59 get_info(char * ifname
, struct wireless_info
* info
)
63 memset((char*) info
, 0, sizeof(struct wireless_info
));
65 /* Get basic information */
66 if(iw_get_basic_config(sock_iwconfig
, ifname
, &(info
->b
)) < 0)
68 /* If no wireless name : no wireless extensions */
69 /* But let's check if the interface exists at all */
72 strncpy(ifr
.ifr_name
, ifname
, IFNAMSIZ
);
73 if(ioctl(sock_iwconfig
, SIOCGIFFLAGS
, &ifr
) < 0)
80 if(iw_get_range_info(sock_iwconfig
, ifname
, &(info
->range
)) >= 0)
84 if(iw_get_ext(sock_iwconfig
, ifname
, SIOCGIWAP
, &wrq
) >= 0)
86 info
->has_ap_addr
= 1;
87 memcpy(&(info
->ap_addr
), &(wrq
.u
.ap_addr
), sizeof (sockaddr
));
91 if(iw_get_ext(sock_iwconfig
, ifname
, SIOCGIWRATE
, &wrq
) >= 0)
93 info
->has_bitrate
= 1;
94 memcpy(&(info
->bitrate
), &(wrq
.u
.bitrate
), sizeof(iwparam
));
97 /* Get Power Management settings */
98 wrq
.u
.power
.flags
= 0;
99 if(iw_get_ext(sock_iwconfig
, ifname
, SIOCGIWPOWER
, &wrq
) >= 0)
102 memcpy(&(info
->power
), &(wrq
.u
.power
), sizeof(iwparam
));
106 if(iw_get_stats(sock_iwconfig
, ifname
, &(info
->stats
),
107 &info
->range
, info
->has_range
) >= 0)
113 wrq
.u
.essid
.pointer
= (caddr_t
) info
->nickname
;
114 wrq
.u
.essid
.length
= IW_ESSID_MAX_SIZE
+ 1;
115 wrq
.u
.essid
.flags
= 0;
116 if(iw_get_ext(sock_iwconfig
, ifname
, SIOCGIWNICKN
, &wrq
) >= 0)
117 if(wrq
.u
.data
.length
> 1)
118 info
->has_nickname
= 1;
120 if((info
->has_range
) && (info
->range
.we_version_compiled
> 9))
122 /* Get Transmit Power */
123 if(iw_get_ext(sock_iwconfig
, ifname
, SIOCGIWTXPOW
, &wrq
) >= 0)
125 info
->has_txpower
= 1;
126 memcpy(&(info
->txpower
), &(wrq
.u
.txpower
), sizeof(iwparam
));
130 /* Get sensitivity */
131 if(iw_get_ext(sock_iwconfig
, ifname
, SIOCGIWSENS
, &wrq
) >= 0)
134 memcpy(&(info
->sens
), &(wrq
.u
.sens
), sizeof(iwparam
));
137 if((info
->has_range
) && (info
->range
.we_version_compiled
> 10))
139 /* Get retry limit/lifetime */
140 if(iw_get_ext(sock_iwconfig
, ifname
, SIOCGIWRETRY
, &wrq
) >= 0)
143 memcpy(&(info
->retry
), &(wrq
.u
.retry
), sizeof(iwparam
));
147 /* Get RTS threshold */
148 if(iw_get_ext(sock_iwconfig
, ifname
, SIOCGIWRTS
, &wrq
) >= 0)
151 memcpy(&(info
->rts
), &(wrq
.u
.rts
), sizeof(iwparam
));
154 /* Get fragmentation threshold */
155 if(iw_get_ext(sock_iwconfig
, ifname
, SIOCGIWFRAG
, &wrq
) >= 0)
158 memcpy(&(info
->frag
), &(wrq
.u
.frag
), sizeof(iwparam
));
164 void iwc_get(lua_State
*L
, char *ifname
)
166 struct wireless_info info
;
167 int rc
= get_info(ifname
, &info
);
172 lua_pushstring(L
, ifname
);
178 add_table_entry(L
, "essid", info
.b
.essid
);
180 add_table_entry(L
, "essid", "off");
184 add_table_entry(L
, "mode", iw_operation_mode
[info
.b
.mode
]);
188 double freq
= info
.b
.freq
; /* Frequency/channel */
189 int channel
= -1; /* Converted to channel */
191 if(info
.has_range
&& (freq
< KILO
))
192 channel
= iw_channel_to_freq((int) freq
, &freq
, &info
.range
);
193 iw_print_freq(buffer
, sizeof(buffer
), freq
, -1, info
.b
.freq_flags
);
194 snprintf(tmp
, 4, "%d", channel
);
195 add_table_entry(L
, "channel", tmp
);
196 add_table_entry(L
, "freq", buffer
);
200 add_table_entry(L
, "macap", iw_sawap_ntop(&info
.ap_addr
, buffer
));
204 iw_print_bitrate(buffer
, sizeof(buffer
), info
.bitrate
.value
);
205 add_table_entry(L
, "bitrate", buffer
);
210 iw_print_txpower(buffer
, sizeof(buffer
), &info
.txpower
);
211 add_table_entry(L
, "txpower", buffer
);
216 int iwc_getall(lua_State
*L
)
221 fp
= fopen("/proc/net/wireless", "r");
224 fgets(buffer
, 128, fp
);
225 fgets(buffer
, 128, fp
);
227 while(fgets(buffer
, 128, fp
))
241 /* taken from wireless tools */
242 int iwc_set_essid(lua_State
*L
)
246 char essid
[IW_ESSID_MAX_SIZE
+ 1];
247 int we_kernel_version
;
249 if(lua_gettop(L
) != 2)
251 lua_pushstring(L
, "invalid arg list");
255 ifname
= (char *)lua_tostring (L
, 1);
256 e
= (char *)lua_tostring (L
, 2);
258 if((!strcasecmp(e
, "off")) | (!strcasecmp(e
, "any")))
260 wrq
.u
.essid
.flags
= 0;
262 } else if(!strcasecmp(e
, "on"))
265 memset(essid
, '\0', sizeof(essid
));
266 wrq
.u
.essid
.pointer
= (caddr_t
) essid
;
267 wrq
.u
.essid
.length
= IW_ESSID_MAX_SIZE
+ 1;
268 wrq
.u
.essid
.flags
= 0;
269 if(iw_get_ext(sock_iwconfig
, ifname
, SIOCGIWESSID
, &wrq
) < 0)
271 wrq
.u
.essid
.flags
= 1;
273 wrq
.u
.essid
.flags
= 1;
274 strcpy(essid
, e
); /* Size checked, all clear */
278 /* Get version from kernel, device may not have range... */
279 we_kernel_version
= iw_get_kernel_we_version();
281 /* Finally set the ESSID value */
282 wrq
.u
.essid
.pointer
= (caddr_t
) essid
;
283 wrq
.u
.essid
.length
= strlen(essid
);
284 if(we_kernel_version
< 21)
285 wrq
.u
.essid
.length
++;
287 if(!iw_set_ext(sock_iwconfig
, ifname
, SIOCSIWESSID
, &wrq
))
288 lua_pushboolean(L
, 1);
290 lua_pushboolean(L
, 0);
294 /* taken from wireless tools */
295 int iwc_set_mode(lua_State
*L
)
298 unsigned int k
; /* Must be unsigned */
301 if(lua_gettop(L
) != 2)
303 lua_pushstring(L
, "invalid arg list");
307 ifname
= (char *)lua_tostring (L
, 1);
308 mode
= (char *)lua_tostring (L
, 2);
310 /* Check if it is a uint, otherwise get is as a string */
311 if(sscanf(mode
, "%ui", &k
) != 1)
314 while((k
< IW_NUM_OPER_MODE
) && strncasecmp(mode
, iw_operation_mode
[k
], 3))
317 if(k
>= IW_NUM_OPER_MODE
)
321 if(!iw_set_ext(sock_iwconfig
, ifname
, SIOCSIWMODE
, &wrq
))
322 lua_pushboolean(L
, 1);
324 lua_pushboolean(L
, 0);
328 int iwc_set_channel(lua_State
*L
)
333 if(lua_gettop(L
) != 2)
335 lua_pushstring(L
, "invalid arg list");
339 ifname
= (char *)lua_tostring (L
, 1);
340 channel
= (int)lua_tointeger(L
, 2);
346 wrq
.u
.freq
.flags
= 0;
348 iw_float2freq(channel
, &wrq
.u
.freq
);
349 wrq
.u
.freq
.flags
= IW_FREQ_FIXED
;
351 if(!iw_set_ext(sock_iwconfig
, ifname
, SIOCSIWFREQ
, &wrq
))
352 lua_pushboolean(L
, 1);
354 lua_pushboolean(L
, 0);
358 static const char * iw_ie_cypher_name
[] = {
366 #define IW_ARRAY_LEN(x) (sizeof(x)/sizeof((x)[0]))
367 #define IW_IE_CYPHER_NUM IW_ARRAY_LEN(iw_ie_cypher_name)
369 static const char * iw_ie_key_mgmt_name
[] = {
374 #define IW_IE_KEY_MGMT_NUM IW_ARRAY_LEN(iw_ie_key_mgmt_name)
376 static inline void iw_print_ie_wpa(lua_State
*L
, unsigned char * iebuf
, int buflen
)
378 int ielen
= iebuf
[1] + 2;
379 int offset
= 2; /* Skip the IE id, and the length. */
380 unsigned char wpa1_oui
[3] = {0x00, 0x50, 0xf2};
381 unsigned char wpa2_oui
[3] = {0x00, 0x0f, 0xac};
382 unsigned char *wpa_oui
;
386 int wpa1
= 0, wpa2
= 0;
393 case 0x30: /* WPA2 */
394 /* Check if we have enough data */
400 case 0xdd: /* WPA or else */
402 /* Not all IEs that start with 0xdd are WPA.
403 * * So check that the OUI is valid. */
405 || ((memcmp(&iebuf
[offset
], wpa_oui
, 3) != 0)
406 && (iebuf
[offset
+3] == 0x01)))
418 /* Pick version number (little endian) */
419 ver
= iebuf
[offset
] | (iebuf
[offset
+ 1] << 8);
427 if(ielen
< (offset
+ 4))
431 add_table_entry(L
, "wpa1gcipher", "TKIP");
432 add_table_entry(L
, "wpa1pcipher", "TKIP");
434 add_table_entry(L
, "wpa2gcipher", "TKIP");
435 add_table_entry(L
, "wpa2pcipher", "TKIP");
440 if(memcmp(&iebuf
[offset
], wpa_oui
, 3) != 0)
443 add_table_entry(L
, "wpa1gcipher", "Proprietary");
445 add_table_entry(L
, "wpa2gcipher", "Proprietary");
448 add_table_entry(L
, "wpa1gcipher", iebuf
[offset
+3][iw_ie_cypher_name
]);
450 add_table_entry(L
, "wpa2gcipher", iebuf
[offset
+3][iw_ie_cypher_name
]);
454 if(ielen
< (offset
+ 2))
457 add_table_entry(L
, "wpa1pcipher", "TKIP");
459 add_table_entry(L
, "wpa2pcipher", "TKIP");
462 /* Otherwise, we have some number of pairwise ciphers. */
463 cnt
= iebuf
[offset
] | (iebuf
[offset
+ 1] << 8);
465 if(ielen
< (offset
+ 4*cnt
))
468 for(i
= 0; i
< cnt
; i
++)
471 strncat(buf
, " ", 256);
472 if(memcmp(&iebuf
[offset
], wpa_oui
, 3) != 0)
474 strncat(buf
, "Proprietary", 256);
476 if(iebuf
[offset
+3] <= IW_IE_CYPHER_NUM
)
477 strncat(buf
, iw_ie_cypher_name
[iebuf
[offset
+3]], 256);
479 strncat(buf
, "unknown", 256);
484 add_table_entry(L
, "wpa1pcipher", buf
);
486 add_table_entry(L
, "wpa2pcipher", buf
);
488 /* Check if we are done */
489 if(ielen
< (offset
+ 2))
492 /* Now, we have authentication suites. */
493 cnt
= iebuf
[offset
] | (iebuf
[offset
+ 1] << 8);
496 if(ielen
< (offset
+ 4*cnt
))
499 for(i
= 0; i
< cnt
; i
++)
502 strncat(buf
, " ", 256);
503 if(memcmp(&iebuf
[offset
], wpa_oui
, 3) != 0)
505 strncat(buf
, "Proprietary", 256);
507 if(iebuf
[offset
+3] <= IW_IE_KEY_MGMT_NUM
)
508 strncat(buf
, iw_ie_key_mgmt_name
[iebuf
[offset
+3]], 256);
510 strncat(buf
, "unknown", 256);
515 add_table_entry(L
, "wpa1auth", buf
);
517 add_table_entry(L
, "wpa2auth", buf
);
518 /* Check if we are done */
519 if(ielen
< (offset
+ 1))
523 static inline void print_scanning_token(lua_State
*L
, struct stream_descr
*stream
,
524 struct iw_event
*event
, struct iwscan_state
*state
, struct iw_range
*iw_range
, int has_range
)
526 char buffer
[128]; /* Temporary buffer */
528 /* Now, let's decode the event */
532 add_table_entry(L
, "addr", iw_saether_ntop(&event
->u
.ap_addr
, buffer
));
537 double freq
; /* Frequency/channel */
538 int channel
= -1; /* Converted to channel */
539 freq
= iw_freq2float(&(event
->u
.freq
));
540 /* Convert to channel if possible */
542 channel
= iw_freq_to_channel(freq
, iw_range
);
543 snprintf(buffer
, 128, "%1.3f", freq
);
544 add_table_entry(L
, "frequency", buffer
);
545 snprintf(buffer
, 128, "%d", channel
);
546 add_table_entry(L
, "channel", buffer
);
547 //iw_print_freq(buffer, sizeof(buffer), freq, channel, event->u.freq.flags);
548 //printf(" %s\n", buffer);
552 /* Note : event->u.mode is unsigned, no need to check <= 0 */
553 if(event
->u
.mode
>= IW_NUM_OPER_MODE
)
554 event
->u
.mode
= IW_NUM_OPER_MODE
;
555 add_table_entry(L
, "mode", iw_operation_mode
[event
->u
.mode
]);
559 char essid
[IW_ESSID_MAX_SIZE
+1];
560 memset(essid
, '\0', sizeof(essid
));
561 if((event
->u
.essid
.pointer
) && (event
->u
.essid
.length
))
562 memcpy(essid
, event
->u
.essid
.pointer
, event
->u
.essid
.length
);
563 if(event
->u
.essid
.flags
)
564 add_table_entry(L
, "essid", essid
);
566 add_table_entry(L
, "essid", "off/any/hidden");
571 unsigned char key
[IW_ENCODING_TOKEN_MAX
];
572 if(event
->u
.data
.pointer
)
573 memcpy(key
, event
->u
.data
.pointer
, event
->u
.data
.length
);
575 event
->u
.data
.flags
|= IW_ENCODE_NOKEY
;
576 if(event
->u
.data
.flags
& IW_ENCODE_DISABLED
)
578 add_table_entry(L
, "key", "off");
580 iw_print_key(buffer
, sizeof(buffer
), key
, event
->u
.data
.length
,
581 event
->u
.data
.flags
);
582 add_table_entry(L
, "key", buffer
);
587 if(state
->val_index
== 0)
589 lua_pushstring(L
, "bitrates");
592 //iw_print_bitrate(buffer, sizeof(buffer), event->u.bitrate.value);
593 snprintf(buffer
, sizeof(buffer
), "%d", event
->u
.bitrate
.value
);
594 lua_pushinteger(L
, state
->val_index
+ 1);
595 lua_pushstring(L
, buffer
);
598 /* Check for termination */
599 if(stream
->value
== NULL
)
602 state
->val_index
= 0;
609 unsigned char *buffer
= event
->u
.data
.pointer
;
610 int buflen
= event
->u
.data
.length
;
611 while(offset
<= (buflen
- 2))
613 switch(buffer
[offset
])
615 case 0xdd: /* WPA1 (and other) */
616 case 0x30: /* WPA2 */
617 iw_print_ie_wpa(L
, buffer
+ offset
, buflen
);
622 offset
+= buffer
[offset
+1] + 2;
628 } /* switch(event->cmd) */
631 int iwc_scan(lua_State
*L
)
634 struct iw_scan_req scanopt
; /* Options for 'set' */
635 int scanflags
= 0; /* Flags for scan */
636 unsigned char *buffer
= NULL
; /* Results */
637 int buflen
= IW_SCAN_MAX_DATA
; /* Min for compat WE<17 */
638 struct iw_range range
;
640 struct timeval tv
; /* Select timeout */
641 int timeout
= 15000000; /* 15s */
643 if(lua_gettop(L
) != 1)
645 lua_pushstring(L
, "invalid arg list");
649 ifname
= (char *)lua_tostring (L
, 1);
651 /* Debugging stuff */
652 if((IW_EV_LCP_PK2_LEN
!= IW_EV_LCP_PK_LEN
) || (IW_EV_POINT_PK2_LEN
!= IW_EV_POINT_PK_LEN
))
654 fprintf(stderr
, "*** Please report to jt@hpl.hp.com your platform details\n");
655 fprintf(stderr
, "*** and the following line :\n");
656 fprintf(stderr
, "*** IW_EV_LCP_PK2_LEN = %zu ; IW_EV_POINT_PK2_LEN = %zu\n\n",
657 IW_EV_LCP_PK2_LEN
, IW_EV_POINT_PK2_LEN
);
660 /* Get range stuff */
661 has_range
= (iw_get_range_info(sock_iwconfig
, ifname
, &range
) >= 0);
663 /* Check if the interface could support scanning. */
664 if((!has_range
) || (range
.we_version_compiled
< 14))
666 lua_pushstring(L
, "interface does not support scanning");
671 /* Init timeout value -> 250ms between set and first get */
675 /* Clean up set args */
676 memset(&scanopt
, 0, sizeof(scanopt
));
678 wrq
.u
.data
.pointer
= NULL
;
679 wrq
.u
.data
.flags
= 0;
680 wrq
.u
.data
.length
= 0;
682 /* Initiate Scanning */
683 if(iw_set_ext(sock_iwconfig
, ifname
, SIOCSIWSCAN
, &wrq
) < 0)
685 if((errno
!= EPERM
) || (scanflags
!= 0))
687 lua_pushstring(L
, "interface does not support scanning");
691 /* If we don't have the permission to initiate the scan, we may
692 * * still have permission to read left-over results.
693 * * But, don't wait !!! */
695 /* Not cool, it display for non wireless interfaces... */
696 fprintf(stderr
, "%-8.16s (Could not trigger scanning, just reading left-over results)\n", ifname
);
700 timeout
-= tv
.tv_usec
;
705 fd_set rfds
; /* File descriptors for select */
706 int last_fd
; /* Last fd */
709 /* Guess what ? We must re-generate rfds each time */
712 /* In here, add the rtnetlink fd in the list */
714 /* Wait until something happens */
715 ret
= select(last_fd
+ 1, &rfds
, NULL
, NULL
, &tv
);
717 /* Check if there was an error */
720 if(errno
== EAGAIN
|| errno
== EINTR
)
722 lua_pushstring(L
, "unhandled signal");
727 /* Check if there was a timeout */
730 unsigned char * newbuf
;
733 /* (Re)allocate the buffer - realloc(NULL, len) == malloc(len) */
734 newbuf
= realloc(buffer
, buflen
);
739 fprintf(stderr
, "%s: Allocation failed\n", __FUNCTION__
);
744 /* Try to read the results */
745 wrq
.u
.data
.pointer
= buffer
;
746 wrq
.u
.data
.flags
= 0;
747 wrq
.u
.data
.length
= buflen
;
748 if(iw_get_ext(sock_iwconfig
, ifname
, SIOCGIWSCAN
, &wrq
) < 0)
750 /* Check if buffer was too small (WE-17 only) */
751 if((errno
== E2BIG
) && (range
.we_version_compiled
> 16))
753 /* Some driver may return very large scan results, either
754 * because there are many cells, or because they have many
755 * large elements in cells (like IWEVCUSTOM). Most will
756 * only need the regular sized buffer. We now use a dynamic
757 * allocation of the buffer to satisfy everybody. Of course,
758 * as we don't know in advance the size of the array, we try
759 * various increasing sizes. Jean II */
761 /* Check if the driver gave us any hints. */
762 if(wrq
.u
.data
.length
> buflen
)
763 buflen
= wrq
.u
.data
.length
;
771 /* Check if results not available yet */
774 /* Restart timer for only 100ms*/
777 timeout
-= tv
.tv_usec
;
779 continue; /* Try again later */
784 fprintf(stderr
, "%-8.16s Failed to read scan data : %s\n\n",
785 ifname
, strerror(errno
));
788 /* We have the results, go to process them */
792 /* In here, check if event and event type
793 * * if scan event, read results. All errors bad & no reset timeout */
796 if(wrq
.u
.data
.length
)
799 struct stream_descr stream
;
800 struct iwscan_state state
= { .ap_num
= 1, .val_index
= 0 };
803 iw_init_event_stream(&stream
, (char *) buffer
, wrq
.u
.data
.length
);
807 /* Extract an event and print it */
808 ret
= iw_extract_event_stream(&stream
, &iwe
,
809 range
.we_version_compiled
);
812 if(iwe
.cmd
== SIOCGIWAP
)
817 lua_pushinteger(L
, state
.ap_num
);
820 print_scanning_token(L
, &stream
, &iwe
, &state
, &range
, has_range
);