2008-02-28 02:34:43 +01:00
|
|
|
/*
|
|
|
|
* WPA Supplicant - Helper functions for scan result processing
|
2008-11-29 21:06:34 +01:00
|
|
|
* Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi>
|
2008-02-28 02:34:43 +01:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* Alternatively, this software may be distributed under the terms of BSD
|
|
|
|
* license.
|
|
|
|
*
|
|
|
|
* See README and COPYING for more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "includes.h"
|
|
|
|
|
|
|
|
#include "common.h"
|
|
|
|
#include "drivers/driver.h"
|
|
|
|
#include "ieee802_11_defs.h"
|
|
|
|
|
|
|
|
|
|
|
|
const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
|
|
|
|
{
|
|
|
|
const u8 *end, *pos;
|
|
|
|
|
|
|
|
pos = (const u8 *) (res + 1);
|
|
|
|
end = pos + res->ie_len;
|
|
|
|
|
|
|
|
while (pos + 1 < end) {
|
|
|
|
if (pos + 2 + pos[1] > end)
|
|
|
|
break;
|
|
|
|
if (pos[0] == ie)
|
|
|
|
return pos;
|
|
|
|
pos += 2 + pos[1];
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
|
|
|
|
u32 vendor_type)
|
|
|
|
{
|
|
|
|
const u8 *end, *pos;
|
|
|
|
|
|
|
|
pos = (const u8 *) (res + 1);
|
|
|
|
end = pos + res->ie_len;
|
|
|
|
|
|
|
|
while (pos + 1 < end) {
|
|
|
|
if (pos + 2 + pos[1] > end)
|
|
|
|
break;
|
|
|
|
if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
|
|
|
|
vendor_type == WPA_GET_BE32(&pos[2]))
|
|
|
|
return pos;
|
|
|
|
pos += 2 + pos[1];
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-29 21:06:34 +01:00
|
|
|
struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
|
|
|
|
u32 vendor_type)
|
|
|
|
{
|
|
|
|
struct wpabuf *buf;
|
|
|
|
const u8 *end, *pos;
|
|
|
|
|
|
|
|
buf = wpabuf_alloc(res->ie_len);
|
|
|
|
if (buf == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
pos = (const u8 *) (res + 1);
|
|
|
|
end = pos + res->ie_len;
|
|
|
|
|
|
|
|
while (pos + 1 < end) {
|
|
|
|
if (pos + 2 + pos[1] > end)
|
|
|
|
break;
|
|
|
|
if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
|
|
|
|
vendor_type == WPA_GET_BE32(&pos[2]))
|
|
|
|
wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4);
|
|
|
|
pos += 2 + pos[1];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wpabuf_len(buf) == 0) {
|
|
|
|
wpabuf_free(buf);
|
|
|
|
buf = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
int wpa_scan_get_max_rate(const struct wpa_scan_res *res)
|
|
|
|
{
|
|
|
|
int rate = 0;
|
|
|
|
const u8 *ie;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
ie = wpa_scan_get_ie(res, WLAN_EID_SUPP_RATES);
|
|
|
|
for (i = 0; ie && i < ie[1]; i++) {
|
2008-07-15 19:05:50 +02:00
|
|
|
if ((ie[i + 2] & 0x7f) > rate)
|
|
|
|
rate = ie[i + 2] & 0x7f;
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ie = wpa_scan_get_ie(res, WLAN_EID_EXT_SUPP_RATES);
|
|
|
|
for (i = 0; ie && i < ie[1]; i++) {
|
2008-07-15 19:05:50 +02:00
|
|
|
if ((ie[i + 2] & 0x7f) > rate)
|
|
|
|
rate = ie[i + 2] & 0x7f;
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return rate;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void wpa_scan_results_free(struct wpa_scan_results *res)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (res == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < res->num; i++)
|
|
|
|
os_free(res->res[i]);
|
|
|
|
os_free(res->res);
|
|
|
|
os_free(res);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Compare function for sorting scan results. Return >0 if @b is considered
|
|
|
|
* better. */
|
|
|
|
static int wpa_scan_result_compar(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
struct wpa_scan_res **_wa = (void *) a;
|
|
|
|
struct wpa_scan_res **_wb = (void *) b;
|
|
|
|
struct wpa_scan_res *wa = *_wa;
|
|
|
|
struct wpa_scan_res *wb = *_wb;
|
|
|
|
int wpa_a, wpa_b, maxrate_a, maxrate_b;
|
|
|
|
|
|
|
|
/* WPA/WPA2 support preferred */
|
|
|
|
wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL ||
|
|
|
|
wpa_scan_get_ie(wa, WLAN_EID_RSN) != NULL;
|
|
|
|
wpa_b = wpa_scan_get_vendor_ie(wb, WPA_IE_VENDOR_TYPE) != NULL ||
|
|
|
|
wpa_scan_get_ie(wb, WLAN_EID_RSN) != NULL;
|
|
|
|
|
|
|
|
if (wpa_b && !wpa_a)
|
|
|
|
return 1;
|
|
|
|
if (!wpa_b && wpa_a)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* privacy support preferred */
|
|
|
|
if ((wa->caps & IEEE80211_CAP_PRIVACY) == 0 &&
|
|
|
|
(wb->caps & IEEE80211_CAP_PRIVACY))
|
|
|
|
return 1;
|
|
|
|
if ((wa->caps & IEEE80211_CAP_PRIVACY) &&
|
|
|
|
(wb->caps & IEEE80211_CAP_PRIVACY) == 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* best/max rate preferred if signal level close enough XXX */
|
2009-02-10 12:47:14 +01:00
|
|
|
if ((wa->level && wb->level && abs(wb->level - wa->level) < 5) ||
|
|
|
|
(wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) {
|
|
|
|
maxrate_a = wpa_scan_get_max_rate(wa);
|
|
|
|
maxrate_b = wpa_scan_get_max_rate(wb);
|
|
|
|
if (maxrate_a != maxrate_b)
|
|
|
|
return maxrate_b - maxrate_a;
|
|
|
|
}
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
/* use freq for channel preference */
|
|
|
|
|
|
|
|
/* all things being equal, use signal level; if signal levels are
|
|
|
|
* identical, use quality values since some drivers may only report
|
|
|
|
* that value and leave the signal level zero */
|
|
|
|
if (wb->level == wa->level)
|
|
|
|
return wb->qual - wa->qual;
|
|
|
|
return wb->level - wa->level;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void wpa_scan_sort_results(struct wpa_scan_results *res)
|
|
|
|
{
|
|
|
|
qsort(res->res, res->num, sizeof(struct wpa_scan_res *),
|
|
|
|
wpa_scan_result_compar);
|
|
|
|
}
|