/* * WPA Supplicant - Helper functions for scan result processing * Copyright (c) 2007-2008, 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; } 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; } 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 */ 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; } /* 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); }