Index: iwlib.c
===================================================================
--- iwlib.c (nonexistent)
+++ iwlib.c (revision 5)
@@ -0,0 +1,3322 @@
+/*
+ * Wireless Tools
+ *
+ * Jean II - HPLB 97->99 - HPL 99->09
+ *
+ * Common subroutines to all the wireless tools...
+ *
+ * This file is released under the GPL license.
+ * Copyright (c) 1997-2009 Jean Tourrilhes <jt@hpl.hp.com>
+ */
+
+/***************************** INCLUDES *****************************/
+
+#include "iwlib-private.h" /* Private header */
+
+/************************ CONSTANTS & MACROS ************************/
+
+/*
+ * 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 ******************************/
+
+/*
+ * Struct iw_range up to WE-15
+ */
+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)
+
+/*
+ * Union to perform unaligned access when working around alignement issues
+ */
+union iw_align_u16
+{
+ __u16 value;
+ unsigned char byte[2];
+};
+
+/**************************** VARIABLES ****************************/
+
+/* Modes as human readable strings */
+const char * const iw_operation_mode[] = { "Auto",
+ "Ad-Hoc",
+ "Managed",
+ "Master",
+ "Repeater",
+ "Secondary",
+ "Monitor",
+ "Unknown/bug" };
+
+/* Modulations as human readable strings */
+const struct iw_modul_descr iw_modul_list[] = {
+ /* Start with aggregate types, so that they display first */
+ { IW_MODUL_11AG, "11ag",
+ "IEEE 802.11a + 802.11g (2.4 & 5 GHz, up to 54 Mb/s)" },
+ { IW_MODUL_11AB, "11ab",
+ "IEEE 802.11a + 802.11b (2.4 & 5 GHz, up to 54 Mb/s)" },
+ { IW_MODUL_11G, "11g", "IEEE 802.11g (2.4 GHz, up to 54 Mb/s)" },
+ { IW_MODUL_11A, "11a", "IEEE 802.11a (5 GHz, up to 54 Mb/s)" },
+ { IW_MODUL_11B, "11b", "IEEE 802.11b (2.4 GHz, up to 11 Mb/s)" },
+
+ /* Proprietary aggregates */
+ { IW_MODUL_TURBO | IW_MODUL_11A, "turboa",
+ "Atheros turbo mode at 5 GHz (up to 108 Mb/s)" },
+ { IW_MODUL_TURBO | IW_MODUL_11G, "turbog",
+ "Atheros turbo mode at 2.4 GHz (up to 108 Mb/s)" },
+ { IW_MODUL_PBCC | IW_MODUL_11B, "11+",
+ "TI 802.11+ (2.4 GHz, up to 22 Mb/s)" },
+
+ /* Individual modulations */
+ { IW_MODUL_OFDM_G, "OFDMg",
+ "802.11g higher rates, OFDM at 2.4 GHz (up to 54 Mb/s)" },
+ { IW_MODUL_OFDM_A, "OFDMa", "802.11a, OFDM at 5 GHz (up to 54 Mb/s)" },
+ { IW_MODUL_CCK, "CCK", "802.11b higher rates (2.4 GHz, up to 11 Mb/s)" },
+ { IW_MODUL_DS, "DS", "802.11 Direct Sequence (2.4 GHz, up to 2 Mb/s)" },
+ { IW_MODUL_FH, "FH", "802.11 Frequency Hopping (2,4 GHz, up to 2 Mb/s)" },
+
+ /* Proprietary modulations */
+ { IW_MODUL_TURBO, "turbo",
+ "Atheros turbo mode, channel bonding (up to 108 Mb/s)" },
+ { IW_MODUL_PBCC, "PBCC",
+ "TI 802.11+ higher rates (2.4 GHz, up to 22 Mb/s)" },
+ { IW_MODUL_CUSTOM, "custom",
+ "Driver specific modulation (check driver documentation)" },
+};
+
+/* Disable runtime version warning in iw_get_range_info() */
+int iw_ignore_version = 1;
+
+/************************ 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 value currently unused, just make sure it's non-NULL */
+ return(end);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * 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/dev 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;
+
+ /* Skip empty or almost empty lines. It seems that in some
+ * cases fgets return a line with only a newline. */
+ if((buff[0] == '\0') || (buff[1] == '\0'))
+ continue;
+
+ /* Extract interface name */
+ s = iw_get_ifname(name, sizeof(name), buff);
+
+ if(!s)
+ {
+ /* Failed to parse, complain and continue */
+#ifndef IW_RESTRIC_ENUM
+ fprintf(stderr, "Cannot parse " PROC_NET_DEV "\n");
+#else
+ fprintf(stderr, "Cannot parse " PROC_NET_WIRELESS "\n");
+#endif
+ }
+ 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 ************************/
+
+/*------------------------------------------------------------------*/
+/*
+ * 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_kernel_we_version(void)
+{
+ char buff[1024];
+ FILE * fh;
+ char * p;
+ int v;
+
+ /* Check if /proc/net/wireless is available */
+ fh = fopen(PROC_NET_WIRELESS, "r");
+
+ if(fh == NULL)
+ {
+ fprintf(stderr, "Cannot read " PROC_NET_WIRELESS "\n");
+ return(-1);
+ }
+
+ /* Read the first line of buffer */
+ fgets(buff, sizeof(buff), fh);
+
+ if(strstr(buff, "| WE") == NULL)
+ {
+ /* Prior to WE16, so explicit version not present */
+
+ /* Black magic */
+ if(strstr(buff, "| Missed") == NULL)
+ v = 11;
+ else
+ v = 15;
+ fclose(fh);
+ return(v);
+ }
+
+ /* 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))
+ {
+ fprintf(stderr, "Cannot parse " PROC_NET_WIRELESS "\n");
+ fclose(fh);
+ return(-1);
+ }
+
+ fclose(fh);
+ return(v);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * 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;
+
+ /* If no wireless name : no wireless extensions.
+ * This enable us to treat the SIOCGIWRANGE failure below properly. */
+ if(iw_get_ext(skfd, ifname, SIOCGIWNAME, &wrq) < 0)
+ return(-1);
+
+ /* 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)
+ {
+ /* Interface support WE (see above), but not IWRANGE */
+ fprintf(stderr, "%-8.16s Driver has no Wireless Extension version information.\n\n", ifname);
+ return(0);
+ }
+
+ /* 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(wrq.u.data.length >= 300)
+ {
+ /* 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);
+ }
+ else
+ {
+ fprintf(stderr, "%-8.16s Wireless Extension version too old.\n\n",
+ ifname);
+ }
+
+
+ return(0);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Print the WE versions of the tools.
+ */
+int
+iw_print_version_info(const char * toolname)
+{
+ int skfd; /* generic raw socket desc. */
+ int we_kernel_version;
+
+ /* 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.16s Wireless-Tools version %d\n", toolname, WT_VERSION);
+ printf(" Compatible with Wireless Extension v11 to v%d.\n\n",
+ WE_MAX_VERSION);
+
+ /* 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)
+ {
+ /* 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));
+ }
+
+ /* 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)
+ {
+ 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");
+ }
+
+ /* We don't like future versions of WE, because we can't cope with
+ * the unknown */
+ if(range->we_version_compiled > WE_MAX_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_MAX_VERSION);
+ fprintf(stderr, "Some things may be broken...\n\n");
+ }
+
+ /* 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 */
+ }
+
+ /* 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,
+ 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 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;
+ }
+
+ /* 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);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * 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,
+ const 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));
+ info->freq_flags = wrq.u.freq.flags;
+ }
+
+ /* 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 + 2;
+ 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;
+ info->essid_len = wrq.u.essid.length;
+ }
+
+ /* Get operation mode */
+ if(iw_get_ext(skfd, ifname, SIOCGIWMODE, &wrq) >= 0)
+ {
+ info->has_mode = 1;
+ /* Note : event->u.mode is unsigned, no need to check <= 0 */
+ if(wrq.u.mode < IW_NUM_OPER_MODE)
+ info->mode = wrq.u.mode;
+ else
+ info->mode = IW_NUM_OPER_MODE; /* Unknown/bug */
+ }
+
+ 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,
+ const 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 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)
+ {
+ strncpy(wrq.ifr_name, ifname, IFNAMSIZ);
+ wrq.u.mode = info->mode;
+
+ if(iw_get_ext(skfd, ifname, SIOCSIWMODE, &wrq) < 0)
+ {
+ fprintf(stderr, "SIOCSIWMODE: %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;
+
+ /* 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",
+ errno, strerror(errno));
+ ret = -1;
+ }
+ }
+
+ /* 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 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)
+ {
+ int we_kernel_version;
+ we_kernel_version = iw_get_kernel_we_version();
+
+ wrq.u.essid.pointer = (caddr_t) info->essid;
+ wrq.u.essid.length = strlen(info->essid);
+ wrq.u.data.flags = info->essid_on;
+ if(we_kernel_version < 21)
+ wrq.u.essid.length++;
+
+ if(iw_set_ext(skfd, ifname, SIOCSIWESSID, &wrq) < 0)
+ {
+ fprintf(stderr, "SIOCSIWESSID: %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, 0 otherwise
+ */
+int
+iw_protocol_compare(const char * protocol1,
+ const char * protocol2)
+{
+ 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))
+ return(1);
+
+ /* Are we dealing with one of the 802.11 variant ? */
+ if( (!strncmp(protocol1, dot11, strlen(dot11))) &&
+ (!strncmp(protocol2, dot11, strlen(dot11))) )
+ {
+ 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 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 */
+ return(0);
+}
+
+/************************ ESSID SUBROUTINES ************************/
+/*
+ * The ESSID identify 802.11 networks, and is an array if 32 bytes.
+ * Most people use it as an ASCII string, and are happy with it.
+ * However, any byte is valid, including the NUL character. Characters
+ * beyond the ASCII range are interpreted according to the locale and
+ * the OS, which is somethign we don't control (network of other
+ * people).
+ * Routines in here try to deal with that in asafe way.
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Escape non-ASCII characters from ESSID.
+ * This allow us to display those weirds characters to the user.
+ *
+ * Source is 32 bytes max.
+ * Destination buffer needs to be at least 129 bytes, will be NUL
+ * terminated.
+ */
+void
+iw_essid_escape(char * dest,
+ const char * src,
+ const int slen)
+{
+ const unsigned char * s = (const unsigned char *) src;
+ const unsigned char * e = s + slen;
+ char * d = dest;
+
+ /* Look every character of the string */
+ while(s < e)
+ {
+ int isescape;
+
+ /* Escape the escape to avoid ambiguity.
+ * We do a fast path test for performance reason. Compiler will
+ * optimise all that ;-) */
+ if(*s == '\\')
+ {
+ /* Check if we would confuse it with an escape sequence */
+ if((e-s) > 4 && (s[1] == 'x')
+ && (isxdigit(s[2])) && (isxdigit(s[3])))
+ {
+ isescape = 1;
+ }
+ else
+ isescape = 0;
+ }
+ else
+ isescape = 0;
+
+
+ /* Is it a non-ASCII character ??? */
+ if(isescape || !isascii(*s) || iscntrl(*s))
+ {
+ /* Escape */
+ sprintf(d, "\\x%02X", *s);
+ d += 4;
+ }
+ else
+ {
+ /* Plain ASCII, just copy */
+ *d = *s;
+ d++;
+ }
+ s++;
+ }
+
+ /* NUL terminate destination */
+ *d = '\0';
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Un-Escape non-ASCII characters from ESSID
+ * This allow the user to specify weird characters in ESSID.
+ *
+ * The source is a NUL terminated string.
+ * Destination buffer is at least the size of source (ESSID will shrink)
+ * Destination may contains NUL, therefore we return the length.
+ * This function still works is src and dest are the same ;-)
+ */
+int
+iw_essid_unescape(char * dest,
+ const char * src)
+{
+ const char * s = src;
+ char * d = dest;
+ char * p;
+ int len;
+
+ /* Look-up the next '\' sequence, stop when no more */
+ while((p = strchr(s, '\\')) != NULL)
+ {
+ /* Copy block of unescaped chars before the '\' */
+ len = p - s;
+ memcpy(d, s, len);
+ d += len;
+ s += len; /* Identical to 's = p' */
+
+ /* Check if it is really an escape sequence. We do also check for NUL */
+ if((s[1] == 'x') && (isxdigit(s[2])) && (isxdigit(s[3])))
+ {
+ unsigned int temp;
+ /* Valid Escape sequence, un-escape it */
+ sscanf(s + 2, "%2X", &temp);
+ *d = temp;
+ d++;
+ s+=4;
+ }
+ else
+ {
+ /* Not valid, don't un-escape it */
+ *d = *s;
+ d++;
+ s++;
+ }
+ }
+
+ /* Copy remaining of the string */
+ len = strlen(s);
+ memcpy(d, s, len + 1);
+ /* Return length */
+ return((d - dest) + len);
+}
+
+/********************** 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)
+{
+#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)
+ {
+ out->m = ((long) (floor(in / pow(10,out->e - 6)))) * 100;
+ out->e -= 8;
+ }
+ else
+ {
+ out->m = (long) in;
+ out->e = 0;
+ }
+#endif /* WE_NOLIBM */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Convert our internal representation of frequencies to a floating point.
+ */
+double
+iw_freq2float(const 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 */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Output a frequency with proper scaling
+ */
+void
+iw_print_freq_value(char * buffer,
+ int buflen,
+ double freq)
+{
+ if(freq < KILO)
+ snprintf(buffer, buflen, "%g", freq);
+ else
+ {
+ char scale;
+ int divisor;
+
+ if(freq >= GIGA)
+ {
+ scale = 'G';
+ divisor = GIGA;
+ }
+ else
+ {
+ if(freq >= MEGA)
+ {
+ scale = 'M';
+ divisor = MEGA;
+ }
+ else
+ {
+ 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);
+ }
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Convert a frequency to a channel (negative -> error)
+ */
+int
+iw_freq_to_channel(double freq,
+ const 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);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * 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 ***********************/
+
+/*------------------------------------------------------------------*/
+/*
+ * Output a bitrate with proper scaling
+ */
+void
+iw_print_bitrate(char * buffer,
+ int buflen,
+ int bitrate)
+{
+ double rate = bitrate;
+ char scale;
+ int divisor;
+
+ if(rate >= GIGA)
+ {
+ scale = 'G';
+ divisor = GIGA;
+ }
+ else
+ {
+ if(rate >= MEGA)
+ {
+ scale = 'M';
+ divisor = MEGA;
+ }
+ else
+ {
+ scale = 'k';
+ divisor = KILO;
+ }
+ }
+ snprintf(buffer, buflen, "%g %cb/s", rate / divisor, scale);
+}
+
+/************************ POWER SUBROUTINES *************************/
+
+/*------------------------------------------------------------------*/
+/*
+ * Convert a value in dBm to a value in milliWatt.
+ */
+int
+iw_dbm2mwatt(int in)
+{
+#ifdef WE_NOLIBM
+ /* Version without libm : slower */
+ int ip = in / 10;
+ int fp = in % 10;
+ int k;
+ double res = 1.0;
+
+ /* Split integral and floating part to avoid accumulating rounding errors */
+ for(k = 0; k < ip; k++)
+ res *= 10;
+ for(k = 0; k < fp; k++)
+ res *= LOG10_MAGIC;
+ return((int) res);
+#else /* WE_NOLIBM */
+ /* Version with libm : faster */
+ return((int) (floor(pow(10.0, (((double) in) / 10.0)))));
+#endif /* WE_NOLIBM */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Convert a value in milliWatt to a value in dBm.
+ */
+int
+iw_mwatt2dbm(int in)
+{
+#ifdef WE_NOLIBM
+ /* Version without libm : slower */
+ double fin = (double) in;
+ int res = 0;
+
+ /* Split integral and floating part to avoid accumulating rounding errors */
+ while(fin > 10.0)
+ {
+ res += 10;
+ fin /= 10.0;
+ }
+ while(fin > 1.000001) /* Eliminate rounding errors, take ceil */
+ {
+ res += 1;
+ fin /= LOG10_MAGIC;
+ }
+ return(res);
+#else /* WE_NOLIBM */
+ /* Version with libm : faster */
+ return((int) (ceil(10.0 * log10((double) 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 **********************/
+
+/*------------------------------------------------------------------*/
+/*
+ * Read /proc/net/wireless to get the latest statistics
+ * Note : strtok not thread safe, not used in WE-12 and later.
+ */
+int
+iw_get_stats(int skfd,
+ const char * ifname,
+ iwstats * stats,
+ const iwrange * range,
+ int has_range)
+{
+ /* 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);
+
+ /* 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;
+ }
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Output the link statistics, taking care of formating
+ */
+void
+iw_print_stats(char * buffer,
+ 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 between -192 and 63.
+ * (Note that up to version 28 of Wireless Tools, dBm used to be
+ * encoded always negative, between -256 and -1).
+ *
+ * How do we separate relative from absolute values ?
+ * The old way is to use the range to do that. As of WE-19, we have
+ * an explicit IW_QUAL_DBM flag in updated...
+ * 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 ; 63].
+ *
+ * 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...
+ * The old way to detect dBm require both the range and a non-null
+ * level (which confuse the test). The new way can deal with level of 0
+ * because it does an explicit test on the flag. */
+ if(has_range && ((qual->level != 0)
+ || (qual->updated & (IW_QUAL_DBM | IW_QUAL_RCPI))))
+ {
+ /* 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;
+ }
+
+ /* Check if the statistics are in RCPI (IEEE 802.11k) */
+ if(qual->updated & IW_QUAL_RCPI)
+ {
+ /* Deal with signal level in RCPI */
+ /* RCPI = int{(Power in dBm +110)*2} for 0dbm > Power > -110dBm */
+ if(!(qual->updated & IW_QUAL_LEVEL_INVALID))
+ {
+ double rcpilevel = (qual->level / 2.0) - 110.0;
+ len = snprintf(buffer, buflen, "Signal level%c%g dBm ",
+ qual->updated & IW_QUAL_LEVEL_UPDATED ? '=' : ':',
+ rcpilevel);
+ buffer += len;
+ buflen -= len;
+ }
+
+ /* Deal with noise level in dBm (absolute power measurement) */
+ if(!(qual->updated & IW_QUAL_NOISE_INVALID))
+ {
+ double rcpinoise = (qual->noise / 2.0) - 110.0;
+ len = snprintf(buffer, buflen, "Noise level%c%g dBm",
+ qual->updated & IW_QUAL_NOISE_UPDATED ? '=' : ':',
+ rcpinoise);
+ }
+ }
+ else
+ {
+ /* Check if the statistics are in dBm */
+ if((qual->updated & IW_QUAL_DBM)
+ || (qual->level > range->max_qual.level))
+ {
+ /* Deal with signal level in dBm (absolute power measurement) */
+ if(!(qual->updated & IW_QUAL_LEVEL_INVALID))
+ {
+ int dblevel = qual->level;
+ /* Implement a range for dBm [-192; 63] */
+ if(qual->level >= 64)
+ dblevel -= 0x100;
+ len = snprintf(buffer, buflen, "Signal level%c%d dBm ",
+ qual->updated & IW_QUAL_LEVEL_UPDATED ? '=' : ':',
+ dblevel);
+ buffer += len;
+ buflen -= len;
+ }
+
+ /* Deal with noise level in dBm (absolute power measurement) */
+ if(!(qual->updated & IW_QUAL_NOISE_INVALID))
+ {
+ int dbnoise = qual->noise;
+ /* Implement a range for dBm [-192; 63] */
+ if(qual->noise >= 64)
+ dbnoise -= 0x100;
+ len = snprintf(buffer, buflen, "Noise level%c%d dBm",
+ qual->updated & IW_QUAL_NOISE_UPDATED ? '=' : ':',
+ dbnoise);
+ }
+ }
+ else
+ {
+ /* 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... */
+ snprintf(buffer, buflen,
+ "Quality:%d Signal level:%d Noise level:%d",
+ qual->qual, qual->level, qual->noise);
+ }
+}
+
+/*********************** ENCODING SUBROUTINES ***********************/
+
+/*------------------------------------------------------------------*/
+/*
+ * Output the encoding key, with a nice formating
+ */
+void
+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, "<too big>");
+ return;
+ }
+
+ /* Is the key present ??? */
+ if(key_flags & IW_ENCODE_NOKEY)
+ {
+ /* Nope : print on or dummy */
+ if(key_size <= 0)
+ strcpy(buffer, "on"); /* Size checked */
+ else
+ {
+ strcpy(buffer, "**"); /* Size checked */
+ buffer +=2;
+ for(i = 1; i < key_size; i++)
+ {
+ if((i & 0x1) == 0)
+ strcpy(buffer++, "-"); /* Size checked */
+ strcpy(buffer, "**"); /* Size checked */
+ buffer +=2;
+ }
+ }
+ }
+ else
+ {
+ /* Yes : print the key */
+ sprintf(buffer, "%.2X", key[0]); /* Size checked */
+ buffer +=2;
+ for(i = 1; i < key_size; i++)
+ {
+ if((i & 0x1) == 0)
+ strcpy(buffer++, "-"); /* Size checked */
+ sprintf(buffer, "%.2X", key[i]); /* Size checked */
+ buffer +=2;
+ }
+ }
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Convert a passphrase into a key
+ * ### NOT IMPLEMENTED ###
+ * Return size of the key, or 0 (no key) or -1 (error)
+ */
+static int
+iw_pass_key(const 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)
+ * If the key is too long, it's simply truncated...
+ */
+int
+iw_in_key(const char * input,
+ unsigned char * key)
+{
+ int keylen = 0;
+
+ /* 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;
+ memcpy(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
+ {
+ const char * p;
+ int dlen; /* Digits sequence length */
+ unsigned char out[IW_ENCODING_TOKEN_MAX];
+
+ /* Third case : as hexadecimal digits */
+ p = input;
+ dlen = -1;
+
+ /* Loop until we run out of chars in input or overflow the output */
+ while(*p != '\0')
+ {
+ int temph;
+ int templ;
+ int count;
+ /* No more chars in this sequence */
+ if(dlen <= 0)
+ {
+ /* Skip separator */
+ if(dlen == 0)
+ p++;
+ /* Calculate num of char to next separator */
+ dlen = strcspn(p, "-:;.,");
+ }
+ /* Get each char separatly (and not by two) so that we don't
+ * get confused by 'enc' (=> '0E'+'0C') and similar */
+ count = sscanf(p, "%1X%1X", &temph, &templ);
+ if(count < 1)
+ return(-1); /* Error -> non-hex char */
+ /* Fixup odd strings such as '123' is '01'+'23' and not '12'+'03'*/
+ if(dlen % 2)
+ count = 1;
+ /* Put back two chars as one byte and output */
+ if(count == 2)
+ templ |= temph << 4;
+ else
+ templ = temph;
+ out[keylen++] = (unsigned char) (templ & 0xFF);
+ /* Check overflow in output */
+ if(keylen >= IW_ENCODING_TOKEN_MAX)
+ break;
+ /* Move on to next chars */
+ p += count;
+ dlen -= count;
+ }
+ /* We use a temporary output buffer 'out' so that if there is
+ * an error, we don't overwrite the original key buffer.
+ * Because of the way iwconfig loop on multiple key/enc arguments
+ * until it finds an error in here, this is necessary to avoid
+ * silently corrupting the encryption key... */
+ memcpy(key, out, keylen);
+ }
+
+#ifdef DEBUG
+ {
+ char buf[IW_ENCODING_TOKEN_MAX * 3];
+ iw_print_key(buf, sizeof(buf), key, keylen, 0);
+ printf("Got key : %d [%s]\n", keylen, buf);
+ }
+#endif
+
+ return(keylen);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Parse a key from the command line.
+ * Return size of the key, or 0 (no key) or -1 (error)
+ */
+int
+iw_in_key_full(int skfd,
+ const char * ifname,
+ const char * input,
+ unsigned char * key,
+ __u16 * flags)
+{
+ int keylen = 0;
+ char * p;
+
+ if(!strncmp(input, "l:", 2))
+ {
+ struct iw_range range;
+
+ /* Extra case : as a login (user:passwd - Cisco LEAP) */
+ keylen = strlen(input + 2) + 1; /* skip "l:", add '\0' */
+ /* Most user/password is 8 char, so 18 char total, < 32 */
+ if(keylen > IW_ENCODING_TOKEN_MAX)
+ keylen = IW_ENCODING_TOKEN_MAX;
+ memcpy(key, input + 2, keylen);
+
+ /* Separate the two strings */
+ p = strchr((char *) key, ':');
+ if(p == NULL)
+ {
+ fprintf(stderr, "Error: Invalid login format\n");
+ return(-1);
+ }
+ *p = '\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)
+ {
+
+ 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);
+ }
+ }
+ else
+ /* Simpler routine above */
+ keylen = iw_in_key(input, key);
+
+ return(keylen);
+}
+
+/******************* POWER MANAGEMENT SUBROUTINES *******************/
+
+/*------------------------------------------------------------------*/
+/*
+ * Output a power management value with all attributes...
+ */
+void
+iw_print_pm_value(char * buffer,
+ int buflen,
+ int value,
+ int flags,
+ int we_version)
+{
+ /* Check size */
+ if(buflen < 25)
+ {
+ snprintf(buffer, buflen, "<too big>");
+ return;
+ }
+ buflen -= 25;
+
+ /* Modifiers */
+ if(flags & IW_POWER_MIN)
+ {
+ strcpy(buffer, " min"); /* Size checked */
+ buffer += 4;
+ }
+ if(flags & IW_POWER_MAX)
+ {
+ strcpy(buffer, " max"); /* Size checked */
+ buffer += 4;
+ }
+
+ /* Type */
+ if(flags & IW_POWER_TIMEOUT)
+ {
+ strcpy(buffer, " timeout:"); /* Size checked */
+ buffer += 9;
+ }
+ else
+ {
+ if(flags & IW_POWER_SAVING)
+ {
+ strcpy(buffer, " saving:"); /* Size checked */
+ buffer += 8;
+ }
+ else
+ {
+ strcpy(buffer, " period:"); /* Size checked */
+ buffer += 8;
+ }
+ }
+
+ /* Display value without units */
+ if(flags & IW_POWER_RELATIVE)
+ {
+ if(we_version < 21)
+ value /= MEGA;
+ snprintf(buffer, buflen, "%d", value);
+ }
+ else
+ {
+ /* Display value with units */
+ if(value >= (int) MEGA)
+ snprintf(buffer, buflen, "%gs", ((double) value) / MEGA);
+ else
+ if(value >= (int) KILO)
+ snprintf(buffer, buflen, "%gms", ((double) value) / KILO);
+ else
+ snprintf(buffer, buflen, "%dus", value);
+ }
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Output a power management mode
+ */
+void
+iw_print_pm_mode(char * buffer,
+ int buflen,
+ int flags)
+{
+ /* Check size */
+ if(buflen < 28)
+ {
+ snprintf(buffer, buflen, "<too big>");
+ return;
+ }
+
+ /* Print the proper mode... */
+ switch(flags & IW_POWER_MODE)
+ {
+ case IW_POWER_UNICAST_R:
+ strcpy(buffer, "mode:Unicast only received"); /* Size checked */
+ break;
+ case IW_POWER_MULTICAST_R:
+ strcpy(buffer, "mode:Multicast only received"); /* Size checked */
+ break;
+ case IW_POWER_ALL_R:
+ strcpy(buffer, "mode:All packets received"); /* Size checked */
+ break;
+ case IW_POWER_FORCE_S:
+ strcpy(buffer, "mode:Force sending"); /* Size checked */
+ break;
+ case IW_POWER_REPEATER:
+ strcpy(buffer, "mode:Repeat multicasts"); /* Size checked */
+ break;
+ default:
+ strcpy(buffer, ""); /* Size checked */
+ break;
+ }
+}
+
+/***************** RETRY LIMIT/LIFETIME SUBROUTINES *****************/
+
+/*------------------------------------------------------------------*/
+/*
+ * Output a retry value with all attributes...
+ */
+void
+iw_print_retry_value(char * buffer,
+ int buflen,
+ int value,
+ int flags,
+ int we_version)
+{
+ /* Check buffer size */
+ if(buflen < 20)
+ {
+ snprintf(buffer, buflen, "<too big>");
+ return;
+ }
+ buflen -= 20;
+
+ /* Modifiers */
+ if(flags & IW_RETRY_MIN)
+ {
+ strcpy(buffer, " min"); /* Size checked */
+ buffer += 4;
+ }
+ if(flags & IW_RETRY_MAX)
+ {
+ strcpy(buffer, " max"); /* Size checked */
+ buffer += 4;
+ }
+ if(flags & IW_RETRY_SHORT)
+ {
+ strcpy(buffer, " short"); /* Size checked */
+ buffer += 6;
+ }
+ if(flags & IW_RETRY_LONG)
+ {
+ strcpy(buffer, " long"); /* Size checked */
+ buffer += 6;
+ }
+
+ /* Type lifetime of limit */
+ if(flags & IW_RETRY_LIFETIME)
+ {
+ strcpy(buffer, " lifetime:"); /* Size checked */
+ buffer += 10;
+
+ /* Display value without units */
+ if(flags & IW_RETRY_RELATIVE)
+ {
+ if(we_version < 21)
+ value /= MEGA;
+ snprintf(buffer, buflen, "%d", value);
+ }
+ else
+ {
+ /* Display value with units */
+ if(value >= (int) MEGA)
+ snprintf(buffer, buflen, "%gs", ((double) value) / MEGA);
+ else
+ if(value >= (int) KILO)
+ snprintf(buffer, buflen, "%gms", ((double) value) / KILO);
+ else
+ snprintf(buffer, buflen, "%dus", value);
+ }
+ }
+ else
+ snprintf(buffer, buflen, " limit:%d", value);
+}
+
+/************************* TIME SUBROUTINES *************************/
+
+/*------------------------------------------------------------------*/
+/*
+ * Print timestamps
+ * Inspired from irdadump...
+ */
+void
+iw_print_timeval(char * buffer,
+ int buflen,
+ const struct timeval * timev,
+ const struct timezone * tz)
+{
+ int s;
+
+ s = (timev->tv_sec - tz->tz_minuteswest * 60) % 86400;
+ snprintf(buffer, buflen, "%02d:%02d:%02d.%06u",
+ s / 3600, (s % 3600) / 60,
+ s % 60, (u_int32_t) timev->tv_usec);
+}
+
+/*********************** 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...
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Check if interface support the right MAC address type...
+ */
+int
+iw_check_mac_addr_type(int skfd,
+ const char * ifname)
+{
+ struct ifreq ifr;
+
+ /* Get the type of hardware address */
+ strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
+ if((ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0) ||
+ ((ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER)
+ && (ifr.ifr_hwaddr.sa_family != ARPHRD_IEEE80211)))
+ {
+ /* Deep trouble... */
+ fprintf(stderr, "Interface %s doesn't support MAC addresses\n",
+ ifname);
+ return(-1);
+ }
+
+#ifdef DEBUG
+ {
+ char buf[20];
+ printf("Hardware : %d - %s\n", ifr.ifr_hwaddr.sa_family,
+ iw_saether_ntop(&ifr.ifr_hwaddr, buf));
+ }
+#endif
+
+ return(0);
+}
+
+
+/*------------------------------------------------------------------*/
+/*
+ * Check if interface support the right interface address type...
+ */
+int
+iw_check_if_addr_type(int skfd,
+ const char * ifname)
+{
+ struct ifreq ifr;
+
+ /* Get the type of interface address */
+ strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
+ 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);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Display an arbitrary length MAC address in readable format.
+ */
+char *
+iw_mac_ntop(const unsigned char * mac,
+ int maclen,
+ char * buf,
+ int buflen)
+{
+ int i;
+
+ /* Overflow check (don't forget '\0') */
+ if(buflen < (maclen * 3 - 1 + 1))
+ return(NULL);
+
+ /* First byte */
+ sprintf(buf, "%02X", mac[0]);
+
+ /* Other bytes */
+ for(i = 1; i < maclen; i++)
+ sprintf(buf + (i * 3) - 1, ":%02X", mac[i]);
+ return(buf);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * 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 Wireless Access Point Socket Address in readable format.
+ * Note : 0x44 is an accident of history, that's what the Orinoco/PrismII
+ * chipset report, and the driver doesn't filter it.
+ */
+char *
+iw_sawap_ntop(const struct sockaddr * sap,
+ char * buf)
+{
+ const struct ether_addr ether_zero = {{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }};
+ const struct ether_addr ether_bcast = {{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }};
+ const struct ether_addr ether_hack = {{ 0x44, 0x44, 0x44, 0x44, 0x44, 0x44 }};
+ const struct ether_addr * ether_wap = (const struct ether_addr *) sap->sa_data;
+
+ if(!iw_ether_cmp(ether_wap, ðer_zero))
+ sprintf(buf, "Not-Associated");
+ else
+ if(!iw_ether_cmp(ether_wap, ðer_bcast))
+ sprintf(buf, "Invalid");
+ else
+ if(!iw_ether_cmp(ether_wap, ðer_hack))
+ sprintf(buf, "None");
+ else
+ iw_ether_ntop(ether_wap, buf);
+ return(buf);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Input an arbitrary length MAC address and convert to binary.
+ * Return address size.
+ */
+int
+iw_mac_aton(const char * orig,
+ unsigned char * mac,
+ int macmax)
+{
+ const char * p = orig;
+ int maclen = 0;
+
+ /* Loop on all bytes of the string */
+ while(*p != '\0')
+ {
+ int temph;
+ int templ;
+ int count;
+ /* Extract one byte as two chars */
+ count = sscanf(p, "%1X%1X", &temph, &templ);
+ if(count != 2)
+ break; /* Error -> non-hex chars */
+ /* Output two chars as one byte */
+ templ |= temph << 4;
+ mac[maclen++] = (unsigned char) (templ & 0xFF);
+
+ /* Check end of string */
+ p += 2;
+ if(*p == '\0')
+ {
+#ifdef DEBUG
+ char buf[20];
+ iw_ether_ntop((const struct ether_addr *) mac, buf);
+ fprintf(stderr, "iw_mac_aton(%s): %s\n", orig, buf);
+#endif
+ return(maclen); /* Normal exit */
+ }
+
+ /* Check overflow */
+ if(maclen >= macmax)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "iw_mac_aton(%s): trailing junk!\n", orig);
+#endif
+ errno = E2BIG;
+ return(0); /* Error -> overflow */
+ }
+
+ /* Check separator */
+ if(*p != ':')
+ break;
+ p++;
+ }
+
+ /* Error... */
+#ifdef DEBUG
+ fprintf(stderr, "iw_mac_aton(%s): invalid ether address!\n", orig);
+#endif
+ errno = EINVAL;
+ return(0);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Input an Ethernet address and convert to binary.
+ */
+int
+iw_ether_aton(const char *orig, struct ether_addr *eth)
+{
+ int maclen;
+ maclen = iw_mac_aton(orig, (unsigned char *) eth, ETH_ALEN);
+ if((maclen > 0) && (maclen < ETH_ALEN))
+ {
+ errno = EINVAL;
+ maclen = 0;
+ }
+ return(maclen);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * 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 *sain = (struct sockaddr_in *) sap;
+
+ /* Grmpf. -FvK */
+ sain->sin_family = AF_INET;
+ sain->sin_port = 0;
+
+ /* Default is special, meaning 0.0.0.0. */
+ if (!strcmp(name, "default")) {
+ 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) {
+ sain->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 *) &sain->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,
+ const char * ifname,
+ char * bufp,
+ struct sockaddr *sap)
+{
+ /* Check if it is a hardware or IP address */
+ if(strchr(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.16s 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);
+ 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
+ {
+ char buf[20];
+ printf("IP Address %s => Hw Address = %s\n",
+ bufp, iw_saether_ntop(sap, buf));
+ }
+#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.16s Interface doesn't support MAC addresses\n", ifname);
+ return(-1);
+ }
+
+ /* Get the hardware address */
+ if(iw_saether_aton(bufp, sap) == 0)
+ {
+ fprintf(stderr, "Invalid hardware address %s\n", bufp);
+ return(-1);
+ }
+ }
+
+#ifdef DEBUG
+ {
+ char buf[20];
+ printf("Hw Address = %s\n", iw_saether_ntop(sap, buf));
+ }
+#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.
+ */
+
+/* -------------------------- CONSTANTS -------------------------- */
+
+/* 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 */
+
+/* Handling flags */
+/* Most are not implemented. I just use them as a reminder of some
+ * cool features we might need one day ;-) */
+#define IW_DESCR_FLAG_NONE 0x0000 /* Obvious */
+/* Wrapper level flags */
+#define IW_DESCR_FLAG_DUMP 0x0001 /* Not part of the dump command */
+#define IW_DESCR_FLAG_EVENT 0x0002 /* Generate an event on SET */
+#define IW_DESCR_FLAG_RESTRICT 0x0004 /* GET : request is ROOT only */
+ /* SET : Omit payload from generated iwevent */
+#define IW_DESCR_FLAG_NOMAX 0x0008 /* GET : no limit on request size */
+/* Driver level flags */
+#define IW_DESCR_FLAG_WAIT 0x0100 /* Wait for driver event */
+
+/* ---------------------------- TYPES ---------------------------- */
+
+/*
+ * Describe how a standard IOCTL looks like.
+ */
+struct iw_ioctl_description
+{
+ __u8 header_type; /* NULL, iw_point or other */
+ __u8 token_type; /* Future */
+ __u16 token_size; /* Granularity of payload */
+ __u16 min_tokens; /* Min acceptable token number */
+ __u16 max_tokens; /* Max acceptable token number */
+ __u32 flags; /* Special handling of the request */
+};
+
+/* -------------------------- VARIABLES -------------------------- */
+
+/*
+ * Meta-data about all the standard Wireless Extension request we
+ * know about.
+ */
+static const struct iw_ioctl_description standard_ioctl_descr[] = {
+ [SIOCSIWCOMMIT - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_NULL,
+ },
+ [SIOCGIWNAME - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_CHAR,
+ .flags = IW_DESCR_FLAG_DUMP,
+ },
+ [SIOCSIWNWID - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ .flags = IW_DESCR_FLAG_EVENT,
+ },
+ [SIOCGIWNWID - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ .flags = IW_DESCR_FLAG_DUMP,
+ },
+ [SIOCSIWFREQ - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_FREQ,
+ .flags = IW_DESCR_FLAG_EVENT,
+ },
+ [SIOCGIWFREQ - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_FREQ,
+ .flags = IW_DESCR_FLAG_DUMP,
+ },
+ [SIOCSIWMODE - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_UINT,
+ .flags = IW_DESCR_FLAG_EVENT,
+ },
+ [SIOCGIWMODE - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_UINT,
+ .flags = IW_DESCR_FLAG_DUMP,
+ },
+ [SIOCSIWSENS - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [SIOCGIWSENS - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [SIOCSIWRANGE - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_NULL,
+ },
+ [SIOCGIWRANGE - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = sizeof(struct iw_range),
+ .flags = IW_DESCR_FLAG_DUMP,
+ },
+ [SIOCSIWPRIV - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_NULL,
+ },
+ [SIOCGIWPRIV - SIOCIWFIRST] = { /* (handled directly by us) */
+ .header_type = IW_HEADER_TYPE_NULL,
+ },
+ [SIOCSIWSTATS - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_NULL,
+ },
+ [SIOCGIWSTATS - SIOCIWFIRST] = { /* (handled directly by us) */
+ .header_type = IW_HEADER_TYPE_NULL,
+ .flags = IW_DESCR_FLAG_DUMP,
+ },
+ [SIOCSIWSPY - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = sizeof(struct sockaddr),
+ .max_tokens = IW_MAX_SPY,
+ },
+ [SIOCGIWSPY - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = sizeof(struct sockaddr) +
+ sizeof(struct iw_quality),
+ .max_tokens = IW_MAX_SPY,
+ },
+ [SIOCSIWTHRSPY - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = sizeof(struct iw_thrspy),
+ .min_tokens = 1,
+ .max_tokens = 1,
+ },
+ [SIOCGIWTHRSPY - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = sizeof(struct iw_thrspy),
+ .min_tokens = 1,
+ .max_tokens = 1,
+ },
+ [SIOCSIWAP - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_ADDR,
+ },
+ [SIOCGIWAP - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_ADDR,
+ .flags = IW_DESCR_FLAG_DUMP,
+ },
+ [SIOCSIWMLME - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .min_tokens = sizeof(struct iw_mlme),
+ .max_tokens = sizeof(struct iw_mlme),
+ },
+ [SIOCGIWAPLIST - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = sizeof(struct sockaddr) +
+ sizeof(struct iw_quality),
+ .max_tokens = IW_MAX_AP,
+ .flags = IW_DESCR_FLAG_NOMAX,
+ },
+ [SIOCSIWSCAN - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .min_tokens = 0,
+ .max_tokens = sizeof(struct iw_scan_req),
+ },
+ [SIOCGIWSCAN - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_SCAN_MAX_DATA,
+ .flags = IW_DESCR_FLAG_NOMAX,
+ },
+ [SIOCSIWESSID - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_ESSID_MAX_SIZE + 1,
+ .flags = IW_DESCR_FLAG_EVENT,
+ },
+ [SIOCGIWESSID - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_ESSID_MAX_SIZE + 1,
+ .flags = IW_DESCR_FLAG_DUMP,
+ },
+ [SIOCSIWNICKN - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_ESSID_MAX_SIZE + 1,
+ },
+ [SIOCGIWNICKN - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_ESSID_MAX_SIZE + 1,
+ },
+ [SIOCSIWRATE - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [SIOCGIWRATE - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [SIOCSIWRTS - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [SIOCGIWRTS - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [SIOCSIWFRAG - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [SIOCGIWFRAG - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [SIOCSIWTXPOW - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [SIOCGIWTXPOW - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [SIOCSIWRETRY - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [SIOCGIWRETRY - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [SIOCSIWENCODE - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ /* Hack : this never returns any payload in event.
+ * Fix the 64->32 bit hack... */
+ .token_size = 0,
+ .max_tokens = IW_ENCODING_TOKEN_MAX,
+ .flags = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT,
+ },
+ [SIOCGIWENCODE - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ /* Hack : this never returns any payload in event.
+ * Fix the 64->32 bit hack... */
+ .token_size = 0,
+ .max_tokens = IW_ENCODING_TOKEN_MAX,
+ .flags = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT,
+ },
+ [SIOCSIWPOWER - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [SIOCGIWPOWER - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [SIOCSIWMODUL - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [SIOCGIWMODUL - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [SIOCSIWGENIE - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_GENERIC_IE_MAX,
+ },
+ [SIOCGIWGENIE - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_GENERIC_IE_MAX,
+ },
+ [SIOCSIWAUTH - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [SIOCGIWAUTH - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_PARAM,
+ },
+ [SIOCSIWENCODEEXT - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .min_tokens = sizeof(struct iw_encode_ext),
+ .max_tokens = sizeof(struct iw_encode_ext) +
+ IW_ENCODING_TOKEN_MAX,
+ },
+ [SIOCGIWENCODEEXT - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .min_tokens = sizeof(struct iw_encode_ext),
+ .max_tokens = sizeof(struct iw_encode_ext) +
+ IW_ENCODING_TOKEN_MAX,
+ },
+ [SIOCSIWPMKSA - SIOCIWFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .min_tokens = sizeof(struct iw_pmksa),
+ .max_tokens = sizeof(struct iw_pmksa),
+ },
+};
+static const unsigned int standard_ioctl_num = (sizeof(standard_ioctl_descr) /
+ sizeof(struct iw_ioctl_description));
+
+/*
+ * Meta-data about all the additional standard Wireless Extension events
+ * we know about.
+ */
+static const struct iw_ioctl_description standard_event_descr[] = {
+ [IWEVTXDROP - IWEVFIRST] = {
+ .header_type = IW_HEADER_TYPE_ADDR,
+ },
+ [IWEVQUAL - IWEVFIRST] = {
+ .header_type = IW_HEADER_TYPE_QUAL,
+ },
+ [IWEVCUSTOM - IWEVFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_CUSTOM_MAX,
+ },
+ [IWEVREGISTERED - IWEVFIRST] = {
+ .header_type = IW_HEADER_TYPE_ADDR,
+ },
+ [IWEVEXPIRED - IWEVFIRST] = {
+ .header_type = IW_HEADER_TYPE_ADDR,
+ },
+ [IWEVGENIE - IWEVFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_GENERIC_IE_MAX,
+ },
+ [IWEVMICHAELMICFAILURE - IWEVFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = sizeof(struct iw_michaelmicfailure),
+ },
+ [IWEVASSOCREQIE - IWEVFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_GENERIC_IE_MAX,
+ },
+ [IWEVASSOCRESPIE - IWEVFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = IW_GENERIC_IE_MAX,
+ },
+ [IWEVPMKIDCAND - IWEVFIRST] = {
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = sizeof(struct iw_pmkid_cand),
+ },
+};
+static const unsigned int standard_event_num = (sizeof(standard_event_descr) /
+ sizeof(struct iw_ioctl_description));
+
+/* Size (in bytes) of various events */
+static const int event_type_size[] = {
+ IW_EV_LCP_PK_LEN, /* IW_HEADER_TYPE_NULL */
+ 0,
+ IW_EV_CHAR_PK_LEN, /* IW_HEADER_TYPE_CHAR */
+ 0,
+ IW_EV_UINT_PK_LEN, /* IW_HEADER_TYPE_UINT */
+ IW_EV_FREQ_PK_LEN, /* IW_HEADER_TYPE_FREQ */
+ IW_EV_ADDR_PK_LEN, /* IW_HEADER_TYPE_ADDR */
+ 0,
+ IW_EV_POINT_PK_LEN, /* Without variable payload */
+ IW_EV_PARAM_PK_LEN, /* IW_HEADER_TYPE_PARAM */
+ IW_EV_QUAL_PK_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 we_version)
+{
+ const struct iw_ioctl_description * descr = NULL;
+ 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_PK_LEN) > stream->end)
+ return(0);
+
+#ifdef DEBUG
+ 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_PK_LEN);
+
+#ifdef DEBUG
+ printf("DBG - iwe->cmd = 0x%X, iwe->len = %d\n",
+ iwe->cmd, iwe->len);
+#endif
+
+ /* Check invalid events */
+ if(iwe->len <= IW_EV_LCP_PK_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)
+ descr = &(standard_ioctl_descr[cmd_index]);
+ }
+ else
+ {
+ cmd_index = iwe->cmd - IWEVFIRST;
+ if(cmd_index < standard_event_num)
+ descr = &(standard_event_descr[cmd_index]);
+ }
+ if(descr != NULL)
+ event_type = descr->header_type;
+ /* Unknown events -> event_type=0 => IW_EV_LCP_PK_LEN */
+ event_len = event_type_size[event_type];
+ /* Fixup for earlier 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_PK_LEN)
+ {
+ /* Skip to next event */
+ stream->current += iwe->len;
+ return(2);
+ }
+ event_len -= IW_EV_LCP_PK_LEN;
+
+ /* Set pointer on data */
+ if(stream->value != NULL)
+ pointer = stream->value; /* Next value in event */
+ else
+ pointer = stream->current + IW_EV_LCP_PK_LEN; /* First value in event */
+
+#ifdef DEBUG
+ 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);
+ }
+ /* Fixup for WE-19 and later : pointer no longer in the stream */
+ /* Beware of alignement. Dest has local alignement, not packed */
+ 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;
+
+ /* Special processing for iw_point events */
+ if(event_type == IW_HEADER_TYPE_POINT)
+ {
+ /* Check the length of the payload */
+ unsigned int extra_len = iwe->len - (event_len + IW_EV_LCP_PK_LEN);
+ if(extra_len > 0)
+ {
+ /* Set pointer on variable part (warning : non aligned) */
+ iwe->u.data.pointer = pointer;
+
+ /* Check that we have a descriptor for the command */
+ if(descr == NULL)
+ /* Can't check payload -> unsafe... */
+ iwe->u.data.pointer = NULL; /* Discard paylod */
+ else
+ {
+ /* Those checks are actually pretty hard to trigger,
+ * because of the checks done in the kernel... */
+
+ unsigned int token_len = iwe->u.data.length * descr->token_size;
+
+ /* Ugly fixup for alignement issues.
+ * If the kernel is 64 bits and userspace 32 bits,
+ * we have an extra 4+4 bytes.
+ * Fixing that in the kernel would break 64 bits userspace. */
+ if((token_len != extra_len) && (extra_len >= 4))
+ {
+ union iw_align_u16 alt_dlen;
+ unsigned int alt_token_len;
+ /* Usespace seems to not always like unaligned access,
+ * so be careful and make sure to align value.
+ * I hope gcc won't play any of its aliasing tricks... */
+ alt_dlen.byte[0] = *(pointer);
+ alt_dlen.byte[1] = *(pointer + 1);
+ alt_token_len = alt_dlen.value * descr->token_size;
+#ifdef DEBUG
+ printf("DBG - alt_token_len = %d\n", alt_token_len);
+#endif
+ /* Verify that data is consistent if assuming 64 bit
+ * alignement... */
+ if((alt_token_len + 8) == extra_len)
+ {
+ /* Ok, let's redo everything */
+ pointer -= event_len;
+ pointer += 4;
+ /* Dest has local alignement, not packed */
+ memcpy((char *) iwe + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
+ pointer, event_len);
+ pointer += event_len + 4;
+ token_len = alt_token_len;
+ /* We may have no payload */
+ if(alt_token_len)
+ iwe->u.data.pointer = pointer;
+ else
+ iwe->u.data.pointer = NULL;
+ }
+ }
+
+ /* Discard bogus events which advertise more tokens than
+ * what they carry... */
+ if(token_len > extra_len)
+ iwe->u.data.pointer = NULL; /* Discard paylod */
+ /* Check that the advertised token size is not going to
+ * produce buffer overflow to our caller... */
+ if((iwe->u.data.length > descr->max_tokens)
+ && !(descr->flags & IW_DESCR_FLAG_NOMAX))
+ iwe->u.data.pointer = NULL; /* Discard paylod */
+ /* Same for underflows... */
+ if(iwe->u.data.length < descr->min_tokens)
+ iwe->u.data.pointer = NULL; /* Discard paylod */
+#ifdef DEBUG
+ printf("DBG - extra_len = %d, token_len = %d, token = %d, max = %d, min = %d\n",
+ extra_len, token_len, iwe->u.data.length, descr->max_tokens, descr->min_tokens);
+#endif
+ }
+ }
+ else
+ /* No data */
+ iwe->u.data.pointer = NULL;
+
+ /* Go to next event */
+ stream->current += iwe->len;
+ }
+ else
+ {
+ /* Ugly fixup for alignement issues.
+ * If the kernel is 64 bits and userspace 32 bits,
+ * we have an extra 4 bytes.
+ * Fixing that in the kernel would break 64 bits userspace. */
+ if((stream->value == NULL)
+ && ((((iwe->len - IW_EV_LCP_PK_LEN) % event_len) == 4)
+ || ((iwe->len == 12) && ((event_type == IW_HEADER_TYPE_UINT) ||
+ (event_type == IW_HEADER_TYPE_QUAL))) ))
+ {
+#ifdef DEBUG
+ printf("DBG - alt iwe->len = %d\n", iwe->len - 4);
+#endif
+ pointer -= event_len;
+ pointer += 4;
+ /* Beware of alignement. Dest has local alignement, not packed */
+ memcpy((char *) iwe + IW_EV_LCP_LEN, pointer, event_len);
+ pointer += event_len;
+ }
+
+ /* 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);
+}
+
+/*********************** 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;
+ memset(wscan->b.essid, '\0', IW_ESSID_MAX_SIZE + 1);
+ if((event->u.essid.pointer) && (event->u.essid.length))
+ memcpy(wscan->b.essid, event->u.essid.pointer, event->u.essid.length);
+ 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(struct iw_quality));
+ break;
+ case SIOCGIWRATE:
+ /* Scan may return a list of bitrates. As we have space for only
+ * a single bitrate, we only keep the largest one. */
+ if((!wscan->has_maxbitrate) ||
+ (event->u.bitrate.value > wscan->maxbitrate.value))
+ {
+ wscan->has_maxbitrate = 1;
+ memcpy(&(wscan->maxbitrate), &(event->u.bitrate), sizeof(iwparam));
+ }
+ 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 (150 * 100 = 15s) */
+ context->retry++;
+ if(context->retry > 150)
+ {
+ 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;
+ /* Remember that as non-root, we will get an EPERM here */
+ if((iw_set_ext(skfd, ifname, SIOCSIWSCAN, &wrq) < 0)
+ && (errno != EPERM))
+ 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) && (buflen < 0xFFFF))
+ {
+ /* 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;
+
+ /* wrq.u.data.length is 16 bits so max size is 65535 */
+ if(buflen > 0xFFFF)
+ buflen = 0xFFFF;
+
+ /* 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;
+#ifdef DEBUG
+ /* 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, (char *) 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);
+}