xref: /freebsd/contrib/wpa/wpa_supplicant/robust_av.c (revision 4b72b91a7132df1f77bbae194e1071ac621f1edb)
1c1d255d3SCy Schubert /*
2c1d255d3SCy Schubert  * wpa_supplicant - Robust AV procedures
3c1d255d3SCy Schubert  * Copyright (c) 2020, The Linux Foundation
4c1d255d3SCy Schubert  *
5c1d255d3SCy Schubert  * This software may be distributed under the terms of the BSD license.
6c1d255d3SCy Schubert  * See README for more details.
7c1d255d3SCy Schubert  */
8c1d255d3SCy Schubert 
9c1d255d3SCy Schubert #include "utils/includes.h"
10c1d255d3SCy Schubert #include "utils/common.h"
11*4b72b91aSCy Schubert #include "utils/eloop.h"
12c1d255d3SCy Schubert #include "common/wpa_ctrl.h"
13c1d255d3SCy Schubert #include "common/ieee802_11_common.h"
14c1d255d3SCy Schubert #include "wpa_supplicant_i.h"
15c1d255d3SCy Schubert #include "driver_i.h"
16c1d255d3SCy Schubert #include "bss.h"
17c1d255d3SCy Schubert 
18c1d255d3SCy Schubert 
19*4b72b91aSCy Schubert #define SCS_RESP_TIMEOUT 1
20*4b72b91aSCy Schubert #define DSCP_REQ_TIMEOUT 5
21*4b72b91aSCy Schubert 
22*4b72b91aSCy Schubert 
23c1d255d3SCy Schubert void wpas_populate_mscs_descriptor_ie(struct robust_av_data *robust_av,
24c1d255d3SCy Schubert 				      struct wpabuf *buf)
25c1d255d3SCy Schubert {
26c1d255d3SCy Schubert 	u8 *len, *len1;
27c1d255d3SCy Schubert 
28c1d255d3SCy Schubert 	/* MSCS descriptor element */
29c1d255d3SCy Schubert 	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
30c1d255d3SCy Schubert 	len = wpabuf_put(buf, 1);
31c1d255d3SCy Schubert 	wpabuf_put_u8(buf, WLAN_EID_EXT_MSCS_DESCRIPTOR);
32c1d255d3SCy Schubert 	wpabuf_put_u8(buf, robust_av->request_type);
33c1d255d3SCy Schubert 	wpabuf_put_u8(buf, robust_av->up_bitmap);
34c1d255d3SCy Schubert 	wpabuf_put_u8(buf, robust_av->up_limit);
35c1d255d3SCy Schubert 	wpabuf_put_le32(buf, robust_av->stream_timeout);
36c1d255d3SCy Schubert 
37c1d255d3SCy Schubert 	if (robust_av->request_type != SCS_REQ_REMOVE) {
38c1d255d3SCy Schubert 		/* TCLAS mask element */
39c1d255d3SCy Schubert 		wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
40c1d255d3SCy Schubert 		len1 = wpabuf_put(buf, 1);
41c1d255d3SCy Schubert 		wpabuf_put_u8(buf, WLAN_EID_EXT_TCLAS_MASK);
42c1d255d3SCy Schubert 
43c1d255d3SCy Schubert 		/* Frame classifier */
44c1d255d3SCy Schubert 		wpabuf_put_data(buf, robust_av->frame_classifier,
45c1d255d3SCy Schubert 				robust_av->frame_classifier_len);
46c1d255d3SCy Schubert 		*len1 = (u8 *) wpabuf_put(buf, 0) - len1 - 1;
47c1d255d3SCy Schubert 	}
48c1d255d3SCy Schubert 
49c1d255d3SCy Schubert 	*len = (u8 *) wpabuf_put(buf, 0) - len - 1;
50c1d255d3SCy Schubert }
51c1d255d3SCy Schubert 
52c1d255d3SCy Schubert 
53*4b72b91aSCy Schubert static int wpas_populate_type4_classifier(struct type4_params *type4_param,
54*4b72b91aSCy Schubert 					  struct wpabuf *buf)
55*4b72b91aSCy Schubert {
56*4b72b91aSCy Schubert 	/* classifier parameters */
57*4b72b91aSCy Schubert 	wpabuf_put_u8(buf, type4_param->classifier_mask);
58*4b72b91aSCy Schubert 	if (type4_param->ip_version == IPV4) {
59*4b72b91aSCy Schubert 		wpabuf_put_u8(buf, IPV4); /* IP version */
60*4b72b91aSCy Schubert 		wpabuf_put_data(buf, &type4_param->ip_params.v4.src_ip.s_addr,
61*4b72b91aSCy Schubert 				4);
62*4b72b91aSCy Schubert 		wpabuf_put_data(buf, &type4_param->ip_params.v4.dst_ip.s_addr,
63*4b72b91aSCy Schubert 				4);
64*4b72b91aSCy Schubert 		wpabuf_put_be16(buf, type4_param->ip_params.v4.src_port);
65*4b72b91aSCy Schubert 		wpabuf_put_be16(buf, type4_param->ip_params.v4.dst_port);
66*4b72b91aSCy Schubert 		wpabuf_put_u8(buf, type4_param->ip_params.v4.dscp);
67*4b72b91aSCy Schubert 		wpabuf_put_u8(buf, type4_param->ip_params.v4.protocol);
68*4b72b91aSCy Schubert 		wpabuf_put_u8(buf, 0); /* Reserved octet */
69*4b72b91aSCy Schubert 	} else {
70*4b72b91aSCy Schubert 		wpabuf_put_u8(buf, IPV6);
71*4b72b91aSCy Schubert 		wpabuf_put_data(buf, &type4_param->ip_params.v6.src_ip.s6_addr,
72*4b72b91aSCy Schubert 				16);
73*4b72b91aSCy Schubert 		wpabuf_put_data(buf, &type4_param->ip_params.v6.dst_ip.s6_addr,
74*4b72b91aSCy Schubert 				16);
75*4b72b91aSCy Schubert 		wpabuf_put_be16(buf, type4_param->ip_params.v6.src_port);
76*4b72b91aSCy Schubert 		wpabuf_put_be16(buf, type4_param->ip_params.v6.dst_port);
77*4b72b91aSCy Schubert 		wpabuf_put_u8(buf, type4_param->ip_params.v6.dscp);
78*4b72b91aSCy Schubert 		wpabuf_put_u8(buf, type4_param->ip_params.v6.next_header);
79*4b72b91aSCy Schubert 		wpabuf_put_data(buf, type4_param->ip_params.v6.flow_label, 3);
80*4b72b91aSCy Schubert 	}
81*4b72b91aSCy Schubert 
82*4b72b91aSCy Schubert 	return 0;
83*4b72b91aSCy Schubert }
84*4b72b91aSCy Schubert 
85*4b72b91aSCy Schubert 
86*4b72b91aSCy Schubert static int wpas_populate_type10_classifier(struct type10_params *type10_param,
87*4b72b91aSCy Schubert 					   struct wpabuf *buf)
88*4b72b91aSCy Schubert {
89*4b72b91aSCy Schubert 	/* classifier parameters */
90*4b72b91aSCy Schubert 	wpabuf_put_u8(buf, type10_param->prot_instance);
91*4b72b91aSCy Schubert 	wpabuf_put_u8(buf, type10_param->prot_number);
92*4b72b91aSCy Schubert 	wpabuf_put_data(buf, type10_param->filter_value,
93*4b72b91aSCy Schubert 			type10_param->filter_len);
94*4b72b91aSCy Schubert 	wpabuf_put_data(buf, type10_param->filter_mask,
95*4b72b91aSCy Schubert 			type10_param->filter_len);
96*4b72b91aSCy Schubert 	return 0;
97*4b72b91aSCy Schubert }
98*4b72b91aSCy Schubert 
99*4b72b91aSCy Schubert 
100*4b72b91aSCy Schubert static int wpas_populate_scs_descriptor_ie(struct scs_desc_elem *desc_elem,
101*4b72b91aSCy Schubert 					   struct wpabuf *buf)
102*4b72b91aSCy Schubert {
103*4b72b91aSCy Schubert 	u8 *len, *len1;
104*4b72b91aSCy Schubert 	struct tclas_element *tclas_elem;
105*4b72b91aSCy Schubert 	unsigned int i;
106*4b72b91aSCy Schubert 
107*4b72b91aSCy Schubert 	/* SCS Descriptor element */
108*4b72b91aSCy Schubert 	wpabuf_put_u8(buf, WLAN_EID_SCS_DESCRIPTOR);
109*4b72b91aSCy Schubert 	len = wpabuf_put(buf, 1);
110*4b72b91aSCy Schubert 	wpabuf_put_u8(buf, desc_elem->scs_id);
111*4b72b91aSCy Schubert 	wpabuf_put_u8(buf, desc_elem->request_type);
112*4b72b91aSCy Schubert 	if (desc_elem->request_type == SCS_REQ_REMOVE)
113*4b72b91aSCy Schubert 		goto end;
114*4b72b91aSCy Schubert 
115*4b72b91aSCy Schubert 	if (desc_elem->intra_access_priority || desc_elem->scs_up_avail) {
116*4b72b91aSCy Schubert 		wpabuf_put_u8(buf, WLAN_EID_INTRA_ACCESS_CATEGORY_PRIORITY);
117*4b72b91aSCy Schubert 		wpabuf_put_u8(buf, 1);
118*4b72b91aSCy Schubert 		wpabuf_put_u8(buf, desc_elem->intra_access_priority);
119*4b72b91aSCy Schubert 	}
120*4b72b91aSCy Schubert 
121*4b72b91aSCy Schubert 	tclas_elem = desc_elem->tclas_elems;
122*4b72b91aSCy Schubert 
123*4b72b91aSCy Schubert 	if (!tclas_elem)
124*4b72b91aSCy Schubert 		return -1;
125*4b72b91aSCy Schubert 
126*4b72b91aSCy Schubert 	for (i = 0; i < desc_elem->num_tclas_elem; i++, tclas_elem++) {
127*4b72b91aSCy Schubert 		int ret;
128*4b72b91aSCy Schubert 
129*4b72b91aSCy Schubert 		/* TCLAS element */
130*4b72b91aSCy Schubert 		wpabuf_put_u8(buf, WLAN_EID_TCLAS);
131*4b72b91aSCy Schubert 		len1 = wpabuf_put(buf, 1);
132*4b72b91aSCy Schubert 		wpabuf_put_u8(buf, 255); /* User Priority: not compared */
133*4b72b91aSCy Schubert 		/* Frame Classifier */
134*4b72b91aSCy Schubert 		wpabuf_put_u8(buf, tclas_elem->classifier_type);
135*4b72b91aSCy Schubert 		/* Frame classifier parameters */
136*4b72b91aSCy Schubert 		switch (tclas_elem->classifier_type) {
137*4b72b91aSCy Schubert 		case 4:
138*4b72b91aSCy Schubert 			ret = wpas_populate_type4_classifier(
139*4b72b91aSCy Schubert 				&tclas_elem->frame_classifier.type4_param,
140*4b72b91aSCy Schubert 				buf);
141*4b72b91aSCy Schubert 			break;
142*4b72b91aSCy Schubert 		case 10:
143*4b72b91aSCy Schubert 			ret = wpas_populate_type10_classifier(
144*4b72b91aSCy Schubert 				&tclas_elem->frame_classifier.type10_param,
145*4b72b91aSCy Schubert 				buf);
146*4b72b91aSCy Schubert 			break;
147*4b72b91aSCy Schubert 		default:
148*4b72b91aSCy Schubert 			return -1;
149*4b72b91aSCy Schubert 		}
150*4b72b91aSCy Schubert 
151*4b72b91aSCy Schubert 		if (ret == -1) {
152*4b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
153*4b72b91aSCy Schubert 				   "Failed to populate frame classifier");
154*4b72b91aSCy Schubert 			return -1;
155*4b72b91aSCy Schubert 		}
156*4b72b91aSCy Schubert 
157*4b72b91aSCy Schubert 		*len1 = (u8 *) wpabuf_put(buf, 0) - len1 - 1;
158*4b72b91aSCy Schubert 	}
159*4b72b91aSCy Schubert 
160*4b72b91aSCy Schubert 	if (desc_elem->num_tclas_elem > 1) {
161*4b72b91aSCy Schubert 		/* TCLAS Processing element */
162*4b72b91aSCy Schubert 		wpabuf_put_u8(buf, WLAN_EID_TCLAS_PROCESSING);
163*4b72b91aSCy Schubert 		wpabuf_put_u8(buf, 1);
164*4b72b91aSCy Schubert 		wpabuf_put_u8(buf, desc_elem->tclas_processing);
165*4b72b91aSCy Schubert 	}
166*4b72b91aSCy Schubert 
167*4b72b91aSCy Schubert end:
168*4b72b91aSCy Schubert 	*len = (u8 *) wpabuf_put(buf, 0) - len - 1;
169*4b72b91aSCy Schubert 	return 0;
170*4b72b91aSCy Schubert }
171*4b72b91aSCy Schubert 
172*4b72b91aSCy Schubert 
173c1d255d3SCy Schubert int wpas_send_mscs_req(struct wpa_supplicant *wpa_s)
174c1d255d3SCy Schubert {
175c1d255d3SCy Schubert 	struct wpabuf *buf;
176c1d255d3SCy Schubert 	size_t buf_len;
177c1d255d3SCy Schubert 	int ret;
178c1d255d3SCy Schubert 
179c1d255d3SCy Schubert 	if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid)
180c1d255d3SCy Schubert 		return 0;
181c1d255d3SCy Schubert 
182c1d255d3SCy Schubert 	if (!wpa_bss_ext_capab(wpa_s->current_bss, WLAN_EXT_CAPAB_MSCS)) {
183c1d255d3SCy Schubert 		wpa_dbg(wpa_s, MSG_INFO,
184c1d255d3SCy Schubert 			"AP does not support MSCS - could not send MSCS Req");
185c1d255d3SCy Schubert 		return -1;
186c1d255d3SCy Schubert 	}
187c1d255d3SCy Schubert 
188c1d255d3SCy Schubert 	if (!wpa_s->mscs_setup_done &&
189c1d255d3SCy Schubert 	    wpa_s->robust_av.request_type != SCS_REQ_ADD) {
190c1d255d3SCy Schubert 		wpa_msg(wpa_s, MSG_INFO,
191c1d255d3SCy Schubert 			"MSCS: Failed to send MSCS Request: request type invalid");
192c1d255d3SCy Schubert 		return -1;
193c1d255d3SCy Schubert 	}
194c1d255d3SCy Schubert 
195c1d255d3SCy Schubert 	buf_len = 3 +	/* Action frame header */
196c1d255d3SCy Schubert 		  3 +	/* MSCS descriptor IE header */
197c1d255d3SCy Schubert 		  1 +	/* Request type */
198c1d255d3SCy Schubert 		  2 +	/* User priority control */
199c1d255d3SCy Schubert 		  4 +	/* Stream timeout */
200c1d255d3SCy Schubert 		  3 +	/* TCLAS Mask IE header */
201c1d255d3SCy Schubert 		  wpa_s->robust_av.frame_classifier_len;
202c1d255d3SCy Schubert 
203c1d255d3SCy Schubert 	buf = wpabuf_alloc(buf_len);
204c1d255d3SCy Schubert 	if (!buf) {
205c1d255d3SCy Schubert 		wpa_printf(MSG_ERROR, "Failed to allocate MSCS req");
206c1d255d3SCy Schubert 		return -1;
207c1d255d3SCy Schubert 	}
208c1d255d3SCy Schubert 
209c1d255d3SCy Schubert 	wpabuf_put_u8(buf, WLAN_ACTION_ROBUST_AV_STREAMING);
210c1d255d3SCy Schubert 	wpabuf_put_u8(buf, ROBUST_AV_MSCS_REQ);
211c1d255d3SCy Schubert 	wpa_s->robust_av.dialog_token++;
212c1d255d3SCy Schubert 	wpabuf_put_u8(buf, wpa_s->robust_av.dialog_token);
213c1d255d3SCy Schubert 
214c1d255d3SCy Schubert 	/* MSCS descriptor element */
215c1d255d3SCy Schubert 	wpas_populate_mscs_descriptor_ie(&wpa_s->robust_av, buf);
216c1d255d3SCy Schubert 
217c1d255d3SCy Schubert 	wpa_hexdump_buf(MSG_MSGDUMP, "MSCS Request", buf);
218c1d255d3SCy Schubert 	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
219c1d255d3SCy Schubert 				  wpa_s->own_addr, wpa_s->bssid,
220c1d255d3SCy Schubert 				  wpabuf_head(buf), wpabuf_len(buf), 0);
221c1d255d3SCy Schubert 	if (ret < 0)
222c1d255d3SCy Schubert 		wpa_dbg(wpa_s, MSG_INFO, "MSCS: Failed to send MSCS Request");
223c1d255d3SCy Schubert 
224c1d255d3SCy Schubert 	wpabuf_free(buf);
225c1d255d3SCy Schubert 	return ret;
226c1d255d3SCy Schubert }
227c1d255d3SCy Schubert 
228c1d255d3SCy Schubert 
229*4b72b91aSCy Schubert static size_t tclas_elem_len(const struct tclas_element *elem)
230*4b72b91aSCy Schubert {
231*4b72b91aSCy Schubert 	size_t buf_len = 0;
232*4b72b91aSCy Schubert 
233*4b72b91aSCy Schubert 	buf_len += 2 +	/* TCLAS element header */
234*4b72b91aSCy Schubert 		1 +	/* User Priority */
235*4b72b91aSCy Schubert 		1 ;	/* Classifier Type */
236*4b72b91aSCy Schubert 
237*4b72b91aSCy Schubert 	if (elem->classifier_type == 4) {
238*4b72b91aSCy Schubert 		enum ip_version ip_ver;
239*4b72b91aSCy Schubert 
240*4b72b91aSCy Schubert 		buf_len += 1 +	/* Classifier mask */
241*4b72b91aSCy Schubert 			1 +	/* IP version */
242*4b72b91aSCy Schubert 			1 +	/* user priority */
243*4b72b91aSCy Schubert 			2 +	/* src_port */
244*4b72b91aSCy Schubert 			2 +	/* dst_port */
245*4b72b91aSCy Schubert 			1 ;	/* dscp */
246*4b72b91aSCy Schubert 		ip_ver = elem->frame_classifier.type4_param.ip_version;
247*4b72b91aSCy Schubert 		if (ip_ver == IPV4) {
248*4b72b91aSCy Schubert 			buf_len += 4 +  /* src_ip */
249*4b72b91aSCy Schubert 				4 +	/* dst_ip */
250*4b72b91aSCy Schubert 				1 +	/* protocol */
251*4b72b91aSCy Schubert 				1 ;  /* Reserved */
252*4b72b91aSCy Schubert 		} else if (ip_ver == IPV6) {
253*4b72b91aSCy Schubert 			buf_len += 16 +  /* src_ip */
254*4b72b91aSCy Schubert 				16 +  /* dst_ip */
255*4b72b91aSCy Schubert 				1  +  /* next_header */
256*4b72b91aSCy Schubert 				3  ;  /* flow_label */
257*4b72b91aSCy Schubert 		} else {
258*4b72b91aSCy Schubert 			wpa_printf(MSG_ERROR, "%s: Incorrect IP version %d",
259*4b72b91aSCy Schubert 				   __func__, ip_ver);
260*4b72b91aSCy Schubert 			return 0;
261*4b72b91aSCy Schubert 		}
262*4b72b91aSCy Schubert 	} else if (elem->classifier_type == 10) {
263*4b72b91aSCy Schubert 		buf_len += 1 +	/* protocol instance */
264*4b72b91aSCy Schubert 			1 +	/* protocol number */
265*4b72b91aSCy Schubert 			2 * elem->frame_classifier.type10_param.filter_len;
266*4b72b91aSCy Schubert 	} else {
267*4b72b91aSCy Schubert 		wpa_printf(MSG_ERROR, "%s: Incorrect classifier type %u",
268*4b72b91aSCy Schubert 			   __func__, elem->classifier_type);
269*4b72b91aSCy Schubert 		return 0;
270*4b72b91aSCy Schubert 	}
271*4b72b91aSCy Schubert 
272*4b72b91aSCy Schubert 	return buf_len;
273*4b72b91aSCy Schubert }
274*4b72b91aSCy Schubert 
275*4b72b91aSCy Schubert 
276*4b72b91aSCy Schubert static struct wpabuf * allocate_scs_buf(struct scs_desc_elem *desc_elem,
277*4b72b91aSCy Schubert 					unsigned int num_scs_desc)
278*4b72b91aSCy Schubert {
279*4b72b91aSCy Schubert 	struct wpabuf *buf;
280*4b72b91aSCy Schubert 	size_t buf_len = 0;
281*4b72b91aSCy Schubert 	unsigned int i, j;
282*4b72b91aSCy Schubert 
283*4b72b91aSCy Schubert 	buf_len = 3; /* Action frame header */
284*4b72b91aSCy Schubert 
285*4b72b91aSCy Schubert 	for (i = 0; i < num_scs_desc; i++, desc_elem++) {
286*4b72b91aSCy Schubert 		struct tclas_element *tclas_elem;
287*4b72b91aSCy Schubert 
288*4b72b91aSCy Schubert 		buf_len += 2 +	/* SCS descriptor IE header */
289*4b72b91aSCy Schubert 			   1 +	/* SCSID */
290*4b72b91aSCy Schubert 			   1 ;	/* Request type */
291*4b72b91aSCy Schubert 
292*4b72b91aSCy Schubert 		if (desc_elem->request_type == SCS_REQ_REMOVE)
293*4b72b91aSCy Schubert 			continue;
294*4b72b91aSCy Schubert 
295*4b72b91aSCy Schubert 		if (desc_elem->intra_access_priority || desc_elem->scs_up_avail)
296*4b72b91aSCy Schubert 			buf_len += 3;
297*4b72b91aSCy Schubert 
298*4b72b91aSCy Schubert 		tclas_elem = desc_elem->tclas_elems;
299*4b72b91aSCy Schubert 		if (!tclas_elem) {
300*4b72b91aSCy Schubert 			wpa_printf(MSG_ERROR, "%s: TCLAS element null",
301*4b72b91aSCy Schubert 				   __func__);
302*4b72b91aSCy Schubert 			return NULL;
303*4b72b91aSCy Schubert 		}
304*4b72b91aSCy Schubert 
305*4b72b91aSCy Schubert 		for (j = 0; j < desc_elem->num_tclas_elem; j++, tclas_elem++) {
306*4b72b91aSCy Schubert 			size_t elen;
307*4b72b91aSCy Schubert 
308*4b72b91aSCy Schubert 			elen = tclas_elem_len(tclas_elem);
309*4b72b91aSCy Schubert 			if (elen == 0)
310*4b72b91aSCy Schubert 				return NULL;
311*4b72b91aSCy Schubert 			buf_len += elen;
312*4b72b91aSCy Schubert 		}
313*4b72b91aSCy Schubert 
314*4b72b91aSCy Schubert 		if (desc_elem->num_tclas_elem > 1) {
315*4b72b91aSCy Schubert 			buf_len += 1 +	/* TCLAS Processing eid */
316*4b72b91aSCy Schubert 				   1 +	/* length */
317*4b72b91aSCy Schubert 				   1 ;	/* processing */
318*4b72b91aSCy Schubert 		}
319*4b72b91aSCy Schubert 	}
320*4b72b91aSCy Schubert 
321*4b72b91aSCy Schubert 	buf = wpabuf_alloc(buf_len);
322*4b72b91aSCy Schubert 	if (!buf) {
323*4b72b91aSCy Schubert 		wpa_printf(MSG_ERROR, "Failed to allocate SCS req");
324*4b72b91aSCy Schubert 		return NULL;
325*4b72b91aSCy Schubert 	}
326*4b72b91aSCy Schubert 
327*4b72b91aSCy Schubert 	return buf;
328*4b72b91aSCy Schubert }
329*4b72b91aSCy Schubert 
330*4b72b91aSCy Schubert 
331*4b72b91aSCy Schubert static void scs_request_timer(void *eloop_ctx, void *timeout_ctx)
332*4b72b91aSCy Schubert {
333*4b72b91aSCy Schubert 	struct wpa_supplicant *wpa_s = eloop_ctx;
334*4b72b91aSCy Schubert 	struct active_scs_elem *scs_desc, *prev;
335*4b72b91aSCy Schubert 
336*4b72b91aSCy Schubert 	if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid)
337*4b72b91aSCy Schubert 		return;
338*4b72b91aSCy Schubert 
339*4b72b91aSCy Schubert 	/* Once timeout is over, remove all SCS descriptors with no response */
340*4b72b91aSCy Schubert 	dl_list_for_each_safe(scs_desc, prev, &wpa_s->active_scs_ids,
341*4b72b91aSCy Schubert 			      struct active_scs_elem, list) {
342*4b72b91aSCy Schubert 		u8 bssid[ETH_ALEN] = { 0 };
343*4b72b91aSCy Schubert 		const u8 *src;
344*4b72b91aSCy Schubert 
345*4b72b91aSCy Schubert 		if (scs_desc->status == SCS_DESC_SUCCESS)
346*4b72b91aSCy Schubert 			continue;
347*4b72b91aSCy Schubert 
348*4b72b91aSCy Schubert 		if (wpa_s->current_bss)
349*4b72b91aSCy Schubert 			src = wpa_s->current_bss->bssid;
350*4b72b91aSCy Schubert 		else
351*4b72b91aSCy Schubert 			src = bssid;
352*4b72b91aSCy Schubert 
353*4b72b91aSCy Schubert 		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCS_RESULT "bssid=" MACSTR
354*4b72b91aSCy Schubert 			" SCSID=%u status_code=timedout", MAC2STR(src),
355*4b72b91aSCy Schubert 			scs_desc->scs_id);
356*4b72b91aSCy Schubert 
357*4b72b91aSCy Schubert 		dl_list_del(&scs_desc->list);
358*4b72b91aSCy Schubert 		wpa_printf(MSG_INFO, "%s: SCSID %d removed after timeout",
359*4b72b91aSCy Schubert 			   __func__, scs_desc->scs_id);
360*4b72b91aSCy Schubert 		os_free(scs_desc);
361*4b72b91aSCy Schubert 	}
362*4b72b91aSCy Schubert 
363*4b72b91aSCy Schubert 	eloop_cancel_timeout(scs_request_timer, wpa_s, NULL);
364*4b72b91aSCy Schubert 	wpa_s->ongoing_scs_req = false;
365*4b72b91aSCy Schubert }
366*4b72b91aSCy Schubert 
367*4b72b91aSCy Schubert 
368*4b72b91aSCy Schubert int wpas_send_scs_req(struct wpa_supplicant *wpa_s)
369*4b72b91aSCy Schubert {
370*4b72b91aSCy Schubert 	struct wpabuf *buf = NULL;
371*4b72b91aSCy Schubert 	struct scs_desc_elem *desc_elem = NULL;
372*4b72b91aSCy Schubert 	int ret = -1;
373*4b72b91aSCy Schubert 	unsigned int i;
374*4b72b91aSCy Schubert 
375*4b72b91aSCy Schubert 	if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid)
376*4b72b91aSCy Schubert 		return -1;
377*4b72b91aSCy Schubert 
378*4b72b91aSCy Schubert 	if (!wpa_bss_ext_capab(wpa_s->current_bss, WLAN_EXT_CAPAB_SCS)) {
379*4b72b91aSCy Schubert 		wpa_dbg(wpa_s, MSG_INFO,
380*4b72b91aSCy Schubert 			"AP does not support SCS - could not send SCS Request");
381*4b72b91aSCy Schubert 		return -1;
382*4b72b91aSCy Schubert 	}
383*4b72b91aSCy Schubert 
384*4b72b91aSCy Schubert 	desc_elem = wpa_s->scs_robust_av_req.scs_desc_elems;
385*4b72b91aSCy Schubert 	if (!desc_elem)
386*4b72b91aSCy Schubert 		return -1;
387*4b72b91aSCy Schubert 
388*4b72b91aSCy Schubert 	buf = allocate_scs_buf(desc_elem,
389*4b72b91aSCy Schubert 			       wpa_s->scs_robust_av_req.num_scs_desc);
390*4b72b91aSCy Schubert 	if (!buf)
391*4b72b91aSCy Schubert 		return -1;
392*4b72b91aSCy Schubert 
393*4b72b91aSCy Schubert 	wpabuf_put_u8(buf, WLAN_ACTION_ROBUST_AV_STREAMING);
394*4b72b91aSCy Schubert 	wpabuf_put_u8(buf, ROBUST_AV_SCS_REQ);
395*4b72b91aSCy Schubert 	wpa_s->scs_dialog_token++;
396*4b72b91aSCy Schubert 	if (wpa_s->scs_dialog_token == 0)
397*4b72b91aSCy Schubert 		wpa_s->scs_dialog_token++;
398*4b72b91aSCy Schubert 	wpabuf_put_u8(buf, wpa_s->scs_dialog_token);
399*4b72b91aSCy Schubert 
400*4b72b91aSCy Schubert 	for (i = 0; i < wpa_s->scs_robust_av_req.num_scs_desc;
401*4b72b91aSCy Schubert 	     i++, desc_elem++) {
402*4b72b91aSCy Schubert 		/* SCS Descriptor element */
403*4b72b91aSCy Schubert 		if (wpas_populate_scs_descriptor_ie(desc_elem, buf) < 0)
404*4b72b91aSCy Schubert 			goto end;
405*4b72b91aSCy Schubert 	}
406*4b72b91aSCy Schubert 
407*4b72b91aSCy Schubert 	wpa_hexdump_buf(MSG_DEBUG, "SCS Request", buf);
408*4b72b91aSCy Schubert 	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
409*4b72b91aSCy Schubert 				  wpa_s->own_addr, wpa_s->bssid,
410*4b72b91aSCy Schubert 				  wpabuf_head(buf), wpabuf_len(buf), 0);
411*4b72b91aSCy Schubert 	if (ret < 0) {
412*4b72b91aSCy Schubert 		wpa_dbg(wpa_s, MSG_ERROR, "SCS: Failed to send SCS Request");
413*4b72b91aSCy Schubert 		wpa_s->scs_dialog_token--;
414*4b72b91aSCy Schubert 		goto end;
415*4b72b91aSCy Schubert 	}
416*4b72b91aSCy Schubert 
417*4b72b91aSCy Schubert 	desc_elem = wpa_s->scs_robust_av_req.scs_desc_elems;
418*4b72b91aSCy Schubert 	for (i = 0; i < wpa_s->scs_robust_av_req.num_scs_desc;
419*4b72b91aSCy Schubert 	     i++, desc_elem++) {
420*4b72b91aSCy Schubert 		struct active_scs_elem *active_scs_elem;
421*4b72b91aSCy Schubert 
422*4b72b91aSCy Schubert 		if (desc_elem->request_type != SCS_REQ_ADD)
423*4b72b91aSCy Schubert 			continue;
424*4b72b91aSCy Schubert 
425*4b72b91aSCy Schubert 		active_scs_elem = os_malloc(sizeof(struct active_scs_elem));
426*4b72b91aSCy Schubert 		if (!active_scs_elem)
427*4b72b91aSCy Schubert 			break;
428*4b72b91aSCy Schubert 		active_scs_elem->scs_id = desc_elem->scs_id;
429*4b72b91aSCy Schubert 		active_scs_elem->status = SCS_DESC_SENT;
430*4b72b91aSCy Schubert 		dl_list_add(&wpa_s->active_scs_ids, &active_scs_elem->list);
431*4b72b91aSCy Schubert 	}
432*4b72b91aSCy Schubert 
433*4b72b91aSCy Schubert 	/*
434*4b72b91aSCy Schubert 	 * Register a timeout after which this request will be removed from
435*4b72b91aSCy Schubert 	 * the cache.
436*4b72b91aSCy Schubert 	 */
437*4b72b91aSCy Schubert 	eloop_register_timeout(SCS_RESP_TIMEOUT, 0, scs_request_timer, wpa_s,
438*4b72b91aSCy Schubert 			       NULL);
439*4b72b91aSCy Schubert 	wpa_s->ongoing_scs_req = true;
440*4b72b91aSCy Schubert 
441*4b72b91aSCy Schubert end:
442*4b72b91aSCy Schubert 	wpabuf_free(buf);
443*4b72b91aSCy Schubert 	free_up_scs_desc(&wpa_s->scs_robust_av_req);
444*4b72b91aSCy Schubert 
445*4b72b91aSCy Schubert 	return ret;
446*4b72b91aSCy Schubert }
447*4b72b91aSCy Schubert 
448*4b72b91aSCy Schubert 
449*4b72b91aSCy Schubert void free_up_tclas_elem(struct scs_desc_elem *elem)
450*4b72b91aSCy Schubert {
451*4b72b91aSCy Schubert 	struct tclas_element *tclas_elems = elem->tclas_elems;
452*4b72b91aSCy Schubert 	unsigned int num_tclas_elem = elem->num_tclas_elem;
453*4b72b91aSCy Schubert 	struct tclas_element *tclas_data;
454*4b72b91aSCy Schubert 	unsigned int j;
455*4b72b91aSCy Schubert 
456*4b72b91aSCy Schubert 	elem->tclas_elems = NULL;
457*4b72b91aSCy Schubert 	elem->num_tclas_elem = 0;
458*4b72b91aSCy Schubert 
459*4b72b91aSCy Schubert 	if (!tclas_elems)
460*4b72b91aSCy Schubert 		return;
461*4b72b91aSCy Schubert 
462*4b72b91aSCy Schubert 	tclas_data = tclas_elems;
463*4b72b91aSCy Schubert 	for (j = 0; j < num_tclas_elem; j++, tclas_data++) {
464*4b72b91aSCy Schubert 		if (tclas_data->classifier_type != 10)
465*4b72b91aSCy Schubert 			continue;
466*4b72b91aSCy Schubert 
467*4b72b91aSCy Schubert 		os_free(tclas_data->frame_classifier.type10_param.filter_value);
468*4b72b91aSCy Schubert 		os_free(tclas_data->frame_classifier.type10_param.filter_mask);
469*4b72b91aSCy Schubert 	}
470*4b72b91aSCy Schubert 
471*4b72b91aSCy Schubert 	os_free(tclas_elems);
472*4b72b91aSCy Schubert }
473*4b72b91aSCy Schubert 
474*4b72b91aSCy Schubert 
475*4b72b91aSCy Schubert void free_up_scs_desc(struct scs_robust_av_data *data)
476*4b72b91aSCy Schubert {
477*4b72b91aSCy Schubert 	struct scs_desc_elem *desc_elems = data->scs_desc_elems;
478*4b72b91aSCy Schubert 	unsigned int num_scs_desc = data->num_scs_desc;
479*4b72b91aSCy Schubert 	struct scs_desc_elem *desc_data;
480*4b72b91aSCy Schubert 	unsigned int i;
481*4b72b91aSCy Schubert 
482*4b72b91aSCy Schubert 	data->scs_desc_elems = NULL;
483*4b72b91aSCy Schubert 	data->num_scs_desc = 0;
484*4b72b91aSCy Schubert 
485*4b72b91aSCy Schubert 	if (!desc_elems)
486*4b72b91aSCy Schubert 		return;
487*4b72b91aSCy Schubert 
488*4b72b91aSCy Schubert 	desc_data = desc_elems;
489*4b72b91aSCy Schubert 	for (i = 0; i < num_scs_desc; i++, desc_data++) {
490*4b72b91aSCy Schubert 		if (desc_data->request_type == SCS_REQ_REMOVE ||
491*4b72b91aSCy Schubert 		    !desc_data->tclas_elems)
492*4b72b91aSCy Schubert 			continue;
493*4b72b91aSCy Schubert 
494*4b72b91aSCy Schubert 		free_up_tclas_elem(desc_data);
495*4b72b91aSCy Schubert 	}
496*4b72b91aSCy Schubert 	os_free(desc_elems);
497*4b72b91aSCy Schubert }
498*4b72b91aSCy Schubert 
499*4b72b91aSCy Schubert 
500c1d255d3SCy Schubert void wpas_handle_robust_av_recv_action(struct wpa_supplicant *wpa_s,
501c1d255d3SCy Schubert 				       const u8 *src, const u8 *buf, size_t len)
502c1d255d3SCy Schubert {
503c1d255d3SCy Schubert 	u8 dialog_token;
504c1d255d3SCy Schubert 	u16 status_code;
505c1d255d3SCy Schubert 
506c1d255d3SCy Schubert 	if (len < 3)
507c1d255d3SCy Schubert 		return;
508c1d255d3SCy Schubert 
509c1d255d3SCy Schubert 	dialog_token = *buf++;
510c1d255d3SCy Schubert 	if (dialog_token != wpa_s->robust_av.dialog_token) {
511c1d255d3SCy Schubert 		wpa_printf(MSG_INFO,
512c1d255d3SCy Schubert 			   "MSCS: Drop received frame due to dialog token mismatch: received:%u expected:%u",
513c1d255d3SCy Schubert 			   dialog_token, wpa_s->robust_av.dialog_token);
514c1d255d3SCy Schubert 		return;
515c1d255d3SCy Schubert 	}
516c1d255d3SCy Schubert 
517c1d255d3SCy Schubert 	status_code = WPA_GET_LE16(buf);
518c1d255d3SCy Schubert 	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_MSCS_RESULT "bssid=" MACSTR
519c1d255d3SCy Schubert 		" status_code=%u", MAC2STR(src), status_code);
520c1d255d3SCy Schubert 	wpa_s->mscs_setup_done = status_code == WLAN_STATUS_SUCCESS;
521c1d255d3SCy Schubert }
522c1d255d3SCy Schubert 
523c1d255d3SCy Schubert 
524c1d255d3SCy Schubert void wpas_handle_assoc_resp_mscs(struct wpa_supplicant *wpa_s, const u8 *bssid,
525c1d255d3SCy Schubert 				 const u8 *ies, size_t ies_len)
526c1d255d3SCy Schubert {
527c1d255d3SCy Schubert 	const u8 *mscs_desc_ie, *mscs_status;
528c1d255d3SCy Schubert 	u16 status;
529c1d255d3SCy Schubert 
530c1d255d3SCy Schubert 	/* Process optional MSCS Status subelement when MSCS IE is in
531c1d255d3SCy Schubert 	 * (Re)Association Response frame */
532c1d255d3SCy Schubert 	if (!ies || ies_len == 0 || !wpa_s->robust_av.valid_config)
533c1d255d3SCy Schubert 		return;
534c1d255d3SCy Schubert 
535c1d255d3SCy Schubert 	mscs_desc_ie = get_ie_ext(ies, ies_len, WLAN_EID_EXT_MSCS_DESCRIPTOR);
536c1d255d3SCy Schubert 	if (!mscs_desc_ie || mscs_desc_ie[1] <= 8)
537c1d255d3SCy Schubert 		return;
538c1d255d3SCy Schubert 
539c1d255d3SCy Schubert 	/* Subelements start after (ie_id(1) + ie_len(1) + ext_id(1) +
540c1d255d3SCy Schubert 	 * request type(1) + upc(2) + stream timeout(4) =) 10.
541c1d255d3SCy Schubert 	 */
542c1d255d3SCy Schubert 	mscs_status = get_ie(&mscs_desc_ie[10], mscs_desc_ie[1] - 8,
543c1d255d3SCy Schubert 			     MCSC_SUBELEM_STATUS);
544c1d255d3SCy Schubert 	if (!mscs_status || mscs_status[1] < 2)
545c1d255d3SCy Schubert 		return;
546c1d255d3SCy Schubert 
547c1d255d3SCy Schubert 	status = WPA_GET_LE16(mscs_status + 2);
548c1d255d3SCy Schubert 	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_MSCS_RESULT "bssid=" MACSTR
549c1d255d3SCy Schubert 		" status_code=%u", MAC2STR(bssid), status);
550c1d255d3SCy Schubert 	wpa_s->mscs_setup_done = status == WLAN_STATUS_SUCCESS;
551c1d255d3SCy Schubert }
552*4b72b91aSCy Schubert 
553*4b72b91aSCy Schubert 
554*4b72b91aSCy Schubert static void wpas_wait_for_dscp_req_timer(void *eloop_ctx, void *timeout_ctx)
555*4b72b91aSCy Schubert {
556*4b72b91aSCy Schubert 	struct wpa_supplicant *wpa_s = eloop_ctx;
557*4b72b91aSCy Schubert 
558*4b72b91aSCy Schubert 	/* Once timeout is over, reset wait flag and allow sending DSCP query */
559*4b72b91aSCy Schubert 	wpa_printf(MSG_DEBUG,
560*4b72b91aSCy Schubert 		   "QM: Wait time over for sending DSCP request - allow DSCP query");
561*4b72b91aSCy Schubert 	wpa_s->wait_for_dscp_req = 0;
562*4b72b91aSCy Schubert 	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "request_wait end");
563*4b72b91aSCy Schubert }
564*4b72b91aSCy Schubert 
565*4b72b91aSCy Schubert 
566*4b72b91aSCy Schubert void wpas_handle_assoc_resp_qos_mgmt(struct wpa_supplicant *wpa_s,
567*4b72b91aSCy Schubert 				     const u8 *ies, size_t ies_len)
568*4b72b91aSCy Schubert {
569*4b72b91aSCy Schubert 	const u8 *wfa_capa;
570*4b72b91aSCy Schubert 
571*4b72b91aSCy Schubert 	wpa_s->connection_dscp = 0;
572*4b72b91aSCy Schubert 	if (wpa_s->wait_for_dscp_req)
573*4b72b91aSCy Schubert 		eloop_cancel_timeout(wpas_wait_for_dscp_req_timer, wpa_s, NULL);
574*4b72b91aSCy Schubert 
575*4b72b91aSCy Schubert 	if (!ies || ies_len == 0 || !wpa_s->enable_dscp_policy_capa)
576*4b72b91aSCy Schubert 		return;
577*4b72b91aSCy Schubert 
578*4b72b91aSCy Schubert 	wfa_capa = get_vendor_ie(ies, ies_len, WFA_CAPA_IE_VENDOR_TYPE);
579*4b72b91aSCy Schubert 	if (!wfa_capa || wfa_capa[1] < 6 || wfa_capa[6] < 1 ||
580*4b72b91aSCy Schubert 	    !(wfa_capa[7] & WFA_CAPA_QM_DSCP_POLICY))
581*4b72b91aSCy Schubert 		return; /* AP does not enable QM DSCP Policy */
582*4b72b91aSCy Schubert 
583*4b72b91aSCy Schubert 	wpa_s->connection_dscp = 1;
584*4b72b91aSCy Schubert 	wpa_s->wait_for_dscp_req = !!(wfa_capa[7] &
585*4b72b91aSCy Schubert 				      WFA_CAPA_QM_UNSOLIC_DSCP);
586*4b72b91aSCy Schubert 	if (!wpa_s->wait_for_dscp_req)
587*4b72b91aSCy Schubert 		return;
588*4b72b91aSCy Schubert 
589*4b72b91aSCy Schubert 	/* Register a timeout after which dscp query can be sent to AP. */
590*4b72b91aSCy Schubert 	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "request_wait start");
591*4b72b91aSCy Schubert 	eloop_register_timeout(DSCP_REQ_TIMEOUT, 0,
592*4b72b91aSCy Schubert 			       wpas_wait_for_dscp_req_timer, wpa_s, NULL);
593*4b72b91aSCy Schubert }
594*4b72b91aSCy Schubert 
595*4b72b91aSCy Schubert 
596*4b72b91aSCy Schubert void wpas_handle_robust_av_scs_recv_action(struct wpa_supplicant *wpa_s,
597*4b72b91aSCy Schubert 					   const u8 *src, const u8 *buf,
598*4b72b91aSCy Schubert 					   size_t len)
599*4b72b91aSCy Schubert {
600*4b72b91aSCy Schubert 	u8 dialog_token;
601*4b72b91aSCy Schubert 	unsigned int i, count;
602*4b72b91aSCy Schubert 	struct active_scs_elem *scs_desc, *prev;
603*4b72b91aSCy Schubert 
604*4b72b91aSCy Schubert 	if (len < 2)
605*4b72b91aSCy Schubert 		return;
606*4b72b91aSCy Schubert 	if (!wpa_s->ongoing_scs_req) {
607*4b72b91aSCy Schubert 		wpa_printf(MSG_INFO,
608*4b72b91aSCy Schubert 			   "SCS: Drop received response due to no ongoing request");
609*4b72b91aSCy Schubert 		return;
610*4b72b91aSCy Schubert 	}
611*4b72b91aSCy Schubert 
612*4b72b91aSCy Schubert 	dialog_token = *buf++;
613*4b72b91aSCy Schubert 	len--;
614*4b72b91aSCy Schubert 	if (dialog_token != wpa_s->scs_dialog_token) {
615*4b72b91aSCy Schubert 		wpa_printf(MSG_INFO,
616*4b72b91aSCy Schubert 			   "SCS: Drop received frame due to dialog token mismatch: received:%u expected:%u",
617*4b72b91aSCy Schubert 			   dialog_token, wpa_s->scs_dialog_token);
618*4b72b91aSCy Schubert 		return;
619*4b72b91aSCy Schubert 	}
620*4b72b91aSCy Schubert 
621*4b72b91aSCy Schubert 	/* This Count field does not exist in the IEEE Std 802.11-2020
622*4b72b91aSCy Schubert 	 * definition of the SCS Response frame. However, it was accepted to
623*4b72b91aSCy Schubert 	 * be added into REVme per REVme/D0.0 CC35 CID 49 (edits in document
624*4b72b91aSCy Schubert 	 * 11-21-0688-07). */
625*4b72b91aSCy Schubert 	count = *buf++;
626*4b72b91aSCy Schubert 	len--;
627*4b72b91aSCy Schubert 	if (count == 0 || count * 3 > len) {
628*4b72b91aSCy Schubert 		wpa_printf(MSG_INFO,
629*4b72b91aSCy Schubert 			   "SCS: Drop received frame due to invalid count: %u (remaining %zu octets)",
630*4b72b91aSCy Schubert 			   count, len);
631*4b72b91aSCy Schubert 		return;
632*4b72b91aSCy Schubert 	}
633*4b72b91aSCy Schubert 
634*4b72b91aSCy Schubert 	for (i = 0; i < count; i++) {
635*4b72b91aSCy Schubert 		u8 id;
636*4b72b91aSCy Schubert 		u16 status;
637*4b72b91aSCy Schubert 		bool scs_desc_found = false;
638*4b72b91aSCy Schubert 
639*4b72b91aSCy Schubert 		id = *buf++;
640*4b72b91aSCy Schubert 		status = WPA_GET_LE16(buf);
641*4b72b91aSCy Schubert 		buf += 2;
642*4b72b91aSCy Schubert 		len -= 3;
643*4b72b91aSCy Schubert 
644*4b72b91aSCy Schubert 		dl_list_for_each(scs_desc, &wpa_s->active_scs_ids,
645*4b72b91aSCy Schubert 				 struct active_scs_elem, list) {
646*4b72b91aSCy Schubert 			if (id == scs_desc->scs_id) {
647*4b72b91aSCy Schubert 				scs_desc_found = true;
648*4b72b91aSCy Schubert 				break;
649*4b72b91aSCy Schubert 			}
650*4b72b91aSCy Schubert 		}
651*4b72b91aSCy Schubert 
652*4b72b91aSCy Schubert 		if (!scs_desc_found) {
653*4b72b91aSCy Schubert 			wpa_printf(MSG_INFO, "SCS: SCS ID invalid %u", id);
654*4b72b91aSCy Schubert 			continue;
655*4b72b91aSCy Schubert 		}
656*4b72b91aSCy Schubert 
657*4b72b91aSCy Schubert 		if (status != WLAN_STATUS_SUCCESS) {
658*4b72b91aSCy Schubert 			dl_list_del(&scs_desc->list);
659*4b72b91aSCy Schubert 			os_free(scs_desc);
660*4b72b91aSCy Schubert 		} else if (status == WLAN_STATUS_SUCCESS) {
661*4b72b91aSCy Schubert 			scs_desc->status = SCS_DESC_SUCCESS;
662*4b72b91aSCy Schubert 		}
663*4b72b91aSCy Schubert 
664*4b72b91aSCy Schubert 		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCS_RESULT "bssid=" MACSTR
665*4b72b91aSCy Schubert 			" SCSID=%u status_code=%u", MAC2STR(src), id, status);
666*4b72b91aSCy Schubert 	}
667*4b72b91aSCy Schubert 
668*4b72b91aSCy Schubert 	eloop_cancel_timeout(scs_request_timer, wpa_s, NULL);
669*4b72b91aSCy Schubert 	wpa_s->ongoing_scs_req = false;
670*4b72b91aSCy Schubert 
671*4b72b91aSCy Schubert 	dl_list_for_each_safe(scs_desc, prev, &wpa_s->active_scs_ids,
672*4b72b91aSCy Schubert 			      struct active_scs_elem, list) {
673*4b72b91aSCy Schubert 		if (scs_desc->status != SCS_DESC_SUCCESS) {
674*4b72b91aSCy Schubert 			wpa_msg(wpa_s, MSG_INFO,
675*4b72b91aSCy Schubert 				WPA_EVENT_SCS_RESULT "bssid=" MACSTR
676*4b72b91aSCy Schubert 				" SCSID=%u status_code=response_not_received",
677*4b72b91aSCy Schubert 				MAC2STR(src), scs_desc->scs_id);
678*4b72b91aSCy Schubert 			dl_list_del(&scs_desc->list);
679*4b72b91aSCy Schubert 			os_free(scs_desc);
680*4b72b91aSCy Schubert 		}
681*4b72b91aSCy Schubert 	}
682*4b72b91aSCy Schubert }
683*4b72b91aSCy Schubert 
684*4b72b91aSCy Schubert 
685*4b72b91aSCy Schubert static void wpas_clear_active_scs_ids(struct wpa_supplicant *wpa_s)
686*4b72b91aSCy Schubert {
687*4b72b91aSCy Schubert 	struct active_scs_elem *scs_elem;
688*4b72b91aSCy Schubert 
689*4b72b91aSCy Schubert 	while ((scs_elem = dl_list_first(&wpa_s->active_scs_ids,
690*4b72b91aSCy Schubert 					 struct active_scs_elem, list))) {
691*4b72b91aSCy Schubert 		dl_list_del(&scs_elem->list);
692*4b72b91aSCy Schubert 		os_free(scs_elem);
693*4b72b91aSCy Schubert 	}
694*4b72b91aSCy Schubert }
695*4b72b91aSCy Schubert 
696*4b72b91aSCy Schubert 
697*4b72b91aSCy Schubert void wpas_scs_deinit(struct wpa_supplicant *wpa_s)
698*4b72b91aSCy Schubert {
699*4b72b91aSCy Schubert 	free_up_scs_desc(&wpa_s->scs_robust_av_req);
700*4b72b91aSCy Schubert 	wpa_s->scs_dialog_token = 0;
701*4b72b91aSCy Schubert 	wpas_clear_active_scs_ids(wpa_s);
702*4b72b91aSCy Schubert 	eloop_cancel_timeout(scs_request_timer, wpa_s, NULL);
703*4b72b91aSCy Schubert 	wpa_s->ongoing_scs_req = false;
704*4b72b91aSCy Schubert }
705*4b72b91aSCy Schubert 
706*4b72b91aSCy Schubert 
707*4b72b91aSCy Schubert static int write_ipv4_info(char *pos, int total_len,
708*4b72b91aSCy Schubert 			   const struct ipv4_params *v4)
709*4b72b91aSCy Schubert {
710*4b72b91aSCy Schubert 	int res, rem_len;
711*4b72b91aSCy Schubert 	char addr[INET_ADDRSTRLEN];
712*4b72b91aSCy Schubert 
713*4b72b91aSCy Schubert 	rem_len = total_len;
714*4b72b91aSCy Schubert 
715*4b72b91aSCy Schubert 	if (v4->param_mask & BIT(1)) {
716*4b72b91aSCy Schubert 		if (!inet_ntop(AF_INET, &v4->src_ip, addr, INET_ADDRSTRLEN)) {
717*4b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
718*4b72b91aSCy Schubert 				   "QM: Failed to set IPv4 source address");
719*4b72b91aSCy Schubert 			return -1;
720*4b72b91aSCy Schubert 		}
721*4b72b91aSCy Schubert 
722*4b72b91aSCy Schubert 		res = os_snprintf(pos, rem_len, " src_ip=%s", addr);
723*4b72b91aSCy Schubert 		if (os_snprintf_error(rem_len, res))
724*4b72b91aSCy Schubert 			return -1;
725*4b72b91aSCy Schubert 
726*4b72b91aSCy Schubert 		pos += res;
727*4b72b91aSCy Schubert 		rem_len -= res;
728*4b72b91aSCy Schubert 	}
729*4b72b91aSCy Schubert 
730*4b72b91aSCy Schubert 	if (v4->param_mask & BIT(2)) {
731*4b72b91aSCy Schubert 		if (!inet_ntop(AF_INET, &v4->dst_ip, addr, INET_ADDRSTRLEN)) {
732*4b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
733*4b72b91aSCy Schubert 				   "QM: Failed to set IPv4 destination address");
734*4b72b91aSCy Schubert 			return -1;
735*4b72b91aSCy Schubert 		}
736*4b72b91aSCy Schubert 
737*4b72b91aSCy Schubert 		res = os_snprintf(pos, rem_len, " dst_ip=%s", addr);
738*4b72b91aSCy Schubert 		if (os_snprintf_error(rem_len, res))
739*4b72b91aSCy Schubert 			return -1;
740*4b72b91aSCy Schubert 
741*4b72b91aSCy Schubert 		pos += res;
742*4b72b91aSCy Schubert 		rem_len -= res;
743*4b72b91aSCy Schubert 	}
744*4b72b91aSCy Schubert 
745*4b72b91aSCy Schubert 	if (v4->param_mask & BIT(3)) {
746*4b72b91aSCy Schubert 		res = os_snprintf(pos, rem_len, " src_port=%d", v4->src_port);
747*4b72b91aSCy Schubert 		if (os_snprintf_error(rem_len, res))
748*4b72b91aSCy Schubert 			return -1;
749*4b72b91aSCy Schubert 
750*4b72b91aSCy Schubert 		pos += res;
751*4b72b91aSCy Schubert 		rem_len -= res;
752*4b72b91aSCy Schubert 	}
753*4b72b91aSCy Schubert 
754*4b72b91aSCy Schubert 	if (v4->param_mask & BIT(4)) {
755*4b72b91aSCy Schubert 		res = os_snprintf(pos, rem_len, " dst_port=%d", v4->dst_port);
756*4b72b91aSCy Schubert 		if (os_snprintf_error(rem_len, res))
757*4b72b91aSCy Schubert 			return -1;
758*4b72b91aSCy Schubert 
759*4b72b91aSCy Schubert 		pos += res;
760*4b72b91aSCy Schubert 		rem_len -= res;
761*4b72b91aSCy Schubert 	}
762*4b72b91aSCy Schubert 
763*4b72b91aSCy Schubert 	if (v4->param_mask & BIT(6)) {
764*4b72b91aSCy Schubert 		res = os_snprintf(pos, rem_len, " protocol=%d", v4->protocol);
765*4b72b91aSCy Schubert 		if (os_snprintf_error(rem_len, res))
766*4b72b91aSCy Schubert 			return -1;
767*4b72b91aSCy Schubert 
768*4b72b91aSCy Schubert 		pos += res;
769*4b72b91aSCy Schubert 		rem_len -= res;
770*4b72b91aSCy Schubert 	}
771*4b72b91aSCy Schubert 
772*4b72b91aSCy Schubert 	return total_len - rem_len;
773*4b72b91aSCy Schubert }
774*4b72b91aSCy Schubert 
775*4b72b91aSCy Schubert 
776*4b72b91aSCy Schubert static int write_ipv6_info(char *pos, int total_len,
777*4b72b91aSCy Schubert 			   const struct ipv6_params *v6)
778*4b72b91aSCy Schubert {
779*4b72b91aSCy Schubert 	int res, rem_len;
780*4b72b91aSCy Schubert 	char addr[INET6_ADDRSTRLEN];
781*4b72b91aSCy Schubert 
782*4b72b91aSCy Schubert 	rem_len = total_len;
783*4b72b91aSCy Schubert 
784*4b72b91aSCy Schubert 	if (v6->param_mask & BIT(1)) {
785*4b72b91aSCy Schubert 		if (!inet_ntop(AF_INET6, &v6->src_ip, addr, INET6_ADDRSTRLEN)) {
786*4b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
787*4b72b91aSCy Schubert 				   "QM: Failed to set IPv6 source addr");
788*4b72b91aSCy Schubert 			return -1;
789*4b72b91aSCy Schubert 		}
790*4b72b91aSCy Schubert 
791*4b72b91aSCy Schubert 		res = os_snprintf(pos, rem_len, " src_ip=%s", addr);
792*4b72b91aSCy Schubert 		if (os_snprintf_error(rem_len, res))
793*4b72b91aSCy Schubert 			return -1;
794*4b72b91aSCy Schubert 
795*4b72b91aSCy Schubert 		pos += res;
796*4b72b91aSCy Schubert 		rem_len -= res;
797*4b72b91aSCy Schubert 	}
798*4b72b91aSCy Schubert 
799*4b72b91aSCy Schubert 	if (v6->param_mask & BIT(2)) {
800*4b72b91aSCy Schubert 		if (!inet_ntop(AF_INET6, &v6->dst_ip, addr, INET6_ADDRSTRLEN)) {
801*4b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
802*4b72b91aSCy Schubert 				   "QM: Failed to set IPv6 destination addr");
803*4b72b91aSCy Schubert 			return -1;
804*4b72b91aSCy Schubert 		}
805*4b72b91aSCy Schubert 
806*4b72b91aSCy Schubert 		res = os_snprintf(pos, rem_len, " dst_ip=%s", addr);
807*4b72b91aSCy Schubert 		if (os_snprintf_error(rem_len, res))
808*4b72b91aSCy Schubert 			return -1;
809*4b72b91aSCy Schubert 
810*4b72b91aSCy Schubert 		pos += res;
811*4b72b91aSCy Schubert 		rem_len -= res;
812*4b72b91aSCy Schubert 	}
813*4b72b91aSCy Schubert 
814*4b72b91aSCy Schubert 	if (v6->param_mask & BIT(3)) {
815*4b72b91aSCy Schubert 		res = os_snprintf(pos, rem_len, " src_port=%d", v6->src_port);
816*4b72b91aSCy Schubert 		if (os_snprintf_error(rem_len, res))
817*4b72b91aSCy Schubert 			return -1;
818*4b72b91aSCy Schubert 
819*4b72b91aSCy Schubert 		pos += res;
820*4b72b91aSCy Schubert 		rem_len -= res;
821*4b72b91aSCy Schubert 	}
822*4b72b91aSCy Schubert 
823*4b72b91aSCy Schubert 	if (v6->param_mask & BIT(4)) {
824*4b72b91aSCy Schubert 		res = os_snprintf(pos, rem_len, " dst_port=%d", v6->dst_port);
825*4b72b91aSCy Schubert 		if (os_snprintf_error(rem_len, res))
826*4b72b91aSCy Schubert 			return -1;
827*4b72b91aSCy Schubert 
828*4b72b91aSCy Schubert 		pos += res;
829*4b72b91aSCy Schubert 		rem_len -= res;
830*4b72b91aSCy Schubert 	}
831*4b72b91aSCy Schubert 
832*4b72b91aSCy Schubert 	if (v6->param_mask & BIT(6)) {
833*4b72b91aSCy Schubert 		res = os_snprintf(pos, rem_len, " protocol=%d",
834*4b72b91aSCy Schubert 				  v6->next_header);
835*4b72b91aSCy Schubert 		if (os_snprintf_error(rem_len, res))
836*4b72b91aSCy Schubert 			return -1;
837*4b72b91aSCy Schubert 
838*4b72b91aSCy Schubert 		pos += res;
839*4b72b91aSCy Schubert 		rem_len -= res;
840*4b72b91aSCy Schubert 	}
841*4b72b91aSCy Schubert 
842*4b72b91aSCy Schubert 	return total_len - rem_len;
843*4b72b91aSCy Schubert }
844*4b72b91aSCy Schubert 
845*4b72b91aSCy Schubert 
846*4b72b91aSCy Schubert struct dscp_policy_data {
847*4b72b91aSCy Schubert 	u8 policy_id;
848*4b72b91aSCy Schubert 	u8 req_type;
849*4b72b91aSCy Schubert 	u8 dscp;
850*4b72b91aSCy Schubert 	bool dscp_info;
851*4b72b91aSCy Schubert 	const u8 *frame_classifier;
852*4b72b91aSCy Schubert 	u8 frame_classifier_len;
853*4b72b91aSCy Schubert 	struct type4_params type4_param;
854*4b72b91aSCy Schubert 	const u8 *domain_name;
855*4b72b91aSCy Schubert 	u8 domain_name_len;
856*4b72b91aSCy Schubert 	u16 start_port;
857*4b72b91aSCy Schubert 	u16 end_port;
858*4b72b91aSCy Schubert 	bool port_range_info;
859*4b72b91aSCy Schubert };
860*4b72b91aSCy Schubert 
861*4b72b91aSCy Schubert 
862*4b72b91aSCy Schubert static int set_frame_classifier_type4_ipv4(struct dscp_policy_data *policy)
863*4b72b91aSCy Schubert {
864*4b72b91aSCy Schubert 	u8 classifier_mask;
865*4b72b91aSCy Schubert 	const u8 *frame_classifier = policy->frame_classifier;
866*4b72b91aSCy Schubert 	struct type4_params *type4_param = &policy->type4_param;
867*4b72b91aSCy Schubert 
868*4b72b91aSCy Schubert 	if (policy->frame_classifier_len < 18) {
869*4b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
870*4b72b91aSCy Schubert 			   "QM: Received IPv4 frame classifier with insufficient length %d",
871*4b72b91aSCy Schubert 			   policy->frame_classifier_len);
872*4b72b91aSCy Schubert 		return -1;
873*4b72b91aSCy Schubert 	}
874*4b72b91aSCy Schubert 
875*4b72b91aSCy Schubert 	classifier_mask = frame_classifier[1];
876*4b72b91aSCy Schubert 
877*4b72b91aSCy Schubert 	/* Classifier Mask - bit 1 = Source IP Address */
878*4b72b91aSCy Schubert 	if (classifier_mask & BIT(1)) {
879*4b72b91aSCy Schubert 		type4_param->ip_params.v4.param_mask |= BIT(1);
880*4b72b91aSCy Schubert 		os_memcpy(&type4_param->ip_params.v4.src_ip,
881*4b72b91aSCy Schubert 			  &frame_classifier[3], 4);
882*4b72b91aSCy Schubert 	}
883*4b72b91aSCy Schubert 
884*4b72b91aSCy Schubert 	/* Classifier Mask - bit 2 = Destination IP Address */
885*4b72b91aSCy Schubert 	if (classifier_mask & BIT(2)) {
886*4b72b91aSCy Schubert 		if (policy->domain_name) {
887*4b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
888*4b72b91aSCy Schubert 				   "QM: IPv4: Both domain name and destination IP address not expected");
889*4b72b91aSCy Schubert 			return -1;
890*4b72b91aSCy Schubert 		}
891*4b72b91aSCy Schubert 
892*4b72b91aSCy Schubert 		type4_param->ip_params.v4.param_mask |= BIT(2);
893*4b72b91aSCy Schubert 		os_memcpy(&type4_param->ip_params.v4.dst_ip,
894*4b72b91aSCy Schubert 			  &frame_classifier[7], 4);
895*4b72b91aSCy Schubert 	}
896*4b72b91aSCy Schubert 
897*4b72b91aSCy Schubert 	/* Classifier Mask - bit 3 = Source Port */
898*4b72b91aSCy Schubert 	if (classifier_mask & BIT(3)) {
899*4b72b91aSCy Schubert 		type4_param->ip_params.v4.param_mask |= BIT(3);
900*4b72b91aSCy Schubert 		type4_param->ip_params.v4.src_port =
901*4b72b91aSCy Schubert 			WPA_GET_BE16(&frame_classifier[11]);
902*4b72b91aSCy Schubert 	}
903*4b72b91aSCy Schubert 
904*4b72b91aSCy Schubert 	/* Classifier Mask - bit 4 = Destination Port */
905*4b72b91aSCy Schubert 	if (classifier_mask & BIT(4)) {
906*4b72b91aSCy Schubert 		if (policy->port_range_info) {
907*4b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
908*4b72b91aSCy Schubert 				   "QM: IPv4: Both port range and destination port not expected");
909*4b72b91aSCy Schubert 			return -1;
910*4b72b91aSCy Schubert 		}
911*4b72b91aSCy Schubert 
912*4b72b91aSCy Schubert 		type4_param->ip_params.v4.param_mask |= BIT(4);
913*4b72b91aSCy Schubert 		type4_param->ip_params.v4.dst_port =
914*4b72b91aSCy Schubert 			WPA_GET_BE16(&frame_classifier[13]);
915*4b72b91aSCy Schubert 	}
916*4b72b91aSCy Schubert 
917*4b72b91aSCy Schubert 	/* Classifier Mask - bit 5 = DSCP (ignored) */
918*4b72b91aSCy Schubert 
919*4b72b91aSCy Schubert 	/* Classifier Mask - bit 6 = Protocol */
920*4b72b91aSCy Schubert 	if (classifier_mask & BIT(6)) {
921*4b72b91aSCy Schubert 		type4_param->ip_params.v4.param_mask |= BIT(6);
922*4b72b91aSCy Schubert 		type4_param->ip_params.v4.protocol = frame_classifier[16];
923*4b72b91aSCy Schubert 	}
924*4b72b91aSCy Schubert 
925*4b72b91aSCy Schubert 	return 0;
926*4b72b91aSCy Schubert }
927*4b72b91aSCy Schubert 
928*4b72b91aSCy Schubert 
929*4b72b91aSCy Schubert static int set_frame_classifier_type4_ipv6(struct dscp_policy_data *policy)
930*4b72b91aSCy Schubert {
931*4b72b91aSCy Schubert 	u8 classifier_mask;
932*4b72b91aSCy Schubert 	const u8 *frame_classifier = policy->frame_classifier;
933*4b72b91aSCy Schubert 	struct type4_params *type4_param = &policy->type4_param;
934*4b72b91aSCy Schubert 
935*4b72b91aSCy Schubert 	if (policy->frame_classifier_len < 44) {
936*4b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
937*4b72b91aSCy Schubert 			   "QM: Received IPv6 frame classifier with insufficient length %d",
938*4b72b91aSCy Schubert 			   policy->frame_classifier_len);
939*4b72b91aSCy Schubert 		return -1;
940*4b72b91aSCy Schubert 	}
941*4b72b91aSCy Schubert 
942*4b72b91aSCy Schubert 	classifier_mask = frame_classifier[1];
943*4b72b91aSCy Schubert 
944*4b72b91aSCy Schubert 	/* Classifier Mask - bit 1 = Source IP Address */
945*4b72b91aSCy Schubert 	if (classifier_mask & BIT(1)) {
946*4b72b91aSCy Schubert 		type4_param->ip_params.v6.param_mask |= BIT(1);
947*4b72b91aSCy Schubert 		os_memcpy(&type4_param->ip_params.v6.src_ip,
948*4b72b91aSCy Schubert 			  &frame_classifier[3], 16);
949*4b72b91aSCy Schubert 	}
950*4b72b91aSCy Schubert 
951*4b72b91aSCy Schubert 	/* Classifier Mask - bit 2 = Destination IP Address */
952*4b72b91aSCy Schubert 	if (classifier_mask & BIT(2)) {
953*4b72b91aSCy Schubert 		if (policy->domain_name) {
954*4b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
955*4b72b91aSCy Schubert 				   "QM: IPv6: Both domain name and destination IP address not expected");
956*4b72b91aSCy Schubert 			return -1;
957*4b72b91aSCy Schubert 		}
958*4b72b91aSCy Schubert 		type4_param->ip_params.v6.param_mask |= BIT(2);
959*4b72b91aSCy Schubert 		os_memcpy(&type4_param->ip_params.v6.dst_ip,
960*4b72b91aSCy Schubert 			  &frame_classifier[19], 16);
961*4b72b91aSCy Schubert 	}
962*4b72b91aSCy Schubert 
963*4b72b91aSCy Schubert 	/* Classifier Mask - bit 3 = Source Port */
964*4b72b91aSCy Schubert 	if (classifier_mask & BIT(3)) {
965*4b72b91aSCy Schubert 		type4_param->ip_params.v6.param_mask |= BIT(3);
966*4b72b91aSCy Schubert 		type4_param->ip_params.v6.src_port =
967*4b72b91aSCy Schubert 				WPA_GET_BE16(&frame_classifier[35]);
968*4b72b91aSCy Schubert 	}
969*4b72b91aSCy Schubert 
970*4b72b91aSCy Schubert 	/* Classifier Mask - bit 4 = Destination Port */
971*4b72b91aSCy Schubert 	if (classifier_mask & BIT(4)) {
972*4b72b91aSCy Schubert 		if (policy->port_range_info) {
973*4b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
974*4b72b91aSCy Schubert 				   "IPv6: Both port range and destination port not expected");
975*4b72b91aSCy Schubert 			return -1;
976*4b72b91aSCy Schubert 		}
977*4b72b91aSCy Schubert 
978*4b72b91aSCy Schubert 		type4_param->ip_params.v6.param_mask |= BIT(4);
979*4b72b91aSCy Schubert 		type4_param->ip_params.v6.dst_port =
980*4b72b91aSCy Schubert 				WPA_GET_BE16(&frame_classifier[37]);
981*4b72b91aSCy Schubert 	}
982*4b72b91aSCy Schubert 
983*4b72b91aSCy Schubert 	/* Classifier Mask - bit 5 = DSCP (ignored) */
984*4b72b91aSCy Schubert 
985*4b72b91aSCy Schubert 	/* Classifier Mask - bit 6 = Next Header */
986*4b72b91aSCy Schubert 	if (classifier_mask & BIT(6)) {
987*4b72b91aSCy Schubert 		type4_param->ip_params.v6.param_mask |= BIT(6);
988*4b72b91aSCy Schubert 		type4_param->ip_params.v6.next_header = frame_classifier[40];
989*4b72b91aSCy Schubert 	}
990*4b72b91aSCy Schubert 
991*4b72b91aSCy Schubert 	return 0;
992*4b72b91aSCy Schubert }
993*4b72b91aSCy Schubert 
994*4b72b91aSCy Schubert 
995*4b72b91aSCy Schubert static int wpas_set_frame_classifier_params(struct dscp_policy_data *policy)
996*4b72b91aSCy Schubert {
997*4b72b91aSCy Schubert 	const u8 *frame_classifier = policy->frame_classifier;
998*4b72b91aSCy Schubert 	u8 frame_classifier_len = policy->frame_classifier_len;
999*4b72b91aSCy Schubert 
1000*4b72b91aSCy Schubert 	if (frame_classifier_len < 3) {
1001*4b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
1002*4b72b91aSCy Schubert 			   "QM: Received frame classifier with insufficient length %d",
1003*4b72b91aSCy Schubert 			   frame_classifier_len);
1004*4b72b91aSCy Schubert 		return -1;
1005*4b72b91aSCy Schubert 	}
1006*4b72b91aSCy Schubert 
1007*4b72b91aSCy Schubert 	/* Only allowed Classifier Type: IP and higher layer parameters (4) */
1008*4b72b91aSCy Schubert 	if (frame_classifier[0] != 4) {
1009*4b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
1010*4b72b91aSCy Schubert 			   "QM: Received frame classifier with invalid classifier type %d",
1011*4b72b91aSCy Schubert 			   frame_classifier[0]);
1012*4b72b91aSCy Schubert 		return -1;
1013*4b72b91aSCy Schubert 	}
1014*4b72b91aSCy Schubert 
1015*4b72b91aSCy Schubert 	/* Classifier Mask - bit 0 = Version */
1016*4b72b91aSCy Schubert 	if (!(frame_classifier[1] & BIT(0))) {
1017*4b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
1018*4b72b91aSCy Schubert 			   "QM: Received frame classifier without IP version");
1019*4b72b91aSCy Schubert 		return -1;
1020*4b72b91aSCy Schubert 	}
1021*4b72b91aSCy Schubert 
1022*4b72b91aSCy Schubert 	/* Version (4 or 6) */
1023*4b72b91aSCy Schubert 	if (frame_classifier[2] == 4) {
1024*4b72b91aSCy Schubert 		if (set_frame_classifier_type4_ipv4(policy)) {
1025*4b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
1026*4b72b91aSCy Schubert 				   "QM: Failed to set IPv4 parameters");
1027*4b72b91aSCy Schubert 			return -1;
1028*4b72b91aSCy Schubert 		}
1029*4b72b91aSCy Schubert 
1030*4b72b91aSCy Schubert 		policy->type4_param.ip_version = IPV4;
1031*4b72b91aSCy Schubert 	} else if (frame_classifier[2] == 6) {
1032*4b72b91aSCy Schubert 		if (set_frame_classifier_type4_ipv6(policy)) {
1033*4b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
1034*4b72b91aSCy Schubert 				   "QM: Failed to set IPv6 parameters");
1035*4b72b91aSCy Schubert 			return -1;
1036*4b72b91aSCy Schubert 		}
1037*4b72b91aSCy Schubert 
1038*4b72b91aSCy Schubert 		policy->type4_param.ip_version = IPV6;
1039*4b72b91aSCy Schubert 	} else {
1040*4b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
1041*4b72b91aSCy Schubert 			   "QM: Received unknown IP version %d",
1042*4b72b91aSCy Schubert 			   frame_classifier[2]);
1043*4b72b91aSCy Schubert 		return -1;
1044*4b72b91aSCy Schubert 	}
1045*4b72b91aSCy Schubert 
1046*4b72b91aSCy Schubert 	return 0;
1047*4b72b91aSCy Schubert }
1048*4b72b91aSCy Schubert 
1049*4b72b91aSCy Schubert 
1050*4b72b91aSCy Schubert static bool dscp_valid_domain_name(const char *str)
1051*4b72b91aSCy Schubert {
1052*4b72b91aSCy Schubert 	if (!str[0])
1053*4b72b91aSCy Schubert 		return false;
1054*4b72b91aSCy Schubert 
1055*4b72b91aSCy Schubert 	while (*str) {
1056*4b72b91aSCy Schubert 		if (is_ctrl_char(*str) || *str == ' ' || *str == '=')
1057*4b72b91aSCy Schubert 			return false;
1058*4b72b91aSCy Schubert 		str++;
1059*4b72b91aSCy Schubert 	}
1060*4b72b91aSCy Schubert 
1061*4b72b91aSCy Schubert 	return true;
1062*4b72b91aSCy Schubert }
1063*4b72b91aSCy Schubert 
1064*4b72b91aSCy Schubert 
1065*4b72b91aSCy Schubert static void wpas_add_dscp_policy(struct wpa_supplicant *wpa_s,
1066*4b72b91aSCy Schubert 				 struct dscp_policy_data *policy)
1067*4b72b91aSCy Schubert {
1068*4b72b91aSCy Schubert 	int ip_ver = 0, res;
1069*4b72b91aSCy Schubert 	char policy_str[1000], *pos;
1070*4b72b91aSCy Schubert 	int len;
1071*4b72b91aSCy Schubert 
1072*4b72b91aSCy Schubert 	if (!policy->frame_classifier && !policy->domain_name &&
1073*4b72b91aSCy Schubert 	    !policy->port_range_info) {
1074*4b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
1075*4b72b91aSCy Schubert 			   "QM: Invalid DSCP policy - no attributes present");
1076*4b72b91aSCy Schubert 		goto fail;
1077*4b72b91aSCy Schubert 	}
1078*4b72b91aSCy Schubert 
1079*4b72b91aSCy Schubert 	policy_str[0] = '\0';
1080*4b72b91aSCy Schubert 	pos = policy_str;
1081*4b72b91aSCy Schubert 	len = sizeof(policy_str);
1082*4b72b91aSCy Schubert 
1083*4b72b91aSCy Schubert 	if (policy->frame_classifier) {
1084*4b72b91aSCy Schubert 		struct type4_params *type4 = &policy->type4_param;
1085*4b72b91aSCy Schubert 
1086*4b72b91aSCy Schubert 		if (wpas_set_frame_classifier_params(policy)) {
1087*4b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
1088*4b72b91aSCy Schubert 				   "QM: Failed to set frame classifier parameters");
1089*4b72b91aSCy Schubert 			goto fail;
1090*4b72b91aSCy Schubert 		}
1091*4b72b91aSCy Schubert 
1092*4b72b91aSCy Schubert 		if (type4->ip_version == IPV4)
1093*4b72b91aSCy Schubert 			res = write_ipv4_info(pos, len, &type4->ip_params.v4);
1094*4b72b91aSCy Schubert 		else
1095*4b72b91aSCy Schubert 			res = write_ipv6_info(pos, len, &type4->ip_params.v6);
1096*4b72b91aSCy Schubert 
1097*4b72b91aSCy Schubert 		if (res <= 0) {
1098*4b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
1099*4b72b91aSCy Schubert 				   "QM: Failed to write IP parameters");
1100*4b72b91aSCy Schubert 			goto fail;
1101*4b72b91aSCy Schubert 		}
1102*4b72b91aSCy Schubert 
1103*4b72b91aSCy Schubert 		ip_ver = type4->ip_version;
1104*4b72b91aSCy Schubert 
1105*4b72b91aSCy Schubert 		pos += res;
1106*4b72b91aSCy Schubert 		len -= res;
1107*4b72b91aSCy Schubert 	}
1108*4b72b91aSCy Schubert 
1109*4b72b91aSCy Schubert 	if (policy->port_range_info) {
1110*4b72b91aSCy Schubert 		res = os_snprintf(pos, len, " start_port=%u end_port=%u",
1111*4b72b91aSCy Schubert 				  policy->start_port, policy->end_port);
1112*4b72b91aSCy Schubert 		if (os_snprintf_error(len, res)) {
1113*4b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
1114*4b72b91aSCy Schubert 				   "QM: Failed to write port range attributes for policy id = %d",
1115*4b72b91aSCy Schubert 				   policy->policy_id);
1116*4b72b91aSCy Schubert 			goto fail;
1117*4b72b91aSCy Schubert 		}
1118*4b72b91aSCy Schubert 
1119*4b72b91aSCy Schubert 		pos += res;
1120*4b72b91aSCy Schubert 		len -= res;
1121*4b72b91aSCy Schubert 	}
1122*4b72b91aSCy Schubert 
1123*4b72b91aSCy Schubert 	if (policy->domain_name) {
1124*4b72b91aSCy Schubert 		char domain_name_str[250];
1125*4b72b91aSCy Schubert 
1126*4b72b91aSCy Schubert 		if (policy->domain_name_len >= sizeof(domain_name_str)) {
1127*4b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
1128*4b72b91aSCy Schubert 				   "QM: Domain name length higher than max expected");
1129*4b72b91aSCy Schubert 			goto fail;
1130*4b72b91aSCy Schubert 		}
1131*4b72b91aSCy Schubert 		os_memcpy(domain_name_str, policy->domain_name,
1132*4b72b91aSCy Schubert 			  policy->domain_name_len);
1133*4b72b91aSCy Schubert 		domain_name_str[policy->domain_name_len] = '\0';
1134*4b72b91aSCy Schubert 		if (!dscp_valid_domain_name(domain_name_str)) {
1135*4b72b91aSCy Schubert 			wpa_printf(MSG_ERROR, "QM: Invalid domain name string");
1136*4b72b91aSCy Schubert 			goto fail;
1137*4b72b91aSCy Schubert 		}
1138*4b72b91aSCy Schubert 		res = os_snprintf(pos, len, " domain_name=%s", domain_name_str);
1139*4b72b91aSCy Schubert 		if (os_snprintf_error(len, res)) {
1140*4b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
1141*4b72b91aSCy Schubert 				   "QM: Failed to write domain name attribute for policy id = %d",
1142*4b72b91aSCy Schubert 				   policy->policy_id);
1143*4b72b91aSCy Schubert 			goto fail;
1144*4b72b91aSCy Schubert 		}
1145*4b72b91aSCy Schubert 	}
1146*4b72b91aSCy Schubert 
1147*4b72b91aSCy Schubert 	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY
1148*4b72b91aSCy Schubert 		"add policy_id=%u dscp=%u ip_version=%d%s",
1149*4b72b91aSCy Schubert 		policy->policy_id, policy->dscp, ip_ver, policy_str);
1150*4b72b91aSCy Schubert 	return;
1151*4b72b91aSCy Schubert fail:
1152*4b72b91aSCy Schubert 	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "reject policy_id=%u",
1153*4b72b91aSCy Schubert 		policy->policy_id);
1154*4b72b91aSCy Schubert }
1155*4b72b91aSCy Schubert 
1156*4b72b91aSCy Schubert 
1157*4b72b91aSCy Schubert void wpas_dscp_deinit(struct wpa_supplicant *wpa_s)
1158*4b72b91aSCy Schubert {
1159*4b72b91aSCy Schubert 	wpa_printf(MSG_DEBUG, "QM: Clear all active DSCP policies");
1160*4b72b91aSCy Schubert 	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "clear_all");
1161*4b72b91aSCy Schubert 	wpa_s->dscp_req_dialog_token = 0;
1162*4b72b91aSCy Schubert 	wpa_s->dscp_query_dialog_token = 0;
1163*4b72b91aSCy Schubert 	wpa_s->connection_dscp = 0;
1164*4b72b91aSCy Schubert 	if (wpa_s->wait_for_dscp_req) {
1165*4b72b91aSCy Schubert 		wpa_s->wait_for_dscp_req = 0;
1166*4b72b91aSCy Schubert 		eloop_cancel_timeout(wpas_wait_for_dscp_req_timer, wpa_s, NULL);
1167*4b72b91aSCy Schubert 	}
1168*4b72b91aSCy Schubert }
1169*4b72b91aSCy Schubert 
1170*4b72b91aSCy Schubert 
1171*4b72b91aSCy Schubert static void wpas_fill_dscp_policy(struct dscp_policy_data *policy, u8 attr_id,
1172*4b72b91aSCy Schubert 				  u8 attr_len, const u8 *attr_data)
1173*4b72b91aSCy Schubert {
1174*4b72b91aSCy Schubert 	switch (attr_id) {
1175*4b72b91aSCy Schubert 	case QM_ATTR_PORT_RANGE:
1176*4b72b91aSCy Schubert 		if (attr_len < 4) {
1177*4b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
1178*4b72b91aSCy Schubert 				   "QM: Received Port Range attribute with insufficient length %d",
1179*4b72b91aSCy Schubert 				    attr_len);
1180*4b72b91aSCy Schubert 			break;
1181*4b72b91aSCy Schubert 		}
1182*4b72b91aSCy Schubert 		policy->start_port = WPA_GET_BE16(attr_data);
1183*4b72b91aSCy Schubert 		policy->end_port = WPA_GET_BE16(attr_data + 2);
1184*4b72b91aSCy Schubert 		policy->port_range_info = true;
1185*4b72b91aSCy Schubert 		break;
1186*4b72b91aSCy Schubert 	case QM_ATTR_DSCP_POLICY:
1187*4b72b91aSCy Schubert 		if (attr_len < 3) {
1188*4b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
1189*4b72b91aSCy Schubert 				   "QM: Received DSCP Policy attribute with insufficient length %d",
1190*4b72b91aSCy Schubert 				   attr_len);
1191*4b72b91aSCy Schubert 			return;
1192*4b72b91aSCy Schubert 		}
1193*4b72b91aSCy Schubert 		policy->policy_id = attr_data[0];
1194*4b72b91aSCy Schubert 		policy->req_type = attr_data[1];
1195*4b72b91aSCy Schubert 		policy->dscp = attr_data[2];
1196*4b72b91aSCy Schubert 		policy->dscp_info = true;
1197*4b72b91aSCy Schubert 		break;
1198*4b72b91aSCy Schubert 	case QM_ATTR_TCLAS:
1199*4b72b91aSCy Schubert 		if (attr_len < 1) {
1200*4b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
1201*4b72b91aSCy Schubert 				   "QM: Received TCLAS attribute with insufficient length %d",
1202*4b72b91aSCy Schubert 				   attr_len);
1203*4b72b91aSCy Schubert 			return;
1204*4b72b91aSCy Schubert 		}
1205*4b72b91aSCy Schubert 		policy->frame_classifier = attr_data;
1206*4b72b91aSCy Schubert 		policy->frame_classifier_len = attr_len;
1207*4b72b91aSCy Schubert 		break;
1208*4b72b91aSCy Schubert 	case QM_ATTR_DOMAIN_NAME:
1209*4b72b91aSCy Schubert 		if (attr_len < 1) {
1210*4b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
1211*4b72b91aSCy Schubert 				   "QM: Received domain name attribute with insufficient length %d",
1212*4b72b91aSCy Schubert 				   attr_len);
1213*4b72b91aSCy Schubert 			return;
1214*4b72b91aSCy Schubert 		}
1215*4b72b91aSCy Schubert 		policy->domain_name = attr_data;
1216*4b72b91aSCy Schubert 		policy->domain_name_len = attr_len;
1217*4b72b91aSCy Schubert 		break;
1218*4b72b91aSCy Schubert 	default:
1219*4b72b91aSCy Schubert 		wpa_printf(MSG_ERROR, "QM: Received invalid QoS attribute %d",
1220*4b72b91aSCy Schubert 			   attr_id);
1221*4b72b91aSCy Schubert 		break;
1222*4b72b91aSCy Schubert 	}
1223*4b72b91aSCy Schubert }
1224*4b72b91aSCy Schubert 
1225*4b72b91aSCy Schubert 
1226*4b72b91aSCy Schubert void wpas_handle_qos_mgmt_recv_action(struct wpa_supplicant *wpa_s,
1227*4b72b91aSCy Schubert 				      const u8 *src,
1228*4b72b91aSCy Schubert 				      const u8 *buf, size_t len)
1229*4b72b91aSCy Schubert {
1230*4b72b91aSCy Schubert 	int rem_len;
1231*4b72b91aSCy Schubert 	const u8 *qos_ie, *attr;
1232*4b72b91aSCy Schubert 	int more, reset;
1233*4b72b91aSCy Schubert 
1234*4b72b91aSCy Schubert 	if (!wpa_s->enable_dscp_policy_capa) {
1235*4b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
1236*4b72b91aSCy Schubert 			   "QM: Ignore DSCP Policy frame since the capability is not enabled");
1237*4b72b91aSCy Schubert 		return;
1238*4b72b91aSCy Schubert 	}
1239*4b72b91aSCy Schubert 
1240*4b72b91aSCy Schubert 	if (!pmf_in_use(wpa_s, src)) {
1241*4b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
1242*4b72b91aSCy Schubert 			   "QM: Ignore DSCP Policy frame since PMF is not in use");
1243*4b72b91aSCy Schubert 		return;
1244*4b72b91aSCy Schubert 	}
1245*4b72b91aSCy Schubert 
1246*4b72b91aSCy Schubert 	if (!wpa_s->connection_dscp) {
1247*4b72b91aSCy Schubert 		 wpa_printf(MSG_DEBUG,
1248*4b72b91aSCy Schubert 			    "QM: DSCP Policy capability not enabled for the current association - ignore QoS Management Action frames");
1249*4b72b91aSCy Schubert 		return;
1250*4b72b91aSCy Schubert 	}
1251*4b72b91aSCy Schubert 
1252*4b72b91aSCy Schubert 	if (len < 1)
1253*4b72b91aSCy Schubert 		return;
1254*4b72b91aSCy Schubert 
1255*4b72b91aSCy Schubert 	/* Handle only DSCP Policy Request frame */
1256*4b72b91aSCy Schubert 	if (buf[0] != QM_DSCP_POLICY_REQ) {
1257*4b72b91aSCy Schubert 		wpa_printf(MSG_ERROR, "QM: Received unexpected QoS action frame %d",
1258*4b72b91aSCy Schubert 			   buf[0]);
1259*4b72b91aSCy Schubert 		return;
1260*4b72b91aSCy Schubert 	}
1261*4b72b91aSCy Schubert 
1262*4b72b91aSCy Schubert 	if (len < 3) {
1263*4b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
1264*4b72b91aSCy Schubert 			   "Received QoS Management DSCP Policy Request frame with invalid length %zu",
1265*4b72b91aSCy Schubert 			   len);
1266*4b72b91aSCy Schubert 		return;
1267*4b72b91aSCy Schubert 	}
1268*4b72b91aSCy Schubert 
1269*4b72b91aSCy Schubert 	/* Clear wait_for_dscp_req on receiving first DSCP request from AP */
1270*4b72b91aSCy Schubert 	if (wpa_s->wait_for_dscp_req) {
1271*4b72b91aSCy Schubert 		wpa_s->wait_for_dscp_req = 0;
1272*4b72b91aSCy Schubert 		eloop_cancel_timeout(wpas_wait_for_dscp_req_timer, wpa_s, NULL);
1273*4b72b91aSCy Schubert 	}
1274*4b72b91aSCy Schubert 
1275*4b72b91aSCy Schubert 	wpa_s->dscp_req_dialog_token = buf[1];
1276*4b72b91aSCy Schubert 	more = buf[2] & DSCP_POLICY_CTRL_MORE;
1277*4b72b91aSCy Schubert 	reset = buf[2] & DSCP_POLICY_CTRL_RESET;
1278*4b72b91aSCy Schubert 
1279*4b72b91aSCy Schubert 	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "request_start%s%s",
1280*4b72b91aSCy Schubert 		reset ? " clear_all" : "", more ? " more" : "");
1281*4b72b91aSCy Schubert 
1282*4b72b91aSCy Schubert 	qos_ie = buf + 3;
1283*4b72b91aSCy Schubert 	rem_len = len - 3;
1284*4b72b91aSCy Schubert 	while (rem_len > 2) {
1285*4b72b91aSCy Schubert 		struct dscp_policy_data policy;
1286*4b72b91aSCy Schubert 		int rem_attrs_len, ie_len;
1287*4b72b91aSCy Schubert 
1288*4b72b91aSCy Schubert 		ie_len = 2 + qos_ie[1];
1289*4b72b91aSCy Schubert 		if (rem_len < ie_len)
1290*4b72b91aSCy Schubert 			break;
1291*4b72b91aSCy Schubert 
1292*4b72b91aSCy Schubert 		if (rem_len < 6 || qos_ie[0] != WLAN_EID_VENDOR_SPECIFIC ||
1293*4b72b91aSCy Schubert 		    qos_ie[1] < 4 ||
1294*4b72b91aSCy Schubert 		    WPA_GET_BE32(&qos_ie[2]) != QM_IE_VENDOR_TYPE) {
1295*4b72b91aSCy Schubert 			rem_len -= ie_len;
1296*4b72b91aSCy Schubert 			qos_ie += ie_len;
1297*4b72b91aSCy Schubert 			continue;
1298*4b72b91aSCy Schubert 		}
1299*4b72b91aSCy Schubert 
1300*4b72b91aSCy Schubert 		os_memset(&policy, 0, sizeof(struct dscp_policy_data));
1301*4b72b91aSCy Schubert 		attr = qos_ie + 6;
1302*4b72b91aSCy Schubert 		rem_attrs_len = qos_ie[1] - 4;
1303*4b72b91aSCy Schubert 
1304*4b72b91aSCy Schubert 		while (rem_attrs_len > 2 && rem_attrs_len >= 2 + attr[1]) {
1305*4b72b91aSCy Schubert 			wpas_fill_dscp_policy(&policy, attr[0], attr[1],
1306*4b72b91aSCy Schubert 					      &attr[2]);
1307*4b72b91aSCy Schubert 			rem_attrs_len -= 2 + attr[1];
1308*4b72b91aSCy Schubert 			attr += 2 + attr[1];
1309*4b72b91aSCy Schubert 		}
1310*4b72b91aSCy Schubert 
1311*4b72b91aSCy Schubert 		rem_len -= ie_len;
1312*4b72b91aSCy Schubert 		qos_ie += ie_len;
1313*4b72b91aSCy Schubert 
1314*4b72b91aSCy Schubert 		if (!policy.dscp_info) {
1315*4b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
1316*4b72b91aSCy Schubert 				   "QM: Received QoS IE without DSCP Policy attribute");
1317*4b72b91aSCy Schubert 			continue;
1318*4b72b91aSCy Schubert 		}
1319*4b72b91aSCy Schubert 
1320*4b72b91aSCy Schubert 		if (policy.req_type == DSCP_POLICY_REQ_ADD)
1321*4b72b91aSCy Schubert 			wpas_add_dscp_policy(wpa_s, &policy);
1322*4b72b91aSCy Schubert 		else if (policy.req_type == DSCP_POLICY_REQ_REMOVE)
1323*4b72b91aSCy Schubert 			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY
1324*4b72b91aSCy Schubert 				"remove policy_id=%u", policy.policy_id);
1325*4b72b91aSCy Schubert 		else
1326*4b72b91aSCy Schubert 			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY
1327*4b72b91aSCy Schubert 				"reject policy_id=%u", policy.policy_id);
1328*4b72b91aSCy Schubert 	}
1329*4b72b91aSCy Schubert 
1330*4b72b91aSCy Schubert 	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "request_end");
1331*4b72b91aSCy Schubert }
1332*4b72b91aSCy Schubert 
1333*4b72b91aSCy Schubert 
1334*4b72b91aSCy Schubert int wpas_send_dscp_response(struct wpa_supplicant *wpa_s,
1335*4b72b91aSCy Schubert 			    struct dscp_resp_data *resp_data)
1336*4b72b91aSCy Schubert {
1337*4b72b91aSCy Schubert 	struct wpabuf *buf = NULL;
1338*4b72b91aSCy Schubert 	size_t buf_len;
1339*4b72b91aSCy Schubert 	int ret = -1, i;
1340*4b72b91aSCy Schubert 	u8 resp_control = 0;
1341*4b72b91aSCy Schubert 
1342*4b72b91aSCy Schubert 	if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid) {
1343*4b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
1344*4b72b91aSCy Schubert 			   "QM: Failed to send DSCP response - not connected to AP");
1345*4b72b91aSCy Schubert 		return -1;
1346*4b72b91aSCy Schubert 	}
1347*4b72b91aSCy Schubert 
1348*4b72b91aSCy Schubert 	if (resp_data->solicited && !wpa_s->dscp_req_dialog_token) {
1349*4b72b91aSCy Schubert 		wpa_printf(MSG_ERROR, "QM: No ongoing DSCP request");
1350*4b72b91aSCy Schubert 		return -1;
1351*4b72b91aSCy Schubert 	}
1352*4b72b91aSCy Schubert 
1353*4b72b91aSCy Schubert 	if (!wpa_s->connection_dscp) {
1354*4b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
1355*4b72b91aSCy Schubert 			   "QM: Failed to send DSCP response - DSCP capability not enabled for the current association");
1356*4b72b91aSCy Schubert 		return -1;
1357*4b72b91aSCy Schubert 
1358*4b72b91aSCy Schubert 	}
1359*4b72b91aSCy Schubert 
1360*4b72b91aSCy Schubert 	buf_len = 1 +	/* Category */
1361*4b72b91aSCy Schubert 		  3 +	/* OUI */
1362*4b72b91aSCy Schubert 		  1 +	/* OUI Type */
1363*4b72b91aSCy Schubert 		  1 +	/* OUI Subtype */
1364*4b72b91aSCy Schubert 		  1 +	/* Dialog Token */
1365*4b72b91aSCy Schubert 		  1 +	/* Response Control */
1366*4b72b91aSCy Schubert 		  1 +	/* Count */
1367*4b72b91aSCy Schubert 		  2 * resp_data->num_policies;  /* Status list */
1368*4b72b91aSCy Schubert 	buf = wpabuf_alloc(buf_len);
1369*4b72b91aSCy Schubert 	if (!buf) {
1370*4b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
1371*4b72b91aSCy Schubert 			   "QM: Failed to allocate DSCP policy response");
1372*4b72b91aSCy Schubert 		return -1;
1373*4b72b91aSCy Schubert 	}
1374*4b72b91aSCy Schubert 
1375*4b72b91aSCy Schubert 	wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED);
1376*4b72b91aSCy Schubert 	wpabuf_put_be24(buf, OUI_WFA);
1377*4b72b91aSCy Schubert 	wpabuf_put_u8(buf, QM_ACTION_OUI_TYPE);
1378*4b72b91aSCy Schubert 	wpabuf_put_u8(buf, QM_DSCP_POLICY_RESP);
1379*4b72b91aSCy Schubert 
1380*4b72b91aSCy Schubert 	wpabuf_put_u8(buf, resp_data->solicited ?
1381*4b72b91aSCy Schubert 		      wpa_s->dscp_req_dialog_token : 0);
1382*4b72b91aSCy Schubert 
1383*4b72b91aSCy Schubert 	if (resp_data->more)
1384*4b72b91aSCy Schubert 		resp_control |= DSCP_POLICY_CTRL_MORE;
1385*4b72b91aSCy Schubert 	if (resp_data->reset)
1386*4b72b91aSCy Schubert 		resp_control |= DSCP_POLICY_CTRL_RESET;
1387*4b72b91aSCy Schubert 	wpabuf_put_u8(buf, resp_control);
1388*4b72b91aSCy Schubert 
1389*4b72b91aSCy Schubert 	wpabuf_put_u8(buf, resp_data->num_policies);
1390*4b72b91aSCy Schubert 	for (i = 0; i < resp_data->num_policies; i++) {
1391*4b72b91aSCy Schubert 		wpabuf_put_u8(buf, resp_data->policy[i].id);
1392*4b72b91aSCy Schubert 		wpabuf_put_u8(buf, resp_data->policy[i].status);
1393*4b72b91aSCy Schubert 	}
1394*4b72b91aSCy Schubert 
1395*4b72b91aSCy Schubert 	wpa_hexdump_buf(MSG_MSGDUMP, "DSCP response frame: ", buf);
1396*4b72b91aSCy Schubert 	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
1397*4b72b91aSCy Schubert 				  wpa_s->own_addr, wpa_s->bssid,
1398*4b72b91aSCy Schubert 				  wpabuf_head(buf), wpabuf_len(buf), 0);
1399*4b72b91aSCy Schubert 	if (ret < 0) {
1400*4b72b91aSCy Schubert 		wpa_msg(wpa_s, MSG_INFO, "QM: Failed to send DSCP response");
1401*4b72b91aSCy Schubert 		goto fail;
1402*4b72b91aSCy Schubert 	}
1403*4b72b91aSCy Schubert 
1404*4b72b91aSCy Schubert 	/*
1405*4b72b91aSCy Schubert 	 * Mark DSCP request complete whether response sent is solicited or
1406*4b72b91aSCy Schubert 	 * unsolicited
1407*4b72b91aSCy Schubert 	 */
1408*4b72b91aSCy Schubert 	wpa_s->dscp_req_dialog_token = 0;
1409*4b72b91aSCy Schubert 
1410*4b72b91aSCy Schubert fail:
1411*4b72b91aSCy Schubert 	wpabuf_free(buf);
1412*4b72b91aSCy Schubert 	return ret;
1413*4b72b91aSCy Schubert }
1414*4b72b91aSCy Schubert 
1415*4b72b91aSCy Schubert 
1416*4b72b91aSCy Schubert int wpas_send_dscp_query(struct wpa_supplicant *wpa_s, const char *domain_name,
1417*4b72b91aSCy Schubert 			 size_t domain_name_length)
1418*4b72b91aSCy Schubert {
1419*4b72b91aSCy Schubert 	struct wpabuf *buf = NULL;
1420*4b72b91aSCy Schubert 	int ret, dscp_query_size;
1421*4b72b91aSCy Schubert 
1422*4b72b91aSCy Schubert 	if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid)
1423*4b72b91aSCy Schubert 		return -1;
1424*4b72b91aSCy Schubert 
1425*4b72b91aSCy Schubert 	if (!wpa_s->connection_dscp) {
1426*4b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
1427*4b72b91aSCy Schubert 			   "QM: Failed to send DSCP query - DSCP capability not enabled for the current association");
1428*4b72b91aSCy Schubert 		return -1;
1429*4b72b91aSCy Schubert 	}
1430*4b72b91aSCy Schubert 
1431*4b72b91aSCy Schubert 	if (wpa_s->wait_for_dscp_req) {
1432*4b72b91aSCy Schubert 		wpa_printf(MSG_INFO, "QM: Wait until AP sends a DSCP request");
1433*4b72b91aSCy Schubert 		return -1;
1434*4b72b91aSCy Schubert 	}
1435*4b72b91aSCy Schubert 
1436*4b72b91aSCy Schubert #define DOMAIN_NAME_OFFSET (4 /* OUI */ + 1 /* Attr Id */ + 1 /* Attr len */)
1437*4b72b91aSCy Schubert 
1438*4b72b91aSCy Schubert 	if (domain_name_length > 255 - DOMAIN_NAME_OFFSET) {
1439*4b72b91aSCy Schubert 		wpa_printf(MSG_ERROR, "QM: Too long domain name");
1440*4b72b91aSCy Schubert 		return -1;
1441*4b72b91aSCy Schubert 	}
1442*4b72b91aSCy Schubert 
1443*4b72b91aSCy Schubert 	dscp_query_size = 1 + /* Category */
1444*4b72b91aSCy Schubert 			  4 + /* OUI Type */
1445*4b72b91aSCy Schubert 			  1 + /* OUI subtype */
1446*4b72b91aSCy Schubert 			  1; /* Dialog Token */
1447*4b72b91aSCy Schubert 	if (domain_name && domain_name_length)
1448*4b72b91aSCy Schubert 		dscp_query_size += 1 + /* Element ID */
1449*4b72b91aSCy Schubert 			1 + /* IE Length */
1450*4b72b91aSCy Schubert 			DOMAIN_NAME_OFFSET + domain_name_length;
1451*4b72b91aSCy Schubert 
1452*4b72b91aSCy Schubert 	buf = wpabuf_alloc(dscp_query_size);
1453*4b72b91aSCy Schubert 	if (!buf) {
1454*4b72b91aSCy Schubert 		wpa_printf(MSG_ERROR, "QM: Failed to allocate DSCP query");
1455*4b72b91aSCy Schubert 		return -1;
1456*4b72b91aSCy Schubert 	}
1457*4b72b91aSCy Schubert 
1458*4b72b91aSCy Schubert 	wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED);
1459*4b72b91aSCy Schubert 	wpabuf_put_be32(buf, QM_ACTION_VENDOR_TYPE);
1460*4b72b91aSCy Schubert 	wpabuf_put_u8(buf, QM_DSCP_POLICY_QUERY);
1461*4b72b91aSCy Schubert 	wpa_s->dscp_query_dialog_token++;
1462*4b72b91aSCy Schubert 	if (wpa_s->dscp_query_dialog_token == 0)
1463*4b72b91aSCy Schubert 		wpa_s->dscp_query_dialog_token++;
1464*4b72b91aSCy Schubert 	wpabuf_put_u8(buf, wpa_s->dscp_query_dialog_token);
1465*4b72b91aSCy Schubert 
1466*4b72b91aSCy Schubert 	if (domain_name && domain_name_length) {
1467*4b72b91aSCy Schubert 		/* Domain Name attribute */
1468*4b72b91aSCy Schubert 		wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
1469*4b72b91aSCy Schubert 		wpabuf_put_u8(buf, DOMAIN_NAME_OFFSET + domain_name_length);
1470*4b72b91aSCy Schubert 		wpabuf_put_be32(buf, QM_IE_VENDOR_TYPE);
1471*4b72b91aSCy Schubert 		wpabuf_put_u8(buf, QM_ATTR_DOMAIN_NAME);
1472*4b72b91aSCy Schubert 		wpabuf_put_u8(buf, domain_name_length);
1473*4b72b91aSCy Schubert 		wpabuf_put_data(buf, domain_name, domain_name_length);
1474*4b72b91aSCy Schubert 	}
1475*4b72b91aSCy Schubert #undef DOMAIN_NAME_OFFSET
1476*4b72b91aSCy Schubert 
1477*4b72b91aSCy Schubert 	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
1478*4b72b91aSCy Schubert 				  wpa_s->own_addr, wpa_s->bssid,
1479*4b72b91aSCy Schubert 				  wpabuf_head(buf), wpabuf_len(buf), 0);
1480*4b72b91aSCy Schubert 	if (ret < 0) {
1481*4b72b91aSCy Schubert 		wpa_dbg(wpa_s, MSG_ERROR, "QM: Failed to send DSCP query");
1482*4b72b91aSCy Schubert 		wpa_s->dscp_query_dialog_token--;
1483*4b72b91aSCy Schubert 	}
1484*4b72b91aSCy Schubert 
1485*4b72b91aSCy Schubert 	wpabuf_free(buf);
1486*4b72b91aSCy Schubert 	return ret;
1487*4b72b91aSCy Schubert }
1488