| /* |
| * 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] & 0x7f) > rate) |
| rate = ie[i + 2] & 0x7f; |
| } |
| |
| ie = wpa_scan_get_ie(res, WLAN_EID_EXT_SUPP_RATES); |
| for (i = 0; ie && i < ie[1]; i++) { |
| if ((ie[i + 2] & 0x7f) > rate) |
| rate = ie[i + 2] & 0x7f; |
| } |
| |
| 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); |
| } |