1 /*
2  * wpa_supplicant - List of temporarily ignored BSSIDs
3  * Copyright (c) 2003-2021, Jouni Malinen <j@w1.fi>
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8 
9 #include "includes.h"
10 
11 #include "common.h"
12 #include "wpa_supplicant_i.h"
13 #include "bssid_ignore.h"
14 
15 /**
16  * wpa_bssid_ignore_get - Get the ignore list entry for a BSSID
17  * @wpa_s: Pointer to wpa_supplicant data
18  * @bssid: BSSID
19  * Returns: Matching entry for the BSSID or %NULL if not found
20  */
21 struct wpa_bssid_ignore * wpa_bssid_ignore_get(struct wpa_supplicant *wpa_s,
22 					       const u8 *bssid)
23 {
24 	struct wpa_bssid_ignore *e;
25 
26 	if (wpa_s == NULL || bssid == NULL)
27 		return NULL;
28 
29 	if (wpa_s->current_ssid &&
30 	    wpa_s->current_ssid->was_recently_reconfigured) {
31 		wpa_bssid_ignore_clear(wpa_s);
32 		wpa_s->current_ssid->was_recently_reconfigured = false;
33 		return NULL;
34 	}
35 
36 	wpa_bssid_ignore_update(wpa_s);
37 
38 	e = wpa_s->bssid_ignore;
39 	while (e) {
40 		if (os_memcmp(e->bssid, bssid, ETH_ALEN) == 0)
41 			return e;
42 		e = e->next;
43 	}
44 
45 	return NULL;
46 }
47 
48 
49 /**
50  * wpa_bssid_ignore_add - Add an BSSID to the ignore list
51  * @wpa_s: Pointer to wpa_supplicant data
52  * @bssid: BSSID to be added to the ignore list
53  * Returns: Current ignore list count on success, -1 on failure
54  *
55  * This function adds the specified BSSID to the ignore list or increases the
56  * ignore count if the BSSID was already listed. It should be called when
57  * an association attempt fails either due to the selected BSS rejecting
58  * association or due to timeout.
59  *
60  * This ignore list is used to force %wpa_supplicant to go through all available
61  * BSSes before retrying to associate with an BSS that rejected or timed out
62  * association. It does not prevent the listed BSS from being used; it only
63  * changes the order in which they are tried.
64  */
65 int wpa_bssid_ignore_add(struct wpa_supplicant *wpa_s, const u8 *bssid)
66 {
67 	struct wpa_bssid_ignore *e;
68 	struct os_reltime now;
69 
70 	if (wpa_s == NULL || bssid == NULL)
71 		return -1;
72 
73 	e = wpa_bssid_ignore_get(wpa_s, bssid);
74 	os_get_reltime(&now);
75 	if (e) {
76 		e->start = now;
77 		e->count++;
78 		if (e->count > 5)
79 			e->timeout_secs = 1800;
80 		else if (e->count == 5)
81 			e->timeout_secs = 600;
82 		else if (e->count == 4)
83 			e->timeout_secs = 120;
84 		else if (e->count == 3)
85 			e->timeout_secs = 60;
86 		else
87 			e->timeout_secs = 10;
88 		wpa_printf(MSG_INFO, "BSSID " MACSTR
89 			   " ignore list count incremented to %d, ignoring for %d seconds",
90 			   MAC2STR(bssid), e->count, e->timeout_secs);
91 		return e->count;
92 	}
93 
94 	e = os_zalloc(sizeof(*e));
95 	if (e == NULL)
96 		return -1;
97 	os_memcpy(e->bssid, bssid, ETH_ALEN);
98 	e->count = 1;
99 	e->timeout_secs = 10;
100 	e->start = now;
101 	e->next = wpa_s->bssid_ignore;
102 	wpa_s->bssid_ignore = e;
103 	wpa_printf(MSG_DEBUG, "Added BSSID " MACSTR
104 		   " into ignore list, ignoring for %d seconds",
105 		   MAC2STR(bssid), e->timeout_secs);
106 
107 	return e->count;
108 }
109 
110 
111 /**
112  * wpa_bssid_ignore_del - Remove an BSSID from the ignore list
113  * @wpa_s: Pointer to wpa_supplicant data
114  * @bssid: BSSID to be removed from the ignore list
115  * Returns: 0 on success, -1 on failure
116  */
117 int wpa_bssid_ignore_del(struct wpa_supplicant *wpa_s, const u8 *bssid)
118 {
119 	struct wpa_bssid_ignore *e, *prev = NULL;
120 
121 	if (wpa_s == NULL || bssid == NULL)
122 		return -1;
123 
124 	e = wpa_s->bssid_ignore;
125 	while (e) {
126 		if (os_memcmp(e->bssid, bssid, ETH_ALEN) == 0) {
127 			if (prev == NULL) {
128 				wpa_s->bssid_ignore = e->next;
129 			} else {
130 				prev->next = e->next;
131 			}
132 			wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR
133 				   " from ignore list", MAC2STR(bssid));
134 			os_free(e);
135 			return 0;
136 		}
137 		prev = e;
138 		e = e->next;
139 	}
140 	return -1;
141 }
142 
143 
144 /**
145  * wpa_bssid_ignore_is_listed - Check whether a BSSID is ignored temporarily
146  * @wpa_s: Pointer to wpa_supplicant data
147  * @bssid: BSSID to be checked
148  * Returns: count if BSS is currently considered to be ignored, 0 otherwise
149  */
150 int wpa_bssid_ignore_is_listed(struct wpa_supplicant *wpa_s, const u8 *bssid)
151 {
152 	struct wpa_bssid_ignore *e;
153 	struct os_reltime now;
154 
155 	e = wpa_bssid_ignore_get(wpa_s, bssid);
156 	if (!e)
157 		return 0;
158 	os_get_reltime(&now);
159 	if (os_reltime_expired(&now, &e->start, e->timeout_secs))
160 		return 0;
161 	return e->count;
162 }
163 
164 
165 /**
166  * wpa_bssid_ignore_clear - Clear the ignore list of all entries
167  * @wpa_s: Pointer to wpa_supplicant data
168  */
169 void wpa_bssid_ignore_clear(struct wpa_supplicant *wpa_s)
170 {
171 	struct wpa_bssid_ignore *e, *prev;
172 
173 	e = wpa_s->bssid_ignore;
174 	wpa_s->bssid_ignore = NULL;
175 	while (e) {
176 		prev = e;
177 		e = e->next;
178 		wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR
179 			   " from ignore list (clear)", MAC2STR(prev->bssid));
180 		os_free(prev);
181 	}
182 }
183 
184 
185 /**
186  * wpa_bssid_ignore_update - Update the entries in the ignore list,
187  * deleting entries that have been expired for over an hour.
188  * @wpa_s: Pointer to wpa_supplicant data
189  */
190 void wpa_bssid_ignore_update(struct wpa_supplicant *wpa_s)
191 {
192 	struct wpa_bssid_ignore *e, *prev = NULL;
193 	struct os_reltime now;
194 
195 	if (!wpa_s)
196 		return;
197 
198 	e = wpa_s->bssid_ignore;
199 	os_get_reltime(&now);
200 	while (e) {
201 		if (os_reltime_expired(&now, &e->start,
202 				       e->timeout_secs + 3600)) {
203 			struct wpa_bssid_ignore *to_delete = e;
204 
205 			if (prev) {
206 				prev->next = e->next;
207 				e = prev->next;
208 			} else {
209 				wpa_s->bssid_ignore = e->next;
210 				e = wpa_s->bssid_ignore;
211 			}
212 			wpa_printf(MSG_INFO, "Removed BSSID " MACSTR
213 				   " from ignore list (expired)",
214 				   MAC2STR(to_delete->bssid));
215 			os_free(to_delete);
216 		} else {
217 			prev = e;
218 			e = e->next;
219 		}
220 	}
221 }
222