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  */
wpa_bssid_ignore_get(struct wpa_supplicant * wpa_s,const u8 * bssid)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 (ether_addr_equal(e->bssid, bssid))
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  */
wpa_bssid_ignore_add(struct wpa_supplicant * wpa_s,const u8 * bssid)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_msg(wpa_s, 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_msg(wpa_s, MSG_INFO, "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  */
wpa_bssid_ignore_del(struct wpa_supplicant * wpa_s,const u8 * bssid)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 (ether_addr_equal(e->bssid, bssid)) {
127                               if (prev == NULL) {
128                                         wpa_s->bssid_ignore = e->next;
129                               } else {
130                                         prev->next = e->next;
131                               }
132                               wpa_msg(wpa_s, MSG_INFO, "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  */
wpa_bssid_ignore_is_listed(struct wpa_supplicant * wpa_s,const u8 * bssid)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  */
wpa_bssid_ignore_clear(struct wpa_supplicant * wpa_s)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_msg(wpa_s, MSG_INFO, "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  */
wpa_bssid_ignore_update(struct wpa_supplicant * wpa_s)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_msg(wpa_s, 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