OSDN Git Service

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