149 lines
3.4 KiB
C
149 lines
3.4 KiB
C
|
/*
|
||
|
* WPA Supplicant - Helper functions for scan result processing
|
||
|
* Copyright (c) 2007, Jouni Malinen <j@w1.fi>
|
||
|
*
|
||
|
* 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;
|
||
|
}
|
||
|
|
||
|
|
||
|
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++) {
|
||
|
if (ie[i + 2] > rate)
|
||
|
rate = ie[i + 2];
|
||
|
}
|
||
|
|
||
|
ie = wpa_scan_get_ie(res, WLAN_EID_EXT_SUPP_RATES);
|
||
|
for (i = 0; ie && i < ie[1]; i++) {
|
||
|
if (ie[i + 2] > rate)
|
||
|
rate = ie[i + 2];
|
||
|
}
|
||
|
|
||
|
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 */
|
||
|
maxrate_a = wpa_scan_get_max_rate(wa);
|
||
|
maxrate_b = wpa_scan_get_max_rate(wb);
|
||
|
if (maxrate_a != maxrate_b && abs(wb->level - wa->level) < 5)
|
||
|
return maxrate_b - maxrate_a;
|
||
|
|
||
|
/* 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);
|
||
|
}
|