X-Git-Url: http://git.osdn.net/view?p=android-x86%2Fexternal-wireless-tools.git;a=blobdiff_plain;f=wireless_tools%2Fiwlib.c;h=b78882f2087555b40160e8870c50585379e8943a;hp=ca7aed0b99612df7dcfa1e34a2d57921973eb983;hb=9d82610feb36dcd56d621e7831cbf5eec3f51104;hpb=db34c20cecd8e65eca87ef9a5397b6b69c24dcad;ds=sidebyside diff --git a/wireless_tools/iwlib.c b/wireless_tools/iwlib.c index ca7aed0..b78882f 100644 --- a/wireless_tools/iwlib.c +++ b/wireless_tools/iwlib.c @@ -1,12 +1,12 @@ /* * Wireless Tools * - * Jean II - HPLB 97->99 - HPL 99->03 + * Jean II - HPLB 97->99 - HPL 99->04 * * Common subroutines to all the wireless tools... * * This file is released under the GPL license. - * Copyright (c) 1997-2003 Jean Tourrilhes + * Copyright (c) 1997-2004 Jean Tourrilhes */ /***************************** INCLUDES *****************************/ @@ -15,40 +15,84 @@ /************************ CONSTANTS & MACROS ************************/ -/* Various versions information */ -/* Recommended Wireless Extension version */ -#define WE_VERSION 16 /* ### don't forget #warning ### */ -/* Version of Wireless Tools */ -#define WT_VERSION 26 +/* + * Constants fof WE-9->15 + */ +#define IW15_MAX_FREQUENCIES 16 +#define IW15_MAX_BITRATES 8 +#define IW15_MAX_TXPOWER 8 +#define IW15_MAX_ENCODING_SIZES 8 +#define IW15_MAX_SPY 8 +#define IW15_MAX_AP 8 + +/****************************** TYPES ******************************/ /* - * Verify a few things about Wireless Extensions. - * I try to maximise backward and forward compatibility, but things are - * tricky because I'm fixing bugs and adding new features. - * Wireless Tools *must* be compiled with the same version of WE - * as the driver. Sometime, the size or layout of some structure changes, - * and might produce interesting results. - * Wireless Tools will usually compile properly against different - * versions of WE, thanks to the zillions of #ifdefs in my code. - * Jean II + * Struct iw_range up to WE-15 */ -#if WIRELESS_EXT < 9 -#error "Wireless Extension v9 or newer required :-(" -#error "Use Wireless Tools v19 or update your kernel headers !" -#endif -#if WIRELESS_EXT < WE_VERSION && !defined(WEXT_HEADER) -#warning "Wireless Extension earlier than v16 detected," -#warning "Not all tools features will be compiled in !" -#warning "No worry, I'll try to make the best of it ;-)" -#endif -#if WIRELESS_EXT > WE_VERSION && !defined(WEXT_HEADER) -#warning "Wireless Extension later than v16 detected," -#warning "Maybe you should get a more recent version" -#warning "of the Wireless Tools package !" -#endif +struct iw15_range +{ + __u32 throughput; + __u32 min_nwid; + __u32 max_nwid; + __u16 num_channels; + __u8 num_frequency; + struct iw_freq freq[IW15_MAX_FREQUENCIES]; + __s32 sensitivity; + struct iw_quality max_qual; + __u8 num_bitrates; + __s32 bitrate[IW15_MAX_BITRATES]; + __s32 min_rts; + __s32 max_rts; + __s32 min_frag; + __s32 max_frag; + __s32 min_pmp; + __s32 max_pmp; + __s32 min_pmt; + __s32 max_pmt; + __u16 pmp_flags; + __u16 pmt_flags; + __u16 pm_capa; + __u16 encoding_size[IW15_MAX_ENCODING_SIZES]; + __u8 num_encoding_sizes; + __u8 max_encoding_tokens; + __u16 txpower_capa; + __u8 num_txpower; + __s32 txpower[IW15_MAX_TXPOWER]; + __u8 we_version_compiled; + __u8 we_version_source; + __u16 retry_capa; + __u16 retry_flags; + __u16 r_time_flags; + __s32 min_retry; + __s32 max_retry; + __s32 min_r_time; + __s32 max_r_time; + struct iw_quality avg_qual; +}; + +/* + * Union for all the versions of iwrange. + * Fortunately, I mostly only add fields at the end, and big-bang + * reorganisations are few. + */ +union iw_range_raw +{ + struct iw15_range range15; /* WE 9->15 */ + struct iw_range range; /* WE 16->current */ +}; + +/* + * Offsets in iw_range struct + */ +#define iwr15_off(f) ( ((char *) &(((struct iw15_range *) NULL)->f)) - \ + (char *) NULL) +#define iwr_off(f) ( ((char *) &(((struct iw_range *) NULL)->f)) - \ + (char *) NULL) /**************************** VARIABLES ****************************/ +/* Modes as human readable strings */ const char * const iw_operation_mode[] = { "Auto", "Ad-Hoc", "Managed", @@ -146,7 +190,7 @@ iw_get_ifname(char * name, /* Where to store the name */ */ void iw_enum_devices(int skfd, - iw_enum_handler fn, + iw_enum_handler fn, char * args[], int count) { @@ -217,77 +261,63 @@ iw_enum_devices(int skfd, /*------------------------------------------------------------------*/ /* - * Get the range information out of the driver + * Extract WE version number from /proc/net/wireless + * In most cases, you really want to get version information from + * the range info (range->we_version_compiled), see below... + * + * If we have WE-16 and later, the WE version is available at the + * end of the header line of the file. + * For version prior to that, we can only detect the change from + * v11 to v12, so we do an approximate job. Fortunately, v12 to v15 + * are highly binary compatible (on the struct level). */ int -iw_get_range_info(int skfd, - char * ifname, - iwrange * range) +iw_get_kernel_we_version(void) { - struct iwreq wrq; - char buffer[sizeof(iwrange) * 2]; /* Large enough */ + char buff[1024]; + FILE * fh; + char * p; + int v; - /* Cleanup */ - memset(buffer, 0, sizeof(buffer)); + /* Check if /proc/net/wireless is available */ + fh = fopen(PROC_NET_WIRELESS, "r"); - wrq.u.data.pointer = (caddr_t) buffer; - wrq.u.data.length = sizeof(buffer); - wrq.u.data.flags = 0; - if(iw_get_ext(skfd, ifname, SIOCGIWRANGE, &wrq) < 0) - return(-1); + if(fh == NULL) + { + fprintf(stderr, "Cannot read " PROC_NET_WIRELESS "\n"); + return(-1); + } - /* Copy stuff at the right place, ignore extra */ - memcpy((char *) range, buffer, sizeof(iwrange)); + /* Read the first line of buffer */ + fgets(buff, sizeof(buff), fh); - /* Lots of people have driver and tools out of sync as far as Wireless - * Extensions are concerned. It's because /usr/include/linux/wireless.h - * and /usr/src/linux/include/linux/wireless.h are different. - * We try to catch this stuff here... */ - if(!iw_ignore_version) + if(strstr(buff, "| WE") == NULL) { - /* For new versions, we can check the version directly, for old versions - * we use magic. 300 bytes is a also magic number, don't touch... */ - if((WIRELESS_EXT > 10) && (wrq.u.data.length >= 300)) - { -#if WIRELESS_EXT > 10 - /* Version verification - for new versions */ - if(range->we_version_compiled != WIRELESS_EXT) - { - fprintf(stderr, "Warning: Driver for device %s has been compiled with version %d\n", ifname, range->we_version_compiled); - fprintf(stderr, "of Wireless Extension, while this program is using version %d.\n", WIRELESS_EXT); - fprintf(stderr, "Some things may be broken...\n\n"); - } - /* Driver version verification */ - if(range->we_version_compiled < range->we_version_source) - { - fprintf(stderr, "Warning: Driver for device %s recommend version %d of Wireless Extension,\n", ifname, range->we_version_source); - fprintf(stderr, "but has been compiled with version %d, therefore some driver features\n", range->we_version_compiled); - fprintf(stderr, "may not be available...\n\n"); - } -#endif /* WIRELESS_EXT > 10 */ - } + /* Prior to WE16, so explicit version not present */ + + /* Black magic */ + if(strstr(buff, "| Missed") == NULL) + v = 11; else - { - /* Version verification - for old versions */ - if(wrq.u.data.length != sizeof(iwrange)) - { - fprintf(stderr, "Warning: Driver for device %s has been compiled with an ancient version\n", ifname); - fprintf(stderr, "of Wireless Extension, while this program is using version %d.\n", WIRELESS_EXT); - fprintf(stderr, "Some things may be broken...\n\n"); - } - } + v = 15; + fclose(fh); + return(v); } - /* Don't complain twice. - * In theory, the test apply to each individual driver, but usually - * all drivers are compiled from the same kernel, and most often - * problem is the system/glibc headers. */ - iw_ignore_version = 1; - /* Note : we are only trying to catch compile difference, not source. - * If the driver source has not been updated to the latest, it doesn't - * matter because the new fields are set to zero */ + /* Read the second line of buffer */ + fgets(buff, sizeof(buff), fh); - return(0); + /* Get to the last separator, to get the version */ + p = strrchr(buff, '|'); + if((p == NULL) || (sscanf(p + 1, "%d", &v) != 1)) + { + fprintf(stderr, "Cannot parse " PROC_NET_WIRELESS "\n"); + fclose(fh); + return(-1); + } + + fclose(fh); + return(v); } /*------------------------------------------------------------------*/ @@ -321,7 +351,7 @@ print_iface_version_info(int skfd, if(iw_get_ext(skfd, ifname, SIOCGIWRANGE, &wrq) < 0) { /* Interface support WE (see above), but not IWRANGE */ - fprintf(stderr, "%-8.8s Driver has no Wireless Extension version information.\n\n", ifname); + fprintf(stderr, "%-8.16s Driver has no Wireless Extension version information.\n\n", ifname); return(0); } @@ -330,21 +360,18 @@ print_iface_version_info(int skfd, /* For new versions, we can check the version directly, for old versions * we use magic. 300 bytes is a also magic number, don't touch... */ - if((WIRELESS_EXT > 10) && (wrq.u.data.length >= 300)) + if(wrq.u.data.length >= 300) { -#if WIRELESS_EXT > 10 - printf("%-8.8s Recommend Wireless Extension v%d or later,\n", + /* Version is always at the same offset, so it's ok */ + printf("%-8.16s Recommend Wireless Extension v%d or later,\n", ifname, range->we_version_source); printf(" Currently compiled with Wireless Extension v%d.\n\n", range->we_version_compiled); -#endif /* WIRELESS_EXT > 10 */ } else { -#if 0 - fprintf(stderr, "%-8.8s Wireless Extension version too old.\n\n", + fprintf(stderr, "%-8.16s Wireless Extension version too old.\n\n", ifname); -#endif } @@ -356,13 +383,10 @@ print_iface_version_info(int skfd, * Print the WE versions of the tools. */ int -iw_print_version_info(char * toolname) +iw_print_version_info(const char * toolname) { int skfd; /* generic raw socket desc. */ - char buff[1024]; - FILE * fh; - char * p; - int v; + int we_kernel_version; /* Create a channel to the NET kernel. */ if((skfd = iw_sockets_open()) < 0) @@ -373,64 +397,214 @@ iw_print_version_info(char * toolname) /* Information about the tools themselves */ if(toolname != NULL) - printf("%-8.8s Version %d\n", toolname, WT_VERSION); - printf(" Compatible with Wireless Extension v%d or earlier,\n", + printf("%-8.16s Wireless-Tools version %d\n", toolname, WT_VERSION); + printf(" Compatible with Wireless Extension v11 to v%d.\n\n", WE_VERSION); - printf(" Currently compiled with Wireless Extension v%d.\n\n", - WIRELESS_EXT); - /* Check if /proc/net/wireless is available */ - fh = fopen(PROC_NET_WIRELESS, "r"); - if(fh != NULL) + /* Get version from kernel */ + we_kernel_version = iw_get_kernel_we_version(); + /* Only version >= 16 can be verified, other are guessed */ + if(we_kernel_version > 15) + printf("Kernel Currently compiled with Wireless Extension v%d.\n\n", + we_kernel_version); + + /* Version for each device */ + iw_enum_devices(skfd, &print_iface_version_info, NULL, 0); + + iw_sockets_close(skfd); + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Get the range information out of the driver + */ +int +iw_get_range_info(int skfd, + const char * ifname, + iwrange * range) +{ + struct iwreq wrq; + char buffer[sizeof(iwrange) * 2]; /* Large enough */ + union iw_range_raw * range_raw; + + /* Cleanup */ + bzero(buffer, sizeof(buffer)); + + wrq.u.data.pointer = (caddr_t) buffer; + wrq.u.data.length = sizeof(buffer); + wrq.u.data.flags = 0; + if(iw_get_ext(skfd, ifname, SIOCGIWRANGE, &wrq) < 0) + return(-1); + + /* Point to the buffer */ + range_raw = (union iw_range_raw *) buffer; + + /* For new versions, we can check the version directly, for old versions + * we use magic. 300 bytes is a also magic number, don't touch... */ + if(wrq.u.data.length < 300) { - /* Read the first line of buffer */ - fgets(buff, sizeof(buff), fh); + /* That's v10 or earlier. Ouch ! Let's make a guess...*/ + range_raw->range.we_version_compiled = 9; + } + + /* Check how it needs to be processed */ + if(range_raw->range.we_version_compiled > 15) + { + /* This is our native format, that's easy... */ + /* Copy stuff at the right place, ignore extra */ + memcpy((char *) range, buffer, sizeof(iwrange)); + } + else + { + /* Zero unknown fields */ + bzero((char *) range, sizeof(struct iw_range)); + + /* Initial part unmoved */ + memcpy((char *) range, + buffer, + iwr15_off(num_channels)); + /* Frequencies pushed futher down towards the end */ + memcpy((char *) range + iwr_off(num_channels), + buffer + iwr15_off(num_channels), + iwr15_off(sensitivity) - iwr15_off(num_channels)); + /* This one moved up */ + memcpy((char *) range + iwr_off(sensitivity), + buffer + iwr15_off(sensitivity), + iwr15_off(num_bitrates) - iwr15_off(sensitivity)); + /* This one goes after avg_qual */ + memcpy((char *) range + iwr_off(num_bitrates), + buffer + iwr15_off(num_bitrates), + iwr15_off(min_rts) - iwr15_off(num_bitrates)); + /* Number of bitrates has changed, put it after */ + memcpy((char *) range + iwr_off(min_rts), + buffer + iwr15_off(min_rts), + iwr15_off(txpower_capa) - iwr15_off(min_rts)); + /* Added encoding_login_index, put it after */ + memcpy((char *) range + iwr_off(txpower_capa), + buffer + iwr15_off(txpower_capa), + iwr15_off(txpower) - iwr15_off(txpower_capa)); + /* Hum... That's an unexpected glitch. Bummer. */ + memcpy((char *) range + iwr_off(txpower), + buffer + iwr15_off(txpower), + iwr15_off(avg_qual) - iwr15_off(txpower)); + /* Avg qual moved up next to max_qual */ + memcpy((char *) range + iwr_off(avg_qual), + buffer + iwr15_off(avg_qual), + sizeof(struct iw_quality)); + } - /* Check if it's WE-16 or later */ - if(strstr(buff, "| WE") != NULL) + /* We are now checking much less than we used to do, because we can + * accomodate more WE version. But, there are still cases where things + * will break... */ + if(!iw_ignore_version) + { + /* We don't like very old version (unfortunately kernel 2.2.X) */ + if(range->we_version_compiled <= 10) { - /* Read the second line of buffer */ - fgets(buff, sizeof(buff), fh); - - /* Get to the last separator, to get the version */ - p = strrchr(buff, '|'); - if((p != NULL) && (sscanf(p + 1, "%d", &v) == 1)) - /* That was it ! */ - printf("Kernel Currently compiled with Wireless Extension v%d.\n\n", v); + fprintf(stderr, "Warning: Driver for device %s has been compiled with an ancient version\n", ifname); + fprintf(stderr, "of Wireless Extension, while this program support version 11 and later.\n"); + fprintf(stderr, "Some things may be broken...\n\n"); } - /* Cleanup */ - fclose(fh); - } - /* Version for each device */ - iw_enum_devices(skfd, &print_iface_version_info, NULL, 0); + /* We don't like future versions of WE, because we can't cope with + * the unknown */ + if(range->we_version_compiled > WE_VERSION) + { + fprintf(stderr, "Warning: Driver for device %s has been compiled with version %d\n", ifname, range->we_version_compiled); + fprintf(stderr, "of Wireless Extension, while this program supports up to version %d.\n", WE_VERSION); + fprintf(stderr, "Some things may be broken...\n\n"); + } - close(skfd); + /* Driver version verification */ + if((range->we_version_compiled > 10) && + (range->we_version_compiled < range->we_version_source)) + { + fprintf(stderr, "Warning: Driver for device %s recommend version %d of Wireless Extension,\n", ifname, range->we_version_source); + fprintf(stderr, "but has been compiled with version %d, therefore some driver features\n", range->we_version_compiled); + fprintf(stderr, "may not be available...\n\n"); + } + /* Note : we are only trying to catch compile difference, not source. + * If the driver source has not been updated to the latest, it doesn't + * matter because the new fields are set to zero */ + } - return 0; + /* Don't complain twice. + * In theory, the test apply to each individual driver, but usually + * all drivers are compiled from the same kernel. */ + iw_ignore_version = 1; + + return(0); } /*------------------------------------------------------------------*/ /* * Get information about what private ioctls are supported by the driver + * + * Note : there is one danger using this function. If it return 0, you + * still need to free() the buffer. Beware. */ int iw_get_priv_info(int skfd, - char * ifname, - iwprivargs * priv, - int maxpriv) + const char * ifname, + iwprivargs ** ppriv) { struct iwreq wrq; + iwprivargs * priv = NULL; /* Not allocated yet */ + int maxpriv = 16; /* Minimum for compatibility WE<13 */ + iwprivargs * newpriv; + + /* Some driver may return a very large number of ioctls. Some + * others a very small number. We now use a dynamic allocation + * of the array to satisfy everybody. Of course, as we don't know + * in advance the size of the array, we try various increasing + * sizes. Jean II */ + do + { + /* (Re)allocate the buffer */ + newpriv = realloc(priv, maxpriv * sizeof(priv[0])); + if(newpriv == NULL) + { + fprintf(stderr, "%s: Allocation failed\n", __FUNCTION__); + break; + } + priv = newpriv; - /* Ask the driver */ - wrq.u.data.pointer = (caddr_t) priv; - wrq.u.data.length = maxpriv; - wrq.u.data.flags = 0; - if(iw_get_ext(skfd, ifname, SIOCGIWPRIV, &wrq) < 0) - return(-1); + /* Ask the driver if it's large enough */ + wrq.u.data.pointer = (caddr_t) priv; + wrq.u.data.length = maxpriv; + wrq.u.data.flags = 0; + if(iw_get_ext(skfd, ifname, SIOCGIWPRIV, &wrq) >= 0) + { + /* Success. Pass the buffer by pointer */ + *ppriv = priv; + /* Return the number of ioctls */ + return(wrq.u.data.length); + } + + /* Only E2BIG means the buffer was too small, abort on other errors */ + if(errno != E2BIG) + { + /* Most likely "not supported". Don't barf. */ + break; + } - /* Return the number of ioctls */ - return(wrq.u.data.length); + /* Failed. We probably need a bigger buffer. Check if the kernel + * gave us any hints. */ + if(wrq.u.data.length > maxpriv) + maxpriv = wrq.u.data.length; + else + maxpriv *= 2; + } + while(maxpriv < 1000); + + /* Cleanup */ + if(priv) + free(priv); + *ppriv = NULL; + + return(-1); } /*------------------------------------------------------------------*/ @@ -443,7 +617,7 @@ iw_get_priv_info(int skfd, */ int iw_get_basic_config(int skfd, - char * ifname, + const char * ifname, wireless_config * info) { struct iwreq wrq; @@ -472,6 +646,7 @@ iw_get_basic_config(int skfd, { info->has_freq = 1; info->freq = iw_freq2float(&(wrq.u.freq)); + info->freq_flags = wrq.u.freq.flags; } /* Get encryption information */ @@ -499,7 +674,7 @@ iw_get_basic_config(int skfd, if(iw_get_ext(skfd, ifname, SIOCGIWMODE, &wrq) >= 0) { info->mode = wrq.u.mode; - if((info->mode < 6) && (info->mode >= 0)) + if((info->mode < IW_NUM_OPER_MODE) && (info->mode >= 0)) info->has_mode = 1; } @@ -515,7 +690,7 @@ iw_get_basic_config(int skfd, */ int iw_set_basic_config(int skfd, - char * ifname, + const char * ifname, wireless_config * info) { struct iwreq wrq; @@ -526,15 +701,18 @@ iw_set_basic_config(int skfd, /* If no wireless name : no wireless extensions */ return(-2); - /* Set Network ID, if available (this is for non-802.11 cards) */ - if(info->has_nwid) + /* Set the current mode of operation + * Mode need to be first : some settings apply only in a specific mode + * (such as frequency). + */ + if(info->has_mode) { - memcpy(&(wrq.u.nwid), &(info->nwid), sizeof(iwparam)); - wrq.u.nwid.fixed = 1; /* Hum... When in Rome... */ + strncpy(wrq.ifr_name, ifname, IFNAMSIZ); + wrq.u.mode = info->mode; - if(iw_set_ext(skfd, ifname, SIOCSIWNWID, &wrq) < 0) + if(iw_get_ext(skfd, ifname, SIOCSIWMODE, &wrq) < 0) { - fprintf(stderr, "SIOCSIWNWID: %s\n", strerror(errno)); + fprintf(stderr, "SIOCSIWMODE: %s\n", strerror(errno)); ret = -1; } } @@ -580,6 +758,10 @@ iw_set_basic_config(int skfd, wrq.u.data.length = info->key_size; wrq.u.data.flags = flags; + /* Compatibility with WE<13 */ + if(flags & IW_ENCODE_NOKEY) + wrq.u.data.pointer = NULL; + if(iw_set_ext(skfd, ifname, SIOCSIWENCODE, &wrq) < 0) { fprintf(stderr, "SIOCSIWENCODE(%d): %s\n", @@ -588,29 +770,33 @@ iw_set_basic_config(int skfd, } } - /* Set ESSID (extended network), if available */ - if(info->has_essid) + /* Set Network ID, if available (this is for non-802.11 cards) */ + if(info->has_nwid) { - wrq.u.essid.pointer = (caddr_t) info->essid; - wrq.u.essid.length = strlen(info->essid) + 1; - wrq.u.data.flags = info->essid_on; + memcpy(&(wrq.u.nwid), &(info->nwid), sizeof(iwparam)); + wrq.u.nwid.fixed = 1; /* Hum... When in Rome... */ - if(iw_set_ext(skfd, ifname, SIOCSIWESSID, &wrq) < 0) + if(iw_set_ext(skfd, ifname, SIOCSIWNWID, &wrq) < 0) { - fprintf(stderr, "SIOCSIWESSID: %s\n", strerror(errno)); + fprintf(stderr, "SIOCSIWNWID: %s\n", strerror(errno)); ret = -1; } } - /* Set the current mode of operation */ - if(info->has_mode) + /* Set ESSID (extended network), if available. + * ESSID need to be last : most device re-perform the scanning/discovery + * when this is set, and things like encryption keys are better be + * defined if we want to discover the right set of APs/nodes. + */ + if(info->has_essid) { - strncpy(wrq.ifr_name, ifname, IFNAMSIZ); - wrq.u.mode = info->mode; + wrq.u.essid.pointer = (caddr_t) info->essid; + wrq.u.essid.length = strlen(info->essid) + 1; + wrq.u.data.flags = info->essid_on; - if(iw_get_ext(skfd, ifname, SIOCSIWMODE, &wrq) < 0) + if(iw_set_ext(skfd, ifname, SIOCSIWESSID, &wrq) < 0) { - fprintf(stderr, "SIOCSIWMODE: %s\n", strerror(errno)); + fprintf(stderr, "SIOCSIWESSID: %s\n", strerror(errno)); ret = -1; } } @@ -632,14 +818,15 @@ iw_set_basic_config(int skfd, * but if they interoperate at some level, and also if they accept the * same type of config (ESSID vs NWID, freq...). * This is supposed to work around the alphabet soup. - * Return 1 if protocols are compatible + * Return 1 if protocols are compatible, 0 otherwise */ int -iw_protocol_compare(char * protocol1, - char * protocol2) +iw_protocol_compare(const char * protocol1, + const char * protocol2) { - char * dot11 = "IEEE 802.11"; - char * dot11_ds = "Dbg"; + const char * dot11 = "IEEE 802.11"; + const char * dot11_ds = "Dbg"; + const char * dot11_5g = "a"; /* If the strings are the same -> easy */ if(!strncmp(protocol1, protocol2, IFNAMSIZ)) @@ -649,18 +836,34 @@ iw_protocol_compare(char * protocol1, if( (!strncmp(protocol1, dot11, strlen(dot11))) && (!strncmp(protocol2, dot11, strlen(dot11))) ) { - char * sub1 = protocol1 + strlen(dot11); - char * sub2 = protocol2 + strlen(dot11); - - /* Skip optional separator */ - if(*sub1 == '-') - sub1++; - if(*sub2 == '-') - sub2++; + const char * sub1 = protocol1 + strlen(dot11); + const char * sub2 = protocol2 + strlen(dot11); + unsigned int i; + int isds1 = 0; + int isds2 = 0; + int is5g1 = 0; + int is5g2 = 0; + + /* Check if we find the magic letters telling it's DS compatible */ + for(i = 0; i < strlen(dot11_ds); i++) + { + if(strchr(sub1, dot11_ds[i]) != NULL) + isds1 = 1; + if(strchr(sub2, dot11_ds[i]) != NULL) + isds2 = 1; + } + if(isds1 && isds2) + return(1); - /* Check if they are both 2.4 GHz Direct Sequence compatible */ - if( (strchr(dot11_ds, *sub1) != NULL) && - (strchr(dot11_ds, *sub2) != NULL) ) + /* Check if we find the magic letters telling it's 5GHz compatible */ + for(i = 0; i < strlen(dot11_5g); i++) + { + if(strchr(sub1, dot11_5g[i]) != NULL) + is5g1 = 1; + if(strchr(sub2, dot11_5g[i]) != NULL) + is5g2 = 1; + } + if(is5g1 && is5g2) return(1); } /* Not compatible */ @@ -718,7 +921,7 @@ iw_float2freq(double in, * Convert our internal representation of frequencies to a floating point. */ double -iw_freq2float(iwfreq * in) +iw_freq2float(const iwfreq * in) { #ifdef WE_NOLIBM /* Version without libm : slower */ @@ -738,22 +941,67 @@ iw_freq2float(iwfreq * in) * Output a frequency with proper scaling */ void -iw_print_freq(char * buffer, - double freq) +iw_print_freq_value(char * buffer, + int buflen, + double freq) { if(freq < KILO) - sprintf(buffer, "Channel:%g", freq); + snprintf(buffer, buflen, "%g", freq); else { + char scale; + int divisor; + if(freq >= GIGA) - sprintf(buffer, "Frequency:%gGHz", freq / GIGA); + { + scale = 'G'; + divisor = GIGA; + } else { if(freq >= MEGA) - sprintf(buffer, "Frequency:%gMHz", freq / MEGA); + { + scale = 'M'; + divisor = MEGA; + } else - sprintf(buffer, "Frequency:%gkHz", freq / KILO); + { + scale = 'k'; + divisor = KILO; + } } + snprintf(buffer, buflen, "%g %cHz", freq / divisor, scale); + } +} + +/*------------------------------------------------------------------*/ +/* + * Output a frequency with proper scaling + */ +void +iw_print_freq(char * buffer, + int buflen, + double freq, + int channel, + int freq_flags) +{ + char sep = ((freq_flags & IW_FREQ_FIXED) ? '=' : ':'); + char vbuf[16]; + + /* Print the frequency/channel value */ + iw_print_freq_value(vbuf, sizeof(vbuf), freq); + + /* Check if channel only */ + if(freq < KILO) + snprintf(buffer, buflen, "Channel%c%s", sep, vbuf); + else + { + /* Frequency. Check if we have a channel as well */ + if(channel >= 0) + snprintf(buffer, buflen, "Frequency%c%s (Channel %d)", + sep, vbuf, channel); + else + snprintf(buffer, buflen, "Frequency%c%s", sep, vbuf); } } @@ -762,8 +1010,8 @@ iw_print_freq(char * buffer, * Convert a frequency to a channel (negative -> error) */ int -iw_freq_to_channel(double freq, - struct iw_range * range) +iw_freq_to_channel(double freq, + const struct iw_range * range) { double ref_freq; int k; @@ -784,6 +1032,41 @@ iw_freq_to_channel(double freq, return(-2); } +/*------------------------------------------------------------------*/ +/* + * Convert a channel to a frequency (negative -> error) + * Return the channel on success + */ +int +iw_channel_to_freq(int channel, + double * pfreq, + const struct iw_range * range) +{ + int has_freq = 0; + int k; + + /* Check if the driver support only channels or if it has frequencies */ + for(k = 0; k < range->num_frequency; k++) + { + if((range->freq[k].e != 0) || (range->freq[k].m > (int) KILO)) + has_freq = 1; + } + if(!has_freq) + return(-1); + + /* Find the correct frequency in the list */ + for(k = 0; k < range->num_frequency; k++) + { + if(range->freq[k].i == channel) + { + *pfreq = iw_freq2float(&(range->freq[k])); + return(channel); + } + } + /* Not found */ + return(-2); +} + /*********************** BITRATE SUBROUTINES ***********************/ /*------------------------------------------------------------------*/ @@ -792,17 +1075,32 @@ iw_freq_to_channel(double freq, */ void iw_print_bitrate(char * buffer, + int buflen, int bitrate) { double rate = bitrate; + char scale; + int divisor; if(rate >= GIGA) - sprintf(buffer, "%gGb/s", rate / GIGA); + { + scale = 'G'; + divisor = GIGA; + } else - if(rate >= MEGA) - sprintf(buffer, "%gMb/s", rate / MEGA); - else - sprintf(buffer, "%gkb/s", rate / KILO); + { + if(rate >= MEGA) + { + scale = 'M'; + divisor = MEGA; + } + else + { + scale = 'k'; + divisor = KILO; + } + } + snprintf(buffer, buflen, "%g %cb/s", rate / divisor, scale); } /************************ POWER SUBROUTINES *************************/ @@ -863,6 +1161,43 @@ iw_mwatt2dbm(int in) #endif /* WE_NOLIBM */ } +/*------------------------------------------------------------------*/ +/* + * Output a txpower with proper conversion + */ +void +iw_print_txpower(char * buffer, + int buflen, + struct iw_param * txpower) +{ + int dbm; + + /* Check if disabled */ + if(txpower->disabled) + { + snprintf(buffer, buflen, "off"); + } + else + { + /* Check for relative values */ + if(txpower->flags & IW_TXPOW_RELATIVE) + { + snprintf(buffer, buflen, "%d", txpower->value); + } + else + { + /* Convert everything to dBm */ + if(txpower->flags & IW_TXPOW_MWATT) + dbm = iw_mwatt2dbm(txpower->value); + else + dbm = txpower->value; + + /* Display */ + snprintf(buffer, buflen, "%d dBm", dbm); + } + } +} + /********************** STATISTICS SUBROUTINES **********************/ /*------------------------------------------------------------------*/ @@ -870,76 +1205,84 @@ iw_mwatt2dbm(int in) * Read /proc/net/wireless to get the latest statistics */ int -iw_get_stats(int skfd, - char * ifname, - iwstats * stats) +iw_get_stats(int skfd, + const char * ifname, + iwstats * stats, + const iwrange * range, + int has_range) { -#if WIRELESS_EXT > 11 - struct iwreq wrq; - wrq.u.data.pointer = (caddr_t) stats; - wrq.u.data.length = 0; - wrq.u.data.flags = 1; /* Clear updated flag */ - strncpy(wrq.ifr_name, ifname, IFNAMSIZ); - if(iw_get_ext(skfd, ifname, SIOCGIWSTATS, &wrq) < 0) - return(-1); + /* Fortunately, we can always detect this condition properly */ + if((has_range) && (range->we_version_compiled > 11)) + { + struct iwreq wrq; + wrq.u.data.pointer = (caddr_t) stats; + wrq.u.data.length = sizeof(struct iw_statistics); + wrq.u.data.flags = 1; /* Clear updated flag */ + strncpy(wrq.ifr_name, ifname, IFNAMSIZ); + if(iw_get_ext(skfd, ifname, SIOCGIWSTATS, &wrq) < 0) + return(-1); - return(0); -#else /* WIRELESS_EXT > 11 */ - FILE * f = fopen(PROC_NET_WIRELESS, "r"); - char buf[256]; - char * bp; - int t; - skfd = skfd; /* Avoid "Unused parameter" warning */ - if(f==NULL) - return -1; - /* Loop on all devices */ - while(fgets(buf,255,f)) - { - bp=buf; - while(*bp&&isspace(*bp)) - bp++; - /* Is it the good device ? */ - if(strncmp(bp,ifname,strlen(ifname))==0 && bp[strlen(ifname)]==':') - { - /* Skip ethX: */ - bp=strchr(bp,':'); - bp++; - /* -- status -- */ - bp = strtok(bp, " "); - sscanf(bp, "%X", &t); - stats->status = (unsigned short) t; - /* -- link quality -- */ - bp = strtok(NULL, " "); - if(strchr(bp,'.') != NULL) - stats->qual.updated |= 1; - sscanf(bp, "%d", &t); - stats->qual.qual = (unsigned char) t; - /* -- signal level -- */ - bp = strtok(NULL, " "); - if(strchr(bp,'.') != NULL) - stats->qual.updated |= 2; - sscanf(bp, "%d", &t); - stats->qual.level = (unsigned char) t; - /* -- noise level -- */ - bp = strtok(NULL, " "); - if(strchr(bp,'.') != NULL) - stats->qual.updated += 4; - sscanf(bp, "%d", &t); - stats->qual.noise = (unsigned char) t; - /* -- discarded packets -- */ - bp = strtok(NULL, " "); - sscanf(bp, "%d", &stats->discard.nwid); - bp = strtok(NULL, " "); - sscanf(bp, "%d", &stats->discard.code); - bp = strtok(NULL, " "); - sscanf(bp, "%d", &stats->discard.misc); - fclose(f); - return 0; - } - } - fclose(f); - return -1; -#endif /* WIRELESS_EXT > 11 */ + /* Format has not changed since WE-12, no conversion */ + return(0); + } + else + { + FILE * f = fopen(PROC_NET_WIRELESS, "r"); + char buf[256]; + char * bp; + int t; + + if(f==NULL) + return -1; + /* Loop on all devices */ + while(fgets(buf,255,f)) + { + bp=buf; + while(*bp&&isspace(*bp)) + bp++; + /* Is it the good device ? */ + if(strncmp(bp,ifname,strlen(ifname))==0 && bp[strlen(ifname)]==':') + { + /* Skip ethX: */ + bp=strchr(bp,':'); + bp++; + /* -- status -- */ + bp = strtok(bp, " "); + sscanf(bp, "%X", &t); + stats->status = (unsigned short) t; + /* -- link quality -- */ + bp = strtok(NULL, " "); + if(strchr(bp,'.') != NULL) + stats->qual.updated |= 1; + sscanf(bp, "%d", &t); + stats->qual.qual = (unsigned char) t; + /* -- signal level -- */ + bp = strtok(NULL, " "); + if(strchr(bp,'.') != NULL) + stats->qual.updated |= 2; + sscanf(bp, "%d", &t); + stats->qual.level = (unsigned char) t; + /* -- noise level -- */ + bp = strtok(NULL, " "); + if(strchr(bp,'.') != NULL) + stats->qual.updated += 4; + sscanf(bp, "%d", &t); + stats->qual.noise = (unsigned char) t; + /* -- discarded packets -- */ + bp = strtok(NULL, " "); + sscanf(bp, "%d", &stats->discard.nwid); + bp = strtok(NULL, " "); + sscanf(bp, "%d", &stats->discard.code); + bp = strtok(NULL, " "); + sscanf(bp, "%d", &stats->discard.misc); + fclose(f); + /* No conversion needed */ + return 0; + } + } + fclose(f); + return -1; + } } /*------------------------------------------------------------------*/ @@ -948,40 +1291,99 @@ iw_get_stats(int skfd, */ void iw_print_stats(char * buffer, - iwqual * qual, - iwrange * range, + int buflen, + const iwqual * qual, + const iwrange * range, int has_range) { + int len; + + /* People are very often confused by the 8 bit arithmetic happening + * here. + * All the values here are encoded in a 8 bit integer. 8 bit integers + * are either unsigned [0 ; 255], signed [-128 ; +127] or + * negative [-255 ; 0]. + * Further, on 8 bits, 0x100 == 256 == 0. + * + * Relative/percent values are always encoded unsigned, between 0 and 255. + * Absolute/dBm values are always encoded negative, between -255 and 0. + * + * How do we separate relative from absolute values ? We use the + * range to do that. The range allow to specify the real min/max + * of the value. As the range struct only specify one bound of the + * value, we assume that the other bound is 0 (zero). + * For relative values, range is [0 ; range->max]. + * For absolute values, range is [range->max ; 0]. + * + * Let's take two example : + * 1) value is 75%. qual->value = 75 ; range->max_qual.value = 100 + * 2) value is -54dBm. noise floor of the radio is -104dBm. + * qual->value = -54 = 202 ; range->max_qual.value = -104 = 152 + * + * Jean II + */ + /* Just do it */ if(has_range && (qual->level != 0)) { - /* If the statistics are in dBm */ + /* Deal with quality : always a relative value */ + if(!(qual->updated & IW_QUAL_QUAL_INVALID)) + { + len = snprintf(buffer, buflen, "Quality%c%d/%d ", + qual->updated & IW_QUAL_QUAL_UPDATED ? '=' : ':', + qual->qual, range->max_qual.qual); + buffer += len; + buflen -= len; + } + + /* If the statistics are in dBm or relative */ if(qual->level > range->max_qual.level) { - /* Statistics are in dBm (absolute power measurement) */ - sprintf(buffer, - "Quality:%d/%d Signal level:%d dBm Noise level:%d dBm%s", - qual->qual, range->max_qual.qual, - qual->level - 0x100, qual->noise - 0x100, - (qual->updated & 0x7) ? " (updated)" : ""); + /* Deal with signal level in dBm (absolute power measurement) */ + if(!(qual->updated & IW_QUAL_LEVEL_INVALID)) + { + len = snprintf(buffer, buflen, "Signal level%c%d dBm ", + qual->updated & IW_QUAL_LEVEL_UPDATED ? '=' : ':', + qual->level - 0x100); + buffer += len; + buflen -= len; + } + + /* Deal with noise level in dBm (absolute power measurement) */ + if(!(qual->updated & IW_QUAL_NOISE_INVALID)) + { + len = snprintf(buffer, buflen, "Noise level%c%d dBm", + qual->updated & IW_QUAL_NOISE_UPDATED ? '=' : ':', + qual->noise - 0x100); + } } else { - /* Statistics are relative values (0 -> max) */ - sprintf(buffer, - "Quality:%d/%d Signal level:%d/%d Noise level:%d/%d%s", - qual->qual, range->max_qual.qual, - qual->level, range->max_qual.level, - qual->noise, range->max_qual.noise, - (qual->updated & 0x7) ? " (updated)" : ""); + /* Deal with signal level as relative value (0 -> max) */ + if(!(qual->updated & IW_QUAL_LEVEL_INVALID)) + { + len = snprintf(buffer, buflen, "Signal level%c%d/%d ", + qual->updated & IW_QUAL_LEVEL_UPDATED ? '=' : ':', + qual->level, range->max_qual.level); + buffer += len; + buflen -= len; + } + + /* Deal with noise level as relative value (0 -> max) */ + if(!(qual->updated & IW_QUAL_NOISE_INVALID)) + { + len = snprintf(buffer, buflen, "Noise level%c%d/%d", + qual->updated & IW_QUAL_NOISE_UPDATED ? '=' : ':', + qual->noise, range->max_qual.noise); + } } } else { /* We can't read the range, so we don't know... */ - sprintf(buffer, "Quality:%d Signal level:%d Noise level:%d%s", - qual->qual, qual->level, qual->noise, - (qual->updated & 0x7) ? " (updated)" : ""); + snprintf(buffer, buflen, + "Quality:%d Signal level:%d Noise level:%d", + qual->qual, qual->level, qual->noise); } } @@ -992,28 +1394,36 @@ iw_print_stats(char * buffer, * Output the encoding key, with a nice formating */ void -iw_print_key(char * buffer, - unsigned char * key, - int key_size, - int key_flags) +iw_print_key(char * buffer, + int buflen, + const unsigned char * key, /* Must be unsigned */ + int key_size, + int key_flags) { int i; + /* Check buffer size -> 1 bytes => 2 digits + 1/2 separator */ + if((key_size * 3) > buflen) + { + snprintf(buffer, buflen, ""); + return; + } + /* Is the key present ??? */ if(key_flags & IW_ENCODE_NOKEY) { /* Nope : print on or dummy */ if(key_size <= 0) - strcpy(buffer, "on"); + strcpy(buffer, "on"); /* Size checked */ else { - strcpy(buffer, "**"); + strcpy(buffer, "**"); /* Size checked */ buffer +=2; for(i = 1; i < key_size; i++) { if((i & 0x1) == 0) - strcpy(buffer++, "-"); - strcpy(buffer, "**"); + strcpy(buffer++, "-"); /* Size checked */ + strcpy(buffer, "**"); /* Size checked */ buffer +=2; } } @@ -1021,13 +1431,13 @@ iw_print_key(char * buffer, else { /* Yes : print the key */ - sprintf(buffer, "%.2X", key[0]); + sprintf(buffer, "%.2X", key[0]); /* Size checked */ buffer +=2; for(i = 1; i < key_size; i++) { if((i & 0x1) == 0) - strcpy(buffer++, "-"); - sprintf(buffer, "%.2X", key[i]); + strcpy(buffer++, "-"); /* Size checked */ + sprintf(buffer, "%.2X", key[i]); /* Size checked */ buffer +=2; } } @@ -1039,8 +1449,8 @@ iw_print_key(char * buffer, * ### NOT IMPLEMENTED ### * Return size of the key, or 0 (no key) or -1 (error) */ -int -iw_pass_key(char * input, +static int +iw_pass_key(const char * input, unsigned char * key) { input = input; key = key; @@ -1054,7 +1464,7 @@ iw_pass_key(char * input, * Return size of the key, or 0 (no key) or -1 (error) */ int -iw_in_key(char * input, +iw_in_key(const char * input, unsigned char * key) { int keylen = 0; @@ -1090,7 +1500,7 @@ iw_in_key(char * input, } /* Preserve original buffers (both in & out) */ hex = buff + IW_ENCODING_TOKEN_MAX; - strcpy(hex, input); + strcpy(hex, input); /* Size checked */ out = buff; /* Parse */ @@ -1136,8 +1546,8 @@ iw_in_key(char * input, */ int iw_in_key_full(int skfd, - char * ifname, - char * input, + const char * ifname, + const char * input, unsigned char * key, __u16 * flags) { @@ -1146,9 +1556,7 @@ iw_in_key_full(int skfd, if(!strncmp(input, "l:", 2)) { -#if WIRELESS_EXT > 15 struct iw_range range; -#endif /* Extra case : as a login (user:passwd - Cisco LEAP) */ keylen = strlen(input + 2) + 1; /* skip "l:", add '\0' */ @@ -1166,22 +1574,27 @@ iw_in_key_full(int skfd, } *p = '\0'; -#if WIRELESS_EXT > 15 - printf("flags = %X, index = %X\n", *flags, range.encoding_login_index); - if((*flags & IW_ENCODE_INDEX) == 0) + /* Extract range info */ + if(iw_get_range_info(skfd, ifname, &range) < 0) + /* Hum... Maybe we should return an error ??? */ + memset(&range, 0, sizeof(range)); + + if(range.we_version_compiled > 15) { - /* Extract range info */ - if(iw_get_range_info(skfd, ifname, &range) < 0) - memset(&range, 0, sizeof(range)); + + printf("flags = %X, index = %X\n", + *flags, range.encoding_login_index); + if((*flags & IW_ENCODE_INDEX) == 0) + { + /* Extract range info */ + if(iw_get_range_info(skfd, ifname, &range) < 0) + memset(&range, 0, sizeof(range)); + printf("flags = %X, index = %X\n", *flags, range.encoding_login_index); + /* Set the index the driver expects */ + *flags |= range.encoding_login_index & IW_ENCODE_INDEX; + } printf("flags = %X, index = %X\n", *flags, range.encoding_login_index); - /* Set the index the driver expects */ - *flags |= range.encoding_login_index & IW_ENCODE_INDEX; } - printf("flags = %X, index = %X\n", *flags, range.encoding_login_index); -#else - /* Avoid "Unused parameter" warning */ - skfd = skfd; ifname = ifname; flags = flags; -#endif } else /* Simpler routine above */ @@ -1198,46 +1611,55 @@ iw_in_key_full(int skfd, */ void iw_print_pm_value(char * buffer, + int buflen, int value, int flags) { + /* Check size */ + if(buflen < 25) + { + snprintf(buffer, buflen, ""); + return; + } + buflen -= 25; + /* Modifiers */ if(flags & IW_POWER_MIN) { - strcpy(buffer, " min"); + strcpy(buffer, " min"); /* Size checked */ buffer += 4; } if(flags & IW_POWER_MAX) { - strcpy(buffer, " max"); + strcpy(buffer, " max"); /* Size checked */ buffer += 4; } /* Type */ if(flags & IW_POWER_TIMEOUT) { - strcpy(buffer, " timeout:"); + strcpy(buffer, " timeout:"); /* Size checked */ buffer += 9; } else { - strcpy(buffer, " period:"); + strcpy(buffer, " period:"); /* Size checked */ buffer += 8; } /* Display value without units */ if(flags & IW_POWER_RELATIVE) - sprintf(buffer, "%g", ((double) value) / MEGA); + snprintf(buffer, buflen, "%g", ((double) value) / MEGA); else { /* Display value with units */ if(value >= (int) MEGA) - sprintf(buffer, "%gs", ((double) value) / MEGA); + snprintf(buffer, buflen, "%gs", ((double) value) / MEGA); else if(value >= (int) KILO) - sprintf(buffer, "%gms", ((double) value) / KILO); + snprintf(buffer, buflen, "%gms", ((double) value) / KILO); else - sprintf(buffer, "%dus", value); + snprintf(buffer, buflen, "%dus", value); } } @@ -1247,81 +1669,96 @@ iw_print_pm_value(char * buffer, */ void iw_print_pm_mode(char * buffer, + int buflen, int flags) { + /* Check size */ + if(buflen < 28) + { + snprintf(buffer, buflen, ""); + return; + } + /* Print the proper mode... */ switch(flags & IW_POWER_MODE) { case IW_POWER_UNICAST_R: - strcpy(buffer, "mode:Unicast only received"); + strcpy(buffer, "mode:Unicast only received"); /* Size checked */ break; case IW_POWER_MULTICAST_R: - strcpy(buffer, "mode:Multicast only received"); + strcpy(buffer, "mode:Multicast only received"); /* Size checked */ break; case IW_POWER_ALL_R: - strcpy(buffer, "mode:All packets received"); + strcpy(buffer, "mode:All packets received"); /* Size checked */ break; case IW_POWER_FORCE_S: - strcpy(buffer, "mode:Force sending"); + strcpy(buffer, "mode:Force sending"); /* Size checked */ break; case IW_POWER_REPEATER: - strcpy(buffer, "mode:Repeat multicasts"); + strcpy(buffer, "mode:Repeat multicasts"); /* Size checked */ break; default: - strcpy(buffer, ""); + strcpy(buffer, ""); /* Size checked */ break; } } /***************** RETRY LIMIT/LIFETIME SUBROUTINES *****************/ -#if WIRELESS_EXT > 10 /*------------------------------------------------------------------*/ /* * Output a retry value with all attributes... */ void iw_print_retry_value(char * buffer, + int buflen, int value, int flags) { + /* Check buffer size */ + if(buflen < 18) + { + snprintf(buffer, buflen, ""); + return; + } + buflen -= 18; + /* Modifiers */ if(flags & IW_RETRY_MIN) { - strcpy(buffer, " min"); + strcpy(buffer, " min"); /* Size checked */ buffer += 4; } if(flags & IW_RETRY_MAX) { - strcpy(buffer, " max"); + strcpy(buffer, " max"); /* Size checked */ buffer += 4; } /* Type lifetime of limit */ if(flags & IW_RETRY_LIFETIME) { - strcpy(buffer, " lifetime:"); + strcpy(buffer, " lifetime:"); /* Size checked */ buffer += 10; /* Display value without units */ if(flags & IW_POWER_RELATIVE) - sprintf(buffer, "%g", ((double) value) / MEGA); + snprintf(buffer, buflen, "%g", ((double) value) / MEGA); else { /* Display value with units */ if(value >= (int) MEGA) - sprintf(buffer, "%gs", ((double) value) / MEGA); + snprintf(buffer, buflen, "%gs", ((double) value) / MEGA); else if(value >= (int) KILO) - sprintf(buffer, "%gms", ((double) value) / KILO); + snprintf(buffer, buflen, "%gms", ((double) value) / KILO); else - sprintf(buffer, "%dus", value); + snprintf(buffer, buflen, "%dus", value); } } else - sprintf(buffer, " limit:%d", value); + snprintf(buffer, buflen, " limit:%d", value); } -#endif /* WIRELESS_EXT > 10 */ /************************* TIME SUBROUTINES *************************/ @@ -1332,12 +1769,13 @@ iw_print_retry_value(char * buffer, */ void iw_print_timeval(char * buffer, + int buflen, const struct timeval * time) { int s; s = (time->tv_sec) % 86400; - sprintf(buffer, "%02d:%02d:%02d.%06u ", + snprintf(buffer, buflen, "%02d:%02d:%02d.%06u ", s / 3600, (s % 3600) / 60, s % 60, (u_int32_t) time->tv_usec); } @@ -1345,6 +1783,7 @@ iw_print_timeval(char * buffer, /*********************** ADDRESS SUBROUTINES ************************/ /* * This section is mostly a cut & past from net-tools-1.2.0 + * (Well... This has evolved over the years) * manage address display and input... */ @@ -1429,6 +1868,31 @@ iw_check_addr_type(int skfd, /*------------------------------------------------------------------*/ /* + * Ask the kernel for the MAC address of an interface. + */ +int +iw_get_mac_addr(int skfd, + const char * ifname, + struct ether_addr * eth, + unsigned short * ptype) +{ + struct ifreq ifr; + int ret; + + /* Prepare request */ + bzero(&ifr, sizeof(struct ifreq)); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + + /* Do it */ + ret = ioctl(skfd, SIOCGIFHWADDR, &ifr); + + memcpy(eth->ether_addr_octet, ifr.ifr_hwaddr.sa_data, 6); + *ptype = ifr.ifr_hwaddr.sa_family; + return(ret); +} + +/*------------------------------------------------------------------*/ +/* * Display an Ethernet address in readable format. */ void @@ -1516,21 +1980,21 @@ iw_in_inet(char *name, struct sockaddr *sap) { struct hostent *hp; struct netent *np; - struct sockaddr_in *sin = (struct sockaddr_in *) sap; + struct sockaddr_in *sain = (struct sockaddr_in *) sap; /* Grmpf. -FvK */ - sin->sin_family = AF_INET; - sin->sin_port = 0; + sain->sin_family = AF_INET; + sain->sin_port = 0; /* Default is special, meaning 0.0.0.0. */ if (!strcmp(name, "default")) { - sin->sin_addr.s_addr = INADDR_ANY; + sain->sin_addr.s_addr = INADDR_ANY; return(1); } /* Try the NETWORKS database to see if this is a known network. */ if ((np = getnetbyname(name)) != (struct netent *)NULL) { - sin->sin_addr.s_addr = htonl(np->n_net); + sain->sin_addr.s_addr = htonl(np->n_net); strcpy(name, np->n_name); return(1); } @@ -1540,7 +2004,7 @@ iw_in_inet(char *name, struct sockaddr *sap) errno = h_errno; return(-1); } - memcpy((char *) &sin->sin_addr, (char *) hp->h_addr_list[0], hp->h_length); + memcpy((char *) &sain->sin_addr, (char *) hp->h_addr_list[0], hp->h_length); strcpy(name, hp->h_name); return(0); } @@ -1564,7 +2028,7 @@ iw_in_addr(int skfd, /* Check if we have valid interface address type */ if(iw_check_if_addr_type(skfd, ifname) < 0) { - fprintf(stderr, "%-8.8s Interface doesn't support IP addresses\n", ifname); + fprintf(stderr, "%-8.16s Interface doesn't support IP addresses\n", ifname); return(-1); } @@ -1607,7 +2071,7 @@ iw_in_addr(int skfd, /* Check if we have valid mac address type */ if(iw_check_mac_addr_type(skfd, ifname) < 0) { - fprintf(stderr, "%-8.8s Interface doesn't support MAC addresses\n", ifname); + fprintf(stderr, "%-8.16s Interface doesn't support MAC addresses\n", ifname); return(-1); } @@ -1660,7 +2124,6 @@ iw_get_priv_size(int args) * Those functions help the decoding of events, so are needed only in * this case. */ -#if WIRELESS_EXT > 13 /* Type of headers we know about (basically union iwreq_data) */ #define IW_HEADER_TYPE_NULL 0 /* Not available */ @@ -1751,6 +2214,10 @@ static const int event_type_size[] = { IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ }; +/* Forward compatibility with WE-19 */ +#define IW_EV_POINT_OFF (((char *) &(((struct iw_point *) NULL)->length)) - \ + (char *) NULL) + /*------------------------------------------------------------------*/ /* * Initialise the struct stream_descr so that we can extract @@ -1775,7 +2242,8 @@ iw_init_event_stream(struct stream_descr * stream, /* Stream of events */ */ int iw_extract_event_stream(struct stream_descr * stream, /* Stream of events */ - struct iw_event * iwe) /* Extracted event */ + struct iw_event * iwe, /* Extracted event */ + int we_version) { int event_type = 0; unsigned int event_len = 1; /* Invalid */ @@ -1783,6 +2251,9 @@ iw_extract_event_stream(struct stream_descr * stream, /* Stream of events */ /* Don't "optimise" the following variable, it will crash */ unsigned cmd_index; /* *MUST* be unsigned */ + /* Unused for now. Will be later on... */ + we_version = we_version; + /* Check for end of stream */ if((stream->current + IW_EV_LCP_LEN) > stream->end) return(0); @@ -1820,6 +2291,9 @@ iw_extract_event_stream(struct stream_descr * stream, /* Stream of events */ } /* Unknown events -> event_type=0 => IW_EV_LCP_LEN */ event_len = event_type_size[event_type]; + /* Fixup for later version of WE */ + if((we_version > 18) && (event_type == IW_HEADER_TYPE_POINT)) + event_len -= IW_EV_POINT_OFF; /* Check if we know about this event */ if(event_len <= IW_EV_LCP_LEN) @@ -1848,7 +2322,12 @@ iw_extract_event_stream(struct stream_descr * stream, /* Stream of events */ stream->current += iwe->len; return(-2); } - memcpy((char *) iwe + IW_EV_LCP_LEN, pointer, event_len); + /* Fixup for later version of WE */ + if((we_version > 18) && (event_type == IW_HEADER_TYPE_POINT)) + memcpy((char *) iwe + IW_EV_LCP_LEN + IW_EV_POINT_OFF, + pointer, event_len); + else + memcpy((char *) iwe + IW_EV_LCP_LEN, pointer, event_len); /* Skip event in the stream */ pointer += event_len; @@ -1883,4 +2362,301 @@ iw_extract_event_stream(struct stream_descr * stream, /* Stream of events */ return(1); } -#endif /* WIRELESS_EXT > 13 */ +/*********************** SCANNING SUBROUTINES ***********************/ +/* + * The Wireless Extension API 14 and greater define Wireless Scanning. + * The normal API is complex, this is an easy API that return + * a subset of the scanning results. This should be enough for most + * applications that want to use Scanning. + * If you want to have use the full/normal API, check iwlist.c... + * + * Precaution when using scanning : + * The scanning operation disable normal network traffic, and therefore + * you should not abuse of scan. + * The scan need to check the presence of network on other frequencies. + * While you are checking those other frequencies, you can *NOT* be on + * your normal frequency to listen to normal traffic in the cell. + * You need typically in the order of one second to actively probe all + * 802.11b channels (do the maths). Some cards may do that in background, + * to reply to scan commands faster, but they still have to do it. + * Leaving the cell for such an extended period of time is pretty bad. + * Any kind of streaming/low latency traffic will be impacted, and the + * user will perceive it (easily checked with telnet). People trying to + * send traffic to you will retry packets and waste bandwidth. Some + * applications may be sensitive to those packet losses in weird ways, + * and tracing those weird behavior back to scanning may take time. + * If you are in ad-hoc mode, if two nodes scan approx at the same + * time, they won't see each other, which may create associations issues. + * For those reasons, the scanning activity should be limited to + * what's really needed, and continuous scanning is a bad idea. + * Jean II + */ + +/*------------------------------------------------------------------*/ +/* + * Process/store one element from the scanning results in wireless_scan + */ +static inline struct wireless_scan * +iw_process_scanning_token(struct iw_event * event, + struct wireless_scan * wscan) +{ + struct wireless_scan * oldwscan; + + /* Now, let's decode the event */ + switch(event->cmd) + { + case SIOCGIWAP: + /* New cell description. Allocate new cell descriptor, zero it. */ + oldwscan = wscan; + wscan = (struct wireless_scan *) malloc(sizeof(struct wireless_scan)); + if(wscan == NULL) + return(wscan); + /* Link at the end of the list */ + if(oldwscan != NULL) + oldwscan->next = wscan; + + /* Reset it */ + bzero(wscan, sizeof(struct wireless_scan)); + + /* Save cell identifier */ + wscan->has_ap_addr = 1; + memcpy(&(wscan->ap_addr), &(event->u.ap_addr), sizeof (sockaddr)); + break; + case SIOCGIWNWID: + wscan->b.has_nwid = 1; + memcpy(&(wscan->b.nwid), &(event->u.nwid), sizeof(iwparam)); + break; + case SIOCGIWFREQ: + wscan->b.has_freq = 1; + wscan->b.freq = iw_freq2float(&(event->u.freq)); + wscan->b.freq_flags = event->u.freq.flags; + break; + case SIOCGIWMODE: + wscan->b.mode = event->u.mode; + if((wscan->b.mode < IW_NUM_OPER_MODE) && (wscan->b.mode >= 0)) + wscan->b.has_mode = 1; + break; + case SIOCGIWESSID: + wscan->b.has_essid = 1; + wscan->b.essid_on = event->u.data.flags; + if((event->u.essid.pointer) && (event->u.essid.length)) + memcpy(wscan->b.essid, event->u.essid.pointer, event->u.essid.length); + wscan->b.essid[event->u.essid.length] = '\0'; + break; + case SIOCGIWENCODE: + wscan->b.has_key = 1; + wscan->b.key_size = event->u.data.length; + wscan->b.key_flags = event->u.data.flags; + if(event->u.data.pointer) + memcpy(wscan->b.key, event->u.essid.pointer, event->u.data.length); + else + wscan->b.key_flags |= IW_ENCODE_NOKEY; + break; + case IWEVQUAL: + /* We don't get complete stats, only qual */ + wscan->has_stats = 1; + memcpy(&wscan->stats.qual, &event->u.qual, sizeof(iwstats)); + break; + case SIOCGIWRATE: + /* Scan may return a list of bitrates. Should we really bother with + * an array of bitrates ? Or only the maximum bitrate ? Jean II */ + case IWEVCUSTOM: + /* How can we deal with those sanely ? Jean II */ + default: + break; + } /* switch(event->cmd) */ + + return(wscan); +} + +/*------------------------------------------------------------------*/ +/* + * Initiate the scan procedure, and process results. + * This is a non-blocking procedure and it will return each time + * it would block, returning the amount of time the caller should wait + * before calling again. + * Return -1 for error, delay to wait for (in ms), or 0 for success. + * Error code is in errno + */ +int +iw_process_scan(int skfd, + char * ifname, + int we_version, + wireless_scan_head * context) +{ + struct iwreq wrq; + unsigned char * buffer = NULL; /* Results */ + int buflen = IW_SCAN_MAX_DATA; /* Min for compat WE<17 */ + unsigned char * newbuf; + + /* Don't waste too much time on interfaces (50 * 100 = 5s) */ + context->retry++; + if(context->retry > 50) + { + errno = ETIME; + return(-1); + } + + /* If we have not yet initiated scanning on the interface */ + if(context->retry == 1) + { + /* Initiate Scan */ + wrq.u.data.pointer = NULL; /* Later */ + wrq.u.data.flags = 0; + wrq.u.data.length = 0; + if(iw_set_ext(skfd, ifname, SIOCSIWSCAN, &wrq) < 0) + return(-1); + /* Success : now, just wait for event or results */ + return(250); /* Wait 250 ms */ + } + + realloc: + /* (Re)allocate the buffer - realloc(NULL, len) == malloc(len) */ + newbuf = realloc(buffer, buflen); + if(newbuf == NULL) + { + /* man says : If realloc() fails the original block is left untouched */ + if(buffer) + free(buffer); + errno = ENOMEM; + return(-1); + } + 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(skfd, ifname, SIOCGIWSCAN, &wrq) < 0) + { + /* Check if buffer was too small (WE-17 only) */ + if((errno == E2BIG) && (we_version > 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) + { + free(buffer); + /* Wait for only 100ms from now on */ + return(100); /* Wait 100 ms */ + } + + free(buffer); + /* Bad error, please don't come back... */ + return(-1); + } + + /* We have the results, process them */ + if(wrq.u.data.length) + { + struct iw_event iwe; + struct stream_descr stream; + struct wireless_scan * wscan = NULL; + int ret; +#if 0 + /* Debugging code. In theory useless, because it's debugged ;-) */ + int i; + printf("Scan result [%02X", buffer[0]); + for(i = 1; i < wrq.u.data.length; i++) + printf(":%02X", buffer[i]); + printf("]\n"); +#endif + + /* Init */ + iw_init_event_stream(&stream, buffer, wrq.u.data.length); + /* This is dangerous, we may leak user data... */ + context->result = NULL; + + /* Look every token */ + do + { + /* Extract an event and print it */ + ret = iw_extract_event_stream(&stream, &iwe, we_version); + if(ret > 0) + { + /* Convert to wireless_scan struct */ + wscan = iw_process_scanning_token(&iwe, wscan); + /* Check problems */ + if(wscan == NULL) + { + free(buffer); + errno = ENOMEM; + return(-1); + } + /* Save head of list */ + if(context->result == NULL) + context->result = wscan; + } + } + while(ret > 0); + } + + /* Done with this interface - return success */ + free(buffer); + return(0); +} + +/*------------------------------------------------------------------*/ +/* + * Perform a wireless scan on the specified interface. + * This is a blocking procedure and it will when the scan is completed + * or when an error occur. + * + * The scan results are given in a linked list of wireless_scan objects. + * The caller *must* free the result himself (by walking the list). + * If there is an error, -1 is returned and the error code is available + * in errno. + * + * The parameter we_version can be extracted from the range structure + * (range.we_version_compiled - see iw_get_range_info()), or using + * iw_get_kernel_we_version(). For performance reason, you should + * cache this parameter when possible rather than querying it every time. + * + * Return -1 for error and 0 for success. + */ +int +iw_scan(int skfd, + char * ifname, + int we_version, + wireless_scan_head * context) +{ + int delay; /* in ms */ + + /* Clean up context. Potential memory leak if(context.result != NULL) */ + context->result = NULL; + context->retry = 0; + + /* Wait until we get results or error */ + while(1) + { + /* Try to get scan results */ + delay = iw_process_scan(skfd, ifname, we_version, context); + + /* Check termination */ + if(delay <= 0) + break; + + /* Wait a bit */ + usleep(delay * 1000); + } + + /* End - return -1 or 0 */ + return(delay); +}