From d7ca0c3e555ef0b5250873ddce48ccf2326b017a Mon Sep 17 00:00:00 2001 From: Jonathan McCrohan Date: Sat, 25 Jan 2014 00:07:30 +0000 Subject: Imported Upstream version 0.7.6 --- iw_scan.c | 264 ++++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 196 insertions(+), 68 deletions(-) (limited to 'iw_scan.c') diff --git a/iw_scan.c b/iw_scan.c index c701718..740bdff 100644 --- a/iw_scan.c +++ b/iw_scan.c @@ -519,7 +519,7 @@ static int iw_extract_event_stream(struct stream_descr *stream, return 1; } -static void iw_extract_ie(struct iw_event *iwe, struct scan_result *sr) +static void iw_extract_ie(struct iw_event *iwe, struct scan_entry *sr) { const uint8_t wpa1_oui[3] = { 0x00, 0x50, 0xf2 }; uint8_t *buffer = iwe->u.data.pointer; @@ -549,64 +549,77 @@ static void iw_extract_ie(struct iw_event *iwe, struct scan_result *sr) /*----------------- End of code copied from iwlib -----------------------*/ /* - * Ordering functions for scan results + * Ordering functions for scan results: all return true for a < b. */ -/* Order by ascending frequency. */ -static int cmp_chan(const struct scan_result *a, const struct scan_result *b) + +/* Order by frequency. */ +static bool cmp_freq(const struct scan_entry *a, const struct scan_entry *b) { return a->freq < b->freq; } -static int cmp_chan_rev(const struct scan_result *a, const struct scan_result *b) +/* Order by signal strength. */ +static bool cmp_sig(const struct scan_entry *a, const struct scan_entry *b) { - return cmp_chan(b, a); + return a->qual.level < b->qual.level; } -/* Order by descending signal strength. */ -static int cmp_sig(const struct scan_result *a, const struct scan_result *b) +/* Order by ESSID, organize entries with same ESSID by frequency and signal. */ +static bool cmp_essid(const struct scan_entry *a, const struct scan_entry *b) { - return a->qual.level - b->qual.level; + int res = strncmp(a->essid, b->essid, IW_ESSID_MAX_SIZE); + + return res == 0 ? (a->freq == b->freq ? cmp_sig(a, b) : cmp_freq(a, b)) + : res < 0; } -/* Order by ascending frequency first, then by descending signal strength. */ -static int cmp_chan_sig(const struct scan_result *a, const struct scan_result *b) +/* Order by frequency, grouping channels by ESSID. */ +static bool cmp_chan(const struct scan_entry *a, const struct scan_entry *b) { - return a->freq == b->freq ? cmp_sig(a, b) : cmp_chan(a, b); + return a->freq == b->freq ? cmp_essid(a, b) : cmp_freq(a, b); } -/* Show open access points first, ordered by mode. */ -static int cmp_open(const struct scan_result *a, const struct scan_result *b) +/* Order by frequency first, then by signal strength. */ +static bool cmp_chan_sig(const struct scan_entry *a, const struct scan_entry *b) { - return a->has_key > b->has_key; + return a->freq == b->freq ? cmp_sig(a, b) : cmp_chan(a, b); } -/* Sort (open) access points by descending signal strength. */ -static int cmp_open_sig(const struct scan_result *a, const struct scan_result *b) +/* Order by openness (open access points frist). */ +static bool cmp_open(const struct scan_entry *a, const struct scan_entry *b) { - return a->has_key == b->has_key ? cmp_sig(a, b) : cmp_open(a, b); + return a->has_key < b->has_key; } -/* Sort (open) access points by ascending frequency, then by descending signal. */ -static int cmp_open_chan_sig(const struct scan_result *a, const struct scan_result *b) +/* Sort (open) access points by signal strength. */ +static bool cmp_open_sig(const struct scan_entry *a, const struct scan_entry *b) { - return a->has_key == b->has_key ? cmp_chan_sig(a, b) : cmp_open(a, b); + return a->has_key == b->has_key ? cmp_sig(a, b) : cmp_open(a, b); } -static int (*scan_cmp[])(const struct scan_result *, const struct scan_result *) = { +static bool (*scan_cmp[])(const struct scan_entry *, const struct scan_entry *) = { [SO_CHAN] = cmp_chan, - [SO_CHAN_REV] = cmp_chan_rev, [SO_SIGNAL] = cmp_sig, + [SO_ESSID] = cmp_essid, [SO_OPEN] = cmp_open, [SO_CHAN_SIG] = cmp_chan_sig, - [SO_OPEN_SIG] = cmp_open_sig, - [SO_OPEN_CH_SI] = cmp_open_chan_sig + [SO_OPEN_SIG] = cmp_open_sig }; -struct scan_result *get_scan_list(int skfd, const char *ifname, int we_version) +/** + * Produce ranked list of scan results. + * @ifname: interface name to run scan on + * @we_version: version of the WE extensions (needed internally) + */ +static struct scan_entry *get_scan_list(const char *ifname, int we_version) { - struct scan_result *head = NULL; + struct scan_entry *head = NULL, **tailp = &head; struct iwreq wrq; int wait, waited = 0; + int skfd = socket(AF_INET, SOCK_DGRAM, 0); + + if (skfd < 0) + err_sys("%s: can not open socket", __func__); /* * Some drivers may return very large scan results, either because there * are many cells, or there are many large elements. Do not bother to @@ -620,7 +633,7 @@ struct scan_result *get_scan_list(int skfd, const char *ifname, int we_version) memset(&wrq, 0, sizeof(wrq)); strncpy(wrq.ifr_ifrn.ifrn_name, ifname, IFNAMSIZ); if (ioctl(skfd, SIOCSIWSCAN, &wrq) < 0) - return NULL; + goto done; /* Larger initial timeout of 250ms between set and first get */ for (wait = 250; (waited += wait) < MAX_SCAN_WAIT; wait = 100) { @@ -641,7 +654,7 @@ struct scan_result *get_scan_list(int skfd, const char *ifname, int we_version) if (wrq.u.data.length) { struct iw_event iwe; struct stream_descr stream; - struct scan_result *new = NULL; + struct scan_entry *new = NULL; int f = 0; /* Idea taken from waproamd */ memset(&stream, 0, sizeof(stream)); @@ -686,37 +699,53 @@ struct scan_result *get_scan_list(int skfd, const char *ifname, int we_version) break; } if (f == 127) { - struct scan_result *cur = head, **prev = &head; - - while (cur && scan_cmp[conf.scan_sort_order](cur, new) > 0) - prev = &cur->next, cur = cur->next; - - *prev = new; - new->next = cur; - new = NULL; - f = 0; + f = 0; + *tailp = new; + tailp = &new->next; + new = NULL; } } - free(new); /* may have been allocated but not filled in */ + free(new); /* may have been allocated, but not filled in */ } +done: + close(skfd); return head; } -void free_scan_result(struct scan_result *head) +/* + * Simple sort routine. + * FIXME: use hash or tree to store entries, a list to display them. + */ +void sort_scan_list(struct scan_entry **headp) +{ + struct scan_entry *head = NULL, *cur, *new = *headp, **prev; + + while (new) { + for (cur = head, prev = &head; cur && + conf.scan_sort_asc == scan_cmp[conf.scan_sort_order](cur, new); + prev = &cur->next, cur = cur->next) + ; + *prev = new; + new = new->next; + (*prev)->next = cur; + } + *headp = head; +} + +static void free_scan_list(struct scan_entry *head) { if (head) { - free_scan_result(head->next); + free_scan_list(head->next); free(head); } } /* - * Channel statistics + * Channel statistics shown at the bottom of scan screen. */ - /* - * For lfind, it compares key value with array member, needs to + * For lsearch, it compares key value with array member, needs to * return 0 if they are the same, non-0 otherwise. */ static int cmp_key(const void *a, const void *b) @@ -727,41 +756,140 @@ static int cmp_key(const void *a, const void *b) /* For quick-sorting the array in descending order of counts */ static int cmp_cnt(const void *a, const void *b) { - if (conf.scan_sort_order == SO_CHAN_REV) + if (conf.scan_sort_order == SO_CHAN && !conf.scan_sort_asc) return ((struct cnt *)a)->count - ((struct cnt *)b)->count; return ((struct cnt *)b)->count - ((struct cnt *)a)->count; } -struct cnt *channel_stats(struct scan_result *head, - struct iw_range *iw_range, int *max_cnt) +/** + * Fill in sr->channel_stats (must not have been allocated yet). + */ +static void compute_channel_stats(struct scan_result *sr) { - struct scan_result *cur; - struct cnt *arr = NULL, *bin, key = {0, 0}; - size_t cnt = 0, n = 0; - - for (cur = head; cur; cur = cur->next) - cnt++; - if (!cnt) - return NULL; - - arr = calloc(cnt, sizeof(key)); - for (cur = head; cur && n < *max_cnt; cur = cur->next) { - key.val = freq_to_channel(cur->freq, iw_range); - - if (key.val >= 0) { - bin = lsearch(&key, arr, &n, sizeof(key), cmp_key); + struct scan_entry *cur; + struct cnt *bin, key = {0, 0}; + size_t n = 0; + + if (!sr->num.entries) + return; + + sr->channel_stats = calloc(sr->num.entries, sizeof(key)); + for (cur = sr->head; cur; cur = cur->next) { + if (cur->chan >= 0) { + key.val = cur->chan; + bin = lsearch(&key, sr->channel_stats, &n, sizeof(key), cmp_key); if (bin) bin->count++; } } - if (n < *max_cnt) - *max_cnt = n; if (n > 0) { - qsort(arr, n, sizeof(key), cmp_cnt); + qsort(sr->channel_stats, n, sizeof(key), cmp_cnt); } else { - free(arr); - return NULL; + free(sr->channel_stats); + sr->channel_stats = NULL; } - return arr; + sr->num.ch_stats = n < MAX_CH_STATS ? n : MAX_CH_STATS; +} + +/* + * Scan results. + */ +void scan_result_init(struct scan_result *sr) +{ + memset(sr, 0, sizeof(*sr)); + iw_getinf_range(conf_ifname(), &sr->range); + pthread_mutex_init(&sr->mutex, NULL); +} + +void scan_result_fini(struct scan_result *sr) +{ + free_scan_list(sr->head); + free(sr->channel_stats); + pthread_mutex_destroy(&sr->mutex); +} + +/** The actual scan thread. */ +void *do_scan(void *sr_ptr) +{ + struct scan_result *sr = (struct scan_result *)sr_ptr; + struct scan_entry *cur; + + pthread_detach(pthread_self()); + + do { + pthread_mutex_lock(&sr->mutex); + + free_scan_list(sr->head); + free(sr->channel_stats); + + sr->head = NULL; + sr->channel_stats = NULL; + sr->msg[0] = '\0'; + sr->max_essid_len = MAX_ESSID_LEN; + memset(&(sr->num), 0, sizeof(sr->num)); + + sr->head = get_scan_list(conf_ifname(), sr->range.we_version_compiled); + if (!sr->head) { + switch(errno) { + case EPERM: + /* Don't try to read leftover results, it does not work reliably. */ + if (!has_net_admin_capability()) + snprintf(sr->msg, sizeof(sr->msg), + "This screen requires CAP_NET_ADMIN permissions"); + break; + case EFAULT: + /* + * EFAULT can occur after a window resizing event and is temporary. + * It may also occur when the interface is down, hence defer handling. + */ + break; + case EINTR: + case EBUSY: + case EAGAIN: + /* Temporary errors. */ + snprintf(sr->msg, sizeof(sr->msg), "Waiting for scan data on %s ...", conf_ifname()); + break; + case ENETDOWN: + snprintf(sr->msg, sizeof(sr->msg), "Interface %s is down - setting it up ...", conf_ifname()); + if (if_set_up(conf_ifname()) < 0) + err_sys("Can not bring up interface '%s'", conf_ifname()); + break; + case E2BIG: + /* + * This is a driver issue, since already using the largest possible + * scan buffer. See comments in iwlist.c of wireless tools. + */ + snprintf(sr->msg, sizeof(sr->msg), + "No scan on %s: Driver returned too much data", conf_ifname()); + break; + case 0: + snprintf(sr->msg, sizeof(sr->msg), "Empty scan results on %s", conf_ifname()); + break; + default: + snprintf(sr->msg, sizeof(sr->msg), + "Scan failed on %s: %s", conf_ifname(), strerror(errno)); + } + } + + for (cur = sr->head; cur; cur = cur->next) { + if (str_is_ascii(cur->essid)) + sr->max_essid_len = clamp(strlen(cur->essid), + sr->max_essid_len, + IW_ESSID_MAX_SIZE); + iw_sanitize(&sr->range, &cur->qual, &cur->dbm); + cur->chan = freq_to_channel(cur->freq, &sr->range); + if (cur->freq >= 5e9) + sr->num.five_gig++; + else if (cur->freq >= 2e9) + sr->num.two_gig++; + sr->num.entries += 1; + sr->num.open += !cur->has_key; + } + compute_channel_stats(sr); + + pthread_mutex_unlock(&sr->mutex); + } while (usleep(conf.stat_iv * 1000) == 0); + + return NULL; } -- cgit v1.2.3