1 /*
2 * WPA Supplicant - Helper functions for scan result processing
3 * Copyright (c) 2007-2008, Jouni Malinen <j@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
wpa_scan_get_ie(const struct wpa_scan_res * res,u8 ie)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
wpa_scan_get_vendor_ie(const struct wpa_scan_res * res,u32 vendor_type)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
wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res * res,u32 vendor_type)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
wpa_scan_get_max_rate(const struct wpa_scan_res * res)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
wpa_scan_results_free(struct wpa_scan_results * res)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. */
wpa_scan_result_compar(const void * a,const void * b)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
wpa_scan_sort_results(struct wpa_scan_results * res)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