/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Copyright (C) 2008 John Crispin * Copyright (C) 2008 Steven Barth * Copyright (C) 2009 Jo-Philipp Wich */ #include #include #include #include #include #include #include #include "iwlib.h" #include #include #include #include "helper.h" static int sock_iwconfig = 0; typedef struct iwscan_state { /* State */ int ap_num; /* Access Point number 1->N */ int val_index; /* Value in table 0->(N-1) */ } iwscan_state; int iwc_startup(void) { if(!sock_iwconfig) sock_iwconfig = iw_sockets_open(); return sock_iwconfig; } void iwc_shutdown(void) { if(!sock_iwconfig) return; iw_sockets_close(sock_iwconfig); sock_iwconfig = 0; } /* taken from wireless tools */ static int get_info(char * ifname, struct wireless_info * info) { struct iwreq wrq; memset((char*) info, 0, sizeof(struct wireless_info)); /* Get basic information */ if(iw_get_basic_config(sock_iwconfig, ifname, &(info->b)) < 0) { /* If no wireless name : no wireless extensions */ /* But let's check if the interface exists at all */ struct ifreq ifr; strncpy(ifr.ifr_name, ifname, IFNAMSIZ); if(ioctl(sock_iwconfig, SIOCGIFFLAGS, &ifr) < 0) return(-ENODEV); else return(-ENOTSUP); } /* Get ranges */ if(iw_get_range_info(sock_iwconfig, ifname, &(info->range)) >= 0) info->has_range = 1; /* Get AP address */ if(iw_get_ext(sock_iwconfig, ifname, SIOCGIWAP, &wrq) >= 0) { info->has_ap_addr = 1; memcpy(&(info->ap_addr), &(wrq.u.ap_addr), sizeof (sockaddr)); } /* Get bit rate */ if(iw_get_ext(sock_iwconfig, ifname, SIOCGIWRATE, &wrq) >= 0) { info->has_bitrate = 1; memcpy(&(info->bitrate), &(wrq.u.bitrate), sizeof(iwparam)); } /* Get Power Management settings */ wrq.u.power.flags = 0; if(iw_get_ext(sock_iwconfig, ifname, SIOCGIWPOWER, &wrq) >= 0) { info->has_power = 1; memcpy(&(info->power), &(wrq.u.power), sizeof(iwparam)); } /* Get stats */ if(iw_get_stats(sock_iwconfig, ifname, &(info->stats), &info->range, info->has_range) >= 0) { info->has_stats = 1; } /* Get NickName */ wrq.u.essid.pointer = (caddr_t) info->nickname; wrq.u.essid.length = IW_ESSID_MAX_SIZE + 1; wrq.u.essid.flags = 0; if(iw_get_ext(sock_iwconfig, ifname, SIOCGIWNICKN, &wrq) >= 0) if(wrq.u.data.length > 1) info->has_nickname = 1; if((info->has_range) && (info->range.we_version_compiled > 9)) { /* Get Transmit Power */ if(iw_get_ext(sock_iwconfig, ifname, SIOCGIWTXPOW, &wrq) >= 0) { info->has_txpower = 1; memcpy(&(info->txpower), &(wrq.u.txpower), sizeof(iwparam)); } } /* Get sensitivity */ if(iw_get_ext(sock_iwconfig, ifname, SIOCGIWSENS, &wrq) >= 0) { info->has_sens = 1; memcpy(&(info->sens), &(wrq.u.sens), sizeof(iwparam)); } if((info->has_range) && (info->range.we_version_compiled > 10)) { /* Get retry limit/lifetime */ if(iw_get_ext(sock_iwconfig, ifname, SIOCGIWRETRY, &wrq) >= 0) { info->has_retry = 1; memcpy(&(info->retry), &(wrq.u.retry), sizeof(iwparam)); } } /* Get RTS threshold */ if(iw_get_ext(sock_iwconfig, ifname, SIOCGIWRTS, &wrq) >= 0) { info->has_rts = 1; memcpy(&(info->rts), &(wrq.u.rts), sizeof(iwparam)); } /* Get fragmentation threshold */ if(iw_get_ext(sock_iwconfig, ifname, SIOCGIWFRAG, &wrq) >= 0) { info->has_frag = 1; memcpy(&(info->frag), &(wrq.u.frag), sizeof(iwparam)); } return(0); } void iwc_get(lua_State *L, char *ifname) { struct wireless_info info; int rc = get_info(ifname, &info); char buffer[128]; if(rc) return; lua_pushstring(L, ifname); lua_newtable(L); if(info.b.has_essid) { if(info.b.essid_on) add_table_entry(L, "essid", info.b.essid); else add_table_entry(L, "essid", "off"); } if(info.b.has_mode) add_table_entry(L, "mode", iw_operation_mode[info.b.mode]); if(info.b.has_freq) { double freq = info.b.freq; /* Frequency/channel */ int channel = -1; /* Converted to channel */ char tmp[4]; if(info.has_range && (freq < KILO)) channel = iw_channel_to_freq((int) freq, &freq, &info.range); iw_print_freq(buffer, sizeof(buffer), freq, -1, info.b.freq_flags); snprintf(tmp, 4, "%d", channel); add_table_entry(L, "channel", tmp); add_table_entry(L, "freq", buffer); } if(info.has_ap_addr) add_table_entry(L, "macap", iw_sawap_ntop(&info.ap_addr, buffer)); if(info.has_bitrate) { iw_print_bitrate(buffer, sizeof(buffer), info.bitrate.value); add_table_entry(L, "bitrate", buffer); } if(info.has_txpower) { iw_print_txpower(buffer, sizeof(buffer), &info.txpower); add_table_entry(L, "txpower", buffer); } lua_settable(L, -3); } int iwc_getall(lua_State *L) { FILE *fp; char buffer[128]; char *b = buffer; fp = fopen("/proc/net/wireless", "r"); if(!fp) return -1; fgets(buffer, 128, fp); fgets(buffer, 128, fp); lua_newtable(L); while(fgets(buffer, 128, fp)) { char *t; b = buffer; while(*b == ' ') b++; t = strstr(b, ":"); if(t) *t = '\0'; iwc_get(L, b); } return 1; } /* taken from wireless tools */ int iwc_set_essid(lua_State *L) { struct iwreq wrq; int i = 1; char essid[IW_ESSID_MAX_SIZE + 1]; int we_kernel_version; char *ifname, *e; if(lua_gettop(L) != 2) { lua_pushstring(L, "invalid arg list"); lua_error(L); return 0; } ifname = (char *)lua_tostring (L, 1); e = (char *)lua_tostring (L, 2); if((!strcasecmp(e, "off")) | (!strcasecmp(e, "any"))) { wrq.u.essid.flags = 0; essid[0] = '\0'; } else if(!strcasecmp(e, "on")) { /* Get old essid */ memset(essid, '\0', sizeof(essid)); wrq.u.essid.pointer = (caddr_t) essid; wrq.u.essid.length = IW_ESSID_MAX_SIZE + 1; wrq.u.essid.flags = 0; if(iw_get_ext(sock_iwconfig, ifname, SIOCGIWESSID, &wrq) < 0) return 0; wrq.u.essid.flags = 1; } else { wrq.u.essid.flags = 1; strcpy(essid, e); /* Size checked, all clear */ i++; } /* Get version from kernel, device may not have range... */ we_kernel_version = iw_get_kernel_we_version(); /* Finally set the ESSID value */ wrq.u.essid.pointer = (caddr_t) essid; wrq.u.essid.length = strlen(essid); if(we_kernel_version < 21) wrq.u.essid.length++; if(!iw_set_ext(sock_iwconfig, ifname, SIOCSIWESSID, &wrq)) lua_pushboolean(L, 1); else lua_pushboolean(L, 0); return 1; } /* taken from wireless tools */ int iwc_set_mode(lua_State *L) { struct iwreq wrq; unsigned int k; /* Must be unsigned */ char *ifname, *mode; if(lua_gettop(L) != 2) { lua_pushstring(L, "invalid arg list"); lua_error(L); return 0; } ifname = (char *)lua_tostring (L, 1); mode = (char *)lua_tostring (L, 2); /* Check if it is a uint, otherwise get is as a string */ if(sscanf(mode, "%ui", &k) != 1) { k = 0; while((k < IW_NUM_OPER_MODE) && strncasecmp(mode, iw_operation_mode[k], 3)) k++; } if(k >= IW_NUM_OPER_MODE) return 0; wrq.u.mode = k; if(!iw_set_ext(sock_iwconfig, ifname, SIOCSIWMODE, &wrq)) lua_pushboolean(L, 1); else lua_pushboolean(L, 0); return 1; } int iwc_set_channel(lua_State *L) { struct iwreq wrq; char *ifname; int channel; if(lua_gettop(L) != 2) { lua_pushstring(L, "invalid arg list"); lua_error(L); return 0; } ifname = (char *)lua_tostring (L, 1); channel = (int)lua_tointeger(L, 2); if(channel == -1) { wrq.u.freq.m = -1; wrq.u.freq.e = 0; wrq.u.freq.flags = 0; } else { iw_float2freq(channel, &wrq.u.freq); wrq.u.freq.flags = IW_FREQ_FIXED; } if(!iw_set_ext(sock_iwconfig, ifname, SIOCSIWFREQ, &wrq)) lua_pushboolean(L, 1); else lua_pushboolean(L, 0); return 1; } static const char * iw_ie_cypher_name[] = { "none", "WEP-40", "TKIP", "WRAP", "CCMP", "WEP-104", }; #define IW_ARRAY_LEN(x) (sizeof(x)/sizeof((x)[0])) #define IW_IE_CYPHER_NUM IW_ARRAY_LEN(iw_ie_cypher_name) static const char * iw_ie_key_mgmt_name[] = { "none", "802.1x", "PSK", }; #define IW_IE_KEY_MGMT_NUM IW_ARRAY_LEN(iw_ie_key_mgmt_name) static inline void iw_print_ie_wpa(lua_State *L, unsigned char * iebuf, int buflen) { int ielen = iebuf[1] + 2; int offset = 2; /* Skip the IE id, and the length. */ unsigned char wpa1_oui[3] = {0x00, 0x50, 0xf2}; unsigned char wpa2_oui[3] = {0x00, 0x0f, 0xac}; unsigned char *wpa_oui; int i; uint16_t ver = 0; uint16_t cnt = 0; int wpa1 = 0, wpa2 = 0; char buf[256]; if(ielen > buflen) ielen = buflen; switch(iebuf[0]) { case 0x30: /* WPA2 */ /* Check if we have enough data */ if(ielen < 4) return; wpa_oui = wpa2_oui; break; case 0xdd: /* WPA or else */ wpa_oui = wpa1_oui; /* Not all IEs that start with 0xdd are WPA. * * So check that the OUI is valid. */ if((ielen < 8) || ((memcmp(&iebuf[offset], wpa_oui, 3) != 0) && (iebuf[offset+3] == 0x01))) { return; } offset += 4; break; default: return; } /* Pick version number (little endian) */ ver = iebuf[offset] | (iebuf[offset + 1] << 8); offset += 2; if(iebuf[0] == 0xdd) wpa1 = 1; if(iebuf[0] == 0x30) wpa2 = 1; if(ielen < (offset + 4)) { if(wpa1) { add_table_entry(L, "wpa1gcipher", "TKIP"); add_table_entry(L, "wpa1pcipher", "TKIP"); } else { add_table_entry(L, "wpa2gcipher", "TKIP"); add_table_entry(L, "wpa2pcipher", "TKIP"); } return; } if(memcmp(&iebuf[offset], wpa_oui, 3) != 0) { if(wpa1) add_table_entry(L, "wpa1gcipher", "Proprietary"); else add_table_entry(L, "wpa2gcipher", "Proprietary"); } else { if(wpa1) add_table_entry(L, "wpa1gcipher", iebuf[offset+3][iw_ie_cypher_name]); else add_table_entry(L, "wpa2gcipher", iebuf[offset+3][iw_ie_cypher_name]); } offset += 4; if(ielen < (offset + 2)) { if(wpa1) add_table_entry(L, "wpa1pcipher", "TKIP"); else add_table_entry(L, "wpa2pcipher", "TKIP"); return; } /* Otherwise, we have some number of pairwise ciphers. */ cnt = iebuf[offset] | (iebuf[offset + 1] << 8); offset += 2; if(ielen < (offset + 4*cnt)) return; *buf = '\0'; for(i = 0; i < cnt; i++) { if(i > 0) strncat(buf, " ", 256); if(memcmp(&iebuf[offset], wpa_oui, 3) != 0) { strncat(buf, "Proprietary", 256); } else { if(iebuf[offset+3] <= IW_IE_CYPHER_NUM) strncat(buf, iw_ie_cypher_name[iebuf[offset+3]], 256); else strncat(buf, "unknown", 256); } offset+=4; } if(wpa1) add_table_entry(L, "wpa1pcipher", buf); else add_table_entry(L, "wpa2pcipher", buf); /* Check if we are done */ if(ielen < (offset + 2)) return; /* Now, we have authentication suites. */ cnt = iebuf[offset] | (iebuf[offset + 1] << 8); offset += 2; *buf = '\0'; if(ielen < (offset + 4*cnt)) return; for(i = 0; i < cnt; i++) { if(i != 0) strncat(buf, " ", 256); if(memcmp(&iebuf[offset], wpa_oui, 3) != 0) { strncat(buf, "Proprietary", 256); } else { if(iebuf[offset+3] <= IW_IE_KEY_MGMT_NUM) strncat(buf, iw_ie_key_mgmt_name[iebuf[offset+3]], 256); else strncat(buf, "unknown", 256); } offset+=4; } if(wpa1) add_table_entry(L, "wpa1auth", buf); else add_table_entry(L, "wpa2auth", buf); /* Check if we are done */ if(ielen < (offset + 1)) return; } static inline void print_scanning_token(lua_State *L, struct stream_descr *stream, struct iw_event *event, struct iwscan_state *state, struct iw_range *iw_range, int has_range) { char buffer[128]; /* Temporary buffer */ /* Now, let's decode the event */ switch(event->cmd) { case SIOCGIWAP: add_table_entry(L, "addr", iw_saether_ntop(&event->u.ap_addr, buffer)); state->ap_num++; break; case SIOCGIWFREQ: { double freq; /* Frequency/channel */ int channel = -1; /* Converted to channel */ freq = iw_freq2float(&(event->u.freq)); /* Convert to channel if possible */ if(has_range) channel = iw_freq_to_channel(freq, iw_range); snprintf(buffer, 128, "%1.3f", freq); add_table_entry(L, "frequency", buffer); snprintf(buffer, 128, "%d", channel); add_table_entry(L, "channel", buffer); //iw_print_freq(buffer, sizeof(buffer), freq, channel, event->u.freq.flags); //printf(" %s\n", buffer); } break; case SIOCGIWMODE: /* Note : event->u.mode is unsigned, no need to check <= 0 */ if(event->u.mode >= IW_NUM_OPER_MODE) event->u.mode = IW_NUM_OPER_MODE; add_table_entry(L, "mode", iw_operation_mode[event->u.mode]); break; case SIOCGIWESSID: { char essid[IW_ESSID_MAX_SIZE+1]; memset(essid, '\0', sizeof(essid)); if((event->u.essid.pointer) && (event->u.essid.length)) memcpy(essid, event->u.essid.pointer, event->u.essid.length); if(event->u.essid.flags) add_table_entry(L, "essid", essid); else add_table_entry(L, "essid", "off/any/hidden"); } break; case SIOCGIWENCODE: { unsigned char key[IW_ENCODING_TOKEN_MAX]; if(event->u.data.pointer) memcpy(key, event->u.data.pointer, event->u.data.length); else event->u.data.flags |= IW_ENCODE_NOKEY; if(event->u.data.flags & IW_ENCODE_DISABLED) { add_table_entry(L, "key", "off"); } else { iw_print_key(buffer, sizeof(buffer), key, event->u.data.length, event->u.data.flags); add_table_entry(L, "key", buffer); } } break; case SIOCGIWRATE: if(state->val_index == 0) { lua_pushstring(L, "bitrates"); lua_newtable(L); } //iw_print_bitrate(buffer, sizeof(buffer), event->u.bitrate.value); snprintf(buffer, sizeof(buffer), "%d", event->u.bitrate.value); lua_pushinteger(L, state->val_index + 1); lua_pushstring(L, buffer); lua_settable(L, -3); /* Check for termination */ if(stream->value == NULL) { lua_settable(L, -3); state->val_index = 0; } else state->val_index++; break; case IWEVGENIE: { int offset = 0; unsigned char *buffer = event->u.data.pointer; int buflen = event->u.data.length; while(offset <= (buflen - 2)) { switch(buffer[offset]) { case 0xdd: /* WPA1 (and other) */ case 0x30: /* WPA2 */ iw_print_ie_wpa(L, buffer + offset, buflen); break; default: break; } offset += buffer[offset+1] + 2; } } break; default: break; } /* switch(event->cmd) */ } int iwc_scan(lua_State *L) { struct iwreq wrq; struct iw_scan_req scanopt; /* Options for 'set' */ int scanflags = 0; /* Flags for scan */ unsigned char *buffer = NULL; /* Results */ int buflen = IW_SCAN_MAX_DATA; /* Min for compat WE<17 */ struct iw_range range; int has_range; struct timeval tv; /* Select timeout */ int timeout = 15000000; /* 15s */ char *ifname; if(lua_gettop(L) != 1) { lua_pushstring(L, "invalid arg list"); lua_error(L); return 0; } ifname = (char *)lua_tostring (L, 1); /* Debugging stuff */ if((IW_EV_LCP_PK2_LEN != IW_EV_LCP_PK_LEN) || (IW_EV_POINT_PK2_LEN != IW_EV_POINT_PK_LEN)) { fprintf(stderr, "*** Please report to jt@hpl.hp.com your platform details\n"); fprintf(stderr, "*** and the following line :\n"); fprintf(stderr, "*** IW_EV_LCP_PK2_LEN = %zu ; IW_EV_POINT_PK2_LEN = %zu\n\n", IW_EV_LCP_PK2_LEN, IW_EV_POINT_PK2_LEN); } /* Get range stuff */ has_range = (iw_get_range_info(sock_iwconfig, ifname, &range) >= 0); /* Check if the interface could support scanning. */ if((!has_range) || (range.we_version_compiled < 14)) { lua_pushstring(L, "interface does not support scanning"); lua_error(L); return 0; } /* Init timeout value -> 250ms between set and first get */ tv.tv_sec = 0; tv.tv_usec = 250000; /* Clean up set args */ memset(&scanopt, 0, sizeof(scanopt)); wrq.u.data.pointer = NULL; wrq.u.data.flags = 0; wrq.u.data.length = 0; /* Initiate Scanning */ if(iw_set_ext(sock_iwconfig, ifname, SIOCSIWSCAN, &wrq) < 0) { if((errno != EPERM) || (scanflags != 0)) { lua_pushstring(L, "interface does not support scanning"); lua_error(L); return 0; } /* If we don't have the permission to initiate the scan, we may * * still have permission to read left-over results. * * But, don't wait !!! */ #if 0 /* Not cool, it display for non wireless interfaces... */ fprintf(stderr, "%-8.16s (Could not trigger scanning, just reading left-over results)\n", ifname); #endif tv.tv_usec = 0; } timeout -= tv.tv_usec; /* Forever */ while(1) { fd_set rfds; /* File descriptors for select */ int last_fd; /* Last fd */ int ret; /* Guess what ? We must re-generate rfds each time */ FD_ZERO(&rfds); last_fd = -1; /* In here, add the rtnetlink fd in the list */ /* Wait until something happens */ ret = select(last_fd + 1, &rfds, NULL, NULL, &tv); /* Check if there was an error */ if(ret < 0) { if(errno == EAGAIN || errno == EINTR) continue; lua_pushstring(L, "unhandled signal"); lua_error(L); return 0; } /* Check if there was a timeout */ if(ret == 0) { unsigned char * newbuf; realloc: /* (Re)allocate the buffer - realloc(NULL, len) == malloc(len) */ newbuf = realloc(buffer, buflen); if(newbuf == NULL) { if(buffer) free(buffer); fprintf(stderr, "%s: Allocation failed\n", __FUNCTION__); return 0; } buffer = newbuf; /* Try to read the results */ wrq.u.data.pointer = buffer; wrq.u.data.flags = 0; wrq.u.data.length = buflen; if(iw_get_ext(sock_iwconfig, ifname, SIOCGIWSCAN, &wrq) < 0) { /* Check if buffer was too small (WE-17 only) */ if((errno == E2BIG) && (range.we_version_compiled > 16)) { /* Some driver may return very large scan results, either * because there are many cells, or because they have many * large elements in cells (like IWEVCUSTOM). Most will * only need the regular sized buffer. We now use a dynamic * allocation of the buffer to satisfy everybody. Of course, * as we don't know in advance the size of the array, we try * various increasing sizes. Jean II */ /* Check if the driver gave us any hints. */ if(wrq.u.data.length > buflen) buflen = wrq.u.data.length; else buflen *= 2; /* Try again */ goto realloc; } /* Check if results not available yet */ if(errno == EAGAIN) { /* Restart timer for only 100ms*/ tv.tv_sec = 0; tv.tv_usec = 100000; timeout -= tv.tv_usec; if(timeout > 0) continue; /* Try again later */ } /* Bad error */ free(buffer); fprintf(stderr, "%-8.16s Failed to read scan data : %s\n\n", ifname, strerror(errno)); return 0; } else /* We have the results, go to process them */ break; } /* In here, check if event and event type * * if scan event, read results. All errors bad & no reset timeout */ } if(wrq.u.data.length) { struct iw_event iwe; struct stream_descr stream; struct iwscan_state state = { .ap_num = 1, .val_index = 0 }; int ret; int table = 0; iw_init_event_stream(&stream, (char *) buffer, wrq.u.data.length); lua_newtable(L); do { /* Extract an event and print it */ ret = iw_extract_event_stream(&stream, &iwe, range.we_version_compiled); if(ret > 0) { if(iwe.cmd == SIOCGIWAP) { if(table) lua_settable(L, -3); table = 1; lua_pushinteger(L, state.ap_num); lua_newtable(L); } print_scanning_token(L, &stream, &iwe, &state, &range, has_range); } } while(ret > 0); lua_settable(L, -3); free(buffer); return 1; } free(buffer); return 0; } int iwc_frequencies(lua_State *L) { int i; int has_range; char *ifname; struct iw_range range; if(lua_gettop(L) != 1) { lua_pushstring(L, "invalid arg list"); lua_error(L); return 0; } ifname = (char *)lua_tostring (L, 1); /* Get range stuff */ has_range = (iw_get_range_info(sock_iwconfig, ifname, &range) >= 0); /* Check if the interface could support scanning. */ if((!has_range) || (range.we_version_compiled < 14)) { lua_pushstring(L, "interface does not support frequency enumeration"); lua_error(L); } else { lua_newtable(L); for(i = 0; i < range.num_frequency; i++) { lua_pushnumber(L, i + 1); lua_newtable(L); lua_pushinteger(L, 1); lua_pushinteger(L, (int)range.freq[i].i); lua_settable(L, -3); lua_pushinteger(L, 2); lua_pushnumber(L, iw_freq2float(&(range.freq[i]))); lua_settable(L, -3); lua_settable(L, -3); } return 1; } return 0; }