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