• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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