xref: /aosp_15_r20/external/wpa_supplicant_8/wpa_supplicant/rrm.c (revision 03f9172ca588f91df233974f4258bab95191f931)
1*03f9172cSAndroid Build Coastguard Worker /*
2*03f9172cSAndroid Build Coastguard Worker  * wpa_supplicant - Radio Measurements
3*03f9172cSAndroid Build Coastguard Worker  * Copyright (c) 2003-2016, Jouni Malinen <[email protected]>
4*03f9172cSAndroid Build Coastguard Worker  *
5*03f9172cSAndroid Build Coastguard Worker  * This software may be distributed under the terms of the BSD license.
6*03f9172cSAndroid Build Coastguard Worker  * See README for more details.
7*03f9172cSAndroid Build Coastguard Worker  */
8*03f9172cSAndroid Build Coastguard Worker 
9*03f9172cSAndroid Build Coastguard Worker #include "includes.h"
10*03f9172cSAndroid Build Coastguard Worker 
11*03f9172cSAndroid Build Coastguard Worker #include "utils/common.h"
12*03f9172cSAndroid Build Coastguard Worker #include "utils/eloop.h"
13*03f9172cSAndroid Build Coastguard Worker #include "common/ieee802_11_common.h"
14*03f9172cSAndroid Build Coastguard Worker #include "wpa_supplicant_i.h"
15*03f9172cSAndroid Build Coastguard Worker #include "driver_i.h"
16*03f9172cSAndroid Build Coastguard Worker #include "bss.h"
17*03f9172cSAndroid Build Coastguard Worker #include "scan.h"
18*03f9172cSAndroid Build Coastguard Worker #include "p2p_supplicant.h"
19*03f9172cSAndroid Build Coastguard Worker 
20*03f9172cSAndroid Build Coastguard Worker 
wpas_rrm_neighbor_rep_timeout_handler(void * data,void * user_ctx)21*03f9172cSAndroid Build Coastguard Worker static void wpas_rrm_neighbor_rep_timeout_handler(void *data, void *user_ctx)
22*03f9172cSAndroid Build Coastguard Worker {
23*03f9172cSAndroid Build Coastguard Worker 	struct rrm_data *rrm = data;
24*03f9172cSAndroid Build Coastguard Worker 
25*03f9172cSAndroid Build Coastguard Worker 	if (!rrm->notify_neighbor_rep) {
26*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_ERROR,
27*03f9172cSAndroid Build Coastguard Worker 			   "RRM: Unexpected neighbor report timeout");
28*03f9172cSAndroid Build Coastguard Worker 		return;
29*03f9172cSAndroid Build Coastguard Worker 	}
30*03f9172cSAndroid Build Coastguard Worker 
31*03f9172cSAndroid Build Coastguard Worker 	wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report - NONE");
32*03f9172cSAndroid Build Coastguard Worker 	rrm->notify_neighbor_rep(rrm->neighbor_rep_cb_ctx, NULL);
33*03f9172cSAndroid Build Coastguard Worker 
34*03f9172cSAndroid Build Coastguard Worker 	rrm->notify_neighbor_rep = NULL;
35*03f9172cSAndroid Build Coastguard Worker 	rrm->neighbor_rep_cb_ctx = NULL;
36*03f9172cSAndroid Build Coastguard Worker }
37*03f9172cSAndroid Build Coastguard Worker 
38*03f9172cSAndroid Build Coastguard Worker 
39*03f9172cSAndroid Build Coastguard Worker /*
40*03f9172cSAndroid Build Coastguard Worker  * wpas_rrm_reset - Clear and reset all RRM data in wpa_supplicant
41*03f9172cSAndroid Build Coastguard Worker  * @wpa_s: Pointer to wpa_supplicant
42*03f9172cSAndroid Build Coastguard Worker  */
wpas_rrm_reset(struct wpa_supplicant * wpa_s)43*03f9172cSAndroid Build Coastguard Worker void wpas_rrm_reset(struct wpa_supplicant *wpa_s)
44*03f9172cSAndroid Build Coastguard Worker {
45*03f9172cSAndroid Build Coastguard Worker 	wpa_s->rrm.rrm_used = 0;
46*03f9172cSAndroid Build Coastguard Worker 
47*03f9172cSAndroid Build Coastguard Worker 	eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm,
48*03f9172cSAndroid Build Coastguard Worker 			     NULL);
49*03f9172cSAndroid Build Coastguard Worker 	if (wpa_s->rrm.notify_neighbor_rep)
50*03f9172cSAndroid Build Coastguard Worker 		wpas_rrm_neighbor_rep_timeout_handler(&wpa_s->rrm, NULL);
51*03f9172cSAndroid Build Coastguard Worker 	wpa_s->rrm.next_neighbor_rep_token = 1;
52*03f9172cSAndroid Build Coastguard Worker 	wpas_clear_beacon_rep_data(wpa_s);
53*03f9172cSAndroid Build Coastguard Worker }
54*03f9172cSAndroid Build Coastguard Worker 
55*03f9172cSAndroid Build Coastguard Worker 
56*03f9172cSAndroid Build Coastguard Worker /*
57*03f9172cSAndroid Build Coastguard Worker  * wpas_rrm_process_neighbor_rep - Handle incoming neighbor report
58*03f9172cSAndroid Build Coastguard Worker  * @wpa_s: Pointer to wpa_supplicant
59*03f9172cSAndroid Build Coastguard Worker  * @report: Neighbor report buffer, prefixed by a 1-byte dialog token
60*03f9172cSAndroid Build Coastguard Worker  * @report_len: Length of neighbor report buffer
61*03f9172cSAndroid Build Coastguard Worker  */
wpas_rrm_process_neighbor_rep(struct wpa_supplicant * wpa_s,const u8 * report,size_t report_len)62*03f9172cSAndroid Build Coastguard Worker void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s,
63*03f9172cSAndroid Build Coastguard Worker 				   const u8 *report, size_t report_len)
64*03f9172cSAndroid Build Coastguard Worker {
65*03f9172cSAndroid Build Coastguard Worker 	struct wpabuf *neighbor_rep;
66*03f9172cSAndroid Build Coastguard Worker 
67*03f9172cSAndroid Build Coastguard Worker 	wpa_hexdump(MSG_DEBUG, "RRM: New Neighbor Report", report, report_len);
68*03f9172cSAndroid Build Coastguard Worker 	if (report_len < 1)
69*03f9172cSAndroid Build Coastguard Worker 		return;
70*03f9172cSAndroid Build Coastguard Worker 
71*03f9172cSAndroid Build Coastguard Worker 	if (report[0] != wpa_s->rrm.next_neighbor_rep_token - 1) {
72*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_DEBUG,
73*03f9172cSAndroid Build Coastguard Worker 			   "RRM: Discarding neighbor report with token %d (expected %d)",
74*03f9172cSAndroid Build Coastguard Worker 			   report[0], wpa_s->rrm.next_neighbor_rep_token - 1);
75*03f9172cSAndroid Build Coastguard Worker 		return;
76*03f9172cSAndroid Build Coastguard Worker 	}
77*03f9172cSAndroid Build Coastguard Worker 
78*03f9172cSAndroid Build Coastguard Worker 	eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm,
79*03f9172cSAndroid Build Coastguard Worker 			     NULL);
80*03f9172cSAndroid Build Coastguard Worker 
81*03f9172cSAndroid Build Coastguard Worker 	if (!wpa_s->rrm.notify_neighbor_rep) {
82*03f9172cSAndroid Build Coastguard Worker 		wpa_msg(wpa_s, MSG_INFO, "RRM: Unexpected neighbor report");
83*03f9172cSAndroid Build Coastguard Worker 		return;
84*03f9172cSAndroid Build Coastguard Worker 	}
85*03f9172cSAndroid Build Coastguard Worker 
86*03f9172cSAndroid Build Coastguard Worker 	/* skipping the first byte, which is only an id (dialog token) */
87*03f9172cSAndroid Build Coastguard Worker 	neighbor_rep = wpabuf_alloc(report_len - 1);
88*03f9172cSAndroid Build Coastguard Worker 	if (!neighbor_rep) {
89*03f9172cSAndroid Build Coastguard Worker 		wpas_rrm_neighbor_rep_timeout_handler(&wpa_s->rrm, NULL);
90*03f9172cSAndroid Build Coastguard Worker 		return;
91*03f9172cSAndroid Build Coastguard Worker 	}
92*03f9172cSAndroid Build Coastguard Worker 	wpabuf_put_data(neighbor_rep, report + 1, report_len - 1);
93*03f9172cSAndroid Build Coastguard Worker 	wpa_dbg(wpa_s, MSG_DEBUG, "RRM: Notifying neighbor report (token = %d)",
94*03f9172cSAndroid Build Coastguard Worker 		report[0]);
95*03f9172cSAndroid Build Coastguard Worker 	wpa_s->rrm.notify_neighbor_rep(wpa_s->rrm.neighbor_rep_cb_ctx,
96*03f9172cSAndroid Build Coastguard Worker 				       neighbor_rep);
97*03f9172cSAndroid Build Coastguard Worker 	wpa_s->rrm.notify_neighbor_rep = NULL;
98*03f9172cSAndroid Build Coastguard Worker 	wpa_s->rrm.neighbor_rep_cb_ctx = NULL;
99*03f9172cSAndroid Build Coastguard Worker }
100*03f9172cSAndroid Build Coastguard Worker 
101*03f9172cSAndroid Build Coastguard Worker 
102*03f9172cSAndroid Build Coastguard Worker #if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS)
103*03f9172cSAndroid Build Coastguard Worker /* Workaround different, undefined for Windows, error codes used here */
104*03f9172cSAndroid Build Coastguard Worker #ifndef ENOTCONN
105*03f9172cSAndroid Build Coastguard Worker #define ENOTCONN -1
106*03f9172cSAndroid Build Coastguard Worker #endif
107*03f9172cSAndroid Build Coastguard Worker #ifndef EOPNOTSUPP
108*03f9172cSAndroid Build Coastguard Worker #define EOPNOTSUPP -1
109*03f9172cSAndroid Build Coastguard Worker #endif
110*03f9172cSAndroid Build Coastguard Worker #ifndef ECANCELED
111*03f9172cSAndroid Build Coastguard Worker #define ECANCELED -1
112*03f9172cSAndroid Build Coastguard Worker #endif
113*03f9172cSAndroid Build Coastguard Worker #endif
114*03f9172cSAndroid Build Coastguard Worker 
115*03f9172cSAndroid Build Coastguard Worker /* Measurement Request element + Location Subject + Maximum Age subelement */
116*03f9172cSAndroid Build Coastguard Worker #define MEASURE_REQUEST_LCI_LEN (3 + 1 + 4)
117*03f9172cSAndroid Build Coastguard Worker /* Measurement Request element + Location Civic Request */
118*03f9172cSAndroid Build Coastguard Worker #define MEASURE_REQUEST_CIVIC_LEN (3 + 5)
119*03f9172cSAndroid Build Coastguard Worker 
120*03f9172cSAndroid Build Coastguard Worker 
121*03f9172cSAndroid Build Coastguard Worker /**
122*03f9172cSAndroid Build Coastguard Worker  * wpas_rrm_send_neighbor_rep_request - Request a neighbor report from our AP
123*03f9172cSAndroid Build Coastguard Worker  * @wpa_s: Pointer to wpa_supplicant
124*03f9172cSAndroid Build Coastguard Worker  * @ssid: if not null, this is sent in the request. Otherwise, no SSID IE
125*03f9172cSAndroid Build Coastguard Worker  *	  is sent in the request.
126*03f9172cSAndroid Build Coastguard Worker  * @lci: if set, neighbor request will include LCI request
127*03f9172cSAndroid Build Coastguard Worker  * @civic: if set, neighbor request will include civic location request
128*03f9172cSAndroid Build Coastguard Worker  * @cb: Callback function to be called once the requested report arrives, or
129*03f9172cSAndroid Build Coastguard Worker  *	timed out after RRM_NEIGHBOR_REPORT_TIMEOUT seconds.
130*03f9172cSAndroid Build Coastguard Worker  *	In the former case, 'neighbor_rep' is a newly allocated wpabuf, and it's
131*03f9172cSAndroid Build Coastguard Worker  *	the requester's responsibility to free it.
132*03f9172cSAndroid Build Coastguard Worker  *	In the latter case NULL will be sent in 'neighbor_rep'.
133*03f9172cSAndroid Build Coastguard Worker  * @cb_ctx: Context value to send the callback function
134*03f9172cSAndroid Build Coastguard Worker  * Returns: 0 in case of success, negative error code otherwise
135*03f9172cSAndroid Build Coastguard Worker  *
136*03f9172cSAndroid Build Coastguard Worker  * In case there is a previous request which has not been answered yet, the
137*03f9172cSAndroid Build Coastguard Worker  * new request fails. The caller may retry after RRM_NEIGHBOR_REPORT_TIMEOUT.
138*03f9172cSAndroid Build Coastguard Worker  * Request must contain a callback function.
139*03f9172cSAndroid Build Coastguard Worker  */
wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant * wpa_s,const struct wpa_ssid_value * ssid,int lci,int civic,void (* cb)(void * ctx,struct wpabuf * neighbor_rep),void * cb_ctx)140*03f9172cSAndroid Build Coastguard Worker int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s,
141*03f9172cSAndroid Build Coastguard Worker 				       const struct wpa_ssid_value *ssid,
142*03f9172cSAndroid Build Coastguard Worker 				       int lci, int civic,
143*03f9172cSAndroid Build Coastguard Worker 				       void (*cb)(void *ctx,
144*03f9172cSAndroid Build Coastguard Worker 						  struct wpabuf *neighbor_rep),
145*03f9172cSAndroid Build Coastguard Worker 				       void *cb_ctx)
146*03f9172cSAndroid Build Coastguard Worker {
147*03f9172cSAndroid Build Coastguard Worker 	struct wpabuf *buf;
148*03f9172cSAndroid Build Coastguard Worker 	const u8 *rrm_ie;
149*03f9172cSAndroid Build Coastguard Worker 
150*03f9172cSAndroid Build Coastguard Worker 	if (wpa_s->wpa_state != WPA_COMPLETED || wpa_s->current_ssid == NULL) {
151*03f9172cSAndroid Build Coastguard Worker 		wpa_dbg(wpa_s, MSG_DEBUG, "RRM: No connection, no RRM.");
152*03f9172cSAndroid Build Coastguard Worker 		return -ENOTCONN;
153*03f9172cSAndroid Build Coastguard Worker 	}
154*03f9172cSAndroid Build Coastguard Worker 
155*03f9172cSAndroid Build Coastguard Worker 	if (!wpa_s->rrm.rrm_used) {
156*03f9172cSAndroid Build Coastguard Worker 		wpa_dbg(wpa_s, MSG_DEBUG, "RRM: No RRM in current connection.");
157*03f9172cSAndroid Build Coastguard Worker 		return -EOPNOTSUPP;
158*03f9172cSAndroid Build Coastguard Worker 	}
159*03f9172cSAndroid Build Coastguard Worker 
160*03f9172cSAndroid Build Coastguard Worker 	rrm_ie = wpa_bss_get_ie(wpa_s->current_bss,
161*03f9172cSAndroid Build Coastguard Worker 				WLAN_EID_RRM_ENABLED_CAPABILITIES);
162*03f9172cSAndroid Build Coastguard Worker 	if (!rrm_ie || !(wpa_s->current_bss->caps & IEEE80211_CAP_RRM) ||
163*03f9172cSAndroid Build Coastguard Worker 	    !(rrm_ie[2] & WLAN_RRM_CAPS_NEIGHBOR_REPORT)) {
164*03f9172cSAndroid Build Coastguard Worker 		wpa_dbg(wpa_s, MSG_DEBUG,
165*03f9172cSAndroid Build Coastguard Worker 			"RRM: No network support for Neighbor Report.");
166*03f9172cSAndroid Build Coastguard Worker 		return -EOPNOTSUPP;
167*03f9172cSAndroid Build Coastguard Worker 	}
168*03f9172cSAndroid Build Coastguard Worker 
169*03f9172cSAndroid Build Coastguard Worker 	/* Refuse if there's a live request */
170*03f9172cSAndroid Build Coastguard Worker 	if (wpa_s->rrm.notify_neighbor_rep) {
171*03f9172cSAndroid Build Coastguard Worker 		wpa_dbg(wpa_s, MSG_DEBUG,
172*03f9172cSAndroid Build Coastguard Worker 			"RRM: Currently handling previous Neighbor Report.");
173*03f9172cSAndroid Build Coastguard Worker 		return -EBUSY;
174*03f9172cSAndroid Build Coastguard Worker 	}
175*03f9172cSAndroid Build Coastguard Worker 
176*03f9172cSAndroid Build Coastguard Worker 	/* 3 = action category + action code + dialog token */
177*03f9172cSAndroid Build Coastguard Worker 	buf = wpabuf_alloc(3 + (ssid ? 2 + ssid->ssid_len : 0) +
178*03f9172cSAndroid Build Coastguard Worker 			   (lci ? 2 + MEASURE_REQUEST_LCI_LEN : 0) +
179*03f9172cSAndroid Build Coastguard Worker 			   (civic ? 2 + MEASURE_REQUEST_CIVIC_LEN : 0));
180*03f9172cSAndroid Build Coastguard Worker 	if (buf == NULL) {
181*03f9172cSAndroid Build Coastguard Worker 		wpa_dbg(wpa_s, MSG_DEBUG,
182*03f9172cSAndroid Build Coastguard Worker 			"RRM: Failed to allocate Neighbor Report Request");
183*03f9172cSAndroid Build Coastguard Worker 		return -ENOMEM;
184*03f9172cSAndroid Build Coastguard Worker 	}
185*03f9172cSAndroid Build Coastguard Worker 
186*03f9172cSAndroid Build Coastguard Worker 	wpa_dbg(wpa_s, MSG_DEBUG,
187*03f9172cSAndroid Build Coastguard Worker 		"RRM: Neighbor report request (for %s), token=%d",
188*03f9172cSAndroid Build Coastguard Worker 		(ssid ? wpa_ssid_txt(ssid->ssid, ssid->ssid_len) : ""),
189*03f9172cSAndroid Build Coastguard Worker 		wpa_s->rrm.next_neighbor_rep_token);
190*03f9172cSAndroid Build Coastguard Worker 
191*03f9172cSAndroid Build Coastguard Worker 	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
192*03f9172cSAndroid Build Coastguard Worker 	wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_REQUEST);
193*03f9172cSAndroid Build Coastguard Worker 	wpabuf_put_u8(buf, wpa_s->rrm.next_neighbor_rep_token);
194*03f9172cSAndroid Build Coastguard Worker 	if (ssid) {
195*03f9172cSAndroid Build Coastguard Worker 		wpabuf_put_u8(buf, WLAN_EID_SSID);
196*03f9172cSAndroid Build Coastguard Worker 		wpabuf_put_u8(buf, ssid->ssid_len);
197*03f9172cSAndroid Build Coastguard Worker 		wpabuf_put_data(buf, ssid->ssid, ssid->ssid_len);
198*03f9172cSAndroid Build Coastguard Worker 	}
199*03f9172cSAndroid Build Coastguard Worker 
200*03f9172cSAndroid Build Coastguard Worker 	if (lci) {
201*03f9172cSAndroid Build Coastguard Worker 		/* IEEE P802.11-REVmc/D5.0 9.4.2.21 */
202*03f9172cSAndroid Build Coastguard Worker 		wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
203*03f9172cSAndroid Build Coastguard Worker 		wpabuf_put_u8(buf, MEASURE_REQUEST_LCI_LEN);
204*03f9172cSAndroid Build Coastguard Worker 
205*03f9172cSAndroid Build Coastguard Worker 		/*
206*03f9172cSAndroid Build Coastguard Worker 		 * Measurement token; nonzero number that is unique among the
207*03f9172cSAndroid Build Coastguard Worker 		 * Measurement Request elements in a particular frame.
208*03f9172cSAndroid Build Coastguard Worker 		 */
209*03f9172cSAndroid Build Coastguard Worker 		wpabuf_put_u8(buf, 1); /* Measurement Token */
210*03f9172cSAndroid Build Coastguard Worker 
211*03f9172cSAndroid Build Coastguard Worker 		/*
212*03f9172cSAndroid Build Coastguard Worker 		 * Parallel, Enable, Request, and Report bits are 0, Duration is
213*03f9172cSAndroid Build Coastguard Worker 		 * reserved.
214*03f9172cSAndroid Build Coastguard Worker 		 */
215*03f9172cSAndroid Build Coastguard Worker 		wpabuf_put_u8(buf, 0); /* Measurement Request Mode */
216*03f9172cSAndroid Build Coastguard Worker 		wpabuf_put_u8(buf, MEASURE_TYPE_LCI); /* Measurement Type */
217*03f9172cSAndroid Build Coastguard Worker 
218*03f9172cSAndroid Build Coastguard Worker 		/* IEEE P802.11-REVmc/D5.0 9.4.2.21.10 - LCI request */
219*03f9172cSAndroid Build Coastguard Worker 		/* Location Subject */
220*03f9172cSAndroid Build Coastguard Worker 		wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE);
221*03f9172cSAndroid Build Coastguard Worker 
222*03f9172cSAndroid Build Coastguard Worker 		/* Optional Subelements */
223*03f9172cSAndroid Build Coastguard Worker 		/*
224*03f9172cSAndroid Build Coastguard Worker 		 * IEEE P802.11-REVmc/D5.0 Figure 9-170
225*03f9172cSAndroid Build Coastguard Worker 		 * The Maximum Age subelement is required, otherwise the AP can
226*03f9172cSAndroid Build Coastguard Worker 		 * send only data that was determined after receiving the
227*03f9172cSAndroid Build Coastguard Worker 		 * request. Setting it here to unlimited age.
228*03f9172cSAndroid Build Coastguard Worker 		 */
229*03f9172cSAndroid Build Coastguard Worker 		wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE);
230*03f9172cSAndroid Build Coastguard Worker 		wpabuf_put_u8(buf, 2);
231*03f9172cSAndroid Build Coastguard Worker 		wpabuf_put_le16(buf, 0xffff);
232*03f9172cSAndroid Build Coastguard Worker 	}
233*03f9172cSAndroid Build Coastguard Worker 
234*03f9172cSAndroid Build Coastguard Worker 	if (civic) {
235*03f9172cSAndroid Build Coastguard Worker 		/* IEEE P802.11-REVmc/D5.0 9.4.2.21 */
236*03f9172cSAndroid Build Coastguard Worker 		wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
237*03f9172cSAndroid Build Coastguard Worker 		wpabuf_put_u8(buf, MEASURE_REQUEST_CIVIC_LEN);
238*03f9172cSAndroid Build Coastguard Worker 
239*03f9172cSAndroid Build Coastguard Worker 		/*
240*03f9172cSAndroid Build Coastguard Worker 		 * Measurement token; nonzero number that is unique among the
241*03f9172cSAndroid Build Coastguard Worker 		 * Measurement Request elements in a particular frame.
242*03f9172cSAndroid Build Coastguard Worker 		 */
243*03f9172cSAndroid Build Coastguard Worker 		wpabuf_put_u8(buf, 2); /* Measurement Token */
244*03f9172cSAndroid Build Coastguard Worker 
245*03f9172cSAndroid Build Coastguard Worker 		/*
246*03f9172cSAndroid Build Coastguard Worker 		 * Parallel, Enable, Request, and Report bits are 0, Duration is
247*03f9172cSAndroid Build Coastguard Worker 		 * reserved.
248*03f9172cSAndroid Build Coastguard Worker 		 */
249*03f9172cSAndroid Build Coastguard Worker 		wpabuf_put_u8(buf, 0); /* Measurement Request Mode */
250*03f9172cSAndroid Build Coastguard Worker 		/* Measurement Type */
251*03f9172cSAndroid Build Coastguard Worker 		wpabuf_put_u8(buf, MEASURE_TYPE_LOCATION_CIVIC);
252*03f9172cSAndroid Build Coastguard Worker 
253*03f9172cSAndroid Build Coastguard Worker 		/* IEEE P802.11-REVmc/D5.0 9.4.2.21.14:
254*03f9172cSAndroid Build Coastguard Worker 		 * Location Civic request */
255*03f9172cSAndroid Build Coastguard Worker 		/* Location Subject */
256*03f9172cSAndroid Build Coastguard Worker 		wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE);
257*03f9172cSAndroid Build Coastguard Worker 		wpabuf_put_u8(buf, 0); /* Civic Location Type: IETF RFC 4776 */
258*03f9172cSAndroid Build Coastguard Worker 		/* Location Service Interval Units: Seconds */
259*03f9172cSAndroid Build Coastguard Worker 		wpabuf_put_u8(buf, 0);
260*03f9172cSAndroid Build Coastguard Worker 		/* Location Service Interval: 0 - Only one report is requested
261*03f9172cSAndroid Build Coastguard Worker 		 */
262*03f9172cSAndroid Build Coastguard Worker 		wpabuf_put_le16(buf, 0);
263*03f9172cSAndroid Build Coastguard Worker 		/* No optional subelements */
264*03f9172cSAndroid Build Coastguard Worker 	}
265*03f9172cSAndroid Build Coastguard Worker 
266*03f9172cSAndroid Build Coastguard Worker 	wpa_s->rrm.next_neighbor_rep_token++;
267*03f9172cSAndroid Build Coastguard Worker 
268*03f9172cSAndroid Build Coastguard Worker 	if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
269*03f9172cSAndroid Build Coastguard Worker 				wpa_s->own_addr, wpa_s->bssid,
270*03f9172cSAndroid Build Coastguard Worker 				wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
271*03f9172cSAndroid Build Coastguard Worker 		wpa_dbg(wpa_s, MSG_DEBUG,
272*03f9172cSAndroid Build Coastguard Worker 			"RRM: Failed to send Neighbor Report Request");
273*03f9172cSAndroid Build Coastguard Worker 		wpabuf_free(buf);
274*03f9172cSAndroid Build Coastguard Worker 		return -ECANCELED;
275*03f9172cSAndroid Build Coastguard Worker 	}
276*03f9172cSAndroid Build Coastguard Worker 
277*03f9172cSAndroid Build Coastguard Worker 	wpa_s->rrm.neighbor_rep_cb_ctx = cb_ctx;
278*03f9172cSAndroid Build Coastguard Worker 	wpa_s->rrm.notify_neighbor_rep = cb;
279*03f9172cSAndroid Build Coastguard Worker 	eloop_register_timeout(RRM_NEIGHBOR_REPORT_TIMEOUT, 0,
280*03f9172cSAndroid Build Coastguard Worker 			       wpas_rrm_neighbor_rep_timeout_handler,
281*03f9172cSAndroid Build Coastguard Worker 			       &wpa_s->rrm, NULL);
282*03f9172cSAndroid Build Coastguard Worker 
283*03f9172cSAndroid Build Coastguard Worker 	wpabuf_free(buf);
284*03f9172cSAndroid Build Coastguard Worker 	return 0;
285*03f9172cSAndroid Build Coastguard Worker }
286*03f9172cSAndroid Build Coastguard Worker 
287*03f9172cSAndroid Build Coastguard Worker 
wpas_rrm_report_elem(struct wpabuf ** buf,u8 token,u8 mode,u8 type,const u8 * data,size_t data_len)288*03f9172cSAndroid Build Coastguard Worker static int wpas_rrm_report_elem(struct wpabuf **buf, u8 token, u8 mode, u8 type,
289*03f9172cSAndroid Build Coastguard Worker 				const u8 *data, size_t data_len)
290*03f9172cSAndroid Build Coastguard Worker {
291*03f9172cSAndroid Build Coastguard Worker 	if (wpabuf_resize(buf, 5 + data_len))
292*03f9172cSAndroid Build Coastguard Worker 		return -1;
293*03f9172cSAndroid Build Coastguard Worker 
294*03f9172cSAndroid Build Coastguard Worker 	wpabuf_put_u8(*buf, WLAN_EID_MEASURE_REPORT);
295*03f9172cSAndroid Build Coastguard Worker 	wpabuf_put_u8(*buf, 3 + data_len);
296*03f9172cSAndroid Build Coastguard Worker 	wpabuf_put_u8(*buf, token);
297*03f9172cSAndroid Build Coastguard Worker 	wpabuf_put_u8(*buf, mode);
298*03f9172cSAndroid Build Coastguard Worker 	wpabuf_put_u8(*buf, type);
299*03f9172cSAndroid Build Coastguard Worker 
300*03f9172cSAndroid Build Coastguard Worker 	if (data_len)
301*03f9172cSAndroid Build Coastguard Worker 		wpabuf_put_data(*buf, data, data_len);
302*03f9172cSAndroid Build Coastguard Worker 
303*03f9172cSAndroid Build Coastguard Worker 	return 0;
304*03f9172cSAndroid Build Coastguard Worker }
305*03f9172cSAndroid Build Coastguard Worker 
306*03f9172cSAndroid Build Coastguard Worker 
307*03f9172cSAndroid Build Coastguard Worker static int
wpas_rrm_build_lci_report(struct wpa_supplicant * wpa_s,const struct rrm_measurement_request_element * req,struct wpabuf ** buf)308*03f9172cSAndroid Build Coastguard Worker wpas_rrm_build_lci_report(struct wpa_supplicant *wpa_s,
309*03f9172cSAndroid Build Coastguard Worker 			  const struct rrm_measurement_request_element *req,
310*03f9172cSAndroid Build Coastguard Worker 			  struct wpabuf **buf)
311*03f9172cSAndroid Build Coastguard Worker {
312*03f9172cSAndroid Build Coastguard Worker 	u8 subject;
313*03f9172cSAndroid Build Coastguard Worker 	u16 max_age = 0;
314*03f9172cSAndroid Build Coastguard Worker 	struct os_reltime t, diff;
315*03f9172cSAndroid Build Coastguard Worker 	unsigned long diff_l;
316*03f9172cSAndroid Build Coastguard Worker 	const u8 *subelem;
317*03f9172cSAndroid Build Coastguard Worker 	const u8 *request = req->variable;
318*03f9172cSAndroid Build Coastguard Worker 	size_t len = req->len - 3;
319*03f9172cSAndroid Build Coastguard Worker 
320*03f9172cSAndroid Build Coastguard Worker 	if (len < 1)
321*03f9172cSAndroid Build Coastguard Worker 		return -1;
322*03f9172cSAndroid Build Coastguard Worker 
323*03f9172cSAndroid Build Coastguard Worker 	if (!wpa_s->lci)
324*03f9172cSAndroid Build Coastguard Worker 		goto reject;
325*03f9172cSAndroid Build Coastguard Worker 
326*03f9172cSAndroid Build Coastguard Worker 	subject = *request++;
327*03f9172cSAndroid Build Coastguard Worker 	len--;
328*03f9172cSAndroid Build Coastguard Worker 
329*03f9172cSAndroid Build Coastguard Worker 	wpa_printf(MSG_DEBUG, "Measurement request location subject=%u",
330*03f9172cSAndroid Build Coastguard Worker 		   subject);
331*03f9172cSAndroid Build Coastguard Worker 
332*03f9172cSAndroid Build Coastguard Worker 	if (subject != LOCATION_SUBJECT_REMOTE) {
333*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_INFO,
334*03f9172cSAndroid Build Coastguard Worker 			   "Not building LCI report - bad location subject");
335*03f9172cSAndroid Build Coastguard Worker 		return 0;
336*03f9172cSAndroid Build Coastguard Worker 	}
337*03f9172cSAndroid Build Coastguard Worker 
338*03f9172cSAndroid Build Coastguard Worker 	/* Subelements are formatted exactly like elements */
339*03f9172cSAndroid Build Coastguard Worker 	wpa_hexdump(MSG_DEBUG, "LCI request subelements", request, len);
340*03f9172cSAndroid Build Coastguard Worker 	subelem = get_ie(request, len, LCI_REQ_SUBELEM_MAX_AGE);
341*03f9172cSAndroid Build Coastguard Worker 	if (subelem && subelem[1] == 2)
342*03f9172cSAndroid Build Coastguard Worker 		max_age = WPA_GET_LE16(subelem + 2);
343*03f9172cSAndroid Build Coastguard Worker 
344*03f9172cSAndroid Build Coastguard Worker 	if (os_get_reltime(&t))
345*03f9172cSAndroid Build Coastguard Worker 		goto reject;
346*03f9172cSAndroid Build Coastguard Worker 
347*03f9172cSAndroid Build Coastguard Worker 	os_reltime_sub(&t, &wpa_s->lci_time, &diff);
348*03f9172cSAndroid Build Coastguard Worker 	/* LCI age is calculated in 10th of a second units. */
349*03f9172cSAndroid Build Coastguard Worker 	diff_l = diff.sec * 10 + diff.usec / 100000;
350*03f9172cSAndroid Build Coastguard Worker 
351*03f9172cSAndroid Build Coastguard Worker 	if (max_age != 0xffff && max_age < diff_l)
352*03f9172cSAndroid Build Coastguard Worker 		goto reject;
353*03f9172cSAndroid Build Coastguard Worker 
354*03f9172cSAndroid Build Coastguard Worker 	if (wpas_rrm_report_elem(buf, req->token,
355*03f9172cSAndroid Build Coastguard Worker 				 MEASUREMENT_REPORT_MODE_ACCEPT, req->type,
356*03f9172cSAndroid Build Coastguard Worker 				 wpabuf_head_u8(wpa_s->lci),
357*03f9172cSAndroid Build Coastguard Worker 				 wpabuf_len(wpa_s->lci)) < 0) {
358*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_DEBUG, "Failed to add LCI report element");
359*03f9172cSAndroid Build Coastguard Worker 		return -1;
360*03f9172cSAndroid Build Coastguard Worker 	}
361*03f9172cSAndroid Build Coastguard Worker 
362*03f9172cSAndroid Build Coastguard Worker 	return 0;
363*03f9172cSAndroid Build Coastguard Worker 
364*03f9172cSAndroid Build Coastguard Worker reject:
365*03f9172cSAndroid Build Coastguard Worker 	if (!is_multicast_ether_addr(wpa_s->rrm.dst_addr) &&
366*03f9172cSAndroid Build Coastguard Worker 	    wpas_rrm_report_elem(buf, req->token,
367*03f9172cSAndroid Build Coastguard Worker 				 MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE,
368*03f9172cSAndroid Build Coastguard Worker 				 req->type, NULL, 0) < 0) {
369*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_DEBUG, "RRM: Failed to add report element");
370*03f9172cSAndroid Build Coastguard Worker 		return -1;
371*03f9172cSAndroid Build Coastguard Worker 	}
372*03f9172cSAndroid Build Coastguard Worker 
373*03f9172cSAndroid Build Coastguard Worker 	return 0;
374*03f9172cSAndroid Build Coastguard Worker }
375*03f9172cSAndroid Build Coastguard Worker 
376*03f9172cSAndroid Build Coastguard Worker 
wpas_rrm_send_msr_report_mpdu(struct wpa_supplicant * wpa_s,const u8 * data,size_t len)377*03f9172cSAndroid Build Coastguard Worker static void wpas_rrm_send_msr_report_mpdu(struct wpa_supplicant *wpa_s,
378*03f9172cSAndroid Build Coastguard Worker 					  const u8 *data, size_t len)
379*03f9172cSAndroid Build Coastguard Worker {
380*03f9172cSAndroid Build Coastguard Worker 	struct wpabuf *report = wpabuf_alloc(len + 3);
381*03f9172cSAndroid Build Coastguard Worker 
382*03f9172cSAndroid Build Coastguard Worker 	if (!report)
383*03f9172cSAndroid Build Coastguard Worker 		return;
384*03f9172cSAndroid Build Coastguard Worker 
385*03f9172cSAndroid Build Coastguard Worker 	wpabuf_put_u8(report, WLAN_ACTION_RADIO_MEASUREMENT);
386*03f9172cSAndroid Build Coastguard Worker 	wpabuf_put_u8(report, WLAN_RRM_RADIO_MEASUREMENT_REPORT);
387*03f9172cSAndroid Build Coastguard Worker 	wpabuf_put_u8(report, wpa_s->rrm.token);
388*03f9172cSAndroid Build Coastguard Worker 
389*03f9172cSAndroid Build Coastguard Worker 	wpabuf_put_data(report, data, len);
390*03f9172cSAndroid Build Coastguard Worker 
391*03f9172cSAndroid Build Coastguard Worker 	if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
392*03f9172cSAndroid Build Coastguard Worker 				wpa_s->own_addr, wpa_s->bssid,
393*03f9172cSAndroid Build Coastguard Worker 				wpabuf_head(report), wpabuf_len(report), 0)) {
394*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_ERROR,
395*03f9172cSAndroid Build Coastguard Worker 			   "RRM: Radio measurement report failed: Sending Action frame failed");
396*03f9172cSAndroid Build Coastguard Worker 	}
397*03f9172cSAndroid Build Coastguard Worker 
398*03f9172cSAndroid Build Coastguard Worker 	wpabuf_free(report);
399*03f9172cSAndroid Build Coastguard Worker }
400*03f9172cSAndroid Build Coastguard Worker 
401*03f9172cSAndroid Build Coastguard Worker 
wpas_rrm_beacon_rep_update_last_frame(u8 * pos,size_t len)402*03f9172cSAndroid Build Coastguard Worker static int wpas_rrm_beacon_rep_update_last_frame(u8 *pos, size_t len)
403*03f9172cSAndroid Build Coastguard Worker {
404*03f9172cSAndroid Build Coastguard Worker 	struct rrm_measurement_report_element *msr_rep;
405*03f9172cSAndroid Build Coastguard Worker 	u8 *end = pos + len;
406*03f9172cSAndroid Build Coastguard Worker 	u8 *msr_rep_end;
407*03f9172cSAndroid Build Coastguard Worker 	struct rrm_measurement_beacon_report *rep = NULL;
408*03f9172cSAndroid Build Coastguard Worker 	u8 *subelem;
409*03f9172cSAndroid Build Coastguard Worker 
410*03f9172cSAndroid Build Coastguard Worker 	/* Find the last beacon report element */
411*03f9172cSAndroid Build Coastguard Worker 	while (end - pos >= (int) sizeof(*msr_rep)) {
412*03f9172cSAndroid Build Coastguard Worker 		msr_rep = (struct rrm_measurement_report_element *) pos;
413*03f9172cSAndroid Build Coastguard Worker 		msr_rep_end = pos + msr_rep->len + 2;
414*03f9172cSAndroid Build Coastguard Worker 
415*03f9172cSAndroid Build Coastguard Worker 		if (msr_rep->eid != WLAN_EID_MEASURE_REPORT ||
416*03f9172cSAndroid Build Coastguard Worker 		    msr_rep_end > end) {
417*03f9172cSAndroid Build Coastguard Worker 			/* Should not happen. This indicates a bug. */
418*03f9172cSAndroid Build Coastguard Worker 			wpa_printf(MSG_ERROR,
419*03f9172cSAndroid Build Coastguard Worker 				   "RRM: non-measurement report element in measurement report frame");
420*03f9172cSAndroid Build Coastguard Worker 			return -1;
421*03f9172cSAndroid Build Coastguard Worker 		}
422*03f9172cSAndroid Build Coastguard Worker 
423*03f9172cSAndroid Build Coastguard Worker 		if (msr_rep->type == MEASURE_TYPE_BEACON)
424*03f9172cSAndroid Build Coastguard Worker 			rep = (struct rrm_measurement_beacon_report *)
425*03f9172cSAndroid Build Coastguard Worker 				msr_rep->variable;
426*03f9172cSAndroid Build Coastguard Worker 
427*03f9172cSAndroid Build Coastguard Worker 		pos += pos[1] + 2;
428*03f9172cSAndroid Build Coastguard Worker 	}
429*03f9172cSAndroid Build Coastguard Worker 
430*03f9172cSAndroid Build Coastguard Worker 	if (!rep)
431*03f9172cSAndroid Build Coastguard Worker 		return 0;
432*03f9172cSAndroid Build Coastguard Worker 
433*03f9172cSAndroid Build Coastguard Worker 	subelem = rep->variable;
434*03f9172cSAndroid Build Coastguard Worker 	while (subelem + 2 < msr_rep_end &&
435*03f9172cSAndroid Build Coastguard Worker 	       subelem[0] != WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION)
436*03f9172cSAndroid Build Coastguard Worker 		subelem += 2 + subelem[1];
437*03f9172cSAndroid Build Coastguard Worker 
438*03f9172cSAndroid Build Coastguard Worker 	if (subelem + 2 < msr_rep_end &&
439*03f9172cSAndroid Build Coastguard Worker 	    subelem[0] == WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION &&
440*03f9172cSAndroid Build Coastguard Worker 	    subelem[1] == 1 &&
441*03f9172cSAndroid Build Coastguard Worker 	    subelem + BEACON_REPORT_LAST_INDICATION_SUBELEM_LEN <= end)
442*03f9172cSAndroid Build Coastguard Worker 		subelem[2] = 1;
443*03f9172cSAndroid Build Coastguard Worker 
444*03f9172cSAndroid Build Coastguard Worker 	return 0;
445*03f9172cSAndroid Build Coastguard Worker }
446*03f9172cSAndroid Build Coastguard Worker 
447*03f9172cSAndroid Build Coastguard Worker 
wpas_rrm_send_msr_report(struct wpa_supplicant * wpa_s,struct wpabuf * buf)448*03f9172cSAndroid Build Coastguard Worker static void wpas_rrm_send_msr_report(struct wpa_supplicant *wpa_s,
449*03f9172cSAndroid Build Coastguard Worker 				     struct wpabuf *buf)
450*03f9172cSAndroid Build Coastguard Worker {
451*03f9172cSAndroid Build Coastguard Worker 	int len = wpabuf_len(buf);
452*03f9172cSAndroid Build Coastguard Worker 	u8 *pos = wpabuf_mhead_u8(buf), *next = pos;
453*03f9172cSAndroid Build Coastguard Worker 
454*03f9172cSAndroid Build Coastguard Worker #define MPDU_REPORT_LEN (int) (IEEE80211_MAX_MMPDU_SIZE - IEEE80211_HDRLEN - 3)
455*03f9172cSAndroid Build Coastguard Worker 
456*03f9172cSAndroid Build Coastguard Worker 	while (len) {
457*03f9172cSAndroid Build Coastguard Worker 		int send_len = (len > MPDU_REPORT_LEN) ? next - pos : len;
458*03f9172cSAndroid Build Coastguard Worker 
459*03f9172cSAndroid Build Coastguard Worker 		if (send_len == len)
460*03f9172cSAndroid Build Coastguard Worker 			wpas_rrm_beacon_rep_update_last_frame(pos, len);
461*03f9172cSAndroid Build Coastguard Worker 
462*03f9172cSAndroid Build Coastguard Worker 		if (send_len == len ||
463*03f9172cSAndroid Build Coastguard Worker 		    (send_len + next[1] + 2) > MPDU_REPORT_LEN) {
464*03f9172cSAndroid Build Coastguard Worker 			wpas_rrm_send_msr_report_mpdu(wpa_s, pos, send_len);
465*03f9172cSAndroid Build Coastguard Worker 			len -= send_len;
466*03f9172cSAndroid Build Coastguard Worker 			pos = next;
467*03f9172cSAndroid Build Coastguard Worker 		}
468*03f9172cSAndroid Build Coastguard Worker 
469*03f9172cSAndroid Build Coastguard Worker 		if (len)
470*03f9172cSAndroid Build Coastguard Worker 			next += next[1] + 2;
471*03f9172cSAndroid Build Coastguard Worker 	}
472*03f9172cSAndroid Build Coastguard Worker #undef MPDU_REPORT_LEN
473*03f9172cSAndroid Build Coastguard Worker }
474*03f9172cSAndroid Build Coastguard Worker 
475*03f9172cSAndroid Build Coastguard Worker 
wpas_add_channel(u8 op_class,u8 chan,u8 num_primary_channels,int * freqs)476*03f9172cSAndroid Build Coastguard Worker static int wpas_add_channel(u8 op_class, u8 chan, u8 num_primary_channels,
477*03f9172cSAndroid Build Coastguard Worker 			    int *freqs)
478*03f9172cSAndroid Build Coastguard Worker {
479*03f9172cSAndroid Build Coastguard Worker 	size_t i;
480*03f9172cSAndroid Build Coastguard Worker 
481*03f9172cSAndroid Build Coastguard Worker 	for (i = 0; i < num_primary_channels; i++) {
482*03f9172cSAndroid Build Coastguard Worker 		u8 primary_chan = chan - (2 * num_primary_channels - 2) + i * 4;
483*03f9172cSAndroid Build Coastguard Worker 
484*03f9172cSAndroid Build Coastguard Worker 		freqs[i] = ieee80211_chan_to_freq(NULL, op_class, primary_chan);
485*03f9172cSAndroid Build Coastguard Worker 		/* ieee80211_chan_to_freq() is not really meant for this
486*03f9172cSAndroid Build Coastguard Worker 		 * conversion of 20 MHz primary channel numbers for wider VHT
487*03f9172cSAndroid Build Coastguard Worker 		 * channels, so handle those as special cases here for now. */
488*03f9172cSAndroid Build Coastguard Worker 		if (freqs[i] < 0 &&
489*03f9172cSAndroid Build Coastguard Worker 		    (op_class == 128 || op_class == 129 || op_class == 130))
490*03f9172cSAndroid Build Coastguard Worker 			freqs[i] = 5000 + 5 * primary_chan;
491*03f9172cSAndroid Build Coastguard Worker 		if (freqs[i] < 0) {
492*03f9172cSAndroid Build Coastguard Worker 			wpa_printf(MSG_DEBUG,
493*03f9172cSAndroid Build Coastguard Worker 				   "Beacon Report: Invalid channel %u",
494*03f9172cSAndroid Build Coastguard Worker 				   chan);
495*03f9172cSAndroid Build Coastguard Worker 			return -1;
496*03f9172cSAndroid Build Coastguard Worker 		}
497*03f9172cSAndroid Build Coastguard Worker 	}
498*03f9172cSAndroid Build Coastguard Worker 
499*03f9172cSAndroid Build Coastguard Worker 	return 0;
500*03f9172cSAndroid Build Coastguard Worker }
501*03f9172cSAndroid Build Coastguard Worker 
502*03f9172cSAndroid Build Coastguard Worker 
wpas_add_channels(const struct oper_class_map * op,struct hostapd_hw_modes * mode,const u8 * channels,const u8 size)503*03f9172cSAndroid Build Coastguard Worker static int * wpas_add_channels(const struct oper_class_map *op,
504*03f9172cSAndroid Build Coastguard Worker 			       struct hostapd_hw_modes *mode,
505*03f9172cSAndroid Build Coastguard Worker 			       const u8 *channels, const u8 size)
506*03f9172cSAndroid Build Coastguard Worker {
507*03f9172cSAndroid Build Coastguard Worker 	int *freqs, *next_freq;
508*03f9172cSAndroid Build Coastguard Worker 	u8 num_primary_channels, i;
509*03f9172cSAndroid Build Coastguard Worker 	u8 num_chans;
510*03f9172cSAndroid Build Coastguard Worker 
511*03f9172cSAndroid Build Coastguard Worker 	num_chans = channels ? size :
512*03f9172cSAndroid Build Coastguard Worker 		(op->max_chan - op->min_chan) / op->inc + 1;
513*03f9172cSAndroid Build Coastguard Worker 
514*03f9172cSAndroid Build Coastguard Worker 	if (op->bw == BW80 || op->bw == BW80P80)
515*03f9172cSAndroid Build Coastguard Worker 		num_primary_channels = 4;
516*03f9172cSAndroid Build Coastguard Worker 	else if (op->bw == BW160)
517*03f9172cSAndroid Build Coastguard Worker 		num_primary_channels = 8;
518*03f9172cSAndroid Build Coastguard Worker 	else if (op->bw == BW320)
519*03f9172cSAndroid Build Coastguard Worker 		num_primary_channels = 16;
520*03f9172cSAndroid Build Coastguard Worker 	else
521*03f9172cSAndroid Build Coastguard Worker 		num_primary_channels = 1;
522*03f9172cSAndroid Build Coastguard Worker 
523*03f9172cSAndroid Build Coastguard Worker 	/* one extra place for the zero-terminator */
524*03f9172cSAndroid Build Coastguard Worker 	freqs = os_calloc(num_chans * num_primary_channels + 1, sizeof(*freqs));
525*03f9172cSAndroid Build Coastguard Worker 	if (!freqs) {
526*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_ERROR,
527*03f9172cSAndroid Build Coastguard Worker 			   "Beacon Report: Failed to allocate freqs array");
528*03f9172cSAndroid Build Coastguard Worker 		return NULL;
529*03f9172cSAndroid Build Coastguard Worker 	}
530*03f9172cSAndroid Build Coastguard Worker 
531*03f9172cSAndroid Build Coastguard Worker 	next_freq = freqs;
532*03f9172cSAndroid Build Coastguard Worker 	for  (i = 0; i < num_chans; i++) {
533*03f9172cSAndroid Build Coastguard Worker 		u8 chan = channels ? channels[i] : op->min_chan + i * op->inc;
534*03f9172cSAndroid Build Coastguard Worker 		enum chan_allowed res = verify_channel(mode, op->op_class, chan,
535*03f9172cSAndroid Build Coastguard Worker 						       op->bw);
536*03f9172cSAndroid Build Coastguard Worker 
537*03f9172cSAndroid Build Coastguard Worker 		if (res == NOT_ALLOWED)
538*03f9172cSAndroid Build Coastguard Worker 			continue;
539*03f9172cSAndroid Build Coastguard Worker 
540*03f9172cSAndroid Build Coastguard Worker 		if (wpas_add_channel(op->op_class, chan, num_primary_channels,
541*03f9172cSAndroid Build Coastguard Worker 				     next_freq) < 0) {
542*03f9172cSAndroid Build Coastguard Worker 			os_free(freqs);
543*03f9172cSAndroid Build Coastguard Worker 			return NULL;
544*03f9172cSAndroid Build Coastguard Worker 		}
545*03f9172cSAndroid Build Coastguard Worker 
546*03f9172cSAndroid Build Coastguard Worker 		next_freq += num_primary_channels;
547*03f9172cSAndroid Build Coastguard Worker 	}
548*03f9172cSAndroid Build Coastguard Worker 
549*03f9172cSAndroid Build Coastguard Worker 	if (!freqs[0]) {
550*03f9172cSAndroid Build Coastguard Worker 		os_free(freqs);
551*03f9172cSAndroid Build Coastguard Worker 		return NULL;
552*03f9172cSAndroid Build Coastguard Worker 	}
553*03f9172cSAndroid Build Coastguard Worker 
554*03f9172cSAndroid Build Coastguard Worker 	return freqs;
555*03f9172cSAndroid Build Coastguard Worker }
556*03f9172cSAndroid Build Coastguard Worker 
557*03f9172cSAndroid Build Coastguard Worker 
wpas_op_class_freqs(const struct oper_class_map * op,struct hostapd_hw_modes * mode)558*03f9172cSAndroid Build Coastguard Worker static int * wpas_op_class_freqs(const struct oper_class_map *op,
559*03f9172cSAndroid Build Coastguard Worker 				 struct hostapd_hw_modes *mode)
560*03f9172cSAndroid Build Coastguard Worker {
561*03f9172cSAndroid Build Coastguard Worker 	u8 channels_80mhz_5ghz[] = { 42, 58, 106, 122, 138, 155, 171 };
562*03f9172cSAndroid Build Coastguard Worker 	u8 channels_160mhz_5ghz[] = { 50, 114, 163 };
563*03f9172cSAndroid Build Coastguard Worker 	u8 channels_80mhz_6ghz[] = { 7, 23, 39, 55, 71, 87, 103, 119, 135, 151,
564*03f9172cSAndroid Build Coastguard Worker 				     167, 183, 199, 215 };
565*03f9172cSAndroid Build Coastguard Worker 	u8 channels_160mhz_6ghz[] = { 15, 47, 79, 111, 143, 175, 207 };
566*03f9172cSAndroid Build Coastguard Worker 	u8 channels_320mhz_6ghz[] = { 31, 63, 95, 127, 159, 191 };
567*03f9172cSAndroid Build Coastguard Worker 	const u8 *channels = NULL;
568*03f9172cSAndroid Build Coastguard Worker 	size_t num_chan = 0;
569*03f9172cSAndroid Build Coastguard Worker 	bool is_6ghz = is_6ghz_op_class(op->op_class);
570*03f9172cSAndroid Build Coastguard Worker 
571*03f9172cSAndroid Build Coastguard Worker 	/*
572*03f9172cSAndroid Build Coastguard Worker 	 * When adding all channels in the operating class, 80 + 80 MHz
573*03f9172cSAndroid Build Coastguard Worker 	 * operating classes are like 80 MHz channels because we add all valid
574*03f9172cSAndroid Build Coastguard Worker 	 * channels anyway.
575*03f9172cSAndroid Build Coastguard Worker 	 */
576*03f9172cSAndroid Build Coastguard Worker 	if (op->bw == BW80 || op->bw == BW80P80) {
577*03f9172cSAndroid Build Coastguard Worker 		channels = is_6ghz ? channels_80mhz_6ghz : channels_80mhz_5ghz;
578*03f9172cSAndroid Build Coastguard Worker 		num_chan = is_6ghz ? ARRAY_SIZE(channels_80mhz_6ghz) :
579*03f9172cSAndroid Build Coastguard Worker 			ARRAY_SIZE(channels_80mhz_5ghz);
580*03f9172cSAndroid Build Coastguard Worker 	} else if (op->bw == BW160) {
581*03f9172cSAndroid Build Coastguard Worker 		channels = is_6ghz ? channels_160mhz_6ghz :
582*03f9172cSAndroid Build Coastguard Worker 			channels_160mhz_5ghz;
583*03f9172cSAndroid Build Coastguard Worker 		num_chan =  is_6ghz ? ARRAY_SIZE(channels_160mhz_6ghz) :
584*03f9172cSAndroid Build Coastguard Worker 			ARRAY_SIZE(channels_160mhz_5ghz);
585*03f9172cSAndroid Build Coastguard Worker 	} else if (op->bw == BW320) {
586*03f9172cSAndroid Build Coastguard Worker 		channels = channels_320mhz_6ghz;
587*03f9172cSAndroid Build Coastguard Worker 		num_chan = ARRAY_SIZE(channels_320mhz_6ghz);
588*03f9172cSAndroid Build Coastguard Worker 	}
589*03f9172cSAndroid Build Coastguard Worker 
590*03f9172cSAndroid Build Coastguard Worker 	return wpas_add_channels(op, mode, channels, num_chan);
591*03f9172cSAndroid Build Coastguard Worker }
592*03f9172cSAndroid Build Coastguard Worker 
593*03f9172cSAndroid Build Coastguard Worker 
wpas_channel_report_freqs(struct wpa_supplicant * wpa_s,const char * country,const u8 * subelems,size_t len)594*03f9172cSAndroid Build Coastguard Worker static int * wpas_channel_report_freqs(struct wpa_supplicant *wpa_s,
595*03f9172cSAndroid Build Coastguard Worker 				       const char *country, const u8 *subelems,
596*03f9172cSAndroid Build Coastguard Worker 				       size_t len)
597*03f9172cSAndroid Build Coastguard Worker {
598*03f9172cSAndroid Build Coastguard Worker 	int *freqs = NULL, *new_freqs;
599*03f9172cSAndroid Build Coastguard Worker 	const u8 *end = subelems + len;
600*03f9172cSAndroid Build Coastguard Worker 
601*03f9172cSAndroid Build Coastguard Worker 	while (end - subelems > 2) {
602*03f9172cSAndroid Build Coastguard Worker 		const struct oper_class_map *op;
603*03f9172cSAndroid Build Coastguard Worker 		const u8 *ap_chan_elem, *pos;
604*03f9172cSAndroid Build Coastguard Worker 		u8 left;
605*03f9172cSAndroid Build Coastguard Worker 		struct hostapd_hw_modes *mode;
606*03f9172cSAndroid Build Coastguard Worker 
607*03f9172cSAndroid Build Coastguard Worker 		ap_chan_elem = get_ie(subelems, end - subelems,
608*03f9172cSAndroid Build Coastguard Worker 				      WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL);
609*03f9172cSAndroid Build Coastguard Worker 		if (!ap_chan_elem)
610*03f9172cSAndroid Build Coastguard Worker 			break;
611*03f9172cSAndroid Build Coastguard Worker 		pos = ap_chan_elem + 2;
612*03f9172cSAndroid Build Coastguard Worker 		left = ap_chan_elem[1];
613*03f9172cSAndroid Build Coastguard Worker 		if (left < 1)
614*03f9172cSAndroid Build Coastguard Worker 			break;
615*03f9172cSAndroid Build Coastguard Worker 		subelems = ap_chan_elem + 2 + left;
616*03f9172cSAndroid Build Coastguard Worker 
617*03f9172cSAndroid Build Coastguard Worker 		op = get_oper_class(country, *pos);
618*03f9172cSAndroid Build Coastguard Worker 		if (!op) {
619*03f9172cSAndroid Build Coastguard Worker 			wpa_printf(MSG_DEBUG,
620*03f9172cSAndroid Build Coastguard Worker 				   "Beacon request: unknown operating class in AP Channel Report subelement %u",
621*03f9172cSAndroid Build Coastguard Worker 				   *pos);
622*03f9172cSAndroid Build Coastguard Worker 			goto out;
623*03f9172cSAndroid Build Coastguard Worker 		}
624*03f9172cSAndroid Build Coastguard Worker 		pos++;
625*03f9172cSAndroid Build Coastguard Worker 		left--;
626*03f9172cSAndroid Build Coastguard Worker 
627*03f9172cSAndroid Build Coastguard Worker 		mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op->mode,
628*03f9172cSAndroid Build Coastguard Worker 				is_6ghz_op_class(op->op_class));
629*03f9172cSAndroid Build Coastguard Worker 		if (!mode)
630*03f9172cSAndroid Build Coastguard Worker 			continue;
631*03f9172cSAndroid Build Coastguard Worker 
632*03f9172cSAndroid Build Coastguard Worker 		/*
633*03f9172cSAndroid Build Coastguard Worker 		 * For 80 + 80 MHz operating classes, this AP Channel Report
634*03f9172cSAndroid Build Coastguard Worker 		 * element should be followed by another element specifying
635*03f9172cSAndroid Build Coastguard Worker 		 * the second 80 MHz channel. For now just add this 80 MHz
636*03f9172cSAndroid Build Coastguard Worker 		 * channel, the second 80 MHz channel will be added when the
637*03f9172cSAndroid Build Coastguard Worker 		 * next element is parsed.
638*03f9172cSAndroid Build Coastguard Worker 		 * TODO: Verify that this AP Channel Report element is followed
639*03f9172cSAndroid Build Coastguard Worker 		 * by a corresponding AP Channel Report element as specified in
640*03f9172cSAndroid Build Coastguard Worker 		 * IEEE Std 802.11-2016, 11.11.9.1.
641*03f9172cSAndroid Build Coastguard Worker 		 */
642*03f9172cSAndroid Build Coastguard Worker 		new_freqs = wpas_add_channels(op, mode, pos, left);
643*03f9172cSAndroid Build Coastguard Worker 		if (new_freqs)
644*03f9172cSAndroid Build Coastguard Worker 			int_array_concat(&freqs, new_freqs);
645*03f9172cSAndroid Build Coastguard Worker 
646*03f9172cSAndroid Build Coastguard Worker 		os_free(new_freqs);
647*03f9172cSAndroid Build Coastguard Worker 	}
648*03f9172cSAndroid Build Coastguard Worker 
649*03f9172cSAndroid Build Coastguard Worker 	return freqs;
650*03f9172cSAndroid Build Coastguard Worker out:
651*03f9172cSAndroid Build Coastguard Worker 	os_free(freqs);
652*03f9172cSAndroid Build Coastguard Worker 	return NULL;
653*03f9172cSAndroid Build Coastguard Worker }
654*03f9172cSAndroid Build Coastguard Worker 
655*03f9172cSAndroid Build Coastguard Worker 
wpas_beacon_request_freqs(struct wpa_supplicant * wpa_s,u8 op_class,u8 chan,const u8 * subelems,size_t len)656*03f9172cSAndroid Build Coastguard Worker static int * wpas_beacon_request_freqs(struct wpa_supplicant *wpa_s,
657*03f9172cSAndroid Build Coastguard Worker 				       u8 op_class, u8 chan,
658*03f9172cSAndroid Build Coastguard Worker 				       const u8 *subelems, size_t len)
659*03f9172cSAndroid Build Coastguard Worker {
660*03f9172cSAndroid Build Coastguard Worker 	int *freqs = NULL, *ext_freqs = NULL;
661*03f9172cSAndroid Build Coastguard Worker 	struct hostapd_hw_modes *mode;
662*03f9172cSAndroid Build Coastguard Worker 	const char *country = NULL;
663*03f9172cSAndroid Build Coastguard Worker 	const struct oper_class_map *op;
664*03f9172cSAndroid Build Coastguard Worker 	const u8 *elem;
665*03f9172cSAndroid Build Coastguard Worker 
666*03f9172cSAndroid Build Coastguard Worker 	if (!wpa_s->current_bss)
667*03f9172cSAndroid Build Coastguard Worker 		return NULL;
668*03f9172cSAndroid Build Coastguard Worker 	elem = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_COUNTRY);
669*03f9172cSAndroid Build Coastguard Worker 	if (elem && elem[1] >= 2)
670*03f9172cSAndroid Build Coastguard Worker 		country = (const char *) (elem + 2);
671*03f9172cSAndroid Build Coastguard Worker 
672*03f9172cSAndroid Build Coastguard Worker 	op = get_oper_class(country, op_class);
673*03f9172cSAndroid Build Coastguard Worker 	if (!op) {
674*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_DEBUG,
675*03f9172cSAndroid Build Coastguard Worker 			   "Beacon request: invalid operating class %d",
676*03f9172cSAndroid Build Coastguard Worker 			   op_class);
677*03f9172cSAndroid Build Coastguard Worker 		return NULL;
678*03f9172cSAndroid Build Coastguard Worker 	}
679*03f9172cSAndroid Build Coastguard Worker 
680*03f9172cSAndroid Build Coastguard Worker 	mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op->mode,
681*03f9172cSAndroid Build Coastguard Worker 			is_6ghz_op_class(op->op_class));
682*03f9172cSAndroid Build Coastguard Worker 	if (!mode)
683*03f9172cSAndroid Build Coastguard Worker 		return NULL;
684*03f9172cSAndroid Build Coastguard Worker 
685*03f9172cSAndroid Build Coastguard Worker 	switch (chan) {
686*03f9172cSAndroid Build Coastguard Worker 	case 0:
687*03f9172cSAndroid Build Coastguard Worker 		freqs = wpas_op_class_freqs(op, mode);
688*03f9172cSAndroid Build Coastguard Worker 		if (!freqs)
689*03f9172cSAndroid Build Coastguard Worker 			return NULL;
690*03f9172cSAndroid Build Coastguard Worker 		break;
691*03f9172cSAndroid Build Coastguard Worker 	case 255:
692*03f9172cSAndroid Build Coastguard Worker 		/* freqs will be added from AP channel subelements */
693*03f9172cSAndroid Build Coastguard Worker 		break;
694*03f9172cSAndroid Build Coastguard Worker 	default:
695*03f9172cSAndroid Build Coastguard Worker 		freqs = wpas_add_channels(op, mode, &chan, 1);
696*03f9172cSAndroid Build Coastguard Worker 		if (!freqs)
697*03f9172cSAndroid Build Coastguard Worker 			return NULL;
698*03f9172cSAndroid Build Coastguard Worker 		break;
699*03f9172cSAndroid Build Coastguard Worker 	}
700*03f9172cSAndroid Build Coastguard Worker 
701*03f9172cSAndroid Build Coastguard Worker 	ext_freqs = wpas_channel_report_freqs(wpa_s, country, subelems, len);
702*03f9172cSAndroid Build Coastguard Worker 	if (ext_freqs) {
703*03f9172cSAndroid Build Coastguard Worker 		int_array_concat(&freqs, ext_freqs);
704*03f9172cSAndroid Build Coastguard Worker 		os_free(ext_freqs);
705*03f9172cSAndroid Build Coastguard Worker 		int_array_sort_unique(freqs);
706*03f9172cSAndroid Build Coastguard Worker 	}
707*03f9172cSAndroid Build Coastguard Worker 
708*03f9172cSAndroid Build Coastguard Worker 	return freqs;
709*03f9172cSAndroid Build Coastguard Worker }
710*03f9172cSAndroid Build Coastguard Worker 
711*03f9172cSAndroid Build Coastguard Worker 
wpas_get_op_chan_phy(int freq,const u8 * ies,size_t ies_len,u8 * op_class,u8 * chan,u8 * phy_type)712*03f9172cSAndroid Build Coastguard Worker int wpas_get_op_chan_phy(int freq, const u8 *ies, size_t ies_len,
713*03f9172cSAndroid Build Coastguard Worker 			 u8 *op_class, u8 *chan, u8 *phy_type)
714*03f9172cSAndroid Build Coastguard Worker {
715*03f9172cSAndroid Build Coastguard Worker 	const u8 *ie;
716*03f9172cSAndroid Build Coastguard Worker 	int sec_chan = 0, vht = 0;
717*03f9172cSAndroid Build Coastguard Worker 	struct ieee80211_ht_operation *ht_oper = NULL;
718*03f9172cSAndroid Build Coastguard Worker 	struct ieee80211_vht_operation *vht_oper = NULL;
719*03f9172cSAndroid Build Coastguard Worker 	u8 seg0, seg1;
720*03f9172cSAndroid Build Coastguard Worker 
721*03f9172cSAndroid Build Coastguard Worker 	ie = get_ie(ies, ies_len, WLAN_EID_HT_OPERATION);
722*03f9172cSAndroid Build Coastguard Worker 	if (ie && ie[1] >= sizeof(struct ieee80211_ht_operation)) {
723*03f9172cSAndroid Build Coastguard Worker 		u8 sec_chan_offset;
724*03f9172cSAndroid Build Coastguard Worker 
725*03f9172cSAndroid Build Coastguard Worker 		ht_oper = (struct ieee80211_ht_operation *) (ie + 2);
726*03f9172cSAndroid Build Coastguard Worker 		sec_chan_offset = ht_oper->ht_param &
727*03f9172cSAndroid Build Coastguard Worker 			HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
728*03f9172cSAndroid Build Coastguard Worker 		if (sec_chan_offset == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
729*03f9172cSAndroid Build Coastguard Worker 			sec_chan = 1;
730*03f9172cSAndroid Build Coastguard Worker 		else if (sec_chan_offset ==
731*03f9172cSAndroid Build Coastguard Worker 			 HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
732*03f9172cSAndroid Build Coastguard Worker 			sec_chan = -1;
733*03f9172cSAndroid Build Coastguard Worker 	}
734*03f9172cSAndroid Build Coastguard Worker 
735*03f9172cSAndroid Build Coastguard Worker 	ie = get_ie(ies, ies_len, WLAN_EID_VHT_OPERATION);
736*03f9172cSAndroid Build Coastguard Worker 	if (ie && ie[1] >= sizeof(struct ieee80211_vht_operation)) {
737*03f9172cSAndroid Build Coastguard Worker 		vht_oper = (struct ieee80211_vht_operation *) (ie + 2);
738*03f9172cSAndroid Build Coastguard Worker 
739*03f9172cSAndroid Build Coastguard Worker 		switch (vht_oper->vht_op_info_chwidth) {
740*03f9172cSAndroid Build Coastguard Worker 		case CHANWIDTH_80MHZ:
741*03f9172cSAndroid Build Coastguard Worker 			seg0 = vht_oper->vht_op_info_chan_center_freq_seg0_idx;
742*03f9172cSAndroid Build Coastguard Worker 			seg1 = vht_oper->vht_op_info_chan_center_freq_seg1_idx;
743*03f9172cSAndroid Build Coastguard Worker 			if (seg1 && abs(seg1 - seg0) == 8)
744*03f9172cSAndroid Build Coastguard Worker 				vht = CONF_OPER_CHWIDTH_160MHZ;
745*03f9172cSAndroid Build Coastguard Worker 			else if (seg1)
746*03f9172cSAndroid Build Coastguard Worker 				vht = CONF_OPER_CHWIDTH_80P80MHZ;
747*03f9172cSAndroid Build Coastguard Worker 			else
748*03f9172cSAndroid Build Coastguard Worker 				vht = CONF_OPER_CHWIDTH_80MHZ;
749*03f9172cSAndroid Build Coastguard Worker 			break;
750*03f9172cSAndroid Build Coastguard Worker 		case CHANWIDTH_160MHZ:
751*03f9172cSAndroid Build Coastguard Worker 			vht = CONF_OPER_CHWIDTH_160MHZ;
752*03f9172cSAndroid Build Coastguard Worker 			break;
753*03f9172cSAndroid Build Coastguard Worker 		case CHANWIDTH_80P80MHZ:
754*03f9172cSAndroid Build Coastguard Worker 			vht = CONF_OPER_CHWIDTH_80P80MHZ;
755*03f9172cSAndroid Build Coastguard Worker 			break;
756*03f9172cSAndroid Build Coastguard Worker 		default:
757*03f9172cSAndroid Build Coastguard Worker 			vht = CONF_OPER_CHWIDTH_USE_HT;
758*03f9172cSAndroid Build Coastguard Worker 			break;
759*03f9172cSAndroid Build Coastguard Worker 		}
760*03f9172cSAndroid Build Coastguard Worker 	}
761*03f9172cSAndroid Build Coastguard Worker 
762*03f9172cSAndroid Build Coastguard Worker 	if (ieee80211_freq_to_channel_ext(freq, sec_chan, vht, op_class,
763*03f9172cSAndroid Build Coastguard Worker 					  chan) == NUM_HOSTAPD_MODES) {
764*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_DEBUG,
765*03f9172cSAndroid Build Coastguard Worker 			   "Cannot determine operating class and channel");
766*03f9172cSAndroid Build Coastguard Worker 		return -1;
767*03f9172cSAndroid Build Coastguard Worker 	}
768*03f9172cSAndroid Build Coastguard Worker 
769*03f9172cSAndroid Build Coastguard Worker 	*phy_type = ieee80211_get_phy_type(freq, ht_oper != NULL,
770*03f9172cSAndroid Build Coastguard Worker 					   vht_oper != NULL);
771*03f9172cSAndroid Build Coastguard Worker 	if (*phy_type == PHY_TYPE_UNSPECIFIED) {
772*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_DEBUG, "Cannot determine phy type");
773*03f9172cSAndroid Build Coastguard Worker 		return -1;
774*03f9172cSAndroid Build Coastguard Worker 	}
775*03f9172cSAndroid Build Coastguard Worker 
776*03f9172cSAndroid Build Coastguard Worker 	return 0;
777*03f9172cSAndroid Build Coastguard Worker }
778*03f9172cSAndroid Build Coastguard Worker 
779*03f9172cSAndroid Build Coastguard Worker 
wpas_beacon_rep_add_frame_body(struct bitfield * eids,struct bitfield * ext_eids,enum beacon_report_detail detail,struct wpa_bss * bss,u8 * buf,size_t buf_len,const u8 ** ies_buf,size_t * ie_len,int add_fixed)780*03f9172cSAndroid Build Coastguard Worker static int wpas_beacon_rep_add_frame_body(struct bitfield *eids,
781*03f9172cSAndroid Build Coastguard Worker 					  struct bitfield *ext_eids,
782*03f9172cSAndroid Build Coastguard Worker 					  enum beacon_report_detail detail,
783*03f9172cSAndroid Build Coastguard Worker 					  struct wpa_bss *bss, u8 *buf,
784*03f9172cSAndroid Build Coastguard Worker 					  size_t buf_len, const u8 **ies_buf,
785*03f9172cSAndroid Build Coastguard Worker 					  size_t *ie_len, int add_fixed)
786*03f9172cSAndroid Build Coastguard Worker {
787*03f9172cSAndroid Build Coastguard Worker 	const u8 *ies = *ies_buf;
788*03f9172cSAndroid Build Coastguard Worker 	size_t ies_len = *ie_len;
789*03f9172cSAndroid Build Coastguard Worker 	u8 *pos = buf;
790*03f9172cSAndroid Build Coastguard Worker 	int rem_len;
791*03f9172cSAndroid Build Coastguard Worker 
792*03f9172cSAndroid Build Coastguard Worker 	rem_len = 255 - sizeof(struct rrm_measurement_beacon_report) -
793*03f9172cSAndroid Build Coastguard Worker 		sizeof(struct rrm_measurement_report_element) - 2 -
794*03f9172cSAndroid Build Coastguard Worker 		REPORTED_FRAME_BODY_SUBELEM_LEN;
795*03f9172cSAndroid Build Coastguard Worker 
796*03f9172cSAndroid Build Coastguard Worker 	if (detail > BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS) {
797*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_DEBUG,
798*03f9172cSAndroid Build Coastguard Worker 			   "Beacon Request: Invalid reporting detail: %d",
799*03f9172cSAndroid Build Coastguard Worker 			   detail);
800*03f9172cSAndroid Build Coastguard Worker 		return -1;
801*03f9172cSAndroid Build Coastguard Worker 	}
802*03f9172cSAndroid Build Coastguard Worker 
803*03f9172cSAndroid Build Coastguard Worker 	if (detail == BEACON_REPORT_DETAIL_NONE)
804*03f9172cSAndroid Build Coastguard Worker 		return 0;
805*03f9172cSAndroid Build Coastguard Worker 
806*03f9172cSAndroid Build Coastguard Worker 	/*
807*03f9172cSAndroid Build Coastguard Worker 	 * Minimal frame body subelement size: EID(1) + length(1) + TSF(8) +
808*03f9172cSAndroid Build Coastguard Worker 	 * beacon interval(2) + capabilities(2) = 14 bytes
809*03f9172cSAndroid Build Coastguard Worker 	 */
810*03f9172cSAndroid Build Coastguard Worker 	if (add_fixed && buf_len < 14)
811*03f9172cSAndroid Build Coastguard Worker 		return -1;
812*03f9172cSAndroid Build Coastguard Worker 
813*03f9172cSAndroid Build Coastguard Worker 	*pos++ = WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY;
814*03f9172cSAndroid Build Coastguard Worker 	/* The length will be filled later */
815*03f9172cSAndroid Build Coastguard Worker 	pos++;
816*03f9172cSAndroid Build Coastguard Worker 
817*03f9172cSAndroid Build Coastguard Worker 	if (add_fixed) {
818*03f9172cSAndroid Build Coastguard Worker 		WPA_PUT_LE64(pos, bss->tsf);
819*03f9172cSAndroid Build Coastguard Worker 		pos += sizeof(bss->tsf);
820*03f9172cSAndroid Build Coastguard Worker 		WPA_PUT_LE16(pos, bss->beacon_int);
821*03f9172cSAndroid Build Coastguard Worker 		pos += 2;
822*03f9172cSAndroid Build Coastguard Worker 		WPA_PUT_LE16(pos, bss->caps);
823*03f9172cSAndroid Build Coastguard Worker 		pos += 2;
824*03f9172cSAndroid Build Coastguard Worker 	}
825*03f9172cSAndroid Build Coastguard Worker 
826*03f9172cSAndroid Build Coastguard Worker 	rem_len -= pos - buf;
827*03f9172cSAndroid Build Coastguard Worker 
828*03f9172cSAndroid Build Coastguard Worker 	/*
829*03f9172cSAndroid Build Coastguard Worker 	 * According to IEEE Std 802.11-2016, 9.4.2.22.7, if the reported frame
830*03f9172cSAndroid Build Coastguard Worker 	 * body subelement causes the element to exceed the maximum element
831*03f9172cSAndroid Build Coastguard Worker 	 * size, the subelement is truncated so that the last IE is a complete
832*03f9172cSAndroid Build Coastguard Worker 	 * IE. So even when required to report all IEs, add elements one after
833*03f9172cSAndroid Build Coastguard Worker 	 * the other and stop once there is no more room in the measurement
834*03f9172cSAndroid Build Coastguard Worker 	 * element.
835*03f9172cSAndroid Build Coastguard Worker 	 */
836*03f9172cSAndroid Build Coastguard Worker 	while (ies_len > 2 && 2U + ies[1] <= ies_len && rem_len > 0) {
837*03f9172cSAndroid Build Coastguard Worker 		if (detail == BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS ||
838*03f9172cSAndroid Build Coastguard Worker 		    (eids && bitfield_is_set(eids, ies[0])) ||
839*03f9172cSAndroid Build Coastguard Worker 		    (ext_eids && ies[0] == WLAN_EID_EXTENSION && ies[1] &&
840*03f9172cSAndroid Build Coastguard Worker 		     bitfield_is_set(ext_eids, ies[2]))) {
841*03f9172cSAndroid Build Coastguard Worker 			u8 elen = ies[1];
842*03f9172cSAndroid Build Coastguard Worker 
843*03f9172cSAndroid Build Coastguard Worker 			if (2 + elen > buf + buf_len - pos ||
844*03f9172cSAndroid Build Coastguard Worker 			    2 + elen > rem_len)
845*03f9172cSAndroid Build Coastguard Worker 				break;
846*03f9172cSAndroid Build Coastguard Worker 
847*03f9172cSAndroid Build Coastguard Worker 			*pos++ = ies[0];
848*03f9172cSAndroid Build Coastguard Worker 			*pos++ = elen;
849*03f9172cSAndroid Build Coastguard Worker 			os_memcpy(pos, ies + 2, elen);
850*03f9172cSAndroid Build Coastguard Worker 			pos += elen;
851*03f9172cSAndroid Build Coastguard Worker 			rem_len -= 2 + elen;
852*03f9172cSAndroid Build Coastguard Worker 		}
853*03f9172cSAndroid Build Coastguard Worker 
854*03f9172cSAndroid Build Coastguard Worker 		ies_len -= 2 + ies[1];
855*03f9172cSAndroid Build Coastguard Worker 		ies += 2 + ies[1];
856*03f9172cSAndroid Build Coastguard Worker 	}
857*03f9172cSAndroid Build Coastguard Worker 
858*03f9172cSAndroid Build Coastguard Worker 	*ie_len = ies_len;
859*03f9172cSAndroid Build Coastguard Worker 	*ies_buf = ies;
860*03f9172cSAndroid Build Coastguard Worker 
861*03f9172cSAndroid Build Coastguard Worker 	/* Now the length is known */
862*03f9172cSAndroid Build Coastguard Worker 	buf[1] = pos - buf - 2;
863*03f9172cSAndroid Build Coastguard Worker 	return pos - buf;
864*03f9172cSAndroid Build Coastguard Worker }
865*03f9172cSAndroid Build Coastguard Worker 
866*03f9172cSAndroid Build Coastguard Worker 
wpas_add_beacon_rep_elem(struct beacon_rep_data * data,struct wpa_bss * bss,struct wpabuf ** wpa_buf,struct rrm_measurement_beacon_report * rep,const u8 ** ie,size_t * ie_len,u8 idx)867*03f9172cSAndroid Build Coastguard Worker static int wpas_add_beacon_rep_elem(struct beacon_rep_data *data,
868*03f9172cSAndroid Build Coastguard Worker 				    struct wpa_bss *bss,
869*03f9172cSAndroid Build Coastguard Worker 				    struct wpabuf **wpa_buf,
870*03f9172cSAndroid Build Coastguard Worker 				    struct rrm_measurement_beacon_report *rep,
871*03f9172cSAndroid Build Coastguard Worker 				    const u8 **ie, size_t *ie_len, u8 idx)
872*03f9172cSAndroid Build Coastguard Worker {
873*03f9172cSAndroid Build Coastguard Worker 	int ret;
874*03f9172cSAndroid Build Coastguard Worker 	u8 *buf, *pos;
875*03f9172cSAndroid Build Coastguard Worker 	u32 subelems_len = REPORTED_FRAME_BODY_SUBELEM_LEN +
876*03f9172cSAndroid Build Coastguard Worker 		(data->last_indication ?
877*03f9172cSAndroid Build Coastguard Worker 		 BEACON_REPORT_LAST_INDICATION_SUBELEM_LEN : 0);
878*03f9172cSAndroid Build Coastguard Worker 
879*03f9172cSAndroid Build Coastguard Worker 	/* Maximum element length: Beacon Report element + Reported Frame Body
880*03f9172cSAndroid Build Coastguard Worker 	 * subelement + all IEs of the reported Beacon frame + Reported Frame
881*03f9172cSAndroid Build Coastguard Worker 	 * Body Fragment ID subelement */
882*03f9172cSAndroid Build Coastguard Worker 	buf = os_malloc(sizeof(*rep) + 14 + *ie_len + subelems_len);
883*03f9172cSAndroid Build Coastguard Worker 	if (!buf)
884*03f9172cSAndroid Build Coastguard Worker 		return -1;
885*03f9172cSAndroid Build Coastguard Worker 
886*03f9172cSAndroid Build Coastguard Worker 	os_memcpy(buf, rep, sizeof(*rep));
887*03f9172cSAndroid Build Coastguard Worker 
888*03f9172cSAndroid Build Coastguard Worker 	ret = wpas_beacon_rep_add_frame_body(data->eids, data->ext_eids,
889*03f9172cSAndroid Build Coastguard Worker 					     data->report_detail,
890*03f9172cSAndroid Build Coastguard Worker 					     bss, buf + sizeof(*rep),
891*03f9172cSAndroid Build Coastguard Worker 					     14 + *ie_len, ie, ie_len,
892*03f9172cSAndroid Build Coastguard Worker 					     idx == 0);
893*03f9172cSAndroid Build Coastguard Worker 	if (ret < 0)
894*03f9172cSAndroid Build Coastguard Worker 		goto out;
895*03f9172cSAndroid Build Coastguard Worker 
896*03f9172cSAndroid Build Coastguard Worker 	pos = buf + ret + sizeof(*rep);
897*03f9172cSAndroid Build Coastguard Worker 	pos[0] = WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY_FRAGMENT_ID;
898*03f9172cSAndroid Build Coastguard Worker 	pos[1] = 2;
899*03f9172cSAndroid Build Coastguard Worker 
900*03f9172cSAndroid Build Coastguard Worker 	/*
901*03f9172cSAndroid Build Coastguard Worker 	 * Only one Beacon Report Measurement is supported at a time, so
902*03f9172cSAndroid Build Coastguard Worker 	 * the Beacon Report ID can always be set to 1.
903*03f9172cSAndroid Build Coastguard Worker 	 */
904*03f9172cSAndroid Build Coastguard Worker 	pos[2] = 1;
905*03f9172cSAndroid Build Coastguard Worker 
906*03f9172cSAndroid Build Coastguard Worker 	/* Fragment ID Number (bits 0..6) and More Frame Body Fragments (bit 7)
907*03f9172cSAndroid Build Coastguard Worker  */
908*03f9172cSAndroid Build Coastguard Worker 	pos[3] = idx;
909*03f9172cSAndroid Build Coastguard Worker 	if (data->report_detail != BEACON_REPORT_DETAIL_NONE && *ie_len)
910*03f9172cSAndroid Build Coastguard Worker 		pos[3] |= REPORTED_FRAME_BODY_MORE_FRAGMENTS;
911*03f9172cSAndroid Build Coastguard Worker 	else
912*03f9172cSAndroid Build Coastguard Worker 		pos[3] &= ~REPORTED_FRAME_BODY_MORE_FRAGMENTS;
913*03f9172cSAndroid Build Coastguard Worker 
914*03f9172cSAndroid Build Coastguard Worker 	pos += REPORTED_FRAME_BODY_SUBELEM_LEN;
915*03f9172cSAndroid Build Coastguard Worker 
916*03f9172cSAndroid Build Coastguard Worker 	if (data->last_indication) {
917*03f9172cSAndroid Build Coastguard Worker 		pos[0] = WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION;
918*03f9172cSAndroid Build Coastguard Worker 		pos[1] = 1;
919*03f9172cSAndroid Build Coastguard Worker 
920*03f9172cSAndroid Build Coastguard Worker 		/* This field will be updated later if this is the last frame */
921*03f9172cSAndroid Build Coastguard Worker 		pos[2] = 0;
922*03f9172cSAndroid Build Coastguard Worker 	}
923*03f9172cSAndroid Build Coastguard Worker 
924*03f9172cSAndroid Build Coastguard Worker 	ret = wpas_rrm_report_elem(wpa_buf, data->token,
925*03f9172cSAndroid Build Coastguard Worker 				   MEASUREMENT_REPORT_MODE_ACCEPT,
926*03f9172cSAndroid Build Coastguard Worker 				   MEASURE_TYPE_BEACON, buf,
927*03f9172cSAndroid Build Coastguard Worker 				   ret + sizeof(*rep) + subelems_len);
928*03f9172cSAndroid Build Coastguard Worker out:
929*03f9172cSAndroid Build Coastguard Worker 	os_free(buf);
930*03f9172cSAndroid Build Coastguard Worker 	return ret;
931*03f9172cSAndroid Build Coastguard Worker }
932*03f9172cSAndroid Build Coastguard Worker 
933*03f9172cSAndroid Build Coastguard Worker 
wpas_add_beacon_rep(struct wpa_supplicant * wpa_s,struct wpabuf ** wpa_buf,struct wpa_bss * bss,u64 start,u64 parent_tsf)934*03f9172cSAndroid Build Coastguard Worker static int wpas_add_beacon_rep(struct wpa_supplicant *wpa_s,
935*03f9172cSAndroid Build Coastguard Worker 			       struct wpabuf **wpa_buf, struct wpa_bss *bss,
936*03f9172cSAndroid Build Coastguard Worker 			       u64 start, u64 parent_tsf)
937*03f9172cSAndroid Build Coastguard Worker {
938*03f9172cSAndroid Build Coastguard Worker 	struct beacon_rep_data *data = &wpa_s->beacon_rep_data;
939*03f9172cSAndroid Build Coastguard Worker 	const u8 *ies = wpa_bss_ie_ptr(bss);
940*03f9172cSAndroid Build Coastguard Worker 	const u8 *pos = ies;
941*03f9172cSAndroid Build Coastguard Worker 	size_t ies_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len;
942*03f9172cSAndroid Build Coastguard Worker 	struct rrm_measurement_beacon_report rep;
943*03f9172cSAndroid Build Coastguard Worker 	u8 idx = 0;
944*03f9172cSAndroid Build Coastguard Worker 
945*03f9172cSAndroid Build Coastguard Worker 	if (!ether_addr_equal(data->bssid, broadcast_ether_addr) &&
946*03f9172cSAndroid Build Coastguard Worker 	    !ether_addr_equal(data->bssid, bss->bssid))
947*03f9172cSAndroid Build Coastguard Worker 		return 0;
948*03f9172cSAndroid Build Coastguard Worker 
949*03f9172cSAndroid Build Coastguard Worker 	if (data->ssid_len &&
950*03f9172cSAndroid Build Coastguard Worker 	    (data->ssid_len != bss->ssid_len ||
951*03f9172cSAndroid Build Coastguard Worker 	     os_memcmp(data->ssid, bss->ssid, bss->ssid_len) != 0))
952*03f9172cSAndroid Build Coastguard Worker 		return 0;
953*03f9172cSAndroid Build Coastguard Worker 
954*03f9172cSAndroid Build Coastguard Worker 	if (wpas_get_op_chan_phy(bss->freq, ies, ies_len, &rep.op_class,
955*03f9172cSAndroid Build Coastguard Worker 				 &rep.channel, &rep.report_info) < 0)
956*03f9172cSAndroid Build Coastguard Worker 		return 0;
957*03f9172cSAndroid Build Coastguard Worker 
958*03f9172cSAndroid Build Coastguard Worker 	rep.start_time = host_to_le64(start);
959*03f9172cSAndroid Build Coastguard Worker 	rep.duration = host_to_le16(data->scan_params.duration);
960*03f9172cSAndroid Build Coastguard Worker 	rep.rcpi = rssi_to_rcpi(bss->level);
961*03f9172cSAndroid Build Coastguard Worker 	rep.rsni = 255; /* 255 indicates that RSNI is not available */
962*03f9172cSAndroid Build Coastguard Worker 	os_memcpy(rep.bssid, bss->bssid, ETH_ALEN);
963*03f9172cSAndroid Build Coastguard Worker 	rep.antenna_id = 0; /* unknown */
964*03f9172cSAndroid Build Coastguard Worker 	rep.parent_tsf = host_to_le32(parent_tsf);
965*03f9172cSAndroid Build Coastguard Worker 
966*03f9172cSAndroid Build Coastguard Worker 	do {
967*03f9172cSAndroid Build Coastguard Worker 		int ret;
968*03f9172cSAndroid Build Coastguard Worker 
969*03f9172cSAndroid Build Coastguard Worker 		ret = wpas_add_beacon_rep_elem(data, bss, wpa_buf, &rep,
970*03f9172cSAndroid Build Coastguard Worker 					       &pos, &ies_len, idx++);
971*03f9172cSAndroid Build Coastguard Worker 		if (ret)
972*03f9172cSAndroid Build Coastguard Worker 			return ret;
973*03f9172cSAndroid Build Coastguard Worker 	} while (data->report_detail != BEACON_REPORT_DETAIL_NONE &&
974*03f9172cSAndroid Build Coastguard Worker 		 ies_len >= 2);
975*03f9172cSAndroid Build Coastguard Worker 
976*03f9172cSAndroid Build Coastguard Worker 	return 0;
977*03f9172cSAndroid Build Coastguard Worker }
978*03f9172cSAndroid Build Coastguard Worker 
979*03f9172cSAndroid Build Coastguard Worker 
wpas_beacon_rep_no_results(struct wpa_supplicant * wpa_s,struct wpabuf ** buf)980*03f9172cSAndroid Build Coastguard Worker static int wpas_beacon_rep_no_results(struct wpa_supplicant *wpa_s,
981*03f9172cSAndroid Build Coastguard Worker 				      struct wpabuf **buf)
982*03f9172cSAndroid Build Coastguard Worker {
983*03f9172cSAndroid Build Coastguard Worker 	return wpas_rrm_report_elem(buf, wpa_s->beacon_rep_data.token,
984*03f9172cSAndroid Build Coastguard Worker 				    MEASUREMENT_REPORT_MODE_ACCEPT,
985*03f9172cSAndroid Build Coastguard Worker 				    MEASURE_TYPE_BEACON, NULL, 0);
986*03f9172cSAndroid Build Coastguard Worker }
987*03f9172cSAndroid Build Coastguard Worker 
988*03f9172cSAndroid Build Coastguard Worker 
wpas_beacon_rep_table(struct wpa_supplicant * wpa_s,struct wpabuf ** buf)989*03f9172cSAndroid Build Coastguard Worker static void wpas_beacon_rep_table(struct wpa_supplicant *wpa_s,
990*03f9172cSAndroid Build Coastguard Worker 				  struct wpabuf **buf)
991*03f9172cSAndroid Build Coastguard Worker {
992*03f9172cSAndroid Build Coastguard Worker 	size_t i;
993*03f9172cSAndroid Build Coastguard Worker 
994*03f9172cSAndroid Build Coastguard Worker 	for (i = 0; i < wpa_s->last_scan_res_used; i++) {
995*03f9172cSAndroid Build Coastguard Worker 		if (wpas_add_beacon_rep(wpa_s, buf, wpa_s->last_scan_res[i],
996*03f9172cSAndroid Build Coastguard Worker 					0, 0) < 0)
997*03f9172cSAndroid Build Coastguard Worker 			break;
998*03f9172cSAndroid Build Coastguard Worker 	}
999*03f9172cSAndroid Build Coastguard Worker 
1000*03f9172cSAndroid Build Coastguard Worker 	if (!(*buf))
1001*03f9172cSAndroid Build Coastguard Worker 		wpas_beacon_rep_no_results(wpa_s, buf);
1002*03f9172cSAndroid Build Coastguard Worker 
1003*03f9172cSAndroid Build Coastguard Worker 	wpa_hexdump_buf(MSG_DEBUG, "RRM: Radio Measurement report", *buf);
1004*03f9172cSAndroid Build Coastguard Worker }
1005*03f9172cSAndroid Build Coastguard Worker 
1006*03f9172cSAndroid Build Coastguard Worker 
wpas_rrm_refuse_request(struct wpa_supplicant * wpa_s)1007*03f9172cSAndroid Build Coastguard Worker void wpas_rrm_refuse_request(struct wpa_supplicant *wpa_s)
1008*03f9172cSAndroid Build Coastguard Worker {
1009*03f9172cSAndroid Build Coastguard Worker 	if (!is_multicast_ether_addr(wpa_s->rrm.dst_addr)) {
1010*03f9172cSAndroid Build Coastguard Worker 		struct wpabuf *buf = NULL;
1011*03f9172cSAndroid Build Coastguard Worker 
1012*03f9172cSAndroid Build Coastguard Worker 		if (wpas_rrm_report_elem(&buf, wpa_s->beacon_rep_data.token,
1013*03f9172cSAndroid Build Coastguard Worker 					 MEASUREMENT_REPORT_MODE_REJECT_REFUSED,
1014*03f9172cSAndroid Build Coastguard Worker 					 MEASURE_TYPE_BEACON, NULL, 0)) {
1015*03f9172cSAndroid Build Coastguard Worker 			wpa_printf(MSG_ERROR, "RRM: Memory allocation failed");
1016*03f9172cSAndroid Build Coastguard Worker 			wpabuf_free(buf);
1017*03f9172cSAndroid Build Coastguard Worker 			return;
1018*03f9172cSAndroid Build Coastguard Worker 		}
1019*03f9172cSAndroid Build Coastguard Worker 
1020*03f9172cSAndroid Build Coastguard Worker 		wpas_rrm_send_msr_report(wpa_s, buf);
1021*03f9172cSAndroid Build Coastguard Worker 		wpabuf_free(buf);
1022*03f9172cSAndroid Build Coastguard Worker 	}
1023*03f9172cSAndroid Build Coastguard Worker 
1024*03f9172cSAndroid Build Coastguard Worker 	wpas_clear_beacon_rep_data(wpa_s);
1025*03f9172cSAndroid Build Coastguard Worker }
1026*03f9172cSAndroid Build Coastguard Worker 
1027*03f9172cSAndroid Build Coastguard Worker 
wpas_rrm_scan_timeout(void * eloop_ctx,void * timeout_ctx)1028*03f9172cSAndroid Build Coastguard Worker static void wpas_rrm_scan_timeout(void *eloop_ctx, void *timeout_ctx)
1029*03f9172cSAndroid Build Coastguard Worker {
1030*03f9172cSAndroid Build Coastguard Worker 	struct wpa_supplicant *wpa_s = eloop_ctx;
1031*03f9172cSAndroid Build Coastguard Worker 	struct wpa_driver_scan_params *params =
1032*03f9172cSAndroid Build Coastguard Worker 		&wpa_s->beacon_rep_data.scan_params;
1033*03f9172cSAndroid Build Coastguard Worker 	u16 prev_duration = params->duration;
1034*03f9172cSAndroid Build Coastguard Worker 
1035*03f9172cSAndroid Build Coastguard Worker 	if (!wpa_s->current_bss)
1036*03f9172cSAndroid Build Coastguard Worker 		return;
1037*03f9172cSAndroid Build Coastguard Worker 
1038*03f9172cSAndroid Build Coastguard Worker 	if (!(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL) &&
1039*03f9172cSAndroid Build Coastguard Worker 	    params->duration) {
1040*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_DEBUG,
1041*03f9172cSAndroid Build Coastguard Worker 			   "RRM: Cannot set scan duration due to missing driver support");
1042*03f9172cSAndroid Build Coastguard Worker 		params->duration = 0;
1043*03f9172cSAndroid Build Coastguard Worker 	}
1044*03f9172cSAndroid Build Coastguard Worker 	os_get_reltime(&wpa_s->beacon_rep_scan);
1045*03f9172cSAndroid Build Coastguard Worker 	if (wpa_s->scanning || wpas_p2p_in_progress(wpa_s) ||
1046*03f9172cSAndroid Build Coastguard Worker 	    wpa_supplicant_trigger_scan(wpa_s, params, true, false))
1047*03f9172cSAndroid Build Coastguard Worker 		wpas_rrm_refuse_request(wpa_s);
1048*03f9172cSAndroid Build Coastguard Worker 	params->duration = prev_duration;
1049*03f9172cSAndroid Build Coastguard Worker }
1050*03f9172cSAndroid Build Coastguard Worker 
1051*03f9172cSAndroid Build Coastguard Worker 
wpas_rm_handle_beacon_req_subelem(struct wpa_supplicant * wpa_s,struct beacon_rep_data * data,u8 sid,u8 slen,const u8 * subelem)1052*03f9172cSAndroid Build Coastguard Worker static int wpas_rm_handle_beacon_req_subelem(struct wpa_supplicant *wpa_s,
1053*03f9172cSAndroid Build Coastguard Worker 					     struct beacon_rep_data *data,
1054*03f9172cSAndroid Build Coastguard Worker 					     u8 sid, u8 slen, const u8 *subelem)
1055*03f9172cSAndroid Build Coastguard Worker {
1056*03f9172cSAndroid Build Coastguard Worker 	struct bitfield *eids;
1057*03f9172cSAndroid Build Coastguard Worker 	u8 report_info, i;
1058*03f9172cSAndroid Build Coastguard Worker 
1059*03f9172cSAndroid Build Coastguard Worker 	switch (sid) {
1060*03f9172cSAndroid Build Coastguard Worker 	case WLAN_BEACON_REQUEST_SUBELEM_SSID:
1061*03f9172cSAndroid Build Coastguard Worker 		if (!slen) {
1062*03f9172cSAndroid Build Coastguard Worker 			wpa_printf(MSG_DEBUG,
1063*03f9172cSAndroid Build Coastguard Worker 				   "SSID subelement with zero length - wildcard SSID");
1064*03f9172cSAndroid Build Coastguard Worker 			break;
1065*03f9172cSAndroid Build Coastguard Worker 		}
1066*03f9172cSAndroid Build Coastguard Worker 
1067*03f9172cSAndroid Build Coastguard Worker 		if (slen > SSID_MAX_LEN) {
1068*03f9172cSAndroid Build Coastguard Worker 			wpa_printf(MSG_DEBUG,
1069*03f9172cSAndroid Build Coastguard Worker 				   "Invalid SSID subelement length: %u", slen);
1070*03f9172cSAndroid Build Coastguard Worker 			return -1;
1071*03f9172cSAndroid Build Coastguard Worker 		}
1072*03f9172cSAndroid Build Coastguard Worker 
1073*03f9172cSAndroid Build Coastguard Worker 		data->ssid_len = slen;
1074*03f9172cSAndroid Build Coastguard Worker 		os_memcpy(data->ssid, subelem, data->ssid_len);
1075*03f9172cSAndroid Build Coastguard Worker 		break;
1076*03f9172cSAndroid Build Coastguard Worker 	case WLAN_BEACON_REQUEST_SUBELEM_INFO:
1077*03f9172cSAndroid Build Coastguard Worker 		if (slen != 2) {
1078*03f9172cSAndroid Build Coastguard Worker 			wpa_printf(MSG_DEBUG,
1079*03f9172cSAndroid Build Coastguard Worker 				   "Invalid reporting information subelement length: %u",
1080*03f9172cSAndroid Build Coastguard Worker 				   slen);
1081*03f9172cSAndroid Build Coastguard Worker 			return -1;
1082*03f9172cSAndroid Build Coastguard Worker 		}
1083*03f9172cSAndroid Build Coastguard Worker 
1084*03f9172cSAndroid Build Coastguard Worker 		report_info = subelem[0];
1085*03f9172cSAndroid Build Coastguard Worker 		if (report_info != 0) {
1086*03f9172cSAndroid Build Coastguard Worker 			wpa_printf(MSG_DEBUG,
1087*03f9172cSAndroid Build Coastguard Worker 				   "reporting information=%u is not supported",
1088*03f9172cSAndroid Build Coastguard Worker 				   report_info);
1089*03f9172cSAndroid Build Coastguard Worker 			return 0;
1090*03f9172cSAndroid Build Coastguard Worker 		}
1091*03f9172cSAndroid Build Coastguard Worker 		break;
1092*03f9172cSAndroid Build Coastguard Worker 	case WLAN_BEACON_REQUEST_SUBELEM_DETAIL:
1093*03f9172cSAndroid Build Coastguard Worker 		if (slen != 1) {
1094*03f9172cSAndroid Build Coastguard Worker 			wpa_printf(MSG_DEBUG,
1095*03f9172cSAndroid Build Coastguard Worker 				   "Invalid reporting detail subelement length: %u",
1096*03f9172cSAndroid Build Coastguard Worker 				   slen);
1097*03f9172cSAndroid Build Coastguard Worker 			return -1;
1098*03f9172cSAndroid Build Coastguard Worker 		}
1099*03f9172cSAndroid Build Coastguard Worker 
1100*03f9172cSAndroid Build Coastguard Worker 		data->report_detail = subelem[0];
1101*03f9172cSAndroid Build Coastguard Worker 		if (data->report_detail >
1102*03f9172cSAndroid Build Coastguard Worker 		    BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS) {
1103*03f9172cSAndroid Build Coastguard Worker 			wpa_printf(MSG_DEBUG, "Invalid reporting detail: %u",
1104*03f9172cSAndroid Build Coastguard Worker 				   subelem[0]);
1105*03f9172cSAndroid Build Coastguard Worker 			return -1;
1106*03f9172cSAndroid Build Coastguard Worker 		}
1107*03f9172cSAndroid Build Coastguard Worker 
1108*03f9172cSAndroid Build Coastguard Worker 		break;
1109*03f9172cSAndroid Build Coastguard Worker 	case WLAN_BEACON_REQUEST_SUBELEM_REQUEST:
1110*03f9172cSAndroid Build Coastguard Worker 	case WLAN_BEACON_REQUEST_SUBELEM_EXT_REQUEST:
1111*03f9172cSAndroid Build Coastguard Worker 		if (data->report_detail !=
1112*03f9172cSAndroid Build Coastguard Worker 		    BEACON_REPORT_DETAIL_REQUESTED_ONLY) {
1113*03f9172cSAndroid Build Coastguard Worker 			wpa_printf(MSG_DEBUG,
1114*03f9172cSAndroid Build Coastguard Worker 				   "Beacon request: request subelement is present but report detail is %u",
1115*03f9172cSAndroid Build Coastguard Worker 				   data->report_detail);
1116*03f9172cSAndroid Build Coastguard Worker 			return -1;
1117*03f9172cSAndroid Build Coastguard Worker 		}
1118*03f9172cSAndroid Build Coastguard Worker 
1119*03f9172cSAndroid Build Coastguard Worker 		if (!slen) {
1120*03f9172cSAndroid Build Coastguard Worker 			wpa_printf(MSG_DEBUG,
1121*03f9172cSAndroid Build Coastguard Worker 				   "Invalid request subelement length: %u",
1122*03f9172cSAndroid Build Coastguard Worker 				   slen);
1123*03f9172cSAndroid Build Coastguard Worker 			return -1;
1124*03f9172cSAndroid Build Coastguard Worker 		}
1125*03f9172cSAndroid Build Coastguard Worker 
1126*03f9172cSAndroid Build Coastguard Worker 		if (sid == WLAN_BEACON_REQUEST_SUBELEM_EXT_REQUEST) {
1127*03f9172cSAndroid Build Coastguard Worker 			if (slen < 2) {
1128*03f9172cSAndroid Build Coastguard Worker 				wpa_printf(MSG_DEBUG,
1129*03f9172cSAndroid Build Coastguard Worker 					   "Invalid extended request");
1130*03f9172cSAndroid Build Coastguard Worker 				return -1;
1131*03f9172cSAndroid Build Coastguard Worker 			}
1132*03f9172cSAndroid Build Coastguard Worker 			if (subelem[0] != WLAN_EID_EXTENSION) {
1133*03f9172cSAndroid Build Coastguard Worker 				wpa_printf(MSG_DEBUG,
1134*03f9172cSAndroid Build Coastguard Worker 					   "Skip unknown Requested Element ID %u in Extended Request subelement",
1135*03f9172cSAndroid Build Coastguard Worker 					   subelem[0]);
1136*03f9172cSAndroid Build Coastguard Worker 				break;
1137*03f9172cSAndroid Build Coastguard Worker 			}
1138*03f9172cSAndroid Build Coastguard Worker 
1139*03f9172cSAndroid Build Coastguard Worker 			/* Skip the Requested Element ID field */
1140*03f9172cSAndroid Build Coastguard Worker 			subelem++;
1141*03f9172cSAndroid Build Coastguard Worker 			slen--;
1142*03f9172cSAndroid Build Coastguard Worker 		}
1143*03f9172cSAndroid Build Coastguard Worker 
1144*03f9172cSAndroid Build Coastguard Worker 		if ((sid == WLAN_BEACON_REQUEST_SUBELEM_REQUEST &&
1145*03f9172cSAndroid Build Coastguard Worker 		     data->eids) ||
1146*03f9172cSAndroid Build Coastguard Worker 		    (sid == WLAN_BEACON_REQUEST_SUBELEM_EXT_REQUEST &&
1147*03f9172cSAndroid Build Coastguard Worker 		    data->ext_eids)) {
1148*03f9172cSAndroid Build Coastguard Worker 			wpa_printf(MSG_DEBUG,
1149*03f9172cSAndroid Build Coastguard Worker 				   "Beacon Request: Request sub elements appear more than once");
1150*03f9172cSAndroid Build Coastguard Worker 			return -1;
1151*03f9172cSAndroid Build Coastguard Worker 		}
1152*03f9172cSAndroid Build Coastguard Worker 
1153*03f9172cSAndroid Build Coastguard Worker 		eids = bitfield_alloc(255);
1154*03f9172cSAndroid Build Coastguard Worker 		if (!eids) {
1155*03f9172cSAndroid Build Coastguard Worker 			wpa_printf(MSG_DEBUG, "Failed to allocate EIDs bitmap");
1156*03f9172cSAndroid Build Coastguard Worker 			return -1;
1157*03f9172cSAndroid Build Coastguard Worker 		}
1158*03f9172cSAndroid Build Coastguard Worker 
1159*03f9172cSAndroid Build Coastguard Worker 		if (sid == WLAN_BEACON_REQUEST_SUBELEM_REQUEST)
1160*03f9172cSAndroid Build Coastguard Worker 			data->eids = eids;
1161*03f9172cSAndroid Build Coastguard Worker 		else
1162*03f9172cSAndroid Build Coastguard Worker 			data->ext_eids = eids;
1163*03f9172cSAndroid Build Coastguard Worker 
1164*03f9172cSAndroid Build Coastguard Worker 		for (i = 0; i < slen; i++)
1165*03f9172cSAndroid Build Coastguard Worker 			bitfield_set(eids, subelem[i]);
1166*03f9172cSAndroid Build Coastguard Worker 		break;
1167*03f9172cSAndroid Build Coastguard Worker 	case WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL:
1168*03f9172cSAndroid Build Coastguard Worker 		/* Skip - it will be processed when freqs are added */
1169*03f9172cSAndroid Build Coastguard Worker 		break;
1170*03f9172cSAndroid Build Coastguard Worker 	case WLAN_BEACON_REQUEST_SUBELEM_LAST_INDICATION:
1171*03f9172cSAndroid Build Coastguard Worker 		if (slen != 1) {
1172*03f9172cSAndroid Build Coastguard Worker 			wpa_printf(MSG_DEBUG,
1173*03f9172cSAndroid Build Coastguard Worker 				   "Beacon request: Invalid last indication request subelement length: %u",
1174*03f9172cSAndroid Build Coastguard Worker 				   slen);
1175*03f9172cSAndroid Build Coastguard Worker 			return -1;
1176*03f9172cSAndroid Build Coastguard Worker 		}
1177*03f9172cSAndroid Build Coastguard Worker 
1178*03f9172cSAndroid Build Coastguard Worker 		data->last_indication = subelem[0];
1179*03f9172cSAndroid Build Coastguard Worker 		break;
1180*03f9172cSAndroid Build Coastguard Worker 	default:
1181*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_DEBUG,
1182*03f9172cSAndroid Build Coastguard Worker 			   "Beacon request: Unknown subelement id %u", sid);
1183*03f9172cSAndroid Build Coastguard Worker 		break;
1184*03f9172cSAndroid Build Coastguard Worker 	}
1185*03f9172cSAndroid Build Coastguard Worker 
1186*03f9172cSAndroid Build Coastguard Worker 	return 1;
1187*03f9172cSAndroid Build Coastguard Worker }
1188*03f9172cSAndroid Build Coastguard Worker 
1189*03f9172cSAndroid Build Coastguard Worker 
1190*03f9172cSAndroid Build Coastguard Worker /**
1191*03f9172cSAndroid Build Coastguard Worker  * Returns 0 if the next element can be processed, 1 if some operation was
1192*03f9172cSAndroid Build Coastguard Worker  * triggered, and -1 if processing failed (i.e., the element is in invalid
1193*03f9172cSAndroid Build Coastguard Worker  * format or an internal error occurred).
1194*03f9172cSAndroid Build Coastguard Worker  */
1195*03f9172cSAndroid Build Coastguard Worker static int
wpas_rm_handle_beacon_req(struct wpa_supplicant * wpa_s,u8 elem_token,int duration_mandatory,const struct rrm_measurement_beacon_request * req,size_t len,struct wpabuf ** buf)1196*03f9172cSAndroid Build Coastguard Worker wpas_rm_handle_beacon_req(struct wpa_supplicant *wpa_s,
1197*03f9172cSAndroid Build Coastguard Worker 			  u8 elem_token, int duration_mandatory,
1198*03f9172cSAndroid Build Coastguard Worker 			  const struct rrm_measurement_beacon_request *req,
1199*03f9172cSAndroid Build Coastguard Worker 			  size_t len, struct wpabuf **buf)
1200*03f9172cSAndroid Build Coastguard Worker {
1201*03f9172cSAndroid Build Coastguard Worker 	struct beacon_rep_data *data = &wpa_s->beacon_rep_data;
1202*03f9172cSAndroid Build Coastguard Worker 	struct wpa_driver_scan_params *params = &data->scan_params;
1203*03f9172cSAndroid Build Coastguard Worker 	const u8 *subelems;
1204*03f9172cSAndroid Build Coastguard Worker 	size_t elems_len;
1205*03f9172cSAndroid Build Coastguard Worker 	u16 rand_interval;
1206*03f9172cSAndroid Build Coastguard Worker 	u32 interval_usec;
1207*03f9172cSAndroid Build Coastguard Worker 	u32 _rand;
1208*03f9172cSAndroid Build Coastguard Worker 	int ret = 0, res;
1209*03f9172cSAndroid Build Coastguard Worker 	u8 reject_mode;
1210*03f9172cSAndroid Build Coastguard Worker 
1211*03f9172cSAndroid Build Coastguard Worker 	if (len < sizeof(*req))
1212*03f9172cSAndroid Build Coastguard Worker 		return -1;
1213*03f9172cSAndroid Build Coastguard Worker 
1214*03f9172cSAndroid Build Coastguard Worker 	if (req->mode != BEACON_REPORT_MODE_PASSIVE &&
1215*03f9172cSAndroid Build Coastguard Worker 	    req->mode != BEACON_REPORT_MODE_ACTIVE &&
1216*03f9172cSAndroid Build Coastguard Worker 	    req->mode != BEACON_REPORT_MODE_TABLE)
1217*03f9172cSAndroid Build Coastguard Worker 		return 0;
1218*03f9172cSAndroid Build Coastguard Worker 
1219*03f9172cSAndroid Build Coastguard Worker 	subelems = req->variable;
1220*03f9172cSAndroid Build Coastguard Worker 	elems_len = len - sizeof(*req);
1221*03f9172cSAndroid Build Coastguard Worker 	rand_interval = le_to_host16(req->rand_interval);
1222*03f9172cSAndroid Build Coastguard Worker 
1223*03f9172cSAndroid Build Coastguard Worker 	os_free(params->freqs);
1224*03f9172cSAndroid Build Coastguard Worker 	os_memset(params, 0, sizeof(*params));
1225*03f9172cSAndroid Build Coastguard Worker 
1226*03f9172cSAndroid Build Coastguard Worker 	data->token = elem_token;
1227*03f9172cSAndroid Build Coastguard Worker 
1228*03f9172cSAndroid Build Coastguard Worker 	/* default reporting detail is all fixed length fields and all
1229*03f9172cSAndroid Build Coastguard Worker 	 * elements */
1230*03f9172cSAndroid Build Coastguard Worker 	data->report_detail = BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS;
1231*03f9172cSAndroid Build Coastguard Worker 	os_memcpy(data->bssid, req->bssid, ETH_ALEN);
1232*03f9172cSAndroid Build Coastguard Worker 
1233*03f9172cSAndroid Build Coastguard Worker 	while (elems_len >= 2) {
1234*03f9172cSAndroid Build Coastguard Worker 		if (subelems[1] > elems_len - 2) {
1235*03f9172cSAndroid Build Coastguard Worker 			wpa_printf(MSG_DEBUG,
1236*03f9172cSAndroid Build Coastguard Worker 				   "Beacon Request: Truncated subelement");
1237*03f9172cSAndroid Build Coastguard Worker 			ret = -1;
1238*03f9172cSAndroid Build Coastguard Worker 			goto out;
1239*03f9172cSAndroid Build Coastguard Worker 		}
1240*03f9172cSAndroid Build Coastguard Worker 
1241*03f9172cSAndroid Build Coastguard Worker 		res = wpas_rm_handle_beacon_req_subelem(
1242*03f9172cSAndroid Build Coastguard Worker 			wpa_s, data, subelems[0], subelems[1], &subelems[2]);
1243*03f9172cSAndroid Build Coastguard Worker 		if (res < 0) {
1244*03f9172cSAndroid Build Coastguard Worker 			ret = res;
1245*03f9172cSAndroid Build Coastguard Worker 			goto out;
1246*03f9172cSAndroid Build Coastguard Worker 		} else if (!res) {
1247*03f9172cSAndroid Build Coastguard Worker 			reject_mode = MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE;
1248*03f9172cSAndroid Build Coastguard Worker 			goto out_reject;
1249*03f9172cSAndroid Build Coastguard Worker 		}
1250*03f9172cSAndroid Build Coastguard Worker 
1251*03f9172cSAndroid Build Coastguard Worker 		elems_len -= 2 + subelems[1];
1252*03f9172cSAndroid Build Coastguard Worker 		subelems += 2 + subelems[1];
1253*03f9172cSAndroid Build Coastguard Worker 	}
1254*03f9172cSAndroid Build Coastguard Worker 
1255*03f9172cSAndroid Build Coastguard Worker 	if (req->mode == BEACON_REPORT_MODE_TABLE) {
1256*03f9172cSAndroid Build Coastguard Worker 		wpas_beacon_rep_table(wpa_s, buf);
1257*03f9172cSAndroid Build Coastguard Worker 		goto out;
1258*03f9172cSAndroid Build Coastguard Worker 	}
1259*03f9172cSAndroid Build Coastguard Worker 
1260*03f9172cSAndroid Build Coastguard Worker 	params->freqs = wpas_beacon_request_freqs(wpa_s, req->oper_class,
1261*03f9172cSAndroid Build Coastguard Worker 						  req->channel, req->variable,
1262*03f9172cSAndroid Build Coastguard Worker 						  len - sizeof(*req));
1263*03f9172cSAndroid Build Coastguard Worker 	if (!params->freqs) {
1264*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_DEBUG, "Beacon request: No valid channels");
1265*03f9172cSAndroid Build Coastguard Worker 		reject_mode = MEASUREMENT_REPORT_MODE_REJECT_REFUSED;
1266*03f9172cSAndroid Build Coastguard Worker 		goto out_reject;
1267*03f9172cSAndroid Build Coastguard Worker 	}
1268*03f9172cSAndroid Build Coastguard Worker 
1269*03f9172cSAndroid Build Coastguard Worker 	params->duration = le_to_host16(req->duration);
1270*03f9172cSAndroid Build Coastguard Worker 	params->duration_mandatory = duration_mandatory;
1271*03f9172cSAndroid Build Coastguard Worker 	if (!params->duration) {
1272*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_DEBUG, "Beacon request: Duration is 0");
1273*03f9172cSAndroid Build Coastguard Worker 		ret = -1;
1274*03f9172cSAndroid Build Coastguard Worker 		goto out;
1275*03f9172cSAndroid Build Coastguard Worker 	}
1276*03f9172cSAndroid Build Coastguard Worker 
1277*03f9172cSAndroid Build Coastguard Worker 	params->only_new_results = 1;
1278*03f9172cSAndroid Build Coastguard Worker 
1279*03f9172cSAndroid Build Coastguard Worker 	if (req->mode == BEACON_REPORT_MODE_ACTIVE) {
1280*03f9172cSAndroid Build Coastguard Worker 		params->ssids[params->num_ssids].ssid = data->ssid;
1281*03f9172cSAndroid Build Coastguard Worker 		params->ssids[params->num_ssids++].ssid_len = data->ssid_len;
1282*03f9172cSAndroid Build Coastguard Worker 	}
1283*03f9172cSAndroid Build Coastguard Worker 
1284*03f9172cSAndroid Build Coastguard Worker 	if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
1285*03f9172cSAndroid Build Coastguard Worker 		_rand = os_random();
1286*03f9172cSAndroid Build Coastguard Worker 	interval_usec = (_rand % (rand_interval + 1)) * 1024;
1287*03f9172cSAndroid Build Coastguard Worker 	eloop_register_timeout(0, interval_usec, wpas_rrm_scan_timeout, wpa_s,
1288*03f9172cSAndroid Build Coastguard Worker 			       NULL);
1289*03f9172cSAndroid Build Coastguard Worker 	return 1;
1290*03f9172cSAndroid Build Coastguard Worker out_reject:
1291*03f9172cSAndroid Build Coastguard Worker 	if (!is_multicast_ether_addr(wpa_s->rrm.dst_addr) &&
1292*03f9172cSAndroid Build Coastguard Worker 	    wpas_rrm_report_elem(buf, elem_token, reject_mode,
1293*03f9172cSAndroid Build Coastguard Worker 				 MEASURE_TYPE_BEACON, NULL, 0) < 0) {
1294*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_DEBUG, "RRM: Failed to add report element");
1295*03f9172cSAndroid Build Coastguard Worker 		ret = -1;
1296*03f9172cSAndroid Build Coastguard Worker 	}
1297*03f9172cSAndroid Build Coastguard Worker out:
1298*03f9172cSAndroid Build Coastguard Worker 	wpas_clear_beacon_rep_data(wpa_s);
1299*03f9172cSAndroid Build Coastguard Worker 	return ret;
1300*03f9172cSAndroid Build Coastguard Worker }
1301*03f9172cSAndroid Build Coastguard Worker 
1302*03f9172cSAndroid Build Coastguard Worker 
1303*03f9172cSAndroid Build Coastguard Worker static int
wpas_rrm_handle_msr_req_element(struct wpa_supplicant * wpa_s,const struct rrm_measurement_request_element * req,struct wpabuf ** buf)1304*03f9172cSAndroid Build Coastguard Worker wpas_rrm_handle_msr_req_element(
1305*03f9172cSAndroid Build Coastguard Worker 	struct wpa_supplicant *wpa_s,
1306*03f9172cSAndroid Build Coastguard Worker 	const struct rrm_measurement_request_element *req,
1307*03f9172cSAndroid Build Coastguard Worker 	struct wpabuf **buf)
1308*03f9172cSAndroid Build Coastguard Worker {
1309*03f9172cSAndroid Build Coastguard Worker 	int duration_mandatory;
1310*03f9172cSAndroid Build Coastguard Worker 
1311*03f9172cSAndroid Build Coastguard Worker 	wpa_printf(MSG_DEBUG, "Measurement request type %d token %d",
1312*03f9172cSAndroid Build Coastguard Worker 		   req->type, req->token);
1313*03f9172cSAndroid Build Coastguard Worker 
1314*03f9172cSAndroid Build Coastguard Worker 	if (req->mode & MEASUREMENT_REQUEST_MODE_ENABLE) {
1315*03f9172cSAndroid Build Coastguard Worker 		/* Enable bit is not supported for now */
1316*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_DEBUG, "RRM: Enable bit not supported, ignore");
1317*03f9172cSAndroid Build Coastguard Worker 		return 0;
1318*03f9172cSAndroid Build Coastguard Worker 	}
1319*03f9172cSAndroid Build Coastguard Worker 
1320*03f9172cSAndroid Build Coastguard Worker 	if ((req->mode & MEASUREMENT_REQUEST_MODE_PARALLEL) &&
1321*03f9172cSAndroid Build Coastguard Worker 	    req->type > MEASURE_TYPE_RPI_HIST) {
1322*03f9172cSAndroid Build Coastguard Worker 		/* Parallel measurements are not supported for now */
1323*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_DEBUG,
1324*03f9172cSAndroid Build Coastguard Worker 			   "RRM: Parallel measurements are not supported, reject");
1325*03f9172cSAndroid Build Coastguard Worker 		goto reject;
1326*03f9172cSAndroid Build Coastguard Worker 	}
1327*03f9172cSAndroid Build Coastguard Worker 
1328*03f9172cSAndroid Build Coastguard Worker 	duration_mandatory =
1329*03f9172cSAndroid Build Coastguard Worker 		!!(req->mode & MEASUREMENT_REQUEST_MODE_DURATION_MANDATORY);
1330*03f9172cSAndroid Build Coastguard Worker 
1331*03f9172cSAndroid Build Coastguard Worker 	switch (req->type) {
1332*03f9172cSAndroid Build Coastguard Worker 	case MEASURE_TYPE_LCI:
1333*03f9172cSAndroid Build Coastguard Worker 		return wpas_rrm_build_lci_report(wpa_s, req, buf);
1334*03f9172cSAndroid Build Coastguard Worker 	case MEASURE_TYPE_BEACON:
1335*03f9172cSAndroid Build Coastguard Worker 		if (duration_mandatory &&
1336*03f9172cSAndroid Build Coastguard Worker 		    !(wpa_s->drv_rrm_flags &
1337*03f9172cSAndroid Build Coastguard Worker 		      WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL)) {
1338*03f9172cSAndroid Build Coastguard Worker 			wpa_printf(MSG_DEBUG,
1339*03f9172cSAndroid Build Coastguard Worker 				   "RRM: Driver does not support dwell time configuration - reject beacon report with mandatory duration");
1340*03f9172cSAndroid Build Coastguard Worker 			goto reject;
1341*03f9172cSAndroid Build Coastguard Worker 		}
1342*03f9172cSAndroid Build Coastguard Worker 		return wpas_rm_handle_beacon_req(wpa_s, req->token,
1343*03f9172cSAndroid Build Coastguard Worker 						 duration_mandatory,
1344*03f9172cSAndroid Build Coastguard Worker 						 (const void *) req->variable,
1345*03f9172cSAndroid Build Coastguard Worker 						 req->len - 3, buf);
1346*03f9172cSAndroid Build Coastguard Worker 	default:
1347*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_INFO,
1348*03f9172cSAndroid Build Coastguard Worker 			   "RRM: Unsupported radio measurement type %u",
1349*03f9172cSAndroid Build Coastguard Worker 			   req->type);
1350*03f9172cSAndroid Build Coastguard Worker 		break;
1351*03f9172cSAndroid Build Coastguard Worker 	}
1352*03f9172cSAndroid Build Coastguard Worker 
1353*03f9172cSAndroid Build Coastguard Worker reject:
1354*03f9172cSAndroid Build Coastguard Worker 	if (!is_multicast_ether_addr(wpa_s->rrm.dst_addr) &&
1355*03f9172cSAndroid Build Coastguard Worker 	    wpas_rrm_report_elem(buf, req->token,
1356*03f9172cSAndroid Build Coastguard Worker 				 MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE,
1357*03f9172cSAndroid Build Coastguard Worker 				 req->type, NULL, 0) < 0) {
1358*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_DEBUG, "RRM: Failed to add report element");
1359*03f9172cSAndroid Build Coastguard Worker 		return -1;
1360*03f9172cSAndroid Build Coastguard Worker 	}
1361*03f9172cSAndroid Build Coastguard Worker 
1362*03f9172cSAndroid Build Coastguard Worker 	return 0;
1363*03f9172cSAndroid Build Coastguard Worker }
1364*03f9172cSAndroid Build Coastguard Worker 
1365*03f9172cSAndroid Build Coastguard Worker 
1366*03f9172cSAndroid Build Coastguard Worker static struct wpabuf *
wpas_rrm_process_msr_req_elems(struct wpa_supplicant * wpa_s,const u8 * pos,size_t len)1367*03f9172cSAndroid Build Coastguard Worker wpas_rrm_process_msr_req_elems(struct wpa_supplicant *wpa_s, const u8 *pos,
1368*03f9172cSAndroid Build Coastguard Worker 			       size_t len)
1369*03f9172cSAndroid Build Coastguard Worker {
1370*03f9172cSAndroid Build Coastguard Worker 	struct wpabuf *buf = NULL;
1371*03f9172cSAndroid Build Coastguard Worker 
1372*03f9172cSAndroid Build Coastguard Worker 	while (len) {
1373*03f9172cSAndroid Build Coastguard Worker 		const struct rrm_measurement_request_element *req;
1374*03f9172cSAndroid Build Coastguard Worker 		int res;
1375*03f9172cSAndroid Build Coastguard Worker 
1376*03f9172cSAndroid Build Coastguard Worker 		if (len < 2) {
1377*03f9172cSAndroid Build Coastguard Worker 			wpa_printf(MSG_DEBUG, "RRM: Truncated element");
1378*03f9172cSAndroid Build Coastguard Worker 			goto out;
1379*03f9172cSAndroid Build Coastguard Worker 		}
1380*03f9172cSAndroid Build Coastguard Worker 
1381*03f9172cSAndroid Build Coastguard Worker 		req = (const struct rrm_measurement_request_element *) pos;
1382*03f9172cSAndroid Build Coastguard Worker 		if (req->eid != WLAN_EID_MEASURE_REQUEST) {
1383*03f9172cSAndroid Build Coastguard Worker 			wpa_printf(MSG_DEBUG,
1384*03f9172cSAndroid Build Coastguard Worker 				   "RRM: Expected Measurement Request element, but EID is %u",
1385*03f9172cSAndroid Build Coastguard Worker 				   req->eid);
1386*03f9172cSAndroid Build Coastguard Worker 			goto out;
1387*03f9172cSAndroid Build Coastguard Worker 		}
1388*03f9172cSAndroid Build Coastguard Worker 
1389*03f9172cSAndroid Build Coastguard Worker 		if (req->len < 3) {
1390*03f9172cSAndroid Build Coastguard Worker 			wpa_printf(MSG_DEBUG, "RRM: Element length too short");
1391*03f9172cSAndroid Build Coastguard Worker 			goto out;
1392*03f9172cSAndroid Build Coastguard Worker 		}
1393*03f9172cSAndroid Build Coastguard Worker 
1394*03f9172cSAndroid Build Coastguard Worker 		if (req->len > len - 2) {
1395*03f9172cSAndroid Build Coastguard Worker 			wpa_printf(MSG_DEBUG, "RRM: Element length too long");
1396*03f9172cSAndroid Build Coastguard Worker 			goto out;
1397*03f9172cSAndroid Build Coastguard Worker 		}
1398*03f9172cSAndroid Build Coastguard Worker 
1399*03f9172cSAndroid Build Coastguard Worker 		res = wpas_rrm_handle_msr_req_element(wpa_s, req, &buf);
1400*03f9172cSAndroid Build Coastguard Worker 		if (res < 0)
1401*03f9172cSAndroid Build Coastguard Worker 			goto out;
1402*03f9172cSAndroid Build Coastguard Worker 
1403*03f9172cSAndroid Build Coastguard Worker 		pos += req->len + 2;
1404*03f9172cSAndroid Build Coastguard Worker 		len -= req->len + 2;
1405*03f9172cSAndroid Build Coastguard Worker 	}
1406*03f9172cSAndroid Build Coastguard Worker 
1407*03f9172cSAndroid Build Coastguard Worker 	return buf;
1408*03f9172cSAndroid Build Coastguard Worker 
1409*03f9172cSAndroid Build Coastguard Worker out:
1410*03f9172cSAndroid Build Coastguard Worker 	wpabuf_free(buf);
1411*03f9172cSAndroid Build Coastguard Worker 	return NULL;
1412*03f9172cSAndroid Build Coastguard Worker }
1413*03f9172cSAndroid Build Coastguard Worker 
1414*03f9172cSAndroid Build Coastguard Worker 
wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant * wpa_s,const u8 * src,const u8 * dst,const u8 * frame,size_t len)1415*03f9172cSAndroid Build Coastguard Worker void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s,
1416*03f9172cSAndroid Build Coastguard Worker 					       const u8 *src, const u8 *dst,
1417*03f9172cSAndroid Build Coastguard Worker 					       const u8 *frame, size_t len)
1418*03f9172cSAndroid Build Coastguard Worker {
1419*03f9172cSAndroid Build Coastguard Worker 	struct wpabuf *report;
1420*03f9172cSAndroid Build Coastguard Worker 
1421*03f9172cSAndroid Build Coastguard Worker 	if (wpa_s->wpa_state != WPA_COMPLETED) {
1422*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_INFO,
1423*03f9172cSAndroid Build Coastguard Worker 			   "RRM: Ignoring radio measurement request: Not associated");
1424*03f9172cSAndroid Build Coastguard Worker 		return;
1425*03f9172cSAndroid Build Coastguard Worker 	}
1426*03f9172cSAndroid Build Coastguard Worker 
1427*03f9172cSAndroid Build Coastguard Worker 	if (!wpa_s->rrm.rrm_used) {
1428*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_INFO,
1429*03f9172cSAndroid Build Coastguard Worker 			   "RRM: Ignoring radio measurement request: Not RRM network");
1430*03f9172cSAndroid Build Coastguard Worker 		return;
1431*03f9172cSAndroid Build Coastguard Worker 	}
1432*03f9172cSAndroid Build Coastguard Worker 
1433*03f9172cSAndroid Build Coastguard Worker 	if (len < 3) {
1434*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_INFO,
1435*03f9172cSAndroid Build Coastguard Worker 			   "RRM: Ignoring too short radio measurement request");
1436*03f9172cSAndroid Build Coastguard Worker 		return;
1437*03f9172cSAndroid Build Coastguard Worker 	}
1438*03f9172cSAndroid Build Coastguard Worker 
1439*03f9172cSAndroid Build Coastguard Worker 	wpa_s->rrm.token = *frame;
1440*03f9172cSAndroid Build Coastguard Worker 	os_memcpy(wpa_s->rrm.dst_addr, dst, ETH_ALEN);
1441*03f9172cSAndroid Build Coastguard Worker 
1442*03f9172cSAndroid Build Coastguard Worker 	/* Number of repetitions is not supported */
1443*03f9172cSAndroid Build Coastguard Worker 
1444*03f9172cSAndroid Build Coastguard Worker 	report = wpas_rrm_process_msr_req_elems(wpa_s, frame + 3, len - 3);
1445*03f9172cSAndroid Build Coastguard Worker 	if (!report)
1446*03f9172cSAndroid Build Coastguard Worker 		return;
1447*03f9172cSAndroid Build Coastguard Worker 
1448*03f9172cSAndroid Build Coastguard Worker 	wpas_rrm_send_msr_report(wpa_s, report);
1449*03f9172cSAndroid Build Coastguard Worker 	wpabuf_free(report);
1450*03f9172cSAndroid Build Coastguard Worker }
1451*03f9172cSAndroid Build Coastguard Worker 
1452*03f9172cSAndroid Build Coastguard Worker 
wpas_rrm_handle_link_measurement_request(struct wpa_supplicant * wpa_s,const u8 * src,const u8 * frame,size_t len,int rssi)1453*03f9172cSAndroid Build Coastguard Worker void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s,
1454*03f9172cSAndroid Build Coastguard Worker 					      const u8 *src,
1455*03f9172cSAndroid Build Coastguard Worker 					      const u8 *frame, size_t len,
1456*03f9172cSAndroid Build Coastguard Worker 					      int rssi)
1457*03f9172cSAndroid Build Coastguard Worker {
1458*03f9172cSAndroid Build Coastguard Worker 	struct wpabuf *buf;
1459*03f9172cSAndroid Build Coastguard Worker 	const struct rrm_link_measurement_request *req;
1460*03f9172cSAndroid Build Coastguard Worker 	struct rrm_link_measurement_report report;
1461*03f9172cSAndroid Build Coastguard Worker 
1462*03f9172cSAndroid Build Coastguard Worker 	if (wpa_s->wpa_state != WPA_COMPLETED) {
1463*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_INFO,
1464*03f9172cSAndroid Build Coastguard Worker 			   "RRM: Ignoring link measurement request. Not associated");
1465*03f9172cSAndroid Build Coastguard Worker 		return;
1466*03f9172cSAndroid Build Coastguard Worker 	}
1467*03f9172cSAndroid Build Coastguard Worker 
1468*03f9172cSAndroid Build Coastguard Worker 	if (!wpa_s->rrm.rrm_used) {
1469*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_INFO,
1470*03f9172cSAndroid Build Coastguard Worker 			   "RRM: Ignoring link measurement request. Not RRM network");
1471*03f9172cSAndroid Build Coastguard Worker 		return;
1472*03f9172cSAndroid Build Coastguard Worker 	}
1473*03f9172cSAndroid Build Coastguard Worker 
1474*03f9172cSAndroid Build Coastguard Worker 	if (!(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION)) {
1475*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_INFO,
1476*03f9172cSAndroid Build Coastguard Worker 			   "RRM: Measurement report failed. TX power insertion not supported");
1477*03f9172cSAndroid Build Coastguard Worker 		return;
1478*03f9172cSAndroid Build Coastguard Worker 	}
1479*03f9172cSAndroid Build Coastguard Worker 
1480*03f9172cSAndroid Build Coastguard Worker 	req = (const struct rrm_link_measurement_request *) frame;
1481*03f9172cSAndroid Build Coastguard Worker 	if (len < sizeof(*req)) {
1482*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_INFO,
1483*03f9172cSAndroid Build Coastguard Worker 			   "RRM: Link measurement report failed. Request too short");
1484*03f9172cSAndroid Build Coastguard Worker 		return;
1485*03f9172cSAndroid Build Coastguard Worker 	}
1486*03f9172cSAndroid Build Coastguard Worker 
1487*03f9172cSAndroid Build Coastguard Worker 	os_memset(&report, 0, sizeof(report));
1488*03f9172cSAndroid Build Coastguard Worker 	report.dialog_token = req->dialog_token;
1489*03f9172cSAndroid Build Coastguard Worker 	report.tpc.eid = WLAN_EID_TPC_REPORT;
1490*03f9172cSAndroid Build Coastguard Worker 	report.tpc.len = 2;
1491*03f9172cSAndroid Build Coastguard Worker 	/* Note: The driver is expected to update report.tpc.tx_power and
1492*03f9172cSAndroid Build Coastguard Worker 	 * report.tpc.link_margin subfields when sending out this frame.
1493*03f9172cSAndroid Build Coastguard Worker 	 * Similarly, the driver would need to update report.rx_ant_id and
1494*03f9172cSAndroid Build Coastguard Worker 	 * report.tx_ant_id subfields. */
1495*03f9172cSAndroid Build Coastguard Worker 	report.rsni = 255; /* 255 indicates that RSNI is not available */
1496*03f9172cSAndroid Build Coastguard Worker 	report.rcpi = rssi_to_rcpi(rssi);
1497*03f9172cSAndroid Build Coastguard Worker 
1498*03f9172cSAndroid Build Coastguard Worker 	/* action_category + action_code */
1499*03f9172cSAndroid Build Coastguard Worker 	buf = wpabuf_alloc(2 + sizeof(report));
1500*03f9172cSAndroid Build Coastguard Worker 	if (buf == NULL) {
1501*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_ERROR,
1502*03f9172cSAndroid Build Coastguard Worker 			   "RRM: Link measurement report failed. Buffer allocation failed");
1503*03f9172cSAndroid Build Coastguard Worker 		return;
1504*03f9172cSAndroid Build Coastguard Worker 	}
1505*03f9172cSAndroid Build Coastguard Worker 
1506*03f9172cSAndroid Build Coastguard Worker 	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
1507*03f9172cSAndroid Build Coastguard Worker 	wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REPORT);
1508*03f9172cSAndroid Build Coastguard Worker 	wpabuf_put_data(buf, &report, sizeof(report));
1509*03f9172cSAndroid Build Coastguard Worker 	wpa_hexdump_buf(MSG_DEBUG, "RRM: Link measurement report", buf);
1510*03f9172cSAndroid Build Coastguard Worker 
1511*03f9172cSAndroid Build Coastguard Worker 	if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, src,
1512*03f9172cSAndroid Build Coastguard Worker 				wpa_s->own_addr, wpa_s->bssid,
1513*03f9172cSAndroid Build Coastguard Worker 				wpabuf_head(buf), wpabuf_len(buf), 0)) {
1514*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_ERROR,
1515*03f9172cSAndroid Build Coastguard Worker 			   "RRM: Link measurement report failed. Send action failed");
1516*03f9172cSAndroid Build Coastguard Worker 	}
1517*03f9172cSAndroid Build Coastguard Worker 	wpabuf_free(buf);
1518*03f9172cSAndroid Build Coastguard Worker }
1519*03f9172cSAndroid Build Coastguard Worker 
1520*03f9172cSAndroid Build Coastguard Worker 
wpas_beacon_rep_scan_match(struct wpa_supplicant * wpa_s,const u8 * bssid)1521*03f9172cSAndroid Build Coastguard Worker static bool wpas_beacon_rep_scan_match(struct wpa_supplicant *wpa_s,
1522*03f9172cSAndroid Build Coastguard Worker 				       const u8 *bssid)
1523*03f9172cSAndroid Build Coastguard Worker {
1524*03f9172cSAndroid Build Coastguard Worker 	u8 i;
1525*03f9172cSAndroid Build Coastguard Worker 
1526*03f9172cSAndroid Build Coastguard Worker 	if (!wpa_s->valid_links)
1527*03f9172cSAndroid Build Coastguard Worker 		return ether_addr_equal(wpa_s->current_bss->bssid, bssid);
1528*03f9172cSAndroid Build Coastguard Worker 
1529*03f9172cSAndroid Build Coastguard Worker 	for_each_link(wpa_s->valid_links, i) {
1530*03f9172cSAndroid Build Coastguard Worker 		if (ether_addr_equal(wpa_s->links[i].bssid, bssid))
1531*03f9172cSAndroid Build Coastguard Worker 			return true;
1532*03f9172cSAndroid Build Coastguard Worker 	}
1533*03f9172cSAndroid Build Coastguard Worker 
1534*03f9172cSAndroid Build Coastguard Worker 	wpa_printf(MSG_DEBUG, "RRM: MLD: no match for TSF BSSID=" MACSTR,
1535*03f9172cSAndroid Build Coastguard Worker 		   MAC2STR(bssid));
1536*03f9172cSAndroid Build Coastguard Worker 
1537*03f9172cSAndroid Build Coastguard Worker 	return false;
1538*03f9172cSAndroid Build Coastguard Worker }
1539*03f9172cSAndroid Build Coastguard Worker 
1540*03f9172cSAndroid Build Coastguard Worker 
wpas_beacon_rep_scan_process(struct wpa_supplicant * wpa_s,struct wpa_scan_results * scan_res,struct scan_info * info)1541*03f9172cSAndroid Build Coastguard Worker int wpas_beacon_rep_scan_process(struct wpa_supplicant *wpa_s,
1542*03f9172cSAndroid Build Coastguard Worker 				 struct wpa_scan_results *scan_res,
1543*03f9172cSAndroid Build Coastguard Worker 				 struct scan_info *info)
1544*03f9172cSAndroid Build Coastguard Worker {
1545*03f9172cSAndroid Build Coastguard Worker 	size_t i = 0;
1546*03f9172cSAndroid Build Coastguard Worker 	struct wpabuf *buf = NULL;
1547*03f9172cSAndroid Build Coastguard Worker 
1548*03f9172cSAndroid Build Coastguard Worker 	if (!wpa_s->beacon_rep_data.token)
1549*03f9172cSAndroid Build Coastguard Worker 		return 0;
1550*03f9172cSAndroid Build Coastguard Worker 
1551*03f9172cSAndroid Build Coastguard Worker 	if (!wpa_s->current_bss)
1552*03f9172cSAndroid Build Coastguard Worker 		goto out;
1553*03f9172cSAndroid Build Coastguard Worker 
1554*03f9172cSAndroid Build Coastguard Worker 	/* If the measurement was aborted, don't report partial results */
1555*03f9172cSAndroid Build Coastguard Worker 	if (info->aborted)
1556*03f9172cSAndroid Build Coastguard Worker 		goto out;
1557*03f9172cSAndroid Build Coastguard Worker 
1558*03f9172cSAndroid Build Coastguard Worker 	wpa_printf(MSG_DEBUG, "RRM: TSF BSSID: " MACSTR " current BSS: " MACSTR,
1559*03f9172cSAndroid Build Coastguard Worker 		   MAC2STR(info->scan_start_tsf_bssid),
1560*03f9172cSAndroid Build Coastguard Worker 		   MAC2STR(wpa_s->current_bss->bssid));
1561*03f9172cSAndroid Build Coastguard Worker 	if ((wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT) &&
1562*03f9172cSAndroid Build Coastguard Worker 	    !wpas_beacon_rep_scan_match(wpa_s, info->scan_start_tsf_bssid)) {
1563*03f9172cSAndroid Build Coastguard Worker 		wpa_printf(MSG_DEBUG,
1564*03f9172cSAndroid Build Coastguard Worker 			   "RRM: Ignore scan results due to mismatching TSF BSSID");
1565*03f9172cSAndroid Build Coastguard Worker 		goto out;
1566*03f9172cSAndroid Build Coastguard Worker 	}
1567*03f9172cSAndroid Build Coastguard Worker 
1568*03f9172cSAndroid Build Coastguard Worker 	for (i = 0; i < scan_res->num; i++) {
1569*03f9172cSAndroid Build Coastguard Worker 		struct wpa_bss *bss =
1570*03f9172cSAndroid Build Coastguard Worker 			wpa_bss_get_bssid(wpa_s, scan_res->res[i]->bssid);
1571*03f9172cSAndroid Build Coastguard Worker 
1572*03f9172cSAndroid Build Coastguard Worker 		if (!bss)
1573*03f9172cSAndroid Build Coastguard Worker 			continue;
1574*03f9172cSAndroid Build Coastguard Worker 
1575*03f9172cSAndroid Build Coastguard Worker 		if ((wpa_s->drv_rrm_flags &
1576*03f9172cSAndroid Build Coastguard Worker 		     WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT) &&
1577*03f9172cSAndroid Build Coastguard Worker 		    !wpas_beacon_rep_scan_match(wpa_s,
1578*03f9172cSAndroid Build Coastguard Worker 						scan_res->res[i]->tsf_bssid)) {
1579*03f9172cSAndroid Build Coastguard Worker 			wpa_printf(MSG_DEBUG,
1580*03f9172cSAndroid Build Coastguard Worker 				   "RRM: Ignore scan result for " MACSTR
1581*03f9172cSAndroid Build Coastguard Worker 				   " due to mismatching TSF BSSID" MACSTR,
1582*03f9172cSAndroid Build Coastguard Worker 				   MAC2STR(scan_res->res[i]->bssid),
1583*03f9172cSAndroid Build Coastguard Worker 				   MAC2STR(scan_res->res[i]->tsf_bssid));
1584*03f9172cSAndroid Build Coastguard Worker 			continue;
1585*03f9172cSAndroid Build Coastguard Worker 		}
1586*03f9172cSAndroid Build Coastguard Worker 
1587*03f9172cSAndroid Build Coastguard Worker 		/*
1588*03f9172cSAndroid Build Coastguard Worker 		 * Don't report results that were not received during the
1589*03f9172cSAndroid Build Coastguard Worker 		 * current measurement.
1590*03f9172cSAndroid Build Coastguard Worker 		 */
1591*03f9172cSAndroid Build Coastguard Worker 		if (!(wpa_s->drv_rrm_flags &
1592*03f9172cSAndroid Build Coastguard Worker 		      WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT)) {
1593*03f9172cSAndroid Build Coastguard Worker 			struct os_reltime update_time, diff;
1594*03f9172cSAndroid Build Coastguard Worker 
1595*03f9172cSAndroid Build Coastguard Worker 			/* For now, allow 8 ms older results due to some
1596*03f9172cSAndroid Build Coastguard Worker 			 * unknown issue with cfg80211 BSS table updates during
1597*03f9172cSAndroid Build Coastguard Worker 			 * a scan with the current BSS.
1598*03f9172cSAndroid Build Coastguard Worker 			 * TODO: Fix this more properly to avoid having to have
1599*03f9172cSAndroid Build Coastguard Worker 			 * this type of hacks in place. */
1600*03f9172cSAndroid Build Coastguard Worker 			calculate_update_time(&scan_res->fetch_time,
1601*03f9172cSAndroid Build Coastguard Worker 					      scan_res->res[i]->age,
1602*03f9172cSAndroid Build Coastguard Worker 					      &update_time);
1603*03f9172cSAndroid Build Coastguard Worker 			os_reltime_sub(&wpa_s->beacon_rep_scan,
1604*03f9172cSAndroid Build Coastguard Worker 				       &update_time, &diff);
1605*03f9172cSAndroid Build Coastguard Worker 			if (os_reltime_before(&update_time,
1606*03f9172cSAndroid Build Coastguard Worker 					      &wpa_s->beacon_rep_scan) &&
1607*03f9172cSAndroid Build Coastguard Worker 			    (diff.sec || diff.usec >= 8000)) {
1608*03f9172cSAndroid Build Coastguard Worker 				wpa_printf(MSG_DEBUG,
1609*03f9172cSAndroid Build Coastguard Worker 					   "RRM: Ignore scan result for " MACSTR
1610*03f9172cSAndroid Build Coastguard Worker 					   " due to old update (age(ms) %u, calculated age %u.%06u seconds)",
1611*03f9172cSAndroid Build Coastguard Worker 					   MAC2STR(scan_res->res[i]->bssid),
1612*03f9172cSAndroid Build Coastguard Worker 					   scan_res->res[i]->age,
1613*03f9172cSAndroid Build Coastguard Worker 					   (unsigned int) diff.sec,
1614*03f9172cSAndroid Build Coastguard Worker 					   (unsigned int) diff.usec);
1615*03f9172cSAndroid Build Coastguard Worker 				continue;
1616*03f9172cSAndroid Build Coastguard Worker 			}
1617*03f9172cSAndroid Build Coastguard Worker 		} else if (info->scan_start_tsf >
1618*03f9172cSAndroid Build Coastguard Worker 			   scan_res->res[i]->parent_tsf) {
1619*03f9172cSAndroid Build Coastguard Worker 			continue;
1620*03f9172cSAndroid Build Coastguard Worker 		}
1621*03f9172cSAndroid Build Coastguard Worker 
1622*03f9172cSAndroid Build Coastguard Worker 		if (wpas_add_beacon_rep(wpa_s, &buf, bss, info->scan_start_tsf,
1623*03f9172cSAndroid Build Coastguard Worker 					scan_res->res[i]->parent_tsf) < 0)
1624*03f9172cSAndroid Build Coastguard Worker 			break;
1625*03f9172cSAndroid Build Coastguard Worker 	}
1626*03f9172cSAndroid Build Coastguard Worker 
1627*03f9172cSAndroid Build Coastguard Worker 	if (!buf && wpas_beacon_rep_no_results(wpa_s, &buf))
1628*03f9172cSAndroid Build Coastguard Worker 		goto out;
1629*03f9172cSAndroid Build Coastguard Worker 
1630*03f9172cSAndroid Build Coastguard Worker 	wpa_hexdump_buf(MSG_DEBUG, "RRM: Radio Measurement report", buf);
1631*03f9172cSAndroid Build Coastguard Worker 
1632*03f9172cSAndroid Build Coastguard Worker 	wpas_rrm_send_msr_report(wpa_s, buf);
1633*03f9172cSAndroid Build Coastguard Worker 	wpabuf_free(buf);
1634*03f9172cSAndroid Build Coastguard Worker 
1635*03f9172cSAndroid Build Coastguard Worker out:
1636*03f9172cSAndroid Build Coastguard Worker 	wpas_clear_beacon_rep_data(wpa_s);
1637*03f9172cSAndroid Build Coastguard Worker 	return 1;
1638*03f9172cSAndroid Build Coastguard Worker }
1639*03f9172cSAndroid Build Coastguard Worker 
1640*03f9172cSAndroid Build Coastguard Worker 
wpas_clear_beacon_rep_data(struct wpa_supplicant * wpa_s)1641*03f9172cSAndroid Build Coastguard Worker void wpas_clear_beacon_rep_data(struct wpa_supplicant *wpa_s)
1642*03f9172cSAndroid Build Coastguard Worker {
1643*03f9172cSAndroid Build Coastguard Worker 	struct beacon_rep_data *data = &wpa_s->beacon_rep_data;
1644*03f9172cSAndroid Build Coastguard Worker 
1645*03f9172cSAndroid Build Coastguard Worker 	eloop_cancel_timeout(wpas_rrm_scan_timeout, wpa_s, NULL);
1646*03f9172cSAndroid Build Coastguard Worker 	bitfield_free(data->eids);
1647*03f9172cSAndroid Build Coastguard Worker 	bitfield_free(data->ext_eids);
1648*03f9172cSAndroid Build Coastguard Worker 	os_free(data->scan_params.freqs);
1649*03f9172cSAndroid Build Coastguard Worker 	os_memset(data, 0, sizeof(*data));
1650*03f9172cSAndroid Build Coastguard Worker }
1651