1 /*
2  * wpa_supplicant - WNM
3  * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8 
9 #include "utils/includes.h"
10 
11 #include "utils/common.h"
12 #include "common/ieee802_11_defs.h"
13 #include "common/ieee802_11_common.h"
14 #include "rsn_supp/wpa.h"
15 #include "wpa_supplicant_i.h"
16 #include "drivers/driver.h"
17 #include "scan.h"
18 #include "bss.h"
19 #include "wnm_sta.h"
20 
21 #define MAX_TFS_IE_LEN  1024
22 #define WNM_MAX_NEIGHBOR_REPORT 10
23 
24 #define WNM_SCAN_RESULT_AGE 2 /* 2 seconds */
25 
wnm_deallocate_memory(struct wpa_supplicant * wpa_s)26 void wnm_deallocate_memory(struct wpa_supplicant *wpa_s)
27 {
28 	int i;
29 
30 	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
31 		os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot);
32 		os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid);
33 	}
34 
35 	wpa_s->wnm_num_neighbor_report = 0;
36 	os_free(wpa_s->wnm_neighbor_report_elements);
37 	wpa_s->wnm_neighbor_report_elements = NULL;
38 }
39 
40 
wnm_parse_neighbor_report_elem(struct neighbor_report * rep,u8 id,u8 elen,const u8 * pos)41 static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep,
42 					   u8 id, u8 elen, const u8 *pos)
43 {
44 	switch (id) {
45 	case WNM_NEIGHBOR_TSF:
46 		if (elen < 2 + 2) {
47 			wpa_printf(MSG_DEBUG, "WNM: Too short TSF");
48 			break;
49 		}
50 		rep->tsf_offset = WPA_GET_LE16(pos);
51 		rep->beacon_int = WPA_GET_LE16(pos + 2);
52 		rep->tsf_present = 1;
53 		break;
54 	case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING:
55 		if (elen < 2) {
56 			wpa_printf(MSG_DEBUG, "WNM: Too short condensed "
57 				   "country string");
58 			break;
59 		}
60 		os_memcpy(rep->country, pos, 2);
61 		rep->country_present = 1;
62 		break;
63 	case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE:
64 		if (elen < 1) {
65 			wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition "
66 				   "candidate");
67 			break;
68 		}
69 		rep->preference = pos[0];
70 		rep->preference_present = 1;
71 		break;
72 	case WNM_NEIGHBOR_BSS_TERMINATION_DURATION:
73 		if (elen < 10) {
74 			wpa_printf(MSG_DEBUG,
75 				   "WNM: Too short BSS termination duration");
76 			break;
77 		}
78 		rep->bss_term_tsf = WPA_GET_LE64(pos);
79 		rep->bss_term_dur = WPA_GET_LE16(pos + 8);
80 		rep->bss_term_present = 1;
81 		break;
82 	case WNM_NEIGHBOR_BEARING:
83 		if (elen < 8) {
84 			wpa_printf(MSG_DEBUG, "WNM: Too short neighbor "
85 				   "bearing");
86 			break;
87 		}
88 		rep->bearing = WPA_GET_LE16(pos);
89 		rep->distance = WPA_GET_LE32(pos + 2);
90 		rep->rel_height = WPA_GET_LE16(pos + 2 + 4);
91 		rep->bearing_present = 1;
92 		break;
93 	case WNM_NEIGHBOR_MEASUREMENT_PILOT:
94 		if (elen < 1) {
95 			wpa_printf(MSG_DEBUG, "WNM: Too short measurement "
96 				   "pilot");
97 			break;
98 		}
99 		os_free(rep->meas_pilot);
100 		rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot));
101 		if (rep->meas_pilot == NULL)
102 			break;
103 		rep->meas_pilot->measurement_pilot = pos[0];
104 		rep->meas_pilot->subelem_len = elen - 1;
105 		os_memcpy(rep->meas_pilot->subelems, pos + 1, elen - 1);
106 		break;
107 	case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES:
108 		if (elen < 5) {
109 			wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled "
110 				   "capabilities");
111 			break;
112 		}
113 		os_memcpy(rep->rm_capab, pos, 5);
114 		rep->rm_capab_present = 1;
115 		break;
116 	case WNM_NEIGHBOR_MULTIPLE_BSSID:
117 		if (elen < 1) {
118 			wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID");
119 			break;
120 		}
121 		os_free(rep->mul_bssid);
122 		rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid));
123 		if (rep->mul_bssid == NULL)
124 			break;
125 		rep->mul_bssid->max_bssid_indicator = pos[0];
126 		rep->mul_bssid->subelem_len = elen - 1;
127 		os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1);
128 		break;
129 	}
130 }
131 
wnm_parse_neighbor_report(struct wpa_supplicant * wpa_s,const u8 * pos,u8 len,struct neighbor_report * rep)132 static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s,
133 				      const u8 *pos, u8 len,
134 				      struct neighbor_report *rep)
135 {
136 	u8 left = len;
137 
138 	if (left < 13) {
139 		wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report");
140 		return;
141 	}
142 
143 	os_memcpy(rep->bssid, pos, ETH_ALEN);
144 	rep->bssid_info = WPA_GET_LE32(pos + ETH_ALEN);
145 	rep->regulatory_class = *(pos + 10);
146 	rep->channel_number = *(pos + 11);
147 	rep->phy_type = *(pos + 12);
148 
149 	pos += 13;
150 	left -= 13;
151 
152 	while (left >= 2) {
153 		u8 id, elen;
154 
155 		id = *pos++;
156 		elen = *pos++;
157 		wpa_printf(MSG_DEBUG, "WNM: Subelement id=%u len=%u", id, elen);
158 		left -= 2;
159 		if (elen > left) {
160 			wpa_printf(MSG_DEBUG,
161 				   "WNM: Truncated neighbor report subelement");
162 			break;
163 		}
164 		wnm_parse_neighbor_report_elem(rep, id, elen, pos);
165 		left -= elen;
166 		pos += elen;
167 	}
168 }
169 
170 
wnm_clear_acceptable(struct wpa_supplicant * wpa_s)171 static void wnm_clear_acceptable(struct wpa_supplicant *wpa_s)
172 {
173 	unsigned int i;
174 
175 	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++)
176 		wpa_s->wnm_neighbor_report_elements[i].acceptable = 0;
177 }
178 
179 
get_first_acceptable(struct wpa_supplicant * wpa_s)180 static struct wpa_bss * get_first_acceptable(struct wpa_supplicant *wpa_s)
181 {
182 	unsigned int i;
183 	struct neighbor_report *nei;
184 
185 	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
186 		nei = &wpa_s->wnm_neighbor_report_elements[i];
187 		if (nei->acceptable)
188 			return wpa_bss_get_bssid(wpa_s, nei->bssid);
189 	}
190 
191 	return NULL;
192 }
193 
194 
195 #ifdef CONFIG_MBO
196 static struct wpa_bss *
get_mbo_transition_candidate(struct wpa_supplicant * wpa_s,enum mbo_transition_reject_reason * reason)197 get_mbo_transition_candidate(struct wpa_supplicant *wpa_s,
198 			     enum mbo_transition_reject_reason *reason)
199 {
200 	struct wpa_bss *target = NULL;
201 	struct wpa_bss_trans_info params;
202 	struct wpa_bss_candidate_info *info = NULL;
203 	struct neighbor_report *nei = wpa_s->wnm_neighbor_report_elements;
204 	u8 *first_candidate_bssid = NULL, *pos;
205 	unsigned int i;
206 
207 	params.mbo_transition_reason = wpa_s->wnm_mbo_transition_reason;
208 	params.n_candidates = 0;
209 	params.bssid = os_calloc(wpa_s->wnm_num_neighbor_report, ETH_ALEN);
210 	if (!params.bssid)
211 		return NULL;
212 
213 	pos = params.bssid;
214 	for (i = 0; i < wpa_s->wnm_num_neighbor_report; nei++, i++) {
215 		if (nei->is_first)
216 			first_candidate_bssid = nei->bssid;
217 		if (!nei->acceptable)
218 			continue;
219 		os_memcpy(pos, nei->bssid, ETH_ALEN);
220 		pos += ETH_ALEN;
221 		params.n_candidates++;
222 	}
223 
224 	if (!params.n_candidates)
225 		goto end;
226 
227 #ifndef ESP_SUPPLICANT
228 	info = wpa_drv_get_bss_trans_status(wpa_s, &params);
229 #endif
230 	if (!info) {
231 		/* If failed to get candidate BSS transition status from driver,
232 		 * get the first acceptable candidate from wpa_supplicant.
233 		 */
234 		target = wpa_bss_get_bssid(wpa_s, params.bssid);
235 		goto end;
236 	}
237 
238 	/* Get the first acceptable candidate from driver */
239 	for (i = 0; i < info->num; i++) {
240 		if (info->candidates[i].is_accept) {
241 			target = wpa_bss_get_bssid(wpa_s,
242 						   info->candidates[i].bssid);
243 			goto end;
244 		}
245 	}
246 
247 	/* If Disassociation Imminent is set and driver rejects all the
248 	 * candidate select first acceptable candidate which has
249 	 * rssi > disassoc_imminent_rssi_threshold
250 	 */
251 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
252 		for (i = 0; i < info->num; i++) {
253 			target = wpa_bss_get_bssid(wpa_s,
254 						   info->candidates[i].bssid);
255 #ifndef ESP_SUPPLICANT
256 			if (target &&
257 			    (target->level <
258 			     wpa_s->conf->disassoc_imminent_rssi_threshold))
259 				continue;
260 #else
261 			if (target)
262 				continue;
263 #endif
264 			goto end;
265 		}
266 	}
267 
268 	/* While sending BTM reject use reason code of the first candidate
269 	 * received in BTM request frame
270 	 */
271 	if (reason) {
272 		for (i = 0; i < info->num; i++) {
273 			if (first_candidate_bssid &&
274 			    os_memcmp(first_candidate_bssid,
275 				      info->candidates[i].bssid, ETH_ALEN) == 0)
276 			{
277 				*reason = info->candidates[i].reject_reason;
278 				break;
279 			}
280 		}
281 	}
282 
283 	target = NULL;
284 
285 end:
286 	os_free(params.bssid);
287 	if (info) {
288 		os_free(info->candidates);
289 		os_free(info);
290 	}
291 	return target;
292 }
293 #endif /* CONFIG_MBO */
294 
295 
296 
297 /* basic function to match candidate profile with current bss */
wpa_scan_res_match(struct wpa_supplicant * wpa_s,struct wpa_bss * current_bss,struct wpa_bss * target_bss)298 bool wpa_scan_res_match(struct wpa_supplicant *wpa_s,
299 			struct wpa_bss *current_bss,
300 			struct wpa_bss *target_bss)
301 {
302 	if (current_bss->ssid_len != target_bss->ssid_len) {
303 		wpa_printf(MSG_DEBUG, "WNM: ssid didn't match");
304 		return false;
305 	}
306 	if (os_memcmp(current_bss->ssid, target_bss->ssid, current_bss->ssid_len) != 0) {
307 		wpa_printf(MSG_DEBUG, "WNM: ssid didn't match");
308 		return false;
309 	}
310 
311 	/* Just check for Open/secure mode */
312 	if ((current_bss->caps & WLAN_CAPABILITY_PRIVACY) != (target_bss->caps & WLAN_CAPABILITY_PRIVACY)) {
313 		wpa_printf(MSG_DEBUG, "WNM: Security didn't match");
314 		return false;
315 	}
316 
317 	return true;
318 }
319 
320 static struct wpa_bss *
compare_scan_neighbor_results(struct wpa_supplicant * wpa_s,os_time_t age_secs,enum mbo_transition_reject_reason * reason)321 compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs,
322 			      enum mbo_transition_reject_reason *reason)
323 {
324 	u8 i;
325 	struct wpa_bss *bss = wpa_s->current_bss;
326 	struct wpa_bss *target;
327 
328 	if (!bss)
329 		return NULL;
330 
331 	wnm_clear_acceptable(wpa_s);
332 
333 	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
334 		struct neighbor_report *nei;
335 
336 		nei = &wpa_s->wnm_neighbor_report_elements[i];
337 		if (nei->preference_present && nei->preference == 0) {
338 			wpa_printf(MSG_DEBUG, "Skip excluded BSS " MACSTR,
339 				   MAC2STR(nei->bssid));
340 			continue;
341 		}
342 
343 		target = wpa_bss_get_bssid(wpa_s, nei->bssid);
344 		if (!target) {
345 			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
346 				   " (pref %d) not found in scan results",
347 				   MAC2STR(nei->bssid),
348 				   nei->preference_present ? nei->preference :
349 				   -1);
350 			continue;
351 		}
352 
353 		if (age_secs) {
354 			struct os_reltime now;
355 
356 			if (os_get_reltime(&now) == 0 &&
357 			    os_reltime_expired(&now, &target->last_update,
358 					       age_secs)) {
359 				wpa_printf(MSG_DEBUG,
360 					   "Candidate BSS is more than %jd seconds old",
361 					   (intmax_t)age_secs);
362 				continue;
363 			}
364 		}
365 
366 		if (bss->ssid_len != target->ssid_len ||
367 		    os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) {
368 			/*
369 			 * TODO: Could consider allowing transition to another
370 			 * ESS if PMF was enabled for the association.
371 			 */
372 			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
373 				   " (pref %d) in different ESS",
374 				   MAC2STR(nei->bssid),
375 				   nei->preference_present ? nei->preference :
376 				   -1);
377 			continue;
378 		}
379 
380 		if (wpa_s->current_bss &&
381 		    !wpa_scan_res_match(wpa_s, wpa_s->current_bss, target)) {
382 			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
383 				   " (pref %d) does not match the current network profile",
384 				   MAC2STR(nei->bssid),
385 				   nei->preference_present ? nei->preference :
386 				   -1);
387 			continue;
388 		}
389 
390 		if (target->level < bss->level && target->level < -80) {
391 			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
392 				   " (pref %d) does not have sufficient signal level (%d)",
393 				   MAC2STR(nei->bssid),
394 				   nei->preference_present ? nei->preference :
395 				   -1,
396 				   target->level);
397 			continue;
398 		}
399 
400 		nei->acceptable = 1;
401 	}
402 
403 #ifdef CONFIG_MBO
404 	if (wpa_s->wnm_mbo_trans_reason_present)
405 		target = get_mbo_transition_candidate(wpa_s, reason);
406 	else
407 		target = get_first_acceptable(wpa_s);
408 #else /* CONFIG_MBO */
409 	target = get_first_acceptable(wpa_s);
410 #endif /* CONFIG_MBO */
411 
412 	if (target) {
413 		wpa_printf(MSG_DEBUG,
414 			   "WNM: Found an acceptable preferred transition candidate BSS "
415 			   MACSTR " (RSSI %d)",
416 			   MAC2STR(target->bssid), target->level);
417 	}
418 
419 	return target;
420 }
421 
422 
wpa_bss_ies_eq(struct wpa_bss * a,struct wpa_bss * b,u8 eid)423 static int wpa_bss_ies_eq(struct wpa_bss *a, struct wpa_bss *b, u8 eid)
424 {
425 	const u8 *ie_a, *ie_b;
426 
427 	if (!a || !b)
428 		return 0;
429 
430 	ie_a = wpa_bss_get_ie(a, eid);
431 	ie_b = wpa_bss_get_ie(b, eid);
432 
433 	if (!ie_a || !ie_b || ie_a[1] != ie_b[1])
434 		return 0;
435 
436 	return os_memcmp(ie_a, ie_b, ie_a[1]) == 0;
437 }
438 
439 
wnm_get_bss_info(struct wpa_supplicant * wpa_s,struct wpa_bss * bss)440 static u32 wnm_get_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
441 {
442 	u32 info = 0;
443 
444 	info |= NEI_REP_BSSID_INFO_AP_UNKNOWN_REACH;
445 
446 	/*
447 	 * Leave the security and key scope bits unset to indicate that the
448 	 * security information is not available.
449 	 */
450 
451 	if (bss->caps & WLAN_CAPABILITY_SPECTRUM_MGMT)
452 		info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
453 	if (bss->caps & WLAN_CAPABILITY_QOS)
454 		info |= NEI_REP_BSSID_INFO_QOS;
455 	if (bss->caps & WLAN_CAPABILITY_APSD)
456 		info |= NEI_REP_BSSID_INFO_APSD;
457 	if (bss->caps & WLAN_CAPABILITY_RADIO_MEASUREMENT)
458 		info |= NEI_REP_BSSID_INFO_RM;
459 	if (bss->caps & WLAN_CAPABILITY_DELAYED_BLOCK_ACK)
460 		info |= NEI_REP_BSSID_INFO_DELAYED_BA;
461 	if (bss->caps & WLAN_CAPABILITY_IMM_BLOCK_ACK)
462 		info |= NEI_REP_BSSID_INFO_IMM_BA;
463 	if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_MOBILITY_DOMAIN))
464 		info |= NEI_REP_BSSID_INFO_MOBILITY_DOMAIN;
465 	if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_HT_CAP))
466 		info |= NEI_REP_BSSID_INFO_HT;
467 
468 	return info;
469 }
470 
471 
wnm_add_nei_rep(struct wpabuf ** buf,const u8 * bssid,u32 bss_info,u8 op_class,u8 chan,u8 phy_type,u8 pref)472 static int wnm_add_nei_rep(struct wpabuf **buf, const u8 *bssid,
473 			   u32 bss_info, u8 op_class, u8 chan, u8 phy_type,
474 			   u8 pref)
475 {
476 	if (wpabuf_len(*buf) + 18 >
477 	    IEEE80211_MAX_MMPDU_SIZE - IEEE80211_HDRLEN) {
478 		wpa_printf(MSG_DEBUG,
479 			   "WNM: No room in frame for Neighbor Report element");
480 		return -1;
481 	}
482 
483 	if (wpabuf_resize(buf, 18) < 0) {
484 		wpa_printf(MSG_DEBUG,
485 			   "WNM: Failed to allocate memory for Neighbor Report element");
486 		return -1;
487 	}
488 
489 	wpabuf_put_u8(*buf, WLAN_EID_NEIGHBOR_REPORT);
490 	/* length: 13 for basic neighbor report + 3 for preference subelement */
491 	wpabuf_put_u8(*buf, 16);
492 	wpabuf_put_data(*buf, bssid, ETH_ALEN);
493 	wpabuf_put_le32(*buf, bss_info);
494 	wpabuf_put_u8(*buf, op_class);
495 	wpabuf_put_u8(*buf, chan);
496 	wpabuf_put_u8(*buf, phy_type);
497 	wpabuf_put_u8(*buf, WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE);
498 	wpabuf_put_u8(*buf, 1);
499 	wpabuf_put_u8(*buf, pref);
500 	return 0;
501 }
502 
wnm_nei_rep_add_bss(struct wpa_supplicant * wpa_s,struct wpa_bss * bss,struct wpabuf ** buf,u8 pref)503 static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s,
504 			       struct wpa_bss *bss, struct wpabuf **buf,
505 			       u8 pref)
506 {
507 	const u8 *ie;
508 	u8 op_class;
509 	int sec_chan = 0;
510 	enum phy_type phy_type;
511 	u32 info;
512 	struct ieee80211_ht_operation *ht_oper = NULL;
513 
514 	ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
515 	if (ie && ie[1] >= 2) {
516 		ht_oper = (struct ieee80211_ht_operation *) (ie + 2);
517 
518 		if (ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
519 			sec_chan = 1;
520 		else if (ht_oper->ht_param &
521 			 HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
522 			sec_chan = -1;
523 	}
524 
525 	op_class = get_operating_class(bss->channel, sec_chan);
526 
527 	phy_type = (sec_chan != 0) ? PHY_TYPE_HT : PHY_TYPE_ERP;
528 
529 	info = wnm_get_bss_info(wpa_s, bss);
530 
531 	return wnm_add_nei_rep(buf, bss->bssid, info, op_class, bss->channel, phy_type,
532 			       pref);
533 }
534 
535 
wnm_add_cand_list(struct wpa_supplicant * wpa_s,struct wpabuf ** buf)536 static void wnm_add_cand_list(struct wpa_supplicant *wpa_s, struct wpabuf **buf)
537 {
538 	unsigned int i, pref = 255;
539 	struct os_reltime now;
540 
541 	if (!wpa_s->current_bss)
542 		return;
543 
544 	/*
545 	 * TODO: Define when scan results are no longer valid for the candidate
546 	 * list.
547 	 */
548 	os_get_reltime(&now);
549 	if (os_reltime_expired(&now, &wpa_s->last_scan, 10))
550 		return;
551 
552 	wpa_printf(MSG_DEBUG,
553 		   "WNM: Add candidate list to BSS Transition Management Response frame");
554 	for (i = 0; i < wpa_s->last_scan_res_used && pref; i++) {
555 		struct wpa_bss *bss = wpa_s->last_scan_res[i];
556 		int res;
557 
558 		if (wpa_scan_res_match(wpa_s, wpa_s->current_bss, bss)) {
559 			res = wnm_nei_rep_add_bss(wpa_s, bss, buf, pref--);
560 			if (res == -2)
561 				continue; /* could not build entry for BSS */
562 			if (res < 0)
563 				break; /* no more room for candidates */
564 			if (pref == 1)
565 				break;
566 		}
567 	}
568 
569 	wpa_hexdump_buf(MSG_DEBUG,
570 			"WNM: BSS Transition Management Response candidate list",
571 			*buf);
572 }
573 
574 
575 #define BTM_RESP_MIN_SIZE	5 + ETH_ALEN
576 
wnm_send_bss_transition_mgmt_resp(struct wpa_supplicant * wpa_s,u8 dialog_token,enum bss_trans_mgmt_status_code status,enum mbo_transition_reject_reason reason,u8 delay,const u8 * target_bssid)577 static void wnm_send_bss_transition_mgmt_resp(
578 	struct wpa_supplicant *wpa_s, u8 dialog_token,
579 	enum bss_trans_mgmt_status_code status,
580 	enum mbo_transition_reject_reason reason,
581 	u8 delay, const u8 *target_bssid)
582 {
583 	struct wpabuf *buf;
584 	int res;
585 
586 	if (!wpa_s->current_bss) {
587 		wpa_printf(MSG_DEBUG,
588 			   "WNM: Current BSS not known - drop response");
589 		return;
590 	}
591 	wpa_printf(MSG_DEBUG,
592 		   "WNM: Send BSS Transition Management Response to " MACSTR
593 		   " dialog_token=%u status=%u reason=%u delay=%d",
594 		   MAC2STR(wpa_s->current_bss->bssid), dialog_token, status, reason, delay);
595 
596 	buf = wpabuf_alloc(BTM_RESP_MIN_SIZE);
597 	if (!buf) {
598 		wpa_printf(MSG_DEBUG,
599 			   "WNM: Failed to allocate memory for BTM response");
600 		return;
601 	}
602 
603 	wpabuf_put_u8(buf, WLAN_ACTION_WNM);
604 	wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_RESP);
605 	wpabuf_put_u8(buf, dialog_token);
606 	wpabuf_put_u8(buf, status);
607 	wpabuf_put_u8(buf, delay);
608 	if (target_bssid) {
609 		wpabuf_put_data(buf, target_bssid, ETH_ALEN);
610 	} else if (status == WNM_BSS_TM_ACCEPT) {
611 		/*
612 		 * P802.11-REVmc clarifies that the Target BSSID field is always
613 		 * present when status code is zero, so use a fake value here if
614 		 * no BSSID is yet known.
615 		 */
616 		wpabuf_put_data(buf, "\0\0\0\0\0\0", ETH_ALEN);
617 	}
618 
619 	if (status == WNM_BSS_TM_ACCEPT)
620 		wnm_add_cand_list(wpa_s, &buf);
621 
622 #ifdef CONFIG_MBO
623 	if (status != WNM_BSS_TM_ACCEPT &&
624 	    wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) {
625 		u8 mbo[10];
626 		size_t ret;
627 
628 		ret = wpas_mbo_ie_bss_trans_reject(wpa_s, mbo, sizeof(mbo),
629 						   reason);
630 		if (ret) {
631 			if (wpabuf_resize(&buf, ret) < 0) {
632 				wpabuf_free(buf);
633 				wpa_printf(MSG_DEBUG,
634 					   "WNM: Failed to allocate memory for MBO IE");
635 				return;
636 			}
637 
638 			wpabuf_put_data(buf, mbo, ret);
639 		}
640 	}
641 #endif /* CONFIG_MBO */
642 
643 	res = wpa_drv_send_action(wpa_s, 0, 0,
644 				  wpabuf_head_u8(buf), wpabuf_len(buf), 0);
645 	if (res < 0) {
646 		wpa_printf(MSG_DEBUG,
647 			   "WNM: Failed to send BSS Transition Management Response");
648 	}
649 
650 	wpabuf_free(buf);
651 }
652 
wnm_bss_tm_connect(struct wpa_supplicant * wpa_s,struct wpa_bss * bss,char * ssid,int after_new_scan)653 void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s,
654 			struct wpa_bss *bss, char *ssid,
655 			int after_new_scan)
656 {
657 	wpa_printf(MSG_DEBUG,
658 		"WNM: Transition to BSS " MACSTR
659 		" based on BSS Transition Management Request after_new_scan=%d)",
660 		MAC2STR(bss->bssid), after_new_scan);
661 
662 	/* Send the BSS Management Response - Accept */
663 	if (wpa_s->wnm_reply) {
664 		wpa_s->wnm_reply = 0;
665 		wpa_printf(MSG_DEBUG,
666 			   "WNM: Sending successful BSS Transition Management Response");
667 		wnm_send_bss_transition_mgmt_resp(
668 			wpa_s, wpa_s->wnm_dialog_token, WNM_BSS_TM_ACCEPT,
669 			MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
670 			bss->bssid);
671 	}
672 
673 	if (bss == wpa_s->current_bss) {
674 		wpa_printf(MSG_DEBUG,
675 			   "WNM: Already associated with the preferred candidate");
676 		wnm_deallocate_memory(wpa_s);
677 		return;
678 	}
679 
680 	wpa_printf(MSG_DEBUG, "WNM: Issuing connect");
681 
682 	wpa_supplicant_connect(wpa_s, bss, ssid);
683 	wnm_deallocate_memory(wpa_s);
684 }
685 
686 
wnm_scan_process(struct wpa_supplicant * wpa_s,int reply_on_fail)687 int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail)
688 {
689 	struct wpa_bss *bss;
690 	enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED;
691 	enum mbo_transition_reject_reason reason =
692 		MBO_TRANSITION_REJECT_REASON_UNSPECIFIED;
693 
694 	if (!wpa_s->wnm_neighbor_report_elements) {
695 		wpa_printf(MSG_INFO, "WNM: Neighbor report not available");
696 		return 0;
697         }
698 
699 	wpa_dbg(wpa_s, MSG_DEBUG,
700 		"WNM: Process scan results for BSS Transition Management");
701 	if (os_reltime_before(&wpa_s->wnm_cand_valid_until,
702 			      &wpa_s->scan_trigger_time)) {
703 		wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it");
704 		wnm_deallocate_memory(wpa_s);
705 		return 0;
706 	}
707 
708 	/* Compare the Neighbor Report and scan results */
709 	bss = compare_scan_neighbor_results(wpa_s, 0, &reason);
710 	if (!bss) {
711 		wpa_printf(MSG_INFO, "WNM: No BSS transition candidate match found");
712 		status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES;
713 		goto send_bss_resp_fail;
714 	}
715 
716 	/* Associate to the network */
717 	wnm_bss_tm_connect(wpa_s, bss, NULL, 1);
718 	return 1;
719 
720 send_bss_resp_fail:
721 	if (!reply_on_fail)
722 		return 0;
723 
724 	/* Send reject response for all the failures */
725 
726 	if (wpa_s->wnm_reply) {
727 		wpa_s->wnm_reply = 0;
728 		wnm_send_bss_transition_mgmt_resp(wpa_s,
729 						  wpa_s->wnm_dialog_token,
730 						  status, reason, 0, NULL);
731 	}
732 	wnm_deallocate_memory(wpa_s);
733 
734 	return 0;
735 }
736 
737 
cand_pref_compar(const void * a,const void * b)738 static int cand_pref_compar(const void *a, const void *b)
739 {
740 	const struct neighbor_report *aa = a;
741 	const struct neighbor_report *bb = b;
742 
743 	if (!aa->preference_present && !bb->preference_present)
744 		return 0;
745 	if (!aa->preference_present)
746 		return 1;
747 	if (!bb->preference_present)
748 		return -1;
749 	if (bb->preference > aa->preference)
750 		return 1;
751 	if (bb->preference < aa->preference)
752 		return -1;
753 	return 0;
754 }
755 
756 
wnm_sort_cand_list(struct wpa_supplicant * wpa_s)757 static void wnm_sort_cand_list(struct wpa_supplicant *wpa_s)
758 {
759 	if (!wpa_s->wnm_neighbor_report_elements)
760 		return;
761 	qsort(wpa_s->wnm_neighbor_report_elements,
762 	      wpa_s->wnm_num_neighbor_report, sizeof(struct neighbor_report),
763 	      cand_pref_compar);
764 }
765 
766 
wnm_dump_cand_list(struct wpa_supplicant * wpa_s)767 static void wnm_dump_cand_list(struct wpa_supplicant *wpa_s)
768 {
769 #ifdef DEBUG_PRINT
770 	unsigned int i;
771 
772 	wpa_printf(MSG_DEBUG, "WNM: BSS Transition Candidate List");
773 	if (!wpa_s->wnm_neighbor_report_elements)
774 		return;
775 	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
776 		struct neighbor_report *nei;
777 
778 		nei = &wpa_s->wnm_neighbor_report_elements[i];
779 		wpa_printf(MSG_DEBUG, "%u: " MACSTR
780 			   " info=0x%x op_class=%u chan=%u phy=%u pref=%d",
781 			   i, MAC2STR(nei->bssid), nei->bssid_info,
782 			   nei->regulatory_class,
783 			   nei->channel_number, nei->phy_type,
784 			   nei->preference_present ? nei->preference : -1);
785 	}
786 #endif
787 }
788 
wnm_set_scan_freqs(struct wpa_supplicant * wpa_s)789 static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s)
790 {
791 	unsigned int i;
792 	int num_chan = 0;
793 	u8 chan = 0;
794 
795 	wpa_s->next_scan_chan = 0;
796 	if (!wpa_s->wnm_neighbor_report_elements)
797 		return;
798 
799 	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
800 		struct neighbor_report *nei;
801 
802 		nei = &wpa_s->wnm_neighbor_report_elements[i];
803 		if (nei->channel_number <= 0) {
804 			wpa_printf(MSG_DEBUG,
805 				   "WNM: Unknown neighbor operating channel_number for "
806 				   MACSTR " - scan all channels",
807 				   MAC2STR(nei->bssid));
808 			return;
809 		}
810 		if (nei->channel_number != chan) {
811 			chan = nei->channel_number;
812 			num_chan++;
813 		}
814 	}
815 
816 	if (num_chan == 1) {
817 		wpa_s->next_scan_chan = chan;
818 	}
819 }
820 
wnm_fetch_scan_results(struct wpa_supplicant * wpa_s)821 static int wnm_fetch_scan_results(struct wpa_supplicant *wpa_s)
822 {
823 	/* ESP doesn't support this */
824 	return 0;
825 }
826 
ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant * wpa_s,const u8 * pos,const u8 * end,int reply)827 static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
828 					     const u8 *pos, const u8 *end,
829 					     int reply)
830 {
831 	unsigned int beacon_int;
832 	u8 valid_int;
833 #ifdef CONFIG_MBO
834 	const u8 *vendor;
835 #endif /* CONFIG_MBO */
836 #ifdef ESP_SUPPLICANT
837 	bool scan_required = false;
838 #endif
839 
840 	if (wpa_s->disable_mbo_oce || wpa_s->disable_btm)
841 		return;
842 
843 	if (end - pos < 5)
844 		return;
845 
846 #ifdef CONFIG_MBO
847 	wpa_s->wnm_mbo_trans_reason_present = 0;
848 	wpa_s->wnm_mbo_transition_reason = 0;
849 #endif /* CONFIG_MBO */
850 
851 	if (wpa_s->current_bss)
852 		beacon_int = wpa_s->current_bss->beacon_int;
853 	else
854 		beacon_int = 100; /* best guess */
855 
856 	wpa_s->wnm_dialog_token = pos[0];
857 	wpa_s->wnm_mode = pos[1];
858 	wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2);
859 	valid_int = pos[4];
860 	wpa_s->wnm_reply = reply;
861 
862 	wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: "
863 		   "dialog_token=%u request_mode=0x%x "
864 		   "disassoc_timer=%u validity_interval=%u",
865 		   wpa_s->wnm_dialog_token, wpa_s->wnm_mode,
866 		   wpa_s->wnm_dissoc_timer, valid_int);
867 
868 #if defined(CONFIG_MBO) && defined(CONFIG_TESTING_OPTIONS)
869 	if (wpa_s->reject_btm_req_reason) {
870 		wpa_printf(MSG_INFO,
871 			   "WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d",
872 			   wpa_s->reject_btm_req_reason);
873 		wnm_send_bss_transition_mgmt_resp(
874 			wpa_s, wpa_s->wnm_dialog_token,
875 			wpa_s->reject_btm_req_reason,
876 			MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
877 		return;
878 	}
879 #endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */
880 
881 	pos += 5;
882 
883 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
884 		if (end - pos < 12) {
885 			wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request");
886 			return;
887 		}
888 		os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12);
889 		pos += 12; /* BSS Termination Duration */
890 	}
891 
892 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) {
893 		char url[256];
894 
895 		if (end - pos < 1 || 1 + pos[0] > end - pos) {
896 			wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
897 				   "Management Request (URL)");
898 			return;
899 		}
900 		os_memcpy(url, pos + 1, pos[0]);
901 		url[pos[0]] = '\0';
902 		pos += 1 + pos[0];
903 
904 		wpa_msg(wpa_s, MSG_DEBUG, "ESS_DISASSOC_IMMINENT %u %s",
905 			wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url);
906 	}
907 
908 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
909 		wpa_msg(wpa_s, MSG_DEBUG, "WNM: Disassociation Imminent - "
910 			"Disassociation Timer %u", wpa_s->wnm_dissoc_timer);
911 		if (wpa_s->wnm_dissoc_timer) {
912 			/* TODO: mark current BSS less preferred for
913 			 * selection */
914 #ifdef ESP_SUPPLICANT
915 			os_memset(wpa_s->next_scan_bssid, 0, ETH_ALEN);
916 			wpa_s->next_scan_chan = 0;
917 			scan_required = true;
918 #else
919 			wpa_printf(MSG_DEBUG, "Trying to find another BSS");
920 			wpa_supplicant_req_scan(wpa_s, 0, 0);
921 #endif
922 		}
923 	}
924 
925 #ifdef CONFIG_MBO
926 	vendor = get_ie(pos, end - pos, WLAN_EID_VENDOR_SPECIFIC);
927 	if (vendor)
928 		wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]);
929 #endif /* CONFIG_MBO */
930 
931 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
932 		unsigned int valid_ms;
933 
934 		wpa_msg(wpa_s, MSG_DEBUG, "WNM: Preferred List Available");
935 		wnm_deallocate_memory(wpa_s);
936 		wpa_s->wnm_neighbor_report_elements = os_calloc(
937 			WNM_MAX_NEIGHBOR_REPORT,
938 			sizeof(struct neighbor_report));
939 		if (wpa_s->wnm_neighbor_report_elements == NULL)
940 			return;
941 
942 		while (end - pos >= 2 &&
943 		       wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT)
944 		{
945 			u8 tag = *pos++;
946 			u8 len = *pos++;
947 
948 			wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u",
949 				   tag);
950 			if (len > end - pos) {
951 				wpa_printf(MSG_DEBUG, "WNM: Truncated request");
952 				return;
953 			}
954 			if (tag == WLAN_EID_NEIGHBOR_REPORT) {
955 				struct neighbor_report *rep;
956 				rep = &wpa_s->wnm_neighbor_report_elements[
957 					wpa_s->wnm_num_neighbor_report];
958 				wnm_parse_neighbor_report(wpa_s, pos, len, rep);
959 				wpa_s->wnm_num_neighbor_report++;
960 #ifdef CONFIG_MBO
961 				if (wpa_s->wnm_mbo_trans_reason_present &&
962 				    wpa_s->wnm_num_neighbor_report == 1) {
963 					rep->is_first = 1;
964 					wpa_printf(MSG_DEBUG,
965 						   "WNM: First transition candidate is "
966 						   MACSTR, MAC2STR(rep->bssid));
967 				}
968 #endif /* CONFIG_MBO */
969 			}
970 
971 			pos += len;
972 		}
973 
974 		if (!wpa_s->wnm_num_neighbor_report) {
975 			wpa_printf(MSG_DEBUG,
976 				   "WNM: Candidate list included bit is set, but no candidates found");
977 			wnm_send_bss_transition_mgmt_resp(
978 				wpa_s, wpa_s->wnm_dialog_token,
979 				WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
980 				MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
981 				NULL);
982 			return;
983 		}
984 
985 		wnm_sort_cand_list(wpa_s);
986 		wnm_dump_cand_list(wpa_s);
987 		valid_ms = valid_int * beacon_int * 128 / 125;
988 		wpa_printf(MSG_DEBUG, "WNM: Candidate list valid for %u ms",
989 			   valid_ms);
990 		os_get_reltime(&wpa_s->wnm_cand_valid_until);
991 		wpa_s->wnm_cand_valid_until.sec += valid_ms / 1000;
992 		wpa_s->wnm_cand_valid_until.usec += (valid_ms % 1000) * 1000;
993 		wpa_s->wnm_cand_valid_until.sec +=
994 			wpa_s->wnm_cand_valid_until.usec / 1000000;
995 		wpa_s->wnm_cand_valid_until.usec %= 1000000;
996 
997 		/*
998 		 * Fetch the latest scan results from the kernel and check for
999 		 * candidates based on those results first. This can help in
1000 		 * finding more up-to-date information should the driver has
1001 		 * done some internal scanning operations after the last scan
1002 		 * result update in wpa_supplicant.
1003 		 */
1004 		if (wnm_fetch_scan_results(wpa_s) > 0)
1005 			return;
1006 
1007 		/*
1008 		 * Try to use previously received scan results, if they are
1009 		 * recent enough to use for a connection.
1010 		 */
1011 		if (wpa_s->last_scan_res_used > 0) {
1012 			struct os_reltime now;
1013 
1014 			os_get_reltime(&now);
1015 			if (!os_reltime_expired(&now, &wpa_s->last_scan, 10)) {
1016 				wpa_printf(MSG_DEBUG,
1017 					   "WNM: Try to use recent scan results");
1018 				if (wnm_scan_process(wpa_s, 0) > 0)
1019 					return;
1020 				wpa_printf(MSG_DEBUG,
1021 					   "WNM: No match in previous scan results - try a new scan");
1022 			}
1023 		}
1024 		wnm_set_scan_freqs(wpa_s);
1025 		if (wpa_s->wnm_num_neighbor_report == 1) {
1026 			os_memcpy(wpa_s->next_scan_bssid,
1027 				  wpa_s->wnm_neighbor_report_elements[0].bssid,
1028 				  ETH_ALEN);
1029 			wpa_printf(MSG_DEBUG,
1030 				   "WNM: Scan only for a specific BSSID since there is only a single candidate "
1031 				   MACSTR, MAC2STR(wpa_s->next_scan_bssid));
1032 		}
1033 #ifdef ESP_SUPPLICANT
1034 		scan_required = true;
1035 	}
1036 	if (scan_required) {
1037 		wpa_printf(MSG_DEBUG, "Trying to find another BSS");
1038 #endif
1039 		wpa_supplicant_req_scan(wpa_s, 0, 0);
1040 	} else if (reply) {
1041 		enum bss_trans_mgmt_status_code status;
1042 		if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT)
1043 			status = WNM_BSS_TM_ACCEPT;
1044 		else {
1045 			wpa_msg(wpa_s, MSG_DEBUG, "WNM: BSS Transition Management Request did not include candidates");
1046 			status = WNM_BSS_TM_REJECT_UNSPECIFIED;
1047 		}
1048 		wpa_s->wnm_reply = 0;
1049 		wnm_send_bss_transition_mgmt_resp(
1050 			wpa_s, wpa_s->wnm_dialog_token, status,
1051 			MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
1052 	}
1053 }
1054 
1055 
1056 #define BTM_QUERY_MIN_SIZE	4
1057 
wnm_send_bss_transition_mgmt_query(struct wpa_supplicant * wpa_s,u8 query_reason,const char * btm_candidates,int cand_list)1058 int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
1059 				       u8 query_reason,
1060 				       const char *btm_candidates,
1061 				       int cand_list)
1062 {
1063 	struct wpabuf *buf;
1064 	int ret;
1065 
1066 	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to "
1067 		   MACSTR " query_reason=%u%s",
1068 		   MAC2STR(wpa_s->current_bss->bssid), query_reason,
1069 		   cand_list ? " candidate list" : "");
1070 
1071 	buf = wpabuf_alloc(BTM_QUERY_MIN_SIZE);
1072 	if (!buf)
1073 		return -1;
1074 
1075 	wpabuf_put_u8(buf, WLAN_ACTION_WNM);
1076 	wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_QUERY);
1077 	wpabuf_put_u8(buf, 1);
1078 	wpabuf_put_u8(buf, query_reason);
1079 
1080 	if (cand_list)
1081 		wnm_add_cand_list(wpa_s, &buf);
1082 
1083 	if (btm_candidates) {
1084 		const size_t max_len = 1000;
1085 
1086 		ret = wpabuf_resize(&buf, max_len);
1087 		if (ret < 0) {
1088 			wpabuf_free(buf);
1089 			return ret;
1090 		}
1091 
1092 		ret = ieee802_11_parse_candidate_list(btm_candidates,
1093 						      wpabuf_put(buf, 0),
1094 						      max_len);
1095 		if (ret < 0) {
1096 			wpabuf_free(buf);
1097 			return ret;
1098 		}
1099 
1100 		wpabuf_put(buf, ret);
1101 	}
1102 
1103 	ret = wpa_drv_send_action(wpa_s, 0, 0,
1104 				  wpabuf_head_u8(buf), wpabuf_len(buf), 0);
1105 
1106 	wpabuf_free(buf);
1107 	return ret;
1108 }
1109 
ieee802_11_rx_wnm_action(struct wpa_supplicant * wpa_s,u8 * sender,u8 * payload,size_t len)1110 void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
1111 			      u8 *sender, u8 *payload, size_t len)
1112 {
1113 	const u8 *pos, *end;
1114 	u8 act;
1115 
1116 	if (len < 2)
1117 		return;
1118 
1119 	pos = payload;
1120 	act = *pos++;
1121 	end = payload + len;
1122 
1123 	wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR,
1124 		   act, MAC2STR(sender));
1125 
1126 	switch (act) {
1127 	case WNM_BSS_TRANS_MGMT_REQ:
1128 		ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end,
1129 						 0x01);
1130 		break;
1131 	default:
1132 		wpa_printf(MSG_ERROR, "WNM: Unknown request");
1133 		break;
1134 	}
1135 }
1136