+/*********************** 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);
+}