Fixed wpa_scan_get_max_rate() to clear the basicrate flag when determining
[mech_eap.git] / src / drivers / scan_helpers.c
1 /*
2  * WPA Supplicant - Helper functions for scan result processing
3  * Copyright (c) 2007, 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
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 int wpa_scan_get_max_rate(const struct wpa_scan_res *res)
63 {
64         int rate = 0;
65         const u8 *ie;
66         int i;
67
68         ie = wpa_scan_get_ie(res, WLAN_EID_SUPP_RATES);
69         for (i = 0; ie && i < ie[1]; i++) {
70                 if ((ie[i + 2] & 0x7f) > rate)
71                         rate = ie[i + 2] & 0x7f;
72         }
73
74         ie = wpa_scan_get_ie(res, WLAN_EID_EXT_SUPP_RATES);
75         for (i = 0; ie && i < ie[1]; i++) {
76                 if ((ie[i + 2] & 0x7f) > rate)
77                         rate = ie[i + 2] & 0x7f;
78         }
79
80         return rate;
81 }
82
83
84 void wpa_scan_results_free(struct wpa_scan_results *res)
85 {
86         size_t i;
87
88         if (res == NULL)
89                 return;
90
91         for (i = 0; i < res->num; i++)
92                 os_free(res->res[i]);
93         os_free(res->res);
94         os_free(res);
95 }
96
97
98 /* Compare function for sorting scan results. Return >0 if @b is considered
99  * better. */
100 static int wpa_scan_result_compar(const void *a, const void *b)
101 {
102         struct wpa_scan_res **_wa = (void *) a;
103         struct wpa_scan_res **_wb = (void *) b;
104         struct wpa_scan_res *wa = *_wa;
105         struct wpa_scan_res *wb = *_wb;
106         int wpa_a, wpa_b, maxrate_a, maxrate_b;
107
108         /* WPA/WPA2 support preferred */
109         wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL ||
110                 wpa_scan_get_ie(wa, WLAN_EID_RSN) != NULL;
111         wpa_b = wpa_scan_get_vendor_ie(wb, WPA_IE_VENDOR_TYPE) != NULL ||
112                 wpa_scan_get_ie(wb, WLAN_EID_RSN) != NULL;
113
114         if (wpa_b && !wpa_a)
115                 return 1;
116         if (!wpa_b && wpa_a)
117                 return -1;
118
119         /* privacy support preferred */
120         if ((wa->caps & IEEE80211_CAP_PRIVACY) == 0 &&
121             (wb->caps & IEEE80211_CAP_PRIVACY))
122                 return 1;
123         if ((wa->caps & IEEE80211_CAP_PRIVACY) &&
124             (wb->caps & IEEE80211_CAP_PRIVACY) == 0)
125                 return -1;
126
127         /* best/max rate preferred if signal level close enough XXX */
128         maxrate_a = wpa_scan_get_max_rate(wa);
129         maxrate_b = wpa_scan_get_max_rate(wb);
130         if (maxrate_a != maxrate_b && abs(wb->level - wa->level) < 5)
131                 return maxrate_b - maxrate_a;
132
133         /* use freq for channel preference */
134
135         /* all things being equal, use signal level; if signal levels are
136          * identical, use quality values since some drivers may only report
137          * that value and leave the signal level zero */
138         if (wb->level == wa->level)
139                 return wb->qual - wa->qual;
140         return wb->level - wa->level;
141 }
142
143
144 void wpa_scan_sort_results(struct wpa_scan_results *res)
145 {
146         qsort(res->res, res->num, sizeof(struct wpa_scan_res *),
147               wpa_scan_result_compar);
148 }