* Copyright (c) 1997-2002 Jean Tourrilhes <jt@hpl.hp.com>
*/
+/***************************** INCLUDES *****************************/
+
#include "iwlib.h" /* Header */
+/************************ 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",
"Managed",
"Master",
"Repeater",
- "Secondary" };
+ "Secondary",
+ "Monitor" };
+
+/* Disable runtime version warning in iw_get_range_info() */
+int iw_ignore_version = 0;
/************************ SOCKET SUBROUTINES *************************/
/*------------------------------------------------------------------*/
/*
- * Extract the interface name out of /proc/net/wireless
- * Important note : this procedure will work only with /proc/net/wireless
- * and not /proc/net/dev, because it doesn't guarantee a ' ' after the ':'.
+ * 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 */
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. */
+ * 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))
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)
{
* 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
- /* Version verification - for new versions */
- if(range->we_version_compiled != WIRELESS_EXT)
- {
- fprintf(stderr, "Warning : Device %s has been compiled with version %d\n", ifname, range->we_version_compiled);
- fprintf(stderr, "of Wireless Extension, while we are using version %d.\n", WIRELESS_EXT);
- fprintf(stderr, "Some things may be broken...\n\n");
- }
+ 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
{
- /* Version verification - for old versions */
- if(wrq.u.data.length != sizeof(iwrange))
- {
- fprintf(stderr, "Warning : Device %s has been compiled with a different version\n", ifname);
- fprintf(stderr, "of Wireless Extension than ours (we are using version %d).\n", WIRELESS_EXT);
- fprintf(stderr, "Some things may be broken...\n\n");
- }
+#if 0
+ fprintf(stderr, "%-8.8s no Wireless Extension version information.\n\n",
+ ifname);
+#endif
}
- /* 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 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)
+ iwprivargs * priv,
+ int maxpriv)
{
struct iwreq wrq;
/* Ask the driver */
wrq.u.data.pointer = (caddr_t) priv;
- wrq.u.data.length = 32;
+ wrq.u.data.length = maxpriv;
wrq.u.data.flags = 0;
if(iw_get_ext(skfd, ifname, SIOCGIWPRIV, &wrq) < 0)
return(-1);
/* If no wireless name : no wireless extensions */
return(-1);
else
- strcpy(info->name, wrq.u.name);
+ {
+ strncpy(info->name, wrq.u.name, IFNAMSIZ);
+ info->name[IFNAMSIZ] = '\0';
+ }
/* Get network ID */
if(iw_get_ext(skfd, ifname, SIOCGIWNWID, &wrq) >= 0)
/* Get ESSID */
wrq.u.essid.pointer = (caddr_t) info->essid;
- wrq.u.essid.length = IW_ESSID_MAX_SIZE;
+ wrq.u.essid.length = IW_ESSID_MAX_SIZE + 1;
wrq.u.essid.flags = 0;
if(iw_get_ext(skfd, ifname, SIOCGIWESSID, &wrq) >= 0)
{
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
+ */
/*------------------------------------------------------------------*/
/*
iw_float2freq(double in,
iwfreq * out)
{
+#ifdef WE_NOLIBM
+ /* Version without libm : slower */
+ out->e = 0;
+ while(in > 1e9)
+ {
+ in /= 10;
+ out->e++;
+ }
+ out->m = (long) in;
+#else /* WE_NOLIBM */
+ /* Version with libm : faster */
out->e = (short) (floor(log10(in)));
if(out->e > 8)
{
}
else
{
- out->m = in;
+ out->m = (long) in;
out->e = 0;
}
+#endif /* WE_NOLIBM */
}
/*------------------------------------------------------------------*/
double
iw_freq2float(iwfreq * in)
{
+#ifdef WE_NOLIBM
+ /* Version without libm : slower */
+ int i;
+ double res = (double) in->m;
+ for(i = 0; i < in->e; i++)
+ res *= 10;
+ return(res);
+#else /* WE_NOLIBM */
+ /* Version with libm : faster */
return ((double) in->m) * pow(10,in->e);
+#endif /* WE_NOLIBM */
}
/*------------------------------------------------------------------*/
*/
void
iw_print_freq(char * buffer,
- float freq)
+ double freq)
{
if(freq < KILO)
sprintf(buffer, "Channel:%g", freq);
}
}
+/*------------------------------------------------------------------*/
+/*
+ * 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 ***********************/
/*------------------------------------------------------------------*/
/*------------------------------------------------------------------*/
/*
+ * 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)
*/
/* Check the type of key */
if(!strncmp(input, "s:", 2))
{
- /* First case : as an ASCII string */
+ /* 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(key, input + 2, keylen);
}
else
- {
- /* Second 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);
- }
+ 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);
}
return(1);
}
+ /* Always use the resolver (DNS name + IP addresses) */
if ((hp = gethostbyname(name)) == (struct hostent *)NULL) {
errno = h_errno;
return(-1);
/************************* 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_byte_size(int args)
+iw_get_priv_size(int args)
{
- int ret = args & IW_PRIV_SIZE_MASK;
+ int num = args & IW_PRIV_SIZE_MASK;
+ int type = (args & IW_PRIV_TYPE_MASK) >> 12;
- if(((args & IW_PRIV_TYPE_MASK) == IW_PRIV_TYPE_INT) ||
- ((args & IW_PRIV_TYPE_MASK) == IW_PRIV_TYPE_FLOAT))
- ret <<= 2;
-
- if((args & IW_PRIV_TYPE_MASK) == IW_PRIV_TYPE_NONE)
- return 0;
-
- return ret;
+ return(num * priv_type_size[type]);
}
/************************ EVENT SUBROUTINES ************************/
#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_POINT 6 /* struct iw_point */
-#define IW_HEADER_TYPE_PARAM 7 /* struct iw_param */
-#define IW_HEADER_TYPE_ADDR 8 /* struct sockaddr */
-#define IW_HEADER_TYPE_QUAL 9 /* struct iw_quality */
+#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[] = {
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_EV_LCP_LEN, /* IW_HEADER_TYPE_NULL */
0,
- IW_EV_CHAR_LEN,
+ IW_EV_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */
0,
- IW_EV_UINT_LEN,
- IW_EV_FREQ_LEN,
- IW_EV_POINT_LEN, /* Without variable payload */
- IW_EV_PARAM_LEN,
- IW_EV_ADDR_LEN,
- IW_EV_QUAL_LEN,
+ 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 */
};
/*------------------------------------------------------------------*/
struct iw_event * iwe) /* Extracted event */
{
int event_type = 0;
- int event_len = 1; /* Invalid */
+ unsigned int event_len = 1; /* Invalid */
char * pointer;
/* Don't "optimise" the following variable, it will crash */
unsigned cmd_index; /* *MUST* be unsigned */
iwe->cmd, iwe->len);
#endif
- /* Get the type and length of that event */
+ /* 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_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 == 0) || (iwe->len == 0))
- return(-1);
+ 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 */
/* Copy the rest of the event (at least, fixed part) */
if((pointer + event_len) > stream->end)
- return(-2);
+ {
+ /* 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 */
/* No data */
iwe->u.data.pointer = NULL;
- /* Go to next event */
+ /* Go to next event */
stream->current += iwe->len;
}
else