Home | History | Annotate | Download | only in drivers
      1 /*
      2  * WPA Supplicant - Helper functions for scan result processing
      3  * Copyright (c) 2007-2008, Jouni Malinen <j (at) w1.fi>
      4  *
      5  * This program is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License version 2 as
      7  * published by the Free Software Foundation.
      8  *
      9  * Alternatively, this software may be distributed under the terms of BSD
     10  * license.
     11  *
     12  * See README and COPYING for more details.
     13  */
     14 
     15 #include "includes.h"
     16 
     17 #include "common.h"
     18 #include "drivers/driver.h"
     19 #include "ieee802_11_defs.h"
     20 
     21 
     22 const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
     23 {
     24 	const u8 *end, *pos;
     25 
     26 	pos = (const u8 *) (res + 1);
     27 	end = pos + res->ie_len;
     28 
     29 	while (pos + 1 < end) {
     30 		if (pos + 2 + pos[1] > end)
     31 			break;
     32 		if (pos[0] == ie)
     33 			return pos;
     34 		pos += 2 + pos[1];
     35 	}
     36 
     37 	return NULL;
     38 }
     39 
     40 
     41 const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
     42 				  u32 vendor_type)
     43 {
     44 	const u8 *end, *pos;
     45 
     46 	pos = (const u8 *) (res + 1);
     47 	end = pos + res->ie_len;
     48 
     49 	while (pos + 1 < end) {
     50 		if (pos + 2 + pos[1] > end)
     51 			break;
     52 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
     53 		    vendor_type == WPA_GET_BE32(&pos[2]))
     54 			return pos;
     55 		pos += 2 + pos[1];
     56 	}
     57 
     58 	return NULL;
     59 }
     60 
     61 
     62 struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
     63 					     u32 vendor_type)
     64 {
     65 	struct wpabuf *buf;
     66 	const u8 *end, *pos;
     67 
     68 	buf = wpabuf_alloc(res->ie_len);
     69 	if (buf == NULL)
     70 		return NULL;
     71 
     72 	pos = (const u8 *) (res + 1);
     73 	end = pos + res->ie_len;
     74 
     75 	while (pos + 1 < end) {
     76 		if (pos + 2 + pos[1] > end)
     77 			break;
     78 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
     79 		    vendor_type == WPA_GET_BE32(&pos[2]))
     80 			wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4);
     81 		pos += 2 + pos[1];
     82 	}
     83 
     84 	if (wpabuf_len(buf) == 0) {
     85 		wpabuf_free(buf);
     86 		buf = NULL;
     87 	}
     88 
     89 	return buf;
     90 }
     91 
     92 
     93 int wpa_scan_get_max_rate(const struct wpa_scan_res *res)
     94 {
     95 	int rate = 0;
     96 	const u8 *ie;
     97 	int i;
     98 
     99 	ie = wpa_scan_get_ie(res, WLAN_EID_SUPP_RATES);
    100 	for (i = 0; ie && i < ie[1]; i++) {
    101 		if ((ie[i + 2] & 0x7f) > rate)
    102 			rate = ie[i + 2] & 0x7f;
    103 	}
    104 
    105 	ie = wpa_scan_get_ie(res, WLAN_EID_EXT_SUPP_RATES);
    106 	for (i = 0; ie && i < ie[1]; i++) {
    107 		if ((ie[i + 2] & 0x7f) > rate)
    108 			rate = ie[i + 2] & 0x7f;
    109 	}
    110 
    111 	return rate;
    112 }
    113 
    114 
    115 void wpa_scan_results_free(struct wpa_scan_results *res)
    116 {
    117 	size_t i;
    118 
    119 	if (res == NULL)
    120 		return;
    121 
    122 	for (i = 0; i < res->num; i++)
    123 		os_free(res->res[i]);
    124 	os_free(res->res);
    125 	os_free(res);
    126 }
    127 
    128 
    129 /* Compare function for sorting scan results. Return >0 if @b is considered
    130  * better. */
    131 static int wpa_scan_result_compar(const void *a, const void *b)
    132 {
    133 	struct wpa_scan_res **_wa = (void *) a;
    134 	struct wpa_scan_res **_wb = (void *) b;
    135 	struct wpa_scan_res *wa = *_wa;
    136 	struct wpa_scan_res *wb = *_wb;
    137 	int wpa_a, wpa_b, maxrate_a, maxrate_b;
    138 
    139 	/* WPA/WPA2 support preferred */
    140 	wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL ||
    141 		wpa_scan_get_ie(wa, WLAN_EID_RSN) != NULL;
    142 	wpa_b = wpa_scan_get_vendor_ie(wb, WPA_IE_VENDOR_TYPE) != NULL ||
    143 		wpa_scan_get_ie(wb, WLAN_EID_RSN) != NULL;
    144 
    145 	if (wpa_b && !wpa_a)
    146 		return 1;
    147 	if (!wpa_b && wpa_a)
    148 		return -1;
    149 
    150 	/* privacy support preferred */
    151 	if ((wa->caps & IEEE80211_CAP_PRIVACY) == 0 &&
    152 	    (wb->caps & IEEE80211_CAP_PRIVACY))
    153 		return 1;
    154 	if ((wa->caps & IEEE80211_CAP_PRIVACY) &&
    155 	    (wb->caps & IEEE80211_CAP_PRIVACY) == 0)
    156 		return -1;
    157 
    158 	/* best/max rate preferred if signal level close enough XXX */
    159 	if ((wa->level && wb->level && abs(wb->level - wa->level) < 5) ||
    160 	    (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) {
    161 		maxrate_a = wpa_scan_get_max_rate(wa);
    162 		maxrate_b = wpa_scan_get_max_rate(wb);
    163 		if (maxrate_a != maxrate_b)
    164 			return maxrate_b - maxrate_a;
    165 	}
    166 
    167 	/* use freq for channel preference */
    168 
    169 	/* all things being equal, use signal level; if signal levels are
    170 	 * identical, use quality values since some drivers may only report
    171 	 * that value and leave the signal level zero */
    172 	if (wb->level == wa->level)
    173 		return wb->qual - wa->qual;
    174 	return wb->level - wa->level;
    175 }
    176 
    177 
    178 void wpa_scan_sort_results(struct wpa_scan_results *res)
    179 {
    180 	qsort(res->res, res->num, sizeof(struct wpa_scan_res *),
    181 	      wpa_scan_result_compar);
    182 }
    183