| /* |
| * Wireless Tools |
| * |
| * Jean II - HPLB 97->99 - HPL 99->01 |
| * |
| * Common subroutines to all the wireless tools... |
| * |
| * This file is released under the GPL license. |
| * Copyright (c) 1997-2002 Jean Tourrilhes <jt@hpl.hp.com> |
| */ |
| |
| /***************************** INCLUDES *****************************/ |
| |
| #include "iwlib.h" /* Header */ |
| |
| #ifdef __ANDROID__ |
| #include <sys/endian.h> |
| #endif |
| |
| /************************ CONSTANTS & MACROS ************************/ |
| |
| /* Various versions information */ |
| /* Recommended Wireless Extension version */ |
| #define WE_VERSION 15 |
| /* Version of Wireless Tools */ |
| #define WT_VERSION 25 |
| |
| /* |
| * 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 |
| */ |
| #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 v15 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 v15 detected," |
| #warning "Maybe you should get a more recent version" |
| #warning "of the Wireless Tools package !" |
| #endif |
| |
| /**************************** VARIABLES ****************************/ |
| |
| const char * const iw_operation_mode[] = { "Auto", |
| "Ad-Hoc", |
| "Managed", |
| "Master", |
| "Repeater", |
| "Secondary", |
| "Monitor" }; |
| |
| /* Disable runtime version warning in iw_get_range_info() */ |
| int iw_ignore_version = 0; |
| |
| /************************ SOCKET SUBROUTINES *************************/ |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Open a socket. |
| * Depending on the protocol present, open the right socket. The socket |
| * will allow us to talk to the driver. |
| */ |
| int |
| iw_sockets_open(void) |
| { |
| static const int families[] = { |
| AF_INET, AF_IPX, AF_AX25, AF_APPLETALK |
| }; |
| unsigned int i; |
| int sock; |
| |
| /* |
| * Now pick any (exisiting) useful socket family for generic queries |
| * Note : don't open all the socket, only returns when one matches, |
| * all protocols might not be valid. |
| * Workaround by Jim Kaba <jkaba@sarnoff.com> |
| * Note : in 99% of the case, we will just open the inet_sock. |
| * The remaining 1% case are not fully correct... |
| */ |
| |
| /* Try all families we support */ |
| for(i = 0; i < sizeof(families)/sizeof(int); ++i) |
| { |
| /* Try to open the socket, if success returns it */ |
| sock = socket(families[i], SOCK_DGRAM, 0); |
| if(sock >= 0) |
| return sock; |
| } |
| |
| return -1; |
| } |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Extract the interface name out of /proc/net/wireless or /proc/net/dev. |
| */ |
| static inline char * |
| iw_get_ifname(char * name, /* Where to store the name */ |
| int nsize, /* Size of name buffer */ |
| char * buf) /* Current position in buffer */ |
| { |
| char * end; |
| |
| /* Skip leading spaces */ |
| while(isspace(*buf)) |
| buf++; |
| |
| #ifndef IW_RESTRIC_ENUM |
| /* Get name up to the last ':'. Aliases may contain ':' in them, |
| * but the last one should be the separator */ |
| end = strrchr(buf, ':'); |
| #else |
| /* Get name up to ": " |
| * Note : we compare to ": " to make sure to process aliased interfaces |
| * properly. Doesn't work on /proc/net/dev, because it doesn't guarantee |
| * a ' ' after the ':'*/ |
| end = strstr(buf, ": "); |
| #endif |
| |
| /* Not found ??? To big ??? */ |
| if((end == NULL) || (((end - buf) + 1) > nsize)) |
| return(NULL); |
| |
| /* Copy */ |
| memcpy(name, buf, (end - buf)); |
| name[end - buf] = '\0'; |
| |
| return(end + 2); |
| } |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Enumerate devices and call specified routine |
| * The new way just use /proc/net/wireless, so get all wireless interfaces, |
| * whether configured or not. This is the default if available. |
| * The old way use SIOCGIFCONF, so get only configured interfaces (wireless |
| * or not). |
| */ |
| void |
| iw_enum_devices(int skfd, |
| iw_enum_handler fn, |
| char * args[], |
| int count) |
| { |
| char buff[1024]; |
| FILE * fh; |
| struct ifconf ifc; |
| struct ifreq *ifr; |
| int i; |
| |
| #ifndef IW_RESTRIC_ENUM |
| /* Check if /proc/net/wireless is available */ |
| fh = fopen(PROC_NET_DEV, "r"); |
| #else |
| /* Check if /proc/net/wireless is available */ |
| fh = fopen(PROC_NET_WIRELESS, "r"); |
| #endif |
| |
| if(fh != NULL) |
| { |
| /* Success : use data from /proc/net/wireless */ |
| |
| /* Eat 2 lines of header */ |
| fgets(buff, sizeof(buff), fh); |
| fgets(buff, sizeof(buff), fh); |
| |
| /* Read each device line */ |
| while(fgets(buff, sizeof(buff), fh)) |
| { |
| char name[IFNAMSIZ + 1]; |
| char *s; |
| |
| /* Extract interface name */ |
| s = iw_get_ifname(name, sizeof(name), buff); |
| |
| if(!s) |
| /* Failed to parse, complain and continue */ |
| fprintf(stderr, "Cannot parse " PROC_NET_WIRELESS "\n"); |
| else |
| /* Got it, print info about this interface */ |
| (*fn)(skfd, name, args, count); |
| } |
| |
| fclose(fh); |
| } |
| else |
| { |
| /* Get list of configured devices using "traditional" way */ |
| ifc.ifc_len = sizeof(buff); |
| ifc.ifc_buf = buff; |
| if(ioctl(skfd, SIOCGIFCONF, &ifc) < 0) |
| { |
| fprintf(stderr, "SIOCGIFCONF: %s\n", strerror(errno)); |
| return; |
| } |
| ifr = ifc.ifc_req; |
| |
| /* Print them */ |
| for(i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; ifr++) |
| (*fn)(skfd, ifr->ifr_name, args, count); |
| } |
| } |
| |
| /*********************** WIRELESS SUBROUTINES ************************/ |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Get the range information out of the driver |
| */ |
| int |
| iw_get_range_info(int skfd, |
| char * ifname, |
| iwrange * range) |
| { |
| struct iwreq wrq; |
| char buffer[sizeof(iwrange) * 2]; /* Large enough */ |
| |
| /* Cleanup */ |
| memset(buffer, 0, sizeof(buffer)); |
| |
| wrq.u.data.pointer = (caddr_t) buffer; |
| wrq.u.data.length = sizeof(buffer); |
| wrq.u.data.flags = 0; |
| if(iw_get_ext(skfd, ifname, SIOCGIWRANGE, &wrq) < 0) |
| return(-1); |
| |
| /* Copy stuff at the right place, ignore extra */ |
| memcpy((char *) range, buffer, sizeof(iwrange)); |
| |
| /* 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) |
| { |
| /* 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 */ |
| } |
| 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"); |
| } |
| } |
| } |
| /* 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 */ |
| |
| return(0); |
| } |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Print the WE versions of the interface. |
| */ |
| static int |
| print_iface_version_info(int skfd, |
| char * ifname, |
| char * args[], /* Command line args */ |
| int count) /* Args count */ |
| { |
| struct iwreq wrq; |
| char buffer[sizeof(iwrange) * 2]; /* Large enough */ |
| struct iw_range * range; |
| |
| /* Avoid "Unused parameter" warning */ |
| args = args; count = count; |
| |
| /* Cleanup */ |
| memset(buffer, 0, sizeof(buffer)); |
| |
| wrq.u.data.pointer = (caddr_t) buffer; |
| wrq.u.data.length = sizeof(buffer); |
| wrq.u.data.flags = 0; |
| if(iw_get_ext(skfd, ifname, SIOCGIWRANGE, &wrq) < 0) |
| return(-1); |
| |
| /* Copy stuff at the right place, ignore extra */ |
| range = (struct iw_range *) buffer; |
| |
| /* For new versions, we can check the version directly, for old versions |
| * we use magic. 300 bytes is a also magic number, don't touch... */ |
| if((WIRELESS_EXT > 10) && (wrq.u.data.length >= 300)) |
| { |
| #if WIRELESS_EXT > 10 |
| printf("%-8.8s 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 no Wireless Extension version information.\n\n", |
| ifname); |
| #endif |
| } |
| |
| |
| return(0); |
| } |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Print the WE versions of the tools. |
| */ |
| int |
| iw_print_version_info(char * toolname) |
| { |
| int skfd; /* generic raw socket desc. */ |
| |
| /* Create a channel to the NET kernel. */ |
| if((skfd = iw_sockets_open()) < 0) |
| { |
| perror("socket"); |
| return -1; |
| } |
| |
| /* Information about the tools themselves */ |
| if(toolname != NULL) |
| printf("%-8.8s Version %d\n", toolname, WT_VERSION); |
| printf(" Compatible with Wireless Extension v%d or earlier,\n", |
| WE_VERSION); |
| printf(" Currently compiled with Wireless Extension v%d.\n\n", |
| WIRELESS_EXT); |
| |
| /* Version for each device */ |
| iw_enum_devices(skfd, &print_iface_version_info, NULL, 0); |
| |
| close(skfd); |
| |
| return 0; |
| } |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Get information about what private ioctls are supported by the driver |
| */ |
| int |
| iw_get_priv_info(int skfd, |
| char * ifname, |
| iwprivargs * priv, |
| int maxpriv) |
| { |
| struct iwreq wrq; |
| |
| /* Ask the driver */ |
| wrq.u.data.pointer = (caddr_t) priv; |
| wrq.u.data.length = maxpriv; |
| wrq.u.data.flags = 0; |
| |
| //printf("iw_get_priv_info pointer=%08x length=%d\n",wrq.u.data.pointer,wrq.u.data.length); |
| |
| if(iw_get_ext(skfd, ifname, SIOCGIWPRIV, &wrq) < 0) |
| return(-1); |
| |
| //printf("iw_get_priv_info wrq.u.data.length=%d\n",wrq.u.data.length); |
| |
| /* Return the number of ioctls */ |
| return(wrq.u.data.length); |
| } |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Get essential wireless config from the device driver |
| * We will call all the classical wireless ioctl on the driver through |
| * the socket to know what is supported and to get the settings... |
| * Note : compare to the version in iwconfig, we extract only |
| * what's *really* needed to configure a device... |
| */ |
| int |
| iw_get_basic_config(int skfd, |
| char * ifname, |
| wireless_config * info) |
| { |
| struct iwreq wrq; |
| |
| memset((char *) info, 0, sizeof(struct wireless_config)); |
| |
| /* Get wireless name */ |
| if(iw_get_ext(skfd, ifname, SIOCGIWNAME, &wrq) < 0) |
| /* If no wireless name : no wireless extensions */ |
| return(-1); |
| else |
| { |
| strncpy(info->name, wrq.u.name, IFNAMSIZ); |
| info->name[IFNAMSIZ] = '\0'; |
| } |
| |
| /* Get network ID */ |
| if(iw_get_ext(skfd, ifname, SIOCGIWNWID, &wrq) >= 0) |
| { |
| info->has_nwid = 1; |
| memcpy(&(info->nwid), &(wrq.u.nwid), sizeof(iwparam)); |
| } |
| |
| /* Get frequency / channel */ |
| if(iw_get_ext(skfd, ifname, SIOCGIWFREQ, &wrq) >= 0) |
| { |
| info->has_freq = 1; |
| info->freq = iw_freq2float(&(wrq.u.freq)); |
| } |
| |
| /* Get encryption information */ |
| wrq.u.data.pointer = (caddr_t) info->key; |
| wrq.u.data.length = IW_ENCODING_TOKEN_MAX; |
| wrq.u.data.flags = 0; |
| if(iw_get_ext(skfd, ifname, SIOCGIWENCODE, &wrq) >= 0) |
| { |
| info->has_key = 1; |
| info->key_size = wrq.u.data.length; |
| info->key_flags = wrq.u.data.flags; |
| } |
| |
| /* Get ESSID */ |
| wrq.u.essid.pointer = (caddr_t) info->essid; |
| wrq.u.essid.length = IW_ESSID_MAX_SIZE + 1; |
| wrq.u.essid.flags = 0; |
| if(iw_get_ext(skfd, ifname, SIOCGIWESSID, &wrq) >= 0) |
| { |
| info->has_essid = 1; |
| info->essid_on = wrq.u.data.flags; |
| } |
| |
| /* Get operation mode */ |
| if(iw_get_ext(skfd, ifname, SIOCGIWMODE, &wrq) >= 0) |
| { |
| info->mode = wrq.u.mode; |
| if((info->mode < 6) && (info->mode >= 0)) |
| info->has_mode = 1; |
| } |
| |
| return(0); |
| } |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Set essential wireless config in the device driver |
| * We will call all the classical wireless ioctl on the driver through |
| * the socket to know what is supported and to set the settings... |
| * We support only the restricted set as above... |
| */ |
| int |
| iw_set_basic_config(int skfd, |
| char * ifname, |
| wireless_config * info) |
| { |
| struct iwreq wrq; |
| int ret = 0; |
| |
| /* Get wireless name (check if interface is valid) */ |
| if(iw_get_ext(skfd, ifname, SIOCGIWNAME, &wrq) < 0) |
| /* If no wireless name : no wireless extensions */ |
| return(-2); |
| |
| /* Set Network ID, if available (this is for non-802.11 cards) */ |
| if(info->has_nwid) |
| { |
| memcpy(&(wrq.u.nwid), &(info->nwid), sizeof(iwparam)); |
| wrq.u.nwid.fixed = 1; /* Hum... When in Rome... */ |
| |
| if(iw_set_ext(skfd, ifname, SIOCSIWNWID, &wrq) < 0) |
| { |
| fprintf(stderr, "SIOCSIWNWID: %s\n", strerror(errno)); |
| ret = -1; |
| } |
| } |
| |
| /* Set frequency / channel */ |
| if(info->has_freq) |
| { |
| iw_float2freq(info->freq, &(wrq.u.freq)); |
| |
| if(iw_set_ext(skfd, ifname, SIOCSIWFREQ, &wrq) < 0) |
| { |
| fprintf(stderr, "SIOCSIWFREQ: %s\n", strerror(errno)); |
| ret = -1; |
| } |
| } |
| |
| /* Set encryption information */ |
| if(info->has_key) |
| { |
| int flags = info->key_flags; |
| |
| /* Check if there is a key index */ |
| if((flags & IW_ENCODE_INDEX) > 0) |
| { |
| /* Set the index */ |
| wrq.u.data.pointer = (caddr_t) NULL; |
| wrq.u.data.flags = (flags & (IW_ENCODE_INDEX)) | IW_ENCODE_NOKEY; |
| wrq.u.data.length = 0; |
| |
| if(iw_set_ext(skfd, ifname, SIOCSIWENCODE, &wrq) < 0) |
| { |
| fprintf(stderr, "SIOCSIWENCODE(%d): %s\n", |
| errno, strerror(errno)); |
| ret = -1; |
| } |
| } |
| |
| /* Mask out index to minimise probability of reject when setting key */ |
| flags = flags & (~IW_ENCODE_INDEX); |
| |
| /* Set the key itself (set current key in this case) */ |
| wrq.u.data.pointer = (caddr_t) info->key; |
| wrq.u.data.length = info->key_size; |
| wrq.u.data.flags = flags; |
| |
| if(iw_set_ext(skfd, ifname, SIOCSIWENCODE, &wrq) < 0) |
| { |
| fprintf(stderr, "SIOCSIWENCODE(%d): %s\n", |
| errno, strerror(errno)); |
| ret = -1; |
| } |
| } |
| |
| /* Set ESSID (extended network), if available */ |
| if(info->has_essid) |
| { |
| 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_set_ext(skfd, ifname, SIOCSIWESSID, &wrq) < 0) |
| { |
| fprintf(stderr, "SIOCSIWESSID: %s\n", strerror(errno)); |
| ret = -1; |
| } |
| } |
| |
| /* Set the current mode of operation */ |
| if(info->has_mode) |
| { |
| strncpy(wrq.ifr_name, ifname, IFNAMSIZ-1); |
| wrq.ifr_name[IFNAMSIZ-1] = '\0'; |
| wrq.u.mode = info->mode; |
| |
| if(iw_get_ext(skfd, ifname, SIOCSIWMODE, &wrq) < 0) |
| { |
| fprintf(stderr, "SIOCSIWMODE: %s\n", strerror(errno)); |
| ret = -1; |
| } |
| } |
| |
| return(ret); |
| } |
| |
| /*********************** PROTOCOL SUBROUTINES ***********************/ |
| /* |
| * Fun stuff with protocol identifiers (SIOCGIWNAME). |
| * We assume that drivers are returning sensible values in there, |
| * which is not always the case :-( |
| */ |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Compare protocol identifiers. |
| * We don't want to know if the two protocols are the exactly same, |
| * but if they interoperate at some level, and also if they accept the |
| * same type of config (ESSID vs NWID, freq...). |
| * This is supposed to work around the alphabet soup. |
| * Return 1 if protocols are compatible |
| */ |
| int |
| iw_protocol_compare(char * protocol1, |
| char * protocol2) |
| { |
| char * dot11 = "IEEE 802.11"; |
| char * dot11_ds = "Dbg"; |
| |
| /* If the strings are the same -> easy */ |
| if(!strncmp(protocol1, protocol2, IFNAMSIZ)) |
| return(1); |
| |
| /* Are we dealing with one of the 802.11 variant ? */ |
| if( (!strncmp(protocol1, dot11, strlen(dot11))) && |
| (!strncmp(protocol2, dot11, strlen(dot11))) ) |
| { |
| char * sub1 = protocol1 + strlen(dot11); |
| char * sub2 = protocol2 + strlen(dot11); |
| |
| /* Skip optional separator */ |
| if(*sub1 == '-') |
| sub1++; |
| if(*sub2 == '-') |
| sub2++; |
| |
| /* Check if they are both 2.4 GHz Direct Sequence compatible */ |
| if( (strchr(dot11_ds, *sub1) != NULL) && |
| (strchr(dot11_ds, *sub2) != NULL) ) |
| return(1); |
| } |
| /* Not compatible */ |
| return(0); |
| } |
| |
| /********************** FREQUENCY SUBROUTINES ***********************/ |
| /* |
| * Note : the two functions below are the cause of troubles on |
| * various embeeded platforms, as they are the reason we require |
| * libm (math library). |
| * In this case, please use enable BUILD_NOLIBM in the makefile |
| * |
| * FIXME : check negative mantissa and exponent |
| */ |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Convert a floating point the our internal representation of |
| * frequencies. |
| * The kernel doesn't want to hear about floating point, so we use |
| * this custom format instead. |
| */ |
| void |
| iw_float2freq(double in, |
| iwfreq * out) |
| { |
| /* Version without libm : slower */ |
| out->e = 0; |
| while(in > 1e9) |
| { |
| in /= 10; |
| out->e++; |
| } |
| out->m = (long) in; |
| } |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Convert our internal representation of frequencies to a floating point. |
| */ |
| double |
| iw_freq2float(iwfreq * in) |
| { |
| /* Version without libm : slower */ |
| int i; |
| double res = (double) in->m; |
| for(i = 0; i < in->e; i++) |
| res *= 10; |
| return(res); |
| } |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Output a frequency with proper scaling |
| */ |
| void |
| iw_print_freq(char * buffer, |
| double freq) |
| { |
| if(freq < KILO) |
| sprintf(buffer, "Channel:%g", freq); |
| else |
| { |
| if(freq >= GIGA) |
| sprintf(buffer, "Frequency:%gGHz", freq / GIGA); |
| else |
| { |
| if(freq >= MEGA) |
| sprintf(buffer, "Frequency:%gMHz", freq / MEGA); |
| else |
| sprintf(buffer, "Frequency:%gkHz", freq / KILO); |
| } |
| } |
| } |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Convert a frequency to a channel (negative -> error) |
| */ |
| int |
| iw_freq_to_channel(double freq, |
| struct iw_range * range) |
| { |
| double ref_freq; |
| int k; |
| |
| /* Check if it's a frequency or not already a channel */ |
| if(freq < KILO) |
| return(-1); |
| |
| /* We compare the frequencies as double to ignore differences |
| * in encoding. Slower, but safer... */ |
| for(k = 0; k < range->num_frequency; k++) |
| { |
| ref_freq = iw_freq2float(&(range->freq[k])); |
| if(freq == ref_freq) |
| return(range->freq[k].i); |
| } |
| /* Not found */ |
| return(-2); |
| } |
| |
| /*********************** BITRATE SUBROUTINES ***********************/ |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Output a bitrate with proper scaling |
| */ |
| void |
| iw_print_bitrate(char * buffer, |
| int bitrate) |
| { |
| double rate = bitrate; |
| |
| if(rate >= GIGA) |
| sprintf(buffer, "%gGb/s", rate / GIGA); |
| else |
| if(rate >= MEGA) |
| sprintf(buffer, "%gMb/s", rate / MEGA); |
| else |
| sprintf(buffer, "%gkb/s", rate / KILO); |
| } |
| |
| /************************ POWER SUBROUTINES *************************/ |
| |
| /*------------------------------------------------------------------*/ |
| #ifndef __ANDROID__ |
| /* |
| * Convert a value in dBm to a value in milliWatt. |
| */ |
| int |
| iw_dbm2mwatt(int in) |
| { |
| //return((int) (floor(pow(10.0, (((double) in) / 10.0))))); |
| } |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Convert a value in milliWatt to a value in dBm. |
| */ |
| int |
| iw_mwatt2dbm(int in) |
| { |
| // return((int) (ceil(10.0 * log10((double) in)))); |
| } |
| #endif |
| /********************** STATISTICS SUBROUTINES **********************/ |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Read /proc/net/wireless to get the latest statistics |
| */ |
| int |
| iw_get_stats(int skfd, |
| char * ifname, |
| iwstats * stats) |
| { |
| #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-1); |
| wrq.ifr_name[IFNAMSIZ-1] = '\0'; |
| 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; |
| 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 */ |
| } |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Output the link statistics, taking care of formating |
| */ |
| void |
| iw_print_stats(char * buffer, |
| iwqual * qual, |
| iwrange * range, |
| int has_range) |
| { |
| /* Just do it */ |
| if(has_range && (qual->level != 0)) |
| { |
| /* If the statistics are in dBm */ |
| 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)" : ""); |
| } |
| 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)" : ""); |
| } |
| } |
| 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)" : ""); |
| } |
| } |
| |
| /*********************** ENCODING SUBROUTINES ***********************/ |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Output the encoding key, with a nice formating |
| */ |
| void |
| iw_print_key(char * buffer, |
| unsigned char * key, |
| int key_size, |
| int key_flags) |
| { |
| int i; |
| |
| /* Is the key present ??? */ |
| if(key_flags & IW_ENCODE_NOKEY) |
| { |
| /* Nope : print on or dummy */ |
| if(key_size <= 0) |
| strcpy(buffer, "on"); |
| else |
| { |
| strcpy(buffer, "**"); |
| buffer +=2; |
| for(i = 1; i < key_size; i++) |
| { |
| if((i & 0x1) == 0) |
| strcpy(buffer++, "-"); |
| strcpy(buffer, "**"); |
| buffer +=2; |
| } |
| } |
| } |
| else |
| { |
| /* Yes : print the key */ |
| sprintf(buffer, "%.2X", key[0]); |
| buffer +=2; |
| for(i = 1; i < key_size; i++) |
| { |
| if((i & 0x1) == 0) |
| strcpy(buffer++, "-"); |
| sprintf(buffer, "%.2X", key[i]); |
| buffer +=2; |
| } |
| } |
| } |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Convert a passphrase into a key |
| * ### NOT IMPLEMENTED ### |
| * Return size of the key, or 0 (no key) or -1 (error) |
| */ |
| int |
| iw_pass_key(char * input, |
| unsigned char * key) |
| { |
| input = input; key = key; |
| fprintf(stderr, "Error: Passphrase not implemented\n"); |
| return(-1); |
| } |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Parse a key from the command line. |
| * Return size of the key, or 0 (no key) or -1 (error) |
| */ |
| int |
| iw_in_key(char * input, |
| unsigned char * key) |
| { |
| int keylen = 0; |
| char * buff; |
| char * p; |
| int temp; |
| |
| /* Check the type of key */ |
| if(!strncmp(input, "s:", 2)) |
| { |
| /* First case : as an ASCII string (Lucent/Agere cards) */ |
| keylen = strlen(input + 2); /* skip "s:" */ |
| if(keylen > IW_ENCODING_TOKEN_MAX) |
| keylen = IW_ENCODING_TOKEN_MAX; |
| strncpy((char *)key, input + 2, keylen); |
| } |
| else |
| if(!strncmp(input, "p:", 2)) |
| { |
| /* Second case : as a passphrase (PrismII cards) */ |
| return(iw_pass_key(input + 2, key)); /* skip "p:" */ |
| } |
| else |
| { |
| /* Third case : as hexadecimal digits */ |
| buff = malloc(strlen(input) + 1); |
| if(buff == NULL) |
| { |
| fprintf(stderr, "Malloc failed (string too long ?)\n"); |
| return(-1); |
| } |
| /* Preserve original buffer */ |
| strcpy(buff, input); |
| |
| /* Parse */ |
| p = strtok(buff, "-:;.,"); |
| while((p != (char *) NULL) && (keylen < IW_ENCODING_TOKEN_MAX)) |
| { |
| if(sscanf(p, "%2X", &temp) != 1) |
| return(-1); /* Error */ |
| key[keylen++] = (unsigned char) (temp & 0xFF); |
| if(strlen(p) > 2) /* Token not finished yet */ |
| p += 2; |
| else |
| p = strtok((char *) NULL, "-:;.,"); |
| } |
| free(buff); |
| } |
| |
| return(keylen); |
| } |
| |
| /******************* POWER MANAGEMENT SUBROUTINES *******************/ |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Output a power management value with all attributes... |
| */ |
| void |
| iw_print_pm_value(char * buffer, |
| int value, |
| int flags) |
| { |
| /* Modifiers */ |
| if(flags & IW_POWER_MIN) |
| { |
| strcpy(buffer, " min"); |
| buffer += 4; |
| } |
| if(flags & IW_POWER_MAX) |
| { |
| strcpy(buffer, " max"); |
| buffer += 4; |
| } |
| |
| /* Type */ |
| if(flags & IW_POWER_TIMEOUT) |
| { |
| strcpy(buffer, " timeout:"); |
| buffer += 9; |
| } |
| else |
| { |
| strcpy(buffer, " period:"); |
| buffer += 8; |
| } |
| |
| /* Display value without units */ |
| if(flags & IW_POWER_RELATIVE) |
| sprintf(buffer, "%g", ((double) value) / MEGA); |
| else |
| { |
| /* Display value with units */ |
| if(value >= (int) MEGA) |
| sprintf(buffer, "%gs", ((double) value) / MEGA); |
| else |
| if(value >= (int) KILO) |
| sprintf(buffer, "%gms", ((double) value) / KILO); |
| else |
| sprintf(buffer, "%dus", value); |
| } |
| } |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Output a power management mode |
| */ |
| void |
| iw_print_pm_mode(char * buffer, |
| int flags) |
| { |
| /* Print the proper mode... */ |
| switch(flags & IW_POWER_MODE) |
| { |
| case IW_POWER_UNICAST_R: |
| strcpy(buffer, "mode:Unicast only received"); |
| break; |
| case IW_POWER_MULTICAST_R: |
| strcpy(buffer, "mode:Multicast only received"); |
| break; |
| case IW_POWER_ALL_R: |
| strcpy(buffer, "mode:All packets received"); |
| break; |
| case IW_POWER_FORCE_S: |
| strcpy(buffer, "mode:Force sending"); |
| break; |
| case IW_POWER_REPEATER: |
| strcpy(buffer, "mode:Repeat multicasts"); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| /***************** RETRY LIMIT/LIFETIME SUBROUTINES *****************/ |
| |
| #if WIRELESS_EXT > 10 |
| /*------------------------------------------------------------------*/ |
| /* |
| * Output a retry value with all attributes... |
| */ |
| void |
| iw_print_retry_value(char * buffer, |
| int value, |
| int flags) |
| { |
| /* Modifiers */ |
| if(flags & IW_RETRY_MIN) |
| { |
| strcpy(buffer, " min"); |
| buffer += 4; |
| } |
| if(flags & IW_RETRY_MAX) |
| { |
| strcpy(buffer, " max"); |
| buffer += 4; |
| } |
| |
| /* Type lifetime of limit */ |
| if(flags & IW_RETRY_LIFETIME) |
| { |
| strcpy(buffer, " lifetime:"); |
| buffer += 10; |
| |
| /* Display value without units */ |
| if(flags & IW_POWER_RELATIVE) |
| sprintf(buffer, "%g", ((double) value) / MEGA); |
| else |
| { |
| /* Display value with units */ |
| if(value >= (int) MEGA) |
| sprintf(buffer, "%gs", ((double) value) / MEGA); |
| else |
| if(value >= (int) KILO) |
| sprintf(buffer, "%gms", ((double) value) / KILO); |
| else |
| sprintf(buffer, "%dus", value); |
| } |
| } |
| else |
| sprintf(buffer, " limit:%d", value); |
| } |
| #endif /* WIRELESS_EXT > 10 */ |
| |
| /************************* TIME SUBROUTINES *************************/ |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Print timestamps |
| * Inspired from irdadump... |
| */ |
| void |
| iw_print_timeval(char * buffer, |
| const struct timeval * time) |
| { |
| int s; |
| |
| s = (time->tv_sec) % 86400; |
| sprintf(buffer, "%02d:%02d:%02d.%06u ", |
| s / 3600, (s % 3600) / 60, |
| s % 60, (u_int32_t) time->tv_usec); |
| } |
| |
| /*********************** ADDRESS SUBROUTINES ************************/ |
| /* |
| * This section is mostly a cut & past from net-tools-1.2.0 |
| * manage address display and input... |
| */ |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Check if interface support the right MAC address type... |
| */ |
| int |
| iw_check_mac_addr_type(int skfd, |
| char * ifname) |
| { |
| struct ifreq ifr; |
| |
| /* Get the type of hardware address */ |
| strncpy(ifr.ifr_name, ifname, IFNAMSIZ-1); |
| ifr.ifr_name[IFNAMSIZ-1] = '\0'; |
| if((ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0) || |
| (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER)) |
| { |
| /* Deep trouble... */ |
| fprintf(stderr, "Interface %s doesn't support MAC addresses\n", |
| ifname); |
| return(-1); |
| } |
| |
| #ifdef DEBUG |
| printf("Hardware : %d - %s\n", ifr.ifr_hwaddr.sa_family, |
| iw_ether_ntoa((struct ether_addr *) ifr.ifr_hwaddr.sa_data)); |
| #endif |
| |
| return(0); |
| } |
| |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Check if interface support the right interface address type... |
| */ |
| int |
| iw_check_if_addr_type(int skfd, |
| char * ifname) |
| { |
| struct ifreq ifr; |
| |
| /* Get the type of interface address */ |
| strncpy(ifr.ifr_name, ifname, IFNAMSIZ-1); |
| ifr.ifr_name[IFNAMSIZ-1] = '\0'; |
| if((ioctl(skfd, SIOCGIFADDR, &ifr) < 0) || |
| (ifr.ifr_addr.sa_family != AF_INET)) |
| { |
| /* Deep trouble... */ |
| fprintf(stderr, "Interface %s doesn't support IP addresses\n", ifname); |
| return(-1); |
| } |
| |
| #ifdef DEBUG |
| printf("Interface : %d - 0x%lX\n", ifr.ifr_addr.sa_family, |
| *((unsigned long *) ifr.ifr_addr.sa_data)); |
| #endif |
| |
| return(0); |
| } |
| |
| #if 0 |
| /*------------------------------------------------------------------*/ |
| /* |
| * Check if interface support the right address types... |
| */ |
| int |
| iw_check_addr_type(int skfd, |
| char * ifname) |
| { |
| /* Check the interface address type */ |
| if(iw_check_if_addr_type(skfd, ifname) < 0) |
| return(-1); |
| |
| /* Check the interface address type */ |
| if(iw_check_mac_addr_type(skfd, ifname) < 0) |
| return(-1); |
| |
| return(0); |
| } |
| #endif |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Display an Ethernet address in readable format. |
| */ |
| void |
| iw_ether_ntop(const struct ether_addr* eth, char* buf) |
| { |
| sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X", |
| eth->ether_addr_octet[0], eth->ether_addr_octet[1], |
| eth->ether_addr_octet[2], eth->ether_addr_octet[3], |
| eth->ether_addr_octet[4], eth->ether_addr_octet[5]); |
| } |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Display an Ethernet address in readable format. |
| * Same with a static buffer |
| */ |
| char * |
| iw_ether_ntoa(const struct ether_addr* eth) |
| { |
| static char buf[20]; |
| iw_ether_ntop(eth, buf); |
| return buf; |
| } |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Input an Ethernet address and convert to binary. |
| */ |
| int |
| iw_ether_aton(const char *orig, struct ether_addr *eth) |
| { |
| const char *bufp; |
| int i; |
| |
| i = 0; |
| for(bufp = orig; *bufp != '\0'; ++bufp) { |
| unsigned int val; |
| unsigned char c = *bufp++; |
| if (isdigit(c)) val = c - '0'; |
| else if (c >= 'a' && c <= 'f') val = c - 'a' + 10; |
| else if (c >= 'A' && c <= 'F') val = c - 'A' + 10; |
| else break; |
| |
| val <<= 4; |
| c = *bufp++; |
| if (isdigit(c)) val |= c - '0'; |
| else if (c >= 'a' && c <= 'f') val |= c - 'a' + 10; |
| else if (c >= 'A' && c <= 'F') val |= c - 'A' + 10; |
| else break; |
| |
| eth->ether_addr_octet[i] = (unsigned char) (val & 0377); |
| if(++i == ETH_ALEN) { |
| /* That's it. Any trailing junk? */ |
| if (*bufp != '\0') { |
| #ifdef DEBUG |
| fprintf(stderr, "iw_ether_aton(%s): trailing junk!\n", orig); |
| errno = EINVAL; |
| return(0); |
| #endif |
| } |
| #ifdef DEBUG |
| fprintf(stderr, "iw_ether_aton(%s): %s\n", |
| orig, ether_ntoa(eth)); |
| #endif |
| return(1); |
| } |
| if (*bufp != ':') |
| break; |
| } |
| |
| #ifdef DEBUG |
| fprintf(stderr, "iw_ether_aton(%s): invalid ether address!\n", orig); |
| #endif |
| errno = EINVAL; |
| return(0); |
| } |
| |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Input an Internet address and convert to binary. |
| */ |
| int |
| iw_in_inet(char *name, struct sockaddr *sap) |
| { |
| struct hostent *hp; |
| struct netent *np; |
| struct sockaddr_in *sin = (struct sockaddr_in *) sap; |
| |
| /* Grmpf. -FvK */ |
| sin->sin_family = AF_INET; |
| sin->sin_port = 0; |
| |
| /* Default is special, meaning 0.0.0.0. */ |
| if (!strcmp(name, "default")) { |
| sin->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); |
| strcpy(name, np->n_name); |
| return(1); |
| } |
| |
| /* Always use the resolver (DNS name + IP addresses) */ |
| if ((hp = gethostbyname(name)) == (struct hostent *)NULL) { |
| errno = h_errno; |
| return(-1); |
| } |
| memcpy((char *) &sin->sin_addr, (char *) hp->h_addr_list[0], hp->h_length); |
| strcpy(name, hp->h_name); |
| return(0); |
| } |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Input an address and convert to binary. |
| */ |
| int |
| iw_in_addr(int skfd, |
| char * ifname, |
| char * bufp, |
| struct sockaddr *sap) |
| { |
| /* Check if it is a hardware or IP address */ |
| if(index(bufp, ':') == NULL) |
| { |
| struct sockaddr if_address; |
| struct arpreq arp_query; |
| |
| /* 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); |
| return(-1); |
| } |
| |
| /* Read interface address */ |
| if(iw_in_inet(bufp, &if_address) < 0) |
| { |
| fprintf(stderr, "Invalid interface address %s\n", bufp); |
| return(-1); |
| } |
| |
| /* Translate IP addresses to MAC addresses */ |
| memcpy((char *) &(arp_query.arp_pa), |
| (char *) &if_address, |
| sizeof(struct sockaddr)); |
| arp_query.arp_ha.sa_family = 0; |
| arp_query.arp_flags = 0; |
| /* The following restrict the search to the interface only */ |
| /* For old kernels which complain, just comment it... */ |
| strncpy(arp_query.arp_dev, ifname, IFNAMSIZ-1); |
| arp_query.arp_dev[IFNAMSIZ-1] = '\0'; |
| if((ioctl(skfd, SIOCGARP, &arp_query) < 0) || |
| !(arp_query.arp_flags & ATF_COM)) |
| { |
| fprintf(stderr, "Arp failed for %s on %s... (%d)\nTry to ping the address before setting it.\n", |
| bufp, ifname, errno); |
| return(-1); |
| } |
| |
| /* Store new MAC address */ |
| memcpy((char *) sap, |
| (char *) &(arp_query.arp_ha), |
| sizeof(struct sockaddr)); |
| |
| #ifdef DEBUG |
| printf("IP Address %s => Hw Address = %s\n", |
| bufp, iw_ether_ntoa((struct ether_addr *) sap->sa_data)); |
| #endif |
| } |
| else /* If it's an hardware address */ |
| { |
| /* 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); |
| return(-1); |
| } |
| |
| /* Get the hardware address */ |
| if(iw_in_ether(bufp, sap) < 0) |
| { |
| fprintf(stderr, "Invalid hardware address %s\n", bufp); |
| return(-1); |
| } |
| } |
| |
| #ifdef DEBUG |
| printf("Hw Address = %s\n", iw_ether_ntoa((struct ether_addr *) sap->sa_data)); |
| #endif |
| |
| return(0); |
| } |
| |
| /************************* MISC SUBROUTINES **************************/ |
| |
| /* Size (in bytes) of various events */ |
| static const int priv_type_size[] = { |
| 0, /* IW_PRIV_TYPE_NONE */ |
| 1, /* IW_PRIV_TYPE_BYTE */ |
| 1, /* IW_PRIV_TYPE_CHAR */ |
| 0, /* Not defined */ |
| sizeof(__u32), /* IW_PRIV_TYPE_INT */ |
| sizeof(struct iw_freq), /* IW_PRIV_TYPE_FLOAT */ |
| sizeof(struct sockaddr), /* IW_PRIV_TYPE_ADDR */ |
| 0, /* Not defined */ |
| }; |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Max size in bytes of an private argument. |
| */ |
| int |
| iw_get_priv_size(int args) |
| { |
| int num = args & IW_PRIV_SIZE_MASK; |
| int type = (args & IW_PRIV_TYPE_MASK) >> 12; |
| |
| return(num * priv_type_size[type]); |
| } |
| |
| /************************ EVENT SUBROUTINES ************************/ |
| /* |
| * The Wireless Extension API 14 and greater define Wireless Events, |
| * that are used for various events and scanning. |
| * 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 */ |
| #define IW_HEADER_TYPE_CHAR 2 /* char [IFNAMSIZ] */ |
| #define IW_HEADER_TYPE_UINT 4 /* __u32 */ |
| #define IW_HEADER_TYPE_FREQ 5 /* struct iw_freq */ |
| #define IW_HEADER_TYPE_ADDR 6 /* struct sockaddr */ |
| #define IW_HEADER_TYPE_POINT 8 /* struct iw_point */ |
| #define IW_HEADER_TYPE_PARAM 9 /* struct iw_param */ |
| #define IW_HEADER_TYPE_QUAL 10 /* struct iw_quality */ |
| |
| /* Headers for the various requests */ |
| static const char standard_ioctl_hdr[] = { |
| IW_HEADER_TYPE_NULL, /* SIOCSIWCOMMIT */ |
| IW_HEADER_TYPE_CHAR, /* SIOCGIWNAME */ |
| IW_HEADER_TYPE_PARAM, /* SIOCSIWNWID */ |
| IW_HEADER_TYPE_PARAM, /* SIOCGIWNWID */ |
| IW_HEADER_TYPE_FREQ, /* SIOCSIWFREQ */ |
| IW_HEADER_TYPE_FREQ, /* SIOCGIWFREQ */ |
| IW_HEADER_TYPE_UINT, /* SIOCSIWMODE */ |
| IW_HEADER_TYPE_UINT, /* SIOCGIWMODE */ |
| IW_HEADER_TYPE_PARAM, /* SIOCSIWSENS */ |
| IW_HEADER_TYPE_PARAM, /* SIOCGIWSENS */ |
| IW_HEADER_TYPE_NULL, /* SIOCSIWRANGE */ |
| IW_HEADER_TYPE_POINT, /* SIOCGIWRANGE */ |
| IW_HEADER_TYPE_NULL, /* SIOCSIWPRIV */ |
| IW_HEADER_TYPE_POINT, /* SIOCGIWPRIV */ |
| IW_HEADER_TYPE_NULL, /* SIOCSIWSTATS */ |
| IW_HEADER_TYPE_POINT, /* SIOCGIWSTATS */ |
| IW_HEADER_TYPE_POINT, /* SIOCSIWSPY */ |
| IW_HEADER_TYPE_POINT, /* SIOCGIWSPY */ |
| IW_HEADER_TYPE_NULL, /* -- hole -- */ |
| IW_HEADER_TYPE_NULL, /* -- hole -- */ |
| IW_HEADER_TYPE_ADDR, /* SIOCSIWAP */ |
| IW_HEADER_TYPE_ADDR, /* SIOCGIWAP */ |
| IW_HEADER_TYPE_NULL, /* -- hole -- */ |
| IW_HEADER_TYPE_POINT, /* SIOCGIWAPLIST */ |
| IW_HEADER_TYPE_PARAM, /* SIOCSIWSCAN */ |
| IW_HEADER_TYPE_POINT, /* SIOCGIWSCAN */ |
| IW_HEADER_TYPE_POINT, /* SIOCSIWESSID */ |
| IW_HEADER_TYPE_POINT, /* SIOCGIWESSID */ |
| IW_HEADER_TYPE_POINT, /* SIOCSIWNICKN */ |
| IW_HEADER_TYPE_POINT, /* SIOCGIWNICKN */ |
| IW_HEADER_TYPE_NULL, /* -- hole -- */ |
| IW_HEADER_TYPE_NULL, /* -- hole -- */ |
| IW_HEADER_TYPE_PARAM, /* SIOCSIWRATE */ |
| IW_HEADER_TYPE_PARAM, /* SIOCGIWRATE */ |
| IW_HEADER_TYPE_PARAM, /* SIOCSIWRTS */ |
| IW_HEADER_TYPE_PARAM, /* SIOCGIWRTS */ |
| IW_HEADER_TYPE_PARAM, /* SIOCSIWFRAG */ |
| IW_HEADER_TYPE_PARAM, /* SIOCGIWFRAG */ |
| IW_HEADER_TYPE_PARAM, /* SIOCSIWTXPOW */ |
| IW_HEADER_TYPE_PARAM, /* SIOCGIWTXPOW */ |
| IW_HEADER_TYPE_PARAM, /* SIOCSIWRETRY */ |
| IW_HEADER_TYPE_PARAM, /* SIOCGIWRETRY */ |
| IW_HEADER_TYPE_POINT, /* SIOCSIWENCODE */ |
| IW_HEADER_TYPE_POINT, /* SIOCGIWENCODE */ |
| IW_HEADER_TYPE_PARAM, /* SIOCSIWPOWER */ |
| IW_HEADER_TYPE_PARAM, /* SIOCGIWPOWER */ |
| }; |
| static const unsigned int standard_ioctl_num = sizeof(standard_ioctl_hdr); |
| |
| /* |
| * Meta-data about all the additional standard Wireless Extension events |
| * we know about. |
| */ |
| static const char standard_event_hdr[] = { |
| IW_HEADER_TYPE_ADDR, /* IWEVTXDROP */ |
| IW_HEADER_TYPE_QUAL, /* IWEVQUAL */ |
| IW_HEADER_TYPE_POINT, /* IWEVCUSTOM */ |
| IW_HEADER_TYPE_ADDR, /* IWEVREGISTERED */ |
| IW_HEADER_TYPE_ADDR, /* IWEVEXPIRED */ |
| }; |
| static const unsigned int standard_event_num = sizeof(standard_event_hdr); |
| |
| /* Size (in bytes) of various events */ |
| static const int event_type_size[] = { |
| IW_EV_LCP_LEN, /* IW_HEADER_TYPE_NULL */ |
| 0, |
| IW_EV_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */ |
| 0, |
| IW_EV_UINT_LEN, /* IW_HEADER_TYPE_UINT */ |
| IW_EV_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */ |
| IW_EV_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */ |
| 0, |
| IW_EV_POINT_LEN, /* Without variable payload */ |
| IW_EV_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */ |
| IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ |
| }; |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Initialise the struct stream_descr so that we can extract |
| * individual events from the event stream. |
| */ |
| void |
| iw_init_event_stream(struct stream_descr * stream, /* Stream of events */ |
| char * data, |
| int len) |
| { |
| /* Cleanup */ |
| memset((char *) stream, '\0', sizeof(struct stream_descr)); |
| |
| /* Set things up */ |
| stream->current = data; |
| stream->end = data + len; |
| } |
| |
| /*------------------------------------------------------------------*/ |
| /* |
| * Extract the next event from the event stream. |
| */ |
| int |
| iw_extract_event_stream(struct stream_descr * stream, /* Stream of events */ |
| struct iw_event * iwe) /* Extracted event */ |
| { |
| int event_type = 0; |
| unsigned int event_len = 1; /* Invalid */ |
| char * pointer; |
| /* Don't "optimise" the following variable, it will crash */ |
| unsigned cmd_index; /* *MUST* be unsigned */ |
| |
| /* Check for end of stream */ |
| if((stream->current + IW_EV_LCP_LEN) > stream->end) |
| return(0); |
| |
| #if 0 |
| printf("DBG - stream->current = %p, stream->value = %p, stream->end = %p\n", |
| stream->current, stream->value, stream->end); |
| #endif |
| |
| /* Extract the event header (to get the event id). |
| * Note : the event may be unaligned, therefore copy... */ |
| memcpy((char *) iwe, stream->current, IW_EV_LCP_LEN); |
| |
| #if 0 |
| printf("DBG - iwe->cmd = 0x%X, iwe->len = %d\n", |
| iwe->cmd, iwe->len); |
| #endif |
| |
| /* Check invalid events */ |
| if(iwe->len <= IW_EV_LCP_LEN) |
| return(-1); |
| |
| /* Get the type and length of that event */ |
| if(iwe->cmd <= SIOCIWLAST) |
| { |
| cmd_index = iwe->cmd - SIOCIWFIRST; |
| if(cmd_index < standard_ioctl_num) |
| event_type = standard_ioctl_hdr[cmd_index]; |
| } |
| else |
| { |
| cmd_index = iwe->cmd - IWEVFIRST; |
| if(cmd_index < standard_event_num) |
| event_type = standard_event_hdr[cmd_index]; |
| } |
| /* Unknown events -> event_type=0 => IW_EV_LCP_LEN */ |
| event_len = event_type_size[event_type]; |
| |
| /* Check if we know about this event */ |
| if(event_len <= IW_EV_LCP_LEN) |
| { |
| /* Skip to next event */ |
| stream->current += iwe->len; |
| return(2); |
| } |
| event_len -= IW_EV_LCP_LEN; |
| |
| /* Set pointer on data */ |
| if(stream->value != NULL) |
| pointer = stream->value; /* Next value in event */ |
| else |
| pointer = stream->current + IW_EV_LCP_LEN; /* First value in event */ |
| |
| #if 0 |
| printf("DBG - event_type = %d, event_len = %d, pointer = %p\n", |
| event_type, event_len, pointer); |
| #endif |
| |
| /* Copy the rest of the event (at least, fixed part) */ |
| if((pointer + event_len) > stream->end) |
| { |
| /* Go to next event */ |
| stream->current += iwe->len; |
| return(-2); |
| } |
| memcpy((char *) iwe + IW_EV_LCP_LEN, pointer, event_len); |
| |
| /* Skip event in the stream */ |
| pointer += event_len; |
| |
| /* Special processing for iw_point events */ |
| if(event_type == IW_HEADER_TYPE_POINT) |
| { |
| /* Check the length of the payload */ |
| if((iwe->len - (event_len + IW_EV_LCP_LEN)) > 0) |
| /* Set pointer on variable part (warning : non aligned) */ |
| iwe->u.data.pointer = pointer; |
| else |
| /* No data */ |
| iwe->u.data.pointer = NULL; |
| |
| /* Go to next event */ |
| stream->current += iwe->len; |
| } |
| else |
| { |
| /* Is there more value in the event ? */ |
| if((pointer + event_len) <= (stream->current + iwe->len)) |
| /* Go to next value */ |
| stream->value = pointer; |
| else |
| { |
| /* Go to next event */ |
| stream->value = NULL; |
| stream->current += iwe->len; |
| } |
| } |
| return(1); |
| } |
| |
| #endif /* WIRELESS_EXT > 13 */ |