xref: /freebsd/contrib/wpa/wpa_supplicant/bgscan_learn.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
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(&params, 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, &params, 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