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 	/* TODO security Match */
312 
313 	return true;
314 }
315 
316 
317 static struct wpa_bss *
compare_scan_neighbor_results(struct wpa_supplicant * wpa_s,os_time_t age_secs,enum mbo_transition_reject_reason * reason)318 compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs,
319 			      enum mbo_transition_reject_reason *reason)
320 {
321 	u8 i;
322 	struct wpa_bss *bss = wpa_s->current_bss;
323 	struct wpa_bss *target;
324 
325 	if (!bss)
326 		return NULL;
327 
328 	wnm_clear_acceptable(wpa_s);
329 
330 	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
331 		struct neighbor_report *nei;
332 
333 		nei = &wpa_s->wnm_neighbor_report_elements[i];
334 		if (nei->preference_present && nei->preference == 0) {
335 			wpa_printf(MSG_DEBUG, "Skip excluded BSS " MACSTR,
336 				   MAC2STR(nei->bssid));
337 			continue;
338 		}
339 
340 		target = wpa_bss_get_bssid(wpa_s, nei->bssid);
341 		if (!target) {
342 			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
343 				   " (pref %d) not found in scan results",
344 				   MAC2STR(nei->bssid),
345 				   nei->preference_present ? nei->preference :
346 				   -1);
347 			continue;
348 		}
349 
350 		if (age_secs) {
351 			struct os_reltime now;
352 
353 			if (os_get_reltime(&now) == 0 &&
354 			    os_time_expired(&now, &target->last_update,
355 					       age_secs)) {
356 				wpa_printf(MSG_DEBUG,
357 					   "Candidate BSS is more than %ld seconds old",
358 					   age_secs);
359 				continue;
360 			}
361 		}
362 
363 		if (bss->ssid_len != target->ssid_len ||
364 		    os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) {
365 			/*
366 			 * TODO: Could consider allowing transition to another
367 			 * ESS if PMF was enabled for the association.
368 			 */
369 			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
370 				   " (pref %d) in different ESS",
371 				   MAC2STR(nei->bssid),
372 				   nei->preference_present ? nei->preference :
373 				   -1);
374 			continue;
375 		}
376 
377 		if (wpa_s->current_bss &&
378 		    !wpa_scan_res_match(wpa_s, wpa_s->current_bss, target)) {
379 			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
380 				   " (pref %d) does not match the current network profile",
381 				   MAC2STR(nei->bssid),
382 				   nei->preference_present ? nei->preference :
383 				   -1);
384 			continue;
385 		}
386 
387 		if (target->level < bss->level && target->level < -80) {
388 			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
389 				   " (pref %d) does not have sufficient signal level (%d)",
390 				   MAC2STR(nei->bssid),
391 				   nei->preference_present ? nei->preference :
392 				   -1,
393 				   target->level);
394 			continue;
395 		}
396 
397 		nei->acceptable = 1;
398 	}
399 
400 #ifdef CONFIG_MBO
401 	if (wpa_s->wnm_mbo_trans_reason_present)
402 		target = get_mbo_transition_candidate(wpa_s, reason);
403 	else
404 		target = get_first_acceptable(wpa_s);
405 #else /* CONFIG_MBO */
406 	target = get_first_acceptable(wpa_s);
407 #endif /* CONFIG_MBO */
408 
409 	if (target) {
410 		wpa_printf(MSG_DEBUG,
411 			   "WNM: Found an acceptable preferred transition candidate BSS "
412 			   MACSTR " (RSSI %d)",
413 			   MAC2STR(target->bssid), target->level);
414 	}
415 
416 	return target;
417 }
418 
419 
wpa_bss_ies_eq(struct wpa_bss * a,struct wpa_bss * b,u8 eid)420 static int wpa_bss_ies_eq(struct wpa_bss *a, struct wpa_bss *b, u8 eid)
421 {
422 	const u8 *ie_a, *ie_b;
423 
424 	if (!a || !b)
425 		return 0;
426 
427 	ie_a = wpa_bss_get_ie(a, eid);
428 	ie_b = wpa_bss_get_ie(b, eid);
429 
430 	if (!ie_a || !ie_b || ie_a[1] != ie_b[1])
431 		return 0;
432 
433 	return os_memcmp(ie_a, ie_b, ie_a[1]) == 0;
434 }
435 
436 
wnm_get_bss_info(struct wpa_supplicant * wpa_s,struct wpa_bss * bss)437 static u32 wnm_get_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
438 {
439 	u32 info = 0;
440 
441 	info |= NEI_REP_BSSID_INFO_AP_UNKNOWN_REACH;
442 
443 	/*
444 	 * Leave the security and key scope bits unset to indicate that the
445 	 * security information is not available.
446 	 */
447 
448 	if (bss->caps & WLAN_CAPABILITY_SPECTRUM_MGMT)
449 		info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
450 	if (bss->caps & WLAN_CAPABILITY_QOS)
451 		info |= NEI_REP_BSSID_INFO_QOS;
452 	if (bss->caps & WLAN_CAPABILITY_APSD)
453 		info |= NEI_REP_BSSID_INFO_APSD;
454 	if (bss->caps & WLAN_CAPABILITY_RADIO_MEASUREMENT)
455 		info |= NEI_REP_BSSID_INFO_RM;
456 	if (bss->caps & WLAN_CAPABILITY_DELAYED_BLOCK_ACK)
457 		info |= NEI_REP_BSSID_INFO_DELAYED_BA;
458 	if (bss->caps & WLAN_CAPABILITY_IMM_BLOCK_ACK)
459 		info |= NEI_REP_BSSID_INFO_IMM_BA;
460 	if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_MOBILITY_DOMAIN))
461 		info |= NEI_REP_BSSID_INFO_MOBILITY_DOMAIN;
462 	if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_HT_CAP))
463 		info |= NEI_REP_BSSID_INFO_HT;
464 
465 	return info;
466 }
467 
468 
wnm_add_nei_rep(struct wpabuf ** buf,const u8 * bssid,u32 bss_info,u8 op_class,u8 chan,u8 phy_type,u8 pref)469 static int wnm_add_nei_rep(struct wpabuf **buf, const u8 *bssid,
470 			   u32 bss_info, u8 op_class, u8 chan, u8 phy_type,
471 			   u8 pref)
472 {
473 	if (wpabuf_len(*buf) + 18 >
474 	    IEEE80211_MAX_MMPDU_SIZE - IEEE80211_HDRLEN) {
475 		wpa_printf(MSG_DEBUG,
476 			   "WNM: No room in frame for Neighbor Report element");
477 		return -1;
478 	}
479 
480 	if (wpabuf_resize(buf, 18) < 0) {
481 		wpa_printf(MSG_DEBUG,
482 			   "WNM: Failed to allocate memory for Neighbor Report element");
483 		return -1;
484 	}
485 
486 	wpabuf_put_u8(*buf, WLAN_EID_NEIGHBOR_REPORT);
487 	/* length: 13 for basic neighbor report + 3 for preference subelement */
488 	wpabuf_put_u8(*buf, 16);
489 	wpabuf_put_data(*buf, bssid, ETH_ALEN);
490 	wpabuf_put_le32(*buf, bss_info);
491 	wpabuf_put_u8(*buf, op_class);
492 	wpabuf_put_u8(*buf, chan);
493 	wpabuf_put_u8(*buf, phy_type);
494 	wpabuf_put_u8(*buf, WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE);
495 	wpabuf_put_u8(*buf, 1);
496 	wpabuf_put_u8(*buf, pref);
497 	return 0;
498 }
499 
wnm_nei_rep_add_bss(struct wpa_supplicant * wpa_s,struct wpa_bss * bss,struct wpabuf ** buf,u8 pref)500 static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s,
501 			       struct wpa_bss *bss, struct wpabuf **buf,
502 			       u8 pref)
503 {
504 	const u8 *ie;
505 	u8 op_class;
506 	int sec_chan = 0;
507 	enum phy_type phy_type;
508 	u32 info;
509 	struct ieee80211_ht_operation *ht_oper = NULL;
510 
511 	ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
512 	if (ie && ie[1] >= 2) {
513 		ht_oper = (struct ieee80211_ht_operation *) (ie + 2);
514 
515 		if (ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
516 			sec_chan = 1;
517 		else if (ht_oper->ht_param &
518 			 HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
519 			sec_chan = -1;
520 	}
521 
522 	op_class = get_operating_class(bss->channel, sec_chan);
523 
524 	phy_type = (sec_chan != 0) ? PHY_TYPE_HT : PHY_TYPE_ERP;
525 
526 	info = wnm_get_bss_info(wpa_s, bss);
527 
528 	return wnm_add_nei_rep(buf, bss->bssid, info, op_class, bss->channel, phy_type,
529 			       pref);
530 }
531 
532 
wnm_add_cand_list(struct wpa_supplicant * wpa_s,struct wpabuf ** buf)533 static void wnm_add_cand_list(struct wpa_supplicant *wpa_s, struct wpabuf **buf)
534 {
535 	unsigned int i, pref = 255;
536 	struct os_reltime now;
537 
538 	if (!wpa_s->current_bss)
539 		return;
540 
541 	/*
542 	 * TODO: Define when scan results are no longer valid for the candidate
543 	 * list.
544 	 */
545 	os_get_reltime(&now);
546 	if (os_time_expired(&now, &wpa_s->last_scan, 10))
547 		return;
548 
549 	wpa_printf(MSG_DEBUG,
550 		   "WNM: Add candidate list to BSS Transition Management Response frame");
551 	for (i = 0; i < wpa_s->last_scan_res_used && pref; i++) {
552 		struct wpa_bss *bss = wpa_s->last_scan_res[i];
553 		int res;
554 
555 		if (wpa_scan_res_match(wpa_s, wpa_s->current_bss, bss)) {
556 			res = wnm_nei_rep_add_bss(wpa_s, bss, buf, pref--);
557 			if (res == -2)
558 				continue; /* could not build entry for BSS */
559 			if (res < 0)
560 				break; /* no more room for candidates */
561 			if (pref == 1)
562 				break;
563 		}
564 	}
565 
566 	wpa_hexdump_buf(MSG_DEBUG,
567 			"WNM: BSS Transition Management Response candidate list",
568 			*buf);
569 }
570 
571 
572 #define BTM_RESP_MIN_SIZE	5 + ETH_ALEN
573 
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)574 static void wnm_send_bss_transition_mgmt_resp(
575 	struct wpa_supplicant *wpa_s, u8 dialog_token,
576 	enum bss_trans_mgmt_status_code status,
577 	enum mbo_transition_reject_reason reason,
578 	u8 delay, const u8 *target_bssid)
579 {
580 	struct wpabuf *buf;
581 	int res;
582 
583 	if (!wpa_s->current_bss) {
584 		wpa_printf(MSG_DEBUG,
585 			   "WNM: Current BSS not known - drop response");
586 		return;
587 	}
588 	wpa_printf(MSG_DEBUG,
589 		   "WNM: Send BSS Transition Management Response to " MACSTR
590 		   " dialog_token=%u status=%u reason=%u delay=%d",
591 		   MAC2STR(wpa_s->current_bss->bssid), dialog_token, status, reason, delay);
592 
593 	buf = wpabuf_alloc(BTM_RESP_MIN_SIZE);
594 	if (!buf) {
595 		wpa_printf(MSG_DEBUG,
596 			   "WNM: Failed to allocate memory for BTM response");
597 		return;
598 	}
599 
600 	wpabuf_put_u8(buf, WLAN_ACTION_WNM);
601 	wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_RESP);
602 	wpabuf_put_u8(buf, dialog_token);
603 	wpabuf_put_u8(buf, status);
604 	wpabuf_put_u8(buf, delay);
605 	if (target_bssid) {
606 		wpabuf_put_data(buf, target_bssid, ETH_ALEN);
607 	} else if (status == WNM_BSS_TM_ACCEPT) {
608 		/*
609 		 * P802.11-REVmc clarifies that the Target BSSID field is always
610 		 * present when status code is zero, so use a fake value here if
611 		 * no BSSID is yet known.
612 		 */
613 		wpabuf_put_data(buf, "\0\0\0\0\0\0", ETH_ALEN);
614 	}
615 
616 	if (status == WNM_BSS_TM_ACCEPT)
617 		wnm_add_cand_list(wpa_s, &buf);
618 
619 #ifdef CONFIG_MBO
620 	if (status != WNM_BSS_TM_ACCEPT &&
621 	    wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) {
622 		u8 mbo[10];
623 		size_t ret;
624 
625 		ret = wpas_mbo_ie_bss_trans_reject(wpa_s, mbo, sizeof(mbo),
626 						   reason);
627 		if (ret) {
628 			if (wpabuf_resize(&buf, ret) < 0) {
629 				wpabuf_free(buf);
630 				wpa_printf(MSG_DEBUG,
631 					   "WNM: Failed to allocate memory for MBO IE");
632 				return;
633 			}
634 
635 			wpabuf_put_data(buf, mbo, ret);
636 		}
637 	}
638 #endif /* CONFIG_MBO */
639 
640 	res = wpa_drv_send_action(wpa_s, 0, 0,
641 				  wpabuf_head_u8(buf), wpabuf_len(buf), 0);
642 	if (res < 0) {
643 		wpa_printf(MSG_DEBUG,
644 			   "WNM: Failed to send BSS Transition Management Response");
645 	}
646 
647 	wpabuf_free(buf);
648 }
649 
wnm_bss_tm_connect(struct wpa_supplicant * wpa_s,struct wpa_bss * bss,char * ssid,int after_new_scan)650 void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s,
651 			struct wpa_bss *bss, char *ssid,
652 			int after_new_scan)
653 {
654 	wpa_printf(MSG_DEBUG,
655 		"WNM: Transition to BSS " MACSTR
656 		" based on BSS Transition Management Request after_new_scan=%d)",
657 		MAC2STR(bss->bssid), after_new_scan);
658 
659 	/* Send the BSS Management Response - Accept */
660 	if (wpa_s->wnm_reply) {
661 		wpa_s->wnm_reply = 0;
662 		wpa_printf(MSG_DEBUG,
663 			   "WNM: Sending successful BSS Transition Management Response");
664 		wnm_send_bss_transition_mgmt_resp(
665 			wpa_s, wpa_s->wnm_dialog_token, WNM_BSS_TM_ACCEPT,
666 			MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
667 			bss->bssid);
668 	}
669 
670 	if (bss == wpa_s->current_bss) {
671 		wpa_printf(MSG_DEBUG,
672 			   "WNM: Already associated with the preferred candidate");
673 		wnm_deallocate_memory(wpa_s);
674 		return;
675 	}
676 
677 	wpa_printf(MSG_DEBUG, "WNM: Issuing connect");
678 
679 	wpa_supplicant_connect(wpa_s, bss, ssid);
680 	wnm_deallocate_memory(wpa_s);
681 }
682 
683 
wnm_scan_process(struct wpa_supplicant * wpa_s,int reply_on_fail)684 int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail)
685 {
686 	struct wpa_bss *bss;
687 	enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED;
688 	enum mbo_transition_reject_reason reason =
689 		MBO_TRANSITION_REJECT_REASON_UNSPECIFIED;
690 
691 	if (!wpa_s->wnm_neighbor_report_elements)
692 		return 0;
693 
694 	wpa_dbg(wpa_s, MSG_DEBUG,
695 		"WNM: Process scan results for BSS Transition Management");
696 	if (os_reltime_before(&wpa_s->wnm_cand_valid_until,
697 			      &wpa_s->scan_trigger_time)) {
698 		wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it");
699 		wnm_deallocate_memory(wpa_s);
700 		return 0;
701 	}
702 
703 	/* Compare the Neighbor Report and scan results */
704 	bss = compare_scan_neighbor_results(wpa_s, 0, &reason);
705 	if (!bss) {
706 		wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found");
707 		status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES;
708 		goto send_bss_resp_fail;
709 	}
710 
711 	/* Associate to the network */
712 	wnm_bss_tm_connect(wpa_s, bss, NULL, 1);
713 	return 1;
714 
715 send_bss_resp_fail:
716 	if (!reply_on_fail)
717 		return 0;
718 
719 	/* Send reject response for all the failures */
720 
721 	if (wpa_s->wnm_reply) {
722 		wpa_s->wnm_reply = 0;
723 		wnm_send_bss_transition_mgmt_resp(wpa_s,
724 						  wpa_s->wnm_dialog_token,
725 						  status, reason, 0, NULL);
726 	}
727 	wnm_deallocate_memory(wpa_s);
728 
729 	return 0;
730 }
731 
732 
cand_pref_compar(const void * a,const void * b)733 static int cand_pref_compar(const void *a, const void *b)
734 {
735 	const struct neighbor_report *aa = a;
736 	const struct neighbor_report *bb = b;
737 
738 	if (!aa->preference_present && !bb->preference_present)
739 		return 0;
740 	if (!aa->preference_present)
741 		return 1;
742 	if (!bb->preference_present)
743 		return -1;
744 	if (bb->preference > aa->preference)
745 		return 1;
746 	if (bb->preference < aa->preference)
747 		return -1;
748 	return 0;
749 }
750 
751 
wnm_sort_cand_list(struct wpa_supplicant * wpa_s)752 static void wnm_sort_cand_list(struct wpa_supplicant *wpa_s)
753 {
754 	if (!wpa_s->wnm_neighbor_report_elements)
755 		return;
756 	qsort(wpa_s->wnm_neighbor_report_elements,
757 	      wpa_s->wnm_num_neighbor_report, sizeof(struct neighbor_report),
758 	      cand_pref_compar);
759 }
760 
761 
wnm_dump_cand_list(struct wpa_supplicant * wpa_s)762 static void wnm_dump_cand_list(struct wpa_supplicant *wpa_s)
763 {
764 #ifdef DEBUG_PRINT
765 	unsigned int i;
766 
767 	wpa_printf(MSG_DEBUG, "WNM: BSS Transition Candidate List");
768 	if (!wpa_s->wnm_neighbor_report_elements)
769 		return;
770 	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
771 		struct neighbor_report *nei;
772 
773 		nei = &wpa_s->wnm_neighbor_report_elements[i];
774 		wpa_printf(MSG_DEBUG, "%u: " MACSTR
775 			   " info=0x%x op_class=%u chan=%u phy=%u pref=%d",
776 			   i, MAC2STR(nei->bssid), nei->bssid_info,
777 			   nei->regulatory_class,
778 			   nei->channel_number, nei->phy_type,
779 			   nei->preference_present ? nei->preference : -1);
780 	}
781 #endif
782 }
783 
wnm_set_scan_freqs(struct wpa_supplicant * wpa_s)784 static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s)
785 {
786 	unsigned int i;
787 	int num_chan = 0;
788 	u8 chan = 0;
789 
790 	wpa_s->next_scan_chan = 0;
791 	if (!wpa_s->wnm_neighbor_report_elements)
792 		return;
793 
794 	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
795 		struct neighbor_report *nei;
796 
797 		nei = &wpa_s->wnm_neighbor_report_elements[i];
798 		if (nei->channel_number <= 0) {
799 			wpa_printf(MSG_DEBUG,
800 				   "WNM: Unknown neighbor operating channel_number for "
801 				   MACSTR " - scan all channels",
802 				   MAC2STR(nei->bssid));
803 			return;
804 		}
805 		if (nei->channel_number != chan) {
806 			chan = nei->channel_number;
807 			num_chan++;
808 		}
809 	}
810 
811 	if (num_chan == 1) {
812 		wpa_s->next_scan_chan = chan;
813 	}
814 }
815 
wnm_fetch_scan_results(struct wpa_supplicant * wpa_s)816 static int wnm_fetch_scan_results(struct wpa_supplicant *wpa_s)
817 {
818 	/* ESP doesn't support this */
819 	return 0;
820 }
821 
ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant * wpa_s,const u8 * pos,const u8 * end,int reply)822 static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
823 					     const u8 *pos, const u8 *end,
824 					     int reply)
825 {
826 	unsigned int beacon_int;
827 	u8 valid_int;
828 #ifdef CONFIG_MBO
829 	const u8 *vendor;
830 #endif /* CONFIG_MBO */
831 #ifdef ESP_SUPPLICANT
832 	bool scan_required = false;
833 #endif
834 
835 	if (wpa_s->disable_mbo_oce || wpa_s->disable_btm)
836 		return;
837 
838 	if (end - pos < 5)
839 		return;
840 
841 #ifdef CONFIG_MBO
842 	wpa_s->wnm_mbo_trans_reason_present = 0;
843 	wpa_s->wnm_mbo_transition_reason = 0;
844 #endif /* CONFIG_MBO */
845 
846 	if (wpa_s->current_bss)
847 		beacon_int = wpa_s->current_bss->beacon_int;
848 	else
849 		beacon_int = 100; /* best guess */
850 
851 	wpa_s->wnm_dialog_token = pos[0];
852 	wpa_s->wnm_mode = pos[1];
853 	wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2);
854 	valid_int = pos[4];
855 	wpa_s->wnm_reply = reply;
856 
857 	wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: "
858 		   "dialog_token=%u request_mode=0x%x "
859 		   "disassoc_timer=%u validity_interval=%u",
860 		   wpa_s->wnm_dialog_token, wpa_s->wnm_mode,
861 		   wpa_s->wnm_dissoc_timer, valid_int);
862 
863 #if defined(CONFIG_MBO) && defined(CONFIG_TESTING_OPTIONS)
864 	if (wpa_s->reject_btm_req_reason) {
865 		wpa_printf(MSG_INFO,
866 			   "WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d",
867 			   wpa_s->reject_btm_req_reason);
868 		wnm_send_bss_transition_mgmt_resp(
869 			wpa_s, wpa_s->wnm_dialog_token,
870 			wpa_s->reject_btm_req_reason,
871 			MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
872 		return;
873 	}
874 #endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */
875 
876 	pos += 5;
877 
878 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
879 		if (end - pos < 12) {
880 			wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request");
881 			return;
882 		}
883 		os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12);
884 		pos += 12; /* BSS Termination Duration */
885 	}
886 
887 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) {
888 		char url[256];
889 
890 		if (end - pos < 1 || 1 + pos[0] > end - pos) {
891 			wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
892 				   "Management Request (URL)");
893 			return;
894 		}
895 		os_memcpy(url, pos + 1, pos[0]);
896 		url[pos[0]] = '\0';
897 		pos += 1 + pos[0];
898 
899 		wpa_msg(wpa_s, MSG_DEBUG, "ESS_DISASSOC_IMMINENT %u %s",
900 			wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url);
901 	}
902 
903 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
904 		wpa_msg(wpa_s, MSG_DEBUG, "WNM: Disassociation Imminent - "
905 			"Disassociation Timer %u", wpa_s->wnm_dissoc_timer);
906 		if (wpa_s->wnm_dissoc_timer) {
907 			/* TODO: mark current BSS less preferred for
908 			 * selection */
909 #ifdef ESP_SUPPLICANT
910 			os_memset(wpa_s->next_scan_bssid, 0, ETH_ALEN);
911 			wpa_s->next_scan_chan = 0;
912 			scan_required = true;
913 #else
914 			wpa_printf(MSG_DEBUG, "Trying to find another BSS");
915 			wpa_supplicant_req_scan(wpa_s, 0, 0);
916 #endif
917 		}
918 	}
919 
920 #ifdef CONFIG_MBO
921 	vendor = get_ie(pos, end - pos, WLAN_EID_VENDOR_SPECIFIC);
922 	if (vendor)
923 		wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]);
924 #endif /* CONFIG_MBO */
925 
926 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
927 		unsigned int valid_ms;
928 
929 		wpa_msg(wpa_s, MSG_DEBUG, "WNM: Preferred List Available");
930 		wnm_deallocate_memory(wpa_s);
931 		wpa_s->wnm_neighbor_report_elements = os_calloc(
932 			WNM_MAX_NEIGHBOR_REPORT,
933 			sizeof(struct neighbor_report));
934 		if (wpa_s->wnm_neighbor_report_elements == NULL)
935 			return;
936 
937 		while (end - pos >= 2 &&
938 		       wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT)
939 		{
940 			u8 tag = *pos++;
941 			u8 len = *pos++;
942 
943 			wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u",
944 				   tag);
945 			if (len > end - pos) {
946 				wpa_printf(MSG_DEBUG, "WNM: Truncated request");
947 				return;
948 			}
949 			if (tag == WLAN_EID_NEIGHBOR_REPORT) {
950 				struct neighbor_report *rep;
951 				rep = &wpa_s->wnm_neighbor_report_elements[
952 					wpa_s->wnm_num_neighbor_report];
953 				wnm_parse_neighbor_report(wpa_s, pos, len, rep);
954 				wpa_s->wnm_num_neighbor_report++;
955 #ifdef CONFIG_MBO
956 				if (wpa_s->wnm_mbo_trans_reason_present &&
957 				    wpa_s->wnm_num_neighbor_report == 1) {
958 					rep->is_first = 1;
959 					wpa_printf(MSG_DEBUG,
960 						   "WNM: First transition candidate is "
961 						   MACSTR, MAC2STR(rep->bssid));
962 				}
963 #endif /* CONFIG_MBO */
964 			}
965 
966 			pos += len;
967 		}
968 
969 		if (!wpa_s->wnm_num_neighbor_report) {
970 			wpa_printf(MSG_DEBUG,
971 				   "WNM: Candidate list included bit is set, but no candidates found");
972 			wnm_send_bss_transition_mgmt_resp(
973 				wpa_s, wpa_s->wnm_dialog_token,
974 				WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
975 				MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
976 				NULL);
977 			return;
978 		}
979 
980 		wnm_sort_cand_list(wpa_s);
981 		wnm_dump_cand_list(wpa_s);
982 		valid_ms = valid_int * beacon_int * 128 / 125;
983 		wpa_printf(MSG_DEBUG, "WNM: Candidate list valid for %u ms",
984 			   valid_ms);
985 		os_get_reltime(&wpa_s->wnm_cand_valid_until);
986 		wpa_s->wnm_cand_valid_until.sec += valid_ms / 1000;
987 		wpa_s->wnm_cand_valid_until.usec += (valid_ms % 1000) * 1000;
988 		wpa_s->wnm_cand_valid_until.sec +=
989 			wpa_s->wnm_cand_valid_until.usec / 1000000;
990 		wpa_s->wnm_cand_valid_until.usec %= 1000000;
991 
992 		/*
993 		 * Fetch the latest scan results from the kernel and check for
994 		 * candidates based on those results first. This can help in
995 		 * finding more up-to-date information should the driver has
996 		 * done some internal scanning operations after the last scan
997 		 * result update in wpa_supplicant.
998 		 */
999 		if (wnm_fetch_scan_results(wpa_s) > 0)
1000 			return;
1001 
1002 		/*
1003 		 * Try to use previously received scan results, if they are
1004 		 * recent enough to use for a connection.
1005 		 */
1006 		if (wpa_s->last_scan_res_used > 0) {
1007 			struct os_reltime now;
1008 
1009 			os_get_reltime(&now);
1010 			if (!os_time_expired(&now, &wpa_s->last_scan, 10)) {
1011 				wpa_printf(MSG_DEBUG,
1012 					   "WNM: Try to use recent scan results");
1013 				if (wnm_scan_process(wpa_s, 0) > 0)
1014 					return;
1015 				wpa_printf(MSG_DEBUG,
1016 					   "WNM: No match in previous scan results - try a new scan");
1017 			}
1018 		}
1019 		wnm_set_scan_freqs(wpa_s);
1020 		if (wpa_s->wnm_num_neighbor_report == 1) {
1021 			os_memcpy(wpa_s->next_scan_bssid,
1022 				  wpa_s->wnm_neighbor_report_elements[0].bssid,
1023 				  ETH_ALEN);
1024 			wpa_printf(MSG_DEBUG,
1025 				   "WNM: Scan only for a specific BSSID since there is only a single candidate "
1026 				   MACSTR, MAC2STR(wpa_s->next_scan_bssid));
1027 		}
1028 #ifdef ESP_SUPPLICANT
1029 		scan_required = true;
1030 	}
1031 	if (scan_required) {
1032 		wpa_printf(MSG_DEBUG, "Trying to find another BSS");
1033 #endif
1034 		wpa_supplicant_req_scan(wpa_s, 0, 0);
1035 	} else if (reply) {
1036 		enum bss_trans_mgmt_status_code status;
1037 		if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT)
1038 			status = WNM_BSS_TM_ACCEPT;
1039 		else {
1040 			wpa_msg(wpa_s, MSG_DEBUG, "WNM: BSS Transition Management Request did not include candidates");
1041 			status = WNM_BSS_TM_REJECT_UNSPECIFIED;
1042 		}
1043 		wpa_s->wnm_reply = 0;
1044 		wnm_send_bss_transition_mgmt_resp(
1045 			wpa_s, wpa_s->wnm_dialog_token, status,
1046 			MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
1047 	}
1048 }
1049 
1050 
1051 #define BTM_QUERY_MIN_SIZE	4
1052 
wnm_send_bss_transition_mgmt_query(struct wpa_supplicant * wpa_s,u8 query_reason,const char * btm_candidates,int cand_list)1053 int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
1054 				       u8 query_reason,
1055 				       const char *btm_candidates,
1056 				       int cand_list)
1057 {
1058 	struct wpabuf *buf;
1059 	int ret;
1060 
1061 	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to "
1062 		   MACSTR " query_reason=%u%s",
1063 		   MAC2STR(wpa_s->current_bss->bssid), query_reason,
1064 		   cand_list ? " candidate list" : "");
1065 
1066 	buf = wpabuf_alloc(BTM_QUERY_MIN_SIZE);
1067 	if (!buf)
1068 		return -1;
1069 
1070 	wpabuf_put_u8(buf, WLAN_ACTION_WNM);
1071 	wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_QUERY);
1072 	wpabuf_put_u8(buf, 1);
1073 	wpabuf_put_u8(buf, query_reason);
1074 
1075 	if (cand_list)
1076 		wnm_add_cand_list(wpa_s, &buf);
1077 
1078 	if (btm_candidates) {
1079 		const size_t max_len = 1000;
1080 
1081 		ret = wpabuf_resize(&buf, max_len);
1082 		if (ret < 0) {
1083 			wpabuf_free(buf);
1084 			return ret;
1085 		}
1086 
1087 		ret = ieee802_11_parse_candidate_list(btm_candidates,
1088 						      wpabuf_put(buf, 0),
1089 						      max_len);
1090 		if (ret < 0) {
1091 			wpabuf_free(buf);
1092 			return ret;
1093 		}
1094 
1095 		wpabuf_put(buf, ret);
1096 	}
1097 
1098 	ret = wpa_drv_send_action(wpa_s, 0, 0,
1099 				  wpabuf_head_u8(buf), wpabuf_len(buf), 0);
1100 
1101 	wpabuf_free(buf);
1102 	return ret;
1103 }
1104 
ieee802_11_rx_wnm_action(struct wpa_supplicant * wpa_s,u8 * sender,u8 * payload,size_t len)1105 void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
1106 			      u8 *sender, u8 *payload, size_t len)
1107 {
1108 	const u8 *pos, *end;
1109 	u8 act;
1110 
1111 	if (len < 2)
1112 		return;
1113 
1114 	pos = payload;
1115 	act = *pos++;
1116 	end = payload + len;
1117 
1118 	wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR,
1119 		   act, MAC2STR(sender));
1120 
1121 	switch (act) {
1122 	case WNM_BSS_TRANS_MGMT_REQ:
1123 		ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end,
1124 						 0x01);
1125 		break;
1126 	default:
1127 		wpa_printf(MSG_ERROR, "WNM: Unknown request");
1128 		break;
1129 	}
1130 }
1131