1f05cddf9SRui Paulo /*
2f05cddf9SRui Paulo * WPA Supplicant - background scan and roaming module: learn
3f05cddf9SRui Paulo * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
4f05cddf9SRui Paulo *
5f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo * See README for more details.
7f05cddf9SRui Paulo */
8f05cddf9SRui Paulo
9f05cddf9SRui Paulo #include "includes.h"
10f05cddf9SRui Paulo
11f05cddf9SRui Paulo #include "common.h"
12f05cddf9SRui Paulo #include "eloop.h"
13f05cddf9SRui Paulo #include "list.h"
14f05cddf9SRui Paulo #include "common/ieee802_11_defs.h"
15f05cddf9SRui Paulo #include "drivers/driver.h"
16f05cddf9SRui Paulo #include "config_ssid.h"
17f05cddf9SRui Paulo #include "wpa_supplicant_i.h"
18f05cddf9SRui Paulo #include "driver_i.h"
19f05cddf9SRui Paulo #include "scan.h"
20f05cddf9SRui Paulo #include "bgscan.h"
21f05cddf9SRui Paulo
22f05cddf9SRui Paulo struct bgscan_learn_bss {
23f05cddf9SRui Paulo struct dl_list list;
24f05cddf9SRui Paulo u8 bssid[ETH_ALEN];
25f05cddf9SRui Paulo int freq;
26f05cddf9SRui Paulo u8 *neigh; /* num_neigh * ETH_ALEN buffer */
27f05cddf9SRui Paulo size_t num_neigh;
28f05cddf9SRui Paulo };
29f05cddf9SRui Paulo
30f05cddf9SRui Paulo struct bgscan_learn_data {
31f05cddf9SRui Paulo struct wpa_supplicant *wpa_s;
32f05cddf9SRui Paulo const struct wpa_ssid *ssid;
33f05cddf9SRui Paulo int scan_interval;
34f05cddf9SRui Paulo int signal_threshold;
35f05cddf9SRui Paulo int short_interval; /* use if signal < threshold */
36f05cddf9SRui Paulo int long_interval; /* use if signal > threshold */
375b9c547cSRui Paulo struct os_reltime last_bgscan;
38f05cddf9SRui Paulo char *fname;
39f05cddf9SRui Paulo struct dl_list bss;
40f05cddf9SRui Paulo int *supp_freqs;
41f05cddf9SRui Paulo int probe_idx;
42f05cddf9SRui Paulo };
43f05cddf9SRui Paulo
44f05cddf9SRui Paulo
bss_free(struct bgscan_learn_bss * bss)45f05cddf9SRui Paulo static void bss_free(struct bgscan_learn_bss *bss)
46f05cddf9SRui Paulo {
47f05cddf9SRui Paulo os_free(bss->neigh);
48f05cddf9SRui Paulo os_free(bss);
49f05cddf9SRui Paulo }
50f05cddf9SRui Paulo
51f05cddf9SRui Paulo
bssid_in_array(u8 * array,size_t array_len,const u8 * bssid)52f05cddf9SRui Paulo static int bssid_in_array(u8 *array, size_t array_len, const u8 *bssid)
53f05cddf9SRui Paulo {
54f05cddf9SRui Paulo size_t i;
55f05cddf9SRui Paulo
56f05cddf9SRui Paulo if (array == NULL || array_len == 0)
57f05cddf9SRui Paulo return 0;
58f05cddf9SRui Paulo
59f05cddf9SRui Paulo for (i = 0; i < array_len; i++) {
60*a90b9d01SCy Schubert if (ether_addr_equal(array + i * ETH_ALEN, bssid))
61f05cddf9SRui Paulo return 1;
62f05cddf9SRui Paulo }
63f05cddf9SRui Paulo
64f05cddf9SRui Paulo return 0;
65f05cddf9SRui Paulo }
66f05cddf9SRui Paulo
67f05cddf9SRui Paulo
bgscan_learn_add_neighbor(struct bgscan_learn_bss * bss,const u8 * bssid)68f05cddf9SRui Paulo static void bgscan_learn_add_neighbor(struct bgscan_learn_bss *bss,
69f05cddf9SRui Paulo const u8 *bssid)
70f05cddf9SRui Paulo {
71f05cddf9SRui Paulo u8 *n;
72f05cddf9SRui Paulo
73*a90b9d01SCy Schubert if (ether_addr_equal(bss->bssid, bssid))
74f05cddf9SRui Paulo return;
75f05cddf9SRui Paulo if (bssid_in_array(bss->neigh, bss->num_neigh, bssid))
76f05cddf9SRui Paulo return;
77f05cddf9SRui Paulo
78f05cddf9SRui Paulo n = os_realloc_array(bss->neigh, bss->num_neigh + 1, ETH_ALEN);
79f05cddf9SRui Paulo if (n == NULL)
80f05cddf9SRui Paulo return;
81f05cddf9SRui Paulo
82f05cddf9SRui Paulo os_memcpy(n + bss->num_neigh * ETH_ALEN, bssid, ETH_ALEN);
83f05cddf9SRui Paulo bss->neigh = n;
84f05cddf9SRui Paulo bss->num_neigh++;
85f05cddf9SRui Paulo }
86f05cddf9SRui Paulo
87f05cddf9SRui Paulo
bgscan_learn_get_bss(struct bgscan_learn_data * data,const u8 * bssid)88f05cddf9SRui Paulo static struct bgscan_learn_bss * bgscan_learn_get_bss(
89f05cddf9SRui Paulo struct bgscan_learn_data *data, const u8 *bssid)
90f05cddf9SRui Paulo {
91f05cddf9SRui Paulo struct bgscan_learn_bss *bss;
92f05cddf9SRui Paulo
93f05cddf9SRui Paulo dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) {
94*a90b9d01SCy Schubert if (ether_addr_equal(bss->bssid, bssid))
95f05cddf9SRui Paulo return bss;
96f05cddf9SRui Paulo }
97f05cddf9SRui Paulo return NULL;
98f05cddf9SRui Paulo }
99f05cddf9SRui Paulo
100f05cddf9SRui Paulo
bgscan_learn_load(struct bgscan_learn_data * data)101f05cddf9SRui Paulo static int bgscan_learn_load(struct bgscan_learn_data *data)
102f05cddf9SRui Paulo {
103f05cddf9SRui Paulo FILE *f;
104f05cddf9SRui Paulo char buf[128];
105f05cddf9SRui Paulo struct bgscan_learn_bss *bss;
106f05cddf9SRui Paulo
107f05cddf9SRui Paulo if (data->fname == NULL)
108f05cddf9SRui Paulo return 0;
109f05cddf9SRui Paulo
110f05cddf9SRui Paulo f = fopen(data->fname, "r");
111f05cddf9SRui Paulo if (f == NULL)
112f05cddf9SRui Paulo return 0;
113f05cddf9SRui Paulo
114f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "bgscan learn: Loading data from %s",
115f05cddf9SRui Paulo data->fname);
116f05cddf9SRui Paulo
117f05cddf9SRui Paulo if (fgets(buf, sizeof(buf), f) == NULL ||
118f05cddf9SRui Paulo os_strncmp(buf, "wpa_supplicant-bgscan-learn\n", 28) != 0) {
119f05cddf9SRui Paulo wpa_printf(MSG_INFO, "bgscan learn: Invalid data file %s",
120f05cddf9SRui Paulo data->fname);
121f05cddf9SRui Paulo fclose(f);
122f05cddf9SRui Paulo return -1;
123f05cddf9SRui Paulo }
124f05cddf9SRui Paulo
125f05cddf9SRui Paulo while (fgets(buf, sizeof(buf), f)) {
126f05cddf9SRui Paulo if (os_strncmp(buf, "BSS ", 4) == 0) {
127f05cddf9SRui Paulo bss = os_zalloc(sizeof(*bss));
128f05cddf9SRui Paulo if (!bss)
129f05cddf9SRui Paulo continue;
130f05cddf9SRui Paulo if (hwaddr_aton(buf + 4, bss->bssid) < 0) {
131f05cddf9SRui Paulo bss_free(bss);
132f05cddf9SRui Paulo continue;
133f05cddf9SRui Paulo }
134f05cddf9SRui Paulo bss->freq = atoi(buf + 4 + 18);
135f05cddf9SRui Paulo dl_list_add(&data->bss, &bss->list);
136f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "bgscan learn: Loaded BSS "
137f05cddf9SRui Paulo "entry: " MACSTR " freq=%d",
138f05cddf9SRui Paulo MAC2STR(bss->bssid), bss->freq);
139f05cddf9SRui Paulo }
140f05cddf9SRui Paulo
141f05cddf9SRui Paulo if (os_strncmp(buf, "NEIGHBOR ", 9) == 0) {
142f05cddf9SRui Paulo u8 addr[ETH_ALEN];
143f05cddf9SRui Paulo
144f05cddf9SRui Paulo if (hwaddr_aton(buf + 9, addr) < 0)
145f05cddf9SRui Paulo continue;
146f05cddf9SRui Paulo bss = bgscan_learn_get_bss(data, addr);
147f05cddf9SRui Paulo if (bss == NULL)
148f05cddf9SRui Paulo continue;
149f05cddf9SRui Paulo if (hwaddr_aton(buf + 9 + 18, addr) < 0)
150f05cddf9SRui Paulo continue;
151f05cddf9SRui Paulo
152f05cddf9SRui Paulo bgscan_learn_add_neighbor(bss, addr);
153f05cddf9SRui Paulo }
154f05cddf9SRui Paulo }
155f05cddf9SRui Paulo
156f05cddf9SRui Paulo fclose(f);
157f05cddf9SRui Paulo return 0;
158f05cddf9SRui Paulo }
159f05cddf9SRui Paulo
160f05cddf9SRui Paulo
bgscan_learn_save(struct bgscan_learn_data * data)161f05cddf9SRui Paulo static void bgscan_learn_save(struct bgscan_learn_data *data)
162f05cddf9SRui Paulo {
163f05cddf9SRui Paulo FILE *f;
164f05cddf9SRui Paulo struct bgscan_learn_bss *bss;
165f05cddf9SRui Paulo
166f05cddf9SRui Paulo if (data->fname == NULL)
167f05cddf9SRui Paulo return;
168f05cddf9SRui Paulo
169f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "bgscan learn: Saving data to %s",
170f05cddf9SRui Paulo data->fname);
171f05cddf9SRui Paulo
172f05cddf9SRui Paulo f = fopen(data->fname, "w");
173f05cddf9SRui Paulo if (f == NULL)
174f05cddf9SRui Paulo return;
175f05cddf9SRui Paulo fprintf(f, "wpa_supplicant-bgscan-learn\n");
176f05cddf9SRui Paulo
177f05cddf9SRui Paulo dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) {
178f05cddf9SRui Paulo fprintf(f, "BSS " MACSTR " %d\n",
179f05cddf9SRui Paulo MAC2STR(bss->bssid), bss->freq);
180f05cddf9SRui Paulo }
181f05cddf9SRui Paulo
182f05cddf9SRui Paulo dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) {
183f05cddf9SRui Paulo size_t i;
184f05cddf9SRui Paulo for (i = 0; i < bss->num_neigh; i++) {
185f05cddf9SRui Paulo fprintf(f, "NEIGHBOR " MACSTR " " MACSTR "\n",
186f05cddf9SRui Paulo MAC2STR(bss->bssid),
187f05cddf9SRui Paulo MAC2STR(bss->neigh + i * ETH_ALEN));
188f05cddf9SRui Paulo }
189f05cddf9SRui Paulo }
190f05cddf9SRui Paulo
191f05cddf9SRui Paulo fclose(f);
192f05cddf9SRui Paulo }
193f05cddf9SRui Paulo
194f05cddf9SRui Paulo
in_array(int * array,int val)195f05cddf9SRui Paulo static int in_array(int *array, int val)
196f05cddf9SRui Paulo {
197f05cddf9SRui Paulo int i;
198f05cddf9SRui Paulo
199f05cddf9SRui Paulo if (array == NULL)
200f05cddf9SRui Paulo return 0;
201f05cddf9SRui Paulo
202f05cddf9SRui Paulo for (i = 0; array[i]; i++) {
203f05cddf9SRui Paulo if (array[i] == val)
204f05cddf9SRui Paulo return 1;
205f05cddf9SRui Paulo }
206f05cddf9SRui Paulo
207f05cddf9SRui Paulo return 0;
208f05cddf9SRui Paulo }
209f05cddf9SRui Paulo
210f05cddf9SRui Paulo
bgscan_learn_get_freqs(struct bgscan_learn_data * data,size_t * count)211f05cddf9SRui Paulo static int * bgscan_learn_get_freqs(struct bgscan_learn_data *data,
212f05cddf9SRui Paulo size_t *count)
213f05cddf9SRui Paulo {
214f05cddf9SRui Paulo struct bgscan_learn_bss *bss;
215f05cddf9SRui Paulo int *freqs = NULL, *n;
216f05cddf9SRui Paulo
217f05cddf9SRui Paulo *count = 0;
218f05cddf9SRui Paulo
219f05cddf9SRui Paulo dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) {
220f05cddf9SRui Paulo if (in_array(freqs, bss->freq))
221f05cddf9SRui Paulo continue;
222f05cddf9SRui Paulo n = os_realloc_array(freqs, *count + 2, sizeof(int));
223f05cddf9SRui Paulo if (n == NULL)
224f05cddf9SRui Paulo return freqs;
225f05cddf9SRui Paulo freqs = n;
226f05cddf9SRui Paulo freqs[*count] = bss->freq;
227f05cddf9SRui Paulo (*count)++;
228f05cddf9SRui Paulo freqs[*count] = 0;
229f05cddf9SRui Paulo }
230f05cddf9SRui Paulo
231f05cddf9SRui Paulo return freqs;
232f05cddf9SRui Paulo }
233f05cddf9SRui Paulo
234f05cddf9SRui Paulo
bgscan_learn_get_probe_freq(struct bgscan_learn_data * data,int * freqs,size_t count)235f05cddf9SRui Paulo static int * bgscan_learn_get_probe_freq(struct bgscan_learn_data *data,
236f05cddf9SRui Paulo int *freqs, size_t count)
237f05cddf9SRui Paulo {
238f05cddf9SRui Paulo int idx, *n;
239f05cddf9SRui Paulo
240f05cddf9SRui Paulo if (data->supp_freqs == NULL)
241f05cddf9SRui Paulo return freqs;
242f05cddf9SRui Paulo
2435b9c547cSRui Paulo idx = data->probe_idx;
2445b9c547cSRui Paulo do {
245f05cddf9SRui Paulo if (!in_array(freqs, data->supp_freqs[idx])) {
246f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "bgscan learn: Probe new freq "
247f05cddf9SRui Paulo "%u", data->supp_freqs[idx]);
2485b9c547cSRui Paulo data->probe_idx = idx + 1;
2495b9c547cSRui Paulo if (data->supp_freqs[data->probe_idx] == 0)
2505b9c547cSRui Paulo data->probe_idx = 0;
251f05cddf9SRui Paulo n = os_realloc_array(freqs, count + 2, sizeof(int));
252f05cddf9SRui Paulo if (n == NULL)
253f05cddf9SRui Paulo return freqs;
254f05cddf9SRui Paulo freqs = n;
255f05cddf9SRui Paulo freqs[count] = data->supp_freqs[idx];
256f05cddf9SRui Paulo count++;
257f05cddf9SRui Paulo freqs[count] = 0;
258f05cddf9SRui Paulo break;
259f05cddf9SRui Paulo }
260f05cddf9SRui Paulo
261f05cddf9SRui Paulo idx++;
2625b9c547cSRui Paulo if (data->supp_freqs[idx] == 0)
2635b9c547cSRui Paulo idx = 0;
2645b9c547cSRui Paulo } while (idx != data->probe_idx);
265f05cddf9SRui Paulo
266f05cddf9SRui Paulo return freqs;
267f05cddf9SRui Paulo }
268f05cddf9SRui Paulo
269f05cddf9SRui Paulo
bgscan_learn_timeout(void * eloop_ctx,void * timeout_ctx)270f05cddf9SRui Paulo static void bgscan_learn_timeout(void *eloop_ctx, void *timeout_ctx)
271f05cddf9SRui Paulo {
272f05cddf9SRui Paulo struct bgscan_learn_data *data = eloop_ctx;
273f05cddf9SRui Paulo struct wpa_supplicant *wpa_s = data->wpa_s;
274f05cddf9SRui Paulo struct wpa_driver_scan_params params;
275f05cddf9SRui Paulo int *freqs = NULL;
276f05cddf9SRui Paulo size_t count, i;
277f05cddf9SRui Paulo char msg[100], *pos;
278f05cddf9SRui Paulo
279f05cddf9SRui Paulo os_memset(¶ms, 0, sizeof(params));
280f05cddf9SRui Paulo params.num_ssids = 1;
281f05cddf9SRui Paulo params.ssids[0].ssid = data->ssid->ssid;
282f05cddf9SRui Paulo params.ssids[0].ssid_len = data->ssid->ssid_len;
283f05cddf9SRui Paulo if (data->ssid->scan_freq)
284f05cddf9SRui Paulo params.freqs = data->ssid->scan_freq;
285f05cddf9SRui Paulo else {
286f05cddf9SRui Paulo freqs = bgscan_learn_get_freqs(data, &count);
287f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "bgscan learn: BSSes in this ESS have "
288f05cddf9SRui Paulo "been seen on %u channels", (unsigned int) count);
289f05cddf9SRui Paulo freqs = bgscan_learn_get_probe_freq(data, freqs, count);
290f05cddf9SRui Paulo
291f05cddf9SRui Paulo msg[0] = '\0';
292f05cddf9SRui Paulo pos = msg;
293f05cddf9SRui Paulo for (i = 0; freqs && freqs[i]; i++) {
294f05cddf9SRui Paulo int ret;
295f05cddf9SRui Paulo ret = os_snprintf(pos, msg + sizeof(msg) - pos, " %d",
296f05cddf9SRui Paulo freqs[i]);
2975b9c547cSRui Paulo if (os_snprintf_error(msg + sizeof(msg) - pos, ret))
298f05cddf9SRui Paulo break;
299f05cddf9SRui Paulo pos += ret;
300f05cddf9SRui Paulo }
301f05cddf9SRui Paulo pos[0] = '\0';
302f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "bgscan learn: Scanning frequencies:%s",
303f05cddf9SRui Paulo msg);
304f05cddf9SRui Paulo params.freqs = freqs;
305f05cddf9SRui Paulo }
306f05cddf9SRui Paulo
307f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "bgscan learn: Request a background scan");
308*a90b9d01SCy Schubert if (wpa_supplicant_trigger_scan(wpa_s, ¶ms, true, false)) {
309f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "bgscan learn: Failed to trigger scan");
310f05cddf9SRui Paulo eloop_register_timeout(data->scan_interval, 0,
311f05cddf9SRui Paulo bgscan_learn_timeout, data, NULL);
312f05cddf9SRui Paulo } else
3135b9c547cSRui Paulo os_get_reltime(&data->last_bgscan);
314f05cddf9SRui Paulo os_free(freqs);
315f05cddf9SRui Paulo }
316f05cddf9SRui Paulo
317f05cddf9SRui Paulo
bgscan_learn_get_params(struct bgscan_learn_data * data,const char * params)318f05cddf9SRui Paulo static int bgscan_learn_get_params(struct bgscan_learn_data *data,
319f05cddf9SRui Paulo const char *params)
320f05cddf9SRui Paulo {
321f05cddf9SRui Paulo const char *pos;
322f05cddf9SRui Paulo
323f05cddf9SRui Paulo data->short_interval = atoi(params);
324f05cddf9SRui Paulo
325f05cddf9SRui Paulo pos = os_strchr(params, ':');
326f05cddf9SRui Paulo if (pos == NULL)
327f05cddf9SRui Paulo return 0;
328f05cddf9SRui Paulo pos++;
329f05cddf9SRui Paulo data->signal_threshold = atoi(pos);
330f05cddf9SRui Paulo pos = os_strchr(pos, ':');
331f05cddf9SRui Paulo if (pos == NULL) {
332f05cddf9SRui Paulo wpa_printf(MSG_ERROR, "bgscan learn: Missing scan interval "
333f05cddf9SRui Paulo "for high signal");
334f05cddf9SRui Paulo return -1;
335f05cddf9SRui Paulo }
336f05cddf9SRui Paulo pos++;
337f05cddf9SRui Paulo data->long_interval = atoi(pos);
338f05cddf9SRui Paulo pos = os_strchr(pos, ':');
339f05cddf9SRui Paulo if (pos) {
340f05cddf9SRui Paulo pos++;
341f05cddf9SRui Paulo data->fname = os_strdup(pos);
342f05cddf9SRui Paulo }
343f05cddf9SRui Paulo
344f05cddf9SRui Paulo return 0;
345f05cddf9SRui Paulo }
346f05cddf9SRui Paulo
347f05cddf9SRui Paulo
bgscan_learn_get_supp_freqs(struct wpa_supplicant * wpa_s)348f05cddf9SRui Paulo static int * bgscan_learn_get_supp_freqs(struct wpa_supplicant *wpa_s)
349f05cddf9SRui Paulo {
350f05cddf9SRui Paulo struct hostapd_hw_modes *modes;
351f05cddf9SRui Paulo int i, j, *freqs = NULL, *n;
352f05cddf9SRui Paulo size_t count = 0;
353f05cddf9SRui Paulo
354f05cddf9SRui Paulo modes = wpa_s->hw.modes;
355f05cddf9SRui Paulo if (modes == NULL)
356f05cddf9SRui Paulo return NULL;
357f05cddf9SRui Paulo
358f05cddf9SRui Paulo for (i = 0; i < wpa_s->hw.num_modes; i++) {
359f05cddf9SRui Paulo for (j = 0; j < modes[i].num_channels; j++) {
360f05cddf9SRui Paulo if (modes[i].channels[j].flag & HOSTAPD_CHAN_DISABLED)
361f05cddf9SRui Paulo continue;
3625b9c547cSRui Paulo /* some hw modes (e.g. 11b & 11g) contain same freqs */
3635b9c547cSRui Paulo if (in_array(freqs, modes[i].channels[j].freq))
3645b9c547cSRui Paulo continue;
365f05cddf9SRui Paulo n = os_realloc_array(freqs, count + 2, sizeof(int));
366f05cddf9SRui Paulo if (n == NULL)
367f05cddf9SRui Paulo continue;
368f05cddf9SRui Paulo
369f05cddf9SRui Paulo freqs = n;
370f05cddf9SRui Paulo freqs[count] = modes[i].channels[j].freq;
371f05cddf9SRui Paulo count++;
372f05cddf9SRui Paulo freqs[count] = 0;
373f05cddf9SRui Paulo }
374f05cddf9SRui Paulo }
375f05cddf9SRui Paulo
376f05cddf9SRui Paulo return freqs;
377f05cddf9SRui Paulo }
378f05cddf9SRui Paulo
379f05cddf9SRui Paulo
bgscan_learn_init(struct wpa_supplicant * wpa_s,const char * params,const struct wpa_ssid * ssid)380f05cddf9SRui Paulo static void * bgscan_learn_init(struct wpa_supplicant *wpa_s,
381f05cddf9SRui Paulo const char *params,
382f05cddf9SRui Paulo const struct wpa_ssid *ssid)
383f05cddf9SRui Paulo {
384f05cddf9SRui Paulo struct bgscan_learn_data *data;
385f05cddf9SRui Paulo
386f05cddf9SRui Paulo data = os_zalloc(sizeof(*data));
387f05cddf9SRui Paulo if (data == NULL)
388f05cddf9SRui Paulo return NULL;
389f05cddf9SRui Paulo dl_list_init(&data->bss);
390f05cddf9SRui Paulo data->wpa_s = wpa_s;
391f05cddf9SRui Paulo data->ssid = ssid;
392f05cddf9SRui Paulo if (bgscan_learn_get_params(data, params) < 0) {
393f05cddf9SRui Paulo os_free(data->fname);
394f05cddf9SRui Paulo os_free(data);
395f05cddf9SRui Paulo return NULL;
396f05cddf9SRui Paulo }
397f05cddf9SRui Paulo if (data->short_interval <= 0)
398f05cddf9SRui Paulo data->short_interval = 30;
399f05cddf9SRui Paulo if (data->long_interval <= 0)
400f05cddf9SRui Paulo data->long_interval = 30;
401f05cddf9SRui Paulo
402f05cddf9SRui Paulo if (bgscan_learn_load(data) < 0) {
403f05cddf9SRui Paulo os_free(data->fname);
404f05cddf9SRui Paulo os_free(data);
405f05cddf9SRui Paulo return NULL;
406f05cddf9SRui Paulo }
407f05cddf9SRui Paulo
408f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "bgscan learn: Signal strength threshold %d "
409f05cddf9SRui Paulo "Short bgscan interval %d Long bgscan interval %d",
410f05cddf9SRui Paulo data->signal_threshold, data->short_interval,
411f05cddf9SRui Paulo data->long_interval);
412f05cddf9SRui Paulo
413f05cddf9SRui Paulo if (data->signal_threshold &&
414f05cddf9SRui Paulo wpa_drv_signal_monitor(wpa_s, data->signal_threshold, 4) < 0) {
415f05cddf9SRui Paulo wpa_printf(MSG_ERROR, "bgscan learn: Failed to enable "
416f05cddf9SRui Paulo "signal strength monitoring");
417f05cddf9SRui Paulo }
418f05cddf9SRui Paulo
419f05cddf9SRui Paulo data->supp_freqs = bgscan_learn_get_supp_freqs(wpa_s);
420f05cddf9SRui Paulo data->scan_interval = data->short_interval;
4215b9c547cSRui Paulo if (data->signal_threshold) {
4225b9c547cSRui Paulo /* Poll for signal info to set initial scan interval */
4235b9c547cSRui Paulo struct wpa_signal_info siginfo;
4245b9c547cSRui Paulo if (wpa_drv_signal_poll(wpa_s, &siginfo) == 0 &&
425*a90b9d01SCy Schubert siginfo.data.signal >= data->signal_threshold)
4265b9c547cSRui Paulo data->scan_interval = data->long_interval;
4275b9c547cSRui Paulo }
4285b9c547cSRui Paulo
429f05cddf9SRui Paulo eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout,
430f05cddf9SRui Paulo data, NULL);
431f05cddf9SRui Paulo
432f05cddf9SRui Paulo /*
433f05cddf9SRui Paulo * This function is called immediately after an association, so it is
434f05cddf9SRui Paulo * reasonable to assume that a scan was completed recently. This makes
435f05cddf9SRui Paulo * us skip an immediate new scan in cases where the current signal
436f05cddf9SRui Paulo * level is below the bgscan threshold.
437f05cddf9SRui Paulo */
4385b9c547cSRui Paulo os_get_reltime(&data->last_bgscan);
439f05cddf9SRui Paulo
440f05cddf9SRui Paulo return data;
441f05cddf9SRui Paulo }
442f05cddf9SRui Paulo
443f05cddf9SRui Paulo
bgscan_learn_deinit(void * priv)444f05cddf9SRui Paulo static void bgscan_learn_deinit(void *priv)
445f05cddf9SRui Paulo {
446f05cddf9SRui Paulo struct bgscan_learn_data *data = priv;
447f05cddf9SRui Paulo struct bgscan_learn_bss *bss, *n;
448f05cddf9SRui Paulo
449f05cddf9SRui Paulo bgscan_learn_save(data);
450f05cddf9SRui Paulo eloop_cancel_timeout(bgscan_learn_timeout, data, NULL);
451f05cddf9SRui Paulo if (data->signal_threshold)
452f05cddf9SRui Paulo wpa_drv_signal_monitor(data->wpa_s, 0, 0);
453f05cddf9SRui Paulo os_free(data->fname);
454f05cddf9SRui Paulo dl_list_for_each_safe(bss, n, &data->bss, struct bgscan_learn_bss,
455f05cddf9SRui Paulo list) {
456f05cddf9SRui Paulo dl_list_del(&bss->list);
457f05cddf9SRui Paulo bss_free(bss);
458f05cddf9SRui Paulo }
459f05cddf9SRui Paulo os_free(data->supp_freqs);
460f05cddf9SRui Paulo os_free(data);
461f05cddf9SRui Paulo }
462f05cddf9SRui Paulo
463f05cddf9SRui Paulo
bgscan_learn_bss_match(struct bgscan_learn_data * data,struct wpa_scan_res * bss)464f05cddf9SRui Paulo static int bgscan_learn_bss_match(struct bgscan_learn_data *data,
465f05cddf9SRui Paulo struct wpa_scan_res *bss)
466f05cddf9SRui Paulo {
467f05cddf9SRui Paulo const u8 *ie;
468f05cddf9SRui Paulo
469f05cddf9SRui Paulo ie = wpa_scan_get_ie(bss, WLAN_EID_SSID);
470f05cddf9SRui Paulo if (ie == NULL)
471f05cddf9SRui Paulo return 0;
472f05cddf9SRui Paulo
473f05cddf9SRui Paulo if (data->ssid->ssid_len != ie[1] ||
474f05cddf9SRui Paulo os_memcmp(data->ssid->ssid, ie + 2, ie[1]) != 0)
475f05cddf9SRui Paulo return 0; /* SSID mismatch */
476f05cddf9SRui Paulo
477f05cddf9SRui Paulo return 1;
478f05cddf9SRui Paulo }
479f05cddf9SRui Paulo
480f05cddf9SRui Paulo
bgscan_learn_notify_scan(void * priv,struct wpa_scan_results * scan_res)481f05cddf9SRui Paulo static int bgscan_learn_notify_scan(void *priv,
482f05cddf9SRui Paulo struct wpa_scan_results *scan_res)
483f05cddf9SRui Paulo {
484f05cddf9SRui Paulo struct bgscan_learn_data *data = priv;
485f05cddf9SRui Paulo size_t i, j;
486f05cddf9SRui Paulo #define MAX_BSS 50
487f05cddf9SRui Paulo u8 bssid[MAX_BSS * ETH_ALEN];
488f05cddf9SRui Paulo size_t num_bssid = 0;
489f05cddf9SRui Paulo
490f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "bgscan learn: scan result notification");
491f05cddf9SRui Paulo
492f05cddf9SRui Paulo eloop_cancel_timeout(bgscan_learn_timeout, data, NULL);
493f05cddf9SRui Paulo eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout,
494f05cddf9SRui Paulo data, NULL);
495f05cddf9SRui Paulo
496f05cddf9SRui Paulo for (i = 0; i < scan_res->num; i++) {
497f05cddf9SRui Paulo struct wpa_scan_res *res = scan_res->res[i];
498f05cddf9SRui Paulo if (!bgscan_learn_bss_match(data, res))
499f05cddf9SRui Paulo continue;
500f05cddf9SRui Paulo
501f05cddf9SRui Paulo if (num_bssid < MAX_BSS) {
502f05cddf9SRui Paulo os_memcpy(bssid + num_bssid * ETH_ALEN, res->bssid,
503f05cddf9SRui Paulo ETH_ALEN);
504f05cddf9SRui Paulo num_bssid++;
505f05cddf9SRui Paulo }
506f05cddf9SRui Paulo }
507f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "bgscan learn: %u matching BSSes in scan "
508f05cddf9SRui Paulo "results", (unsigned int) num_bssid);
509f05cddf9SRui Paulo
510f05cddf9SRui Paulo for (i = 0; i < scan_res->num; i++) {
511f05cddf9SRui Paulo struct wpa_scan_res *res = scan_res->res[i];
512f05cddf9SRui Paulo struct bgscan_learn_bss *bss;
513f05cddf9SRui Paulo
514f05cddf9SRui Paulo if (!bgscan_learn_bss_match(data, res))
515f05cddf9SRui Paulo continue;
516f05cddf9SRui Paulo
517f05cddf9SRui Paulo bss = bgscan_learn_get_bss(data, res->bssid);
518f05cddf9SRui Paulo if (bss && bss->freq != res->freq) {
519f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "bgscan learn: Update BSS "
520f05cddf9SRui Paulo MACSTR " freq %d -> %d",
521f05cddf9SRui Paulo MAC2STR(res->bssid), bss->freq, res->freq);
522f05cddf9SRui Paulo bss->freq = res->freq;
523f05cddf9SRui Paulo } else if (!bss) {
524f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "bgscan learn: Add BSS " MACSTR
525f05cddf9SRui Paulo " freq=%d", MAC2STR(res->bssid), res->freq);
526f05cddf9SRui Paulo bss = os_zalloc(sizeof(*bss));
527f05cddf9SRui Paulo if (!bss)
528f05cddf9SRui Paulo continue;
529f05cddf9SRui Paulo os_memcpy(bss->bssid, res->bssid, ETH_ALEN);
530f05cddf9SRui Paulo bss->freq = res->freq;
531f05cddf9SRui Paulo dl_list_add(&data->bss, &bss->list);
532f05cddf9SRui Paulo }
533f05cddf9SRui Paulo
534f05cddf9SRui Paulo for (j = 0; j < num_bssid; j++) {
535f05cddf9SRui Paulo u8 *addr = bssid + j * ETH_ALEN;
536f05cddf9SRui Paulo bgscan_learn_add_neighbor(bss, addr);
537f05cddf9SRui Paulo }
538f05cddf9SRui Paulo }
539f05cddf9SRui Paulo
540f05cddf9SRui Paulo /*
541f05cddf9SRui Paulo * A more advanced bgscan could process scan results internally, select
542f05cddf9SRui Paulo * the BSS and request roam if needed. This sample uses the existing
543f05cddf9SRui Paulo * BSS/ESS selection routine. Change this to return 1 if selection is
544f05cddf9SRui Paulo * done inside the bgscan module.
545f05cddf9SRui Paulo */
546f05cddf9SRui Paulo
547f05cddf9SRui Paulo return 0;
548f05cddf9SRui Paulo }
549f05cddf9SRui Paulo
550f05cddf9SRui Paulo
bgscan_learn_notify_beacon_loss(void * priv)551f05cddf9SRui Paulo static void bgscan_learn_notify_beacon_loss(void *priv)
552f05cddf9SRui Paulo {
553f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "bgscan learn: beacon loss");
554f05cddf9SRui Paulo /* TODO: speed up background scanning */
555f05cddf9SRui Paulo }
556f05cddf9SRui Paulo
557f05cddf9SRui Paulo
bgscan_learn_notify_signal_change(void * priv,int above,int current_signal,int current_noise,int current_txrate)558f05cddf9SRui Paulo static void bgscan_learn_notify_signal_change(void *priv, int above,
559f05cddf9SRui Paulo int current_signal,
560f05cddf9SRui Paulo int current_noise,
561f05cddf9SRui Paulo int current_txrate)
562f05cddf9SRui Paulo {
563f05cddf9SRui Paulo struct bgscan_learn_data *data = priv;
564f05cddf9SRui Paulo int scan = 0;
5655b9c547cSRui Paulo struct os_reltime now;
566f05cddf9SRui Paulo
567f05cddf9SRui Paulo if (data->short_interval == data->long_interval ||
568f05cddf9SRui Paulo data->signal_threshold == 0)
569f05cddf9SRui Paulo return;
570f05cddf9SRui Paulo
571f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "bgscan learn: signal level changed "
572f05cddf9SRui Paulo "(above=%d current_signal=%d current_noise=%d "
573f05cddf9SRui Paulo "current_txrate=%d)", above, current_signal,
574f05cddf9SRui Paulo current_noise, current_txrate);
575f05cddf9SRui Paulo if (data->scan_interval == data->long_interval && !above) {
576f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "bgscan learn: Start using short bgscan "
577f05cddf9SRui Paulo "interval");
578f05cddf9SRui Paulo data->scan_interval = data->short_interval;
5795b9c547cSRui Paulo os_get_reltime(&now);
580f05cddf9SRui Paulo if (now.sec > data->last_bgscan.sec + 1)
581f05cddf9SRui Paulo scan = 1;
582f05cddf9SRui Paulo } else if (data->scan_interval == data->short_interval && above) {
583f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "bgscan learn: Start using long bgscan "
584f05cddf9SRui Paulo "interval");
585f05cddf9SRui Paulo data->scan_interval = data->long_interval;
586f05cddf9SRui Paulo eloop_cancel_timeout(bgscan_learn_timeout, data, NULL);
587f05cddf9SRui Paulo eloop_register_timeout(data->scan_interval, 0,
588f05cddf9SRui Paulo bgscan_learn_timeout, data, NULL);
589f05cddf9SRui Paulo } else if (!above) {
590f05cddf9SRui Paulo /*
591f05cddf9SRui Paulo * Signal dropped further 4 dB. Request a new scan if we have
592f05cddf9SRui Paulo * not yet scanned in a while.
593f05cddf9SRui Paulo */
5945b9c547cSRui Paulo os_get_reltime(&now);
595f05cddf9SRui Paulo if (now.sec > data->last_bgscan.sec + 10)
596f05cddf9SRui Paulo scan = 1;
597f05cddf9SRui Paulo }
598f05cddf9SRui Paulo
599f05cddf9SRui Paulo if (scan) {
600f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "bgscan learn: Trigger immediate scan");
601f05cddf9SRui Paulo eloop_cancel_timeout(bgscan_learn_timeout, data, NULL);
602f05cddf9SRui Paulo eloop_register_timeout(0, 0, bgscan_learn_timeout, data, NULL);
603f05cddf9SRui Paulo }
604f05cddf9SRui Paulo }
605f05cddf9SRui Paulo
606f05cddf9SRui Paulo
607f05cddf9SRui Paulo const struct bgscan_ops bgscan_learn_ops = {
608f05cddf9SRui Paulo .name = "learn",
609f05cddf9SRui Paulo .init = bgscan_learn_init,
610f05cddf9SRui Paulo .deinit = bgscan_learn_deinit,
611f05cddf9SRui Paulo .notify_scan = bgscan_learn_notify_scan,
612f05cddf9SRui Paulo .notify_beacon_loss = bgscan_learn_notify_beacon_loss,
613f05cddf9SRui Paulo .notify_signal_change = bgscan_learn_notify_signal_change,
614f05cddf9SRui Paulo };
615