xref: /freebsd/contrib/wpa/wpa_supplicant/robust_av.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
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"
114b72b91aSCy 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 
194b72b91aSCy Schubert #define SCS_RESP_TIMEOUT 1
204b72b91aSCy Schubert #define DSCP_REQ_TIMEOUT 5
214b72b91aSCy Schubert 
224b72b91aSCy Schubert 
wpas_populate_mscs_descriptor_ie(struct robust_av_data * robust_av,struct wpabuf * buf)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 
wpas_populate_type4_classifier(struct type4_params * type4_param,struct wpabuf * buf)534b72b91aSCy Schubert static int wpas_populate_type4_classifier(struct type4_params *type4_param,
544b72b91aSCy Schubert 					  struct wpabuf *buf)
554b72b91aSCy Schubert {
564b72b91aSCy Schubert 	/* classifier parameters */
574b72b91aSCy Schubert 	wpabuf_put_u8(buf, type4_param->classifier_mask);
584b72b91aSCy Schubert 	if (type4_param->ip_version == IPV4) {
594b72b91aSCy Schubert 		wpabuf_put_u8(buf, IPV4); /* IP version */
604b72b91aSCy Schubert 		wpabuf_put_data(buf, &type4_param->ip_params.v4.src_ip.s_addr,
614b72b91aSCy Schubert 				4);
624b72b91aSCy Schubert 		wpabuf_put_data(buf, &type4_param->ip_params.v4.dst_ip.s_addr,
634b72b91aSCy Schubert 				4);
644b72b91aSCy Schubert 		wpabuf_put_be16(buf, type4_param->ip_params.v4.src_port);
654b72b91aSCy Schubert 		wpabuf_put_be16(buf, type4_param->ip_params.v4.dst_port);
664b72b91aSCy Schubert 		wpabuf_put_u8(buf, type4_param->ip_params.v4.dscp);
674b72b91aSCy Schubert 		wpabuf_put_u8(buf, type4_param->ip_params.v4.protocol);
684b72b91aSCy Schubert 		wpabuf_put_u8(buf, 0); /* Reserved octet */
694b72b91aSCy Schubert 	} else {
704b72b91aSCy Schubert 		wpabuf_put_u8(buf, IPV6);
714b72b91aSCy Schubert 		wpabuf_put_data(buf, &type4_param->ip_params.v6.src_ip.s6_addr,
724b72b91aSCy Schubert 				16);
734b72b91aSCy Schubert 		wpabuf_put_data(buf, &type4_param->ip_params.v6.dst_ip.s6_addr,
744b72b91aSCy Schubert 				16);
754b72b91aSCy Schubert 		wpabuf_put_be16(buf, type4_param->ip_params.v6.src_port);
764b72b91aSCy Schubert 		wpabuf_put_be16(buf, type4_param->ip_params.v6.dst_port);
774b72b91aSCy Schubert 		wpabuf_put_u8(buf, type4_param->ip_params.v6.dscp);
784b72b91aSCy Schubert 		wpabuf_put_u8(buf, type4_param->ip_params.v6.next_header);
794b72b91aSCy Schubert 		wpabuf_put_data(buf, type4_param->ip_params.v6.flow_label, 3);
804b72b91aSCy Schubert 	}
814b72b91aSCy Schubert 
824b72b91aSCy Schubert 	return 0;
834b72b91aSCy Schubert }
844b72b91aSCy Schubert 
854b72b91aSCy Schubert 
wpas_populate_type10_classifier(struct type10_params * type10_param,struct wpabuf * buf)864b72b91aSCy Schubert static int wpas_populate_type10_classifier(struct type10_params *type10_param,
874b72b91aSCy Schubert 					   struct wpabuf *buf)
884b72b91aSCy Schubert {
894b72b91aSCy Schubert 	/* classifier parameters */
904b72b91aSCy Schubert 	wpabuf_put_u8(buf, type10_param->prot_instance);
914b72b91aSCy Schubert 	wpabuf_put_u8(buf, type10_param->prot_number);
924b72b91aSCy Schubert 	wpabuf_put_data(buf, type10_param->filter_value,
934b72b91aSCy Schubert 			type10_param->filter_len);
944b72b91aSCy Schubert 	wpabuf_put_data(buf, type10_param->filter_mask,
954b72b91aSCy Schubert 			type10_param->filter_len);
964b72b91aSCy Schubert 	return 0;
974b72b91aSCy Schubert }
984b72b91aSCy Schubert 
994b72b91aSCy Schubert 
tclas_elem_required(const struct qos_characteristics * qos_elem)100*a90b9d01SCy Schubert static bool tclas_elem_required(const struct qos_characteristics *qos_elem)
101*a90b9d01SCy Schubert {
102*a90b9d01SCy Schubert 	if (!qos_elem || !qos_elem->available)
103*a90b9d01SCy Schubert 		return true;
104*a90b9d01SCy Schubert 
105*a90b9d01SCy Schubert 	if (qos_elem->direction == SCS_DIRECTION_DOWN)
106*a90b9d01SCy Schubert 		return true;
107*a90b9d01SCy Schubert 
108*a90b9d01SCy Schubert 	return false;
109*a90b9d01SCy Schubert }
110*a90b9d01SCy Schubert 
111*a90b9d01SCy Schubert 
wpas_populate_scs_descriptor_ie(struct scs_desc_elem * desc_elem,struct wpabuf * buf,bool allow_scs_traffic_desc)1124b72b91aSCy Schubert static int wpas_populate_scs_descriptor_ie(struct scs_desc_elem *desc_elem,
113*a90b9d01SCy Schubert 					   struct wpabuf *buf,
114*a90b9d01SCy Schubert 					   bool allow_scs_traffic_desc)
1154b72b91aSCy Schubert {
1164b72b91aSCy Schubert 	u8 *len, *len1;
1174b72b91aSCy Schubert 	struct tclas_element *tclas_elem;
1184b72b91aSCy Schubert 	unsigned int i;
119*a90b9d01SCy Schubert 	struct qos_characteristics *qos_elem;
120*a90b9d01SCy Schubert 	u32 control_info = 0;
1214b72b91aSCy Schubert 
1224b72b91aSCy Schubert 	/* SCS Descriptor element */
1234b72b91aSCy Schubert 	wpabuf_put_u8(buf, WLAN_EID_SCS_DESCRIPTOR);
1244b72b91aSCy Schubert 	len = wpabuf_put(buf, 1);
1254b72b91aSCy Schubert 	wpabuf_put_u8(buf, desc_elem->scs_id);
1264b72b91aSCy Schubert 	wpabuf_put_u8(buf, desc_elem->request_type);
1274b72b91aSCy Schubert 	if (desc_elem->request_type == SCS_REQ_REMOVE)
1284b72b91aSCy Schubert 		goto end;
1294b72b91aSCy Schubert 
130*a90b9d01SCy Schubert 	if (!tclas_elem_required(&desc_elem->qos_char_elem))
131*a90b9d01SCy Schubert 		goto skip_tclas_elem;
132*a90b9d01SCy Schubert 
1334b72b91aSCy Schubert 	if (desc_elem->intra_access_priority || desc_elem->scs_up_avail) {
1344b72b91aSCy Schubert 		wpabuf_put_u8(buf, WLAN_EID_INTRA_ACCESS_CATEGORY_PRIORITY);
1354b72b91aSCy Schubert 		wpabuf_put_u8(buf, 1);
1364b72b91aSCy Schubert 		wpabuf_put_u8(buf, desc_elem->intra_access_priority);
1374b72b91aSCy Schubert 	}
1384b72b91aSCy Schubert 
1394b72b91aSCy Schubert 	tclas_elem = desc_elem->tclas_elems;
1404b72b91aSCy Schubert 
1414b72b91aSCy Schubert 	if (!tclas_elem)
1424b72b91aSCy Schubert 		return -1;
1434b72b91aSCy Schubert 
1444b72b91aSCy Schubert 	for (i = 0; i < desc_elem->num_tclas_elem; i++, tclas_elem++) {
1454b72b91aSCy Schubert 		int ret;
1464b72b91aSCy Schubert 
1474b72b91aSCy Schubert 		/* TCLAS element */
1484b72b91aSCy Schubert 		wpabuf_put_u8(buf, WLAN_EID_TCLAS);
1494b72b91aSCy Schubert 		len1 = wpabuf_put(buf, 1);
1504b72b91aSCy Schubert 		wpabuf_put_u8(buf, 255); /* User Priority: not compared */
1514b72b91aSCy Schubert 		/* Frame Classifier */
1524b72b91aSCy Schubert 		wpabuf_put_u8(buf, tclas_elem->classifier_type);
1534b72b91aSCy Schubert 		/* Frame classifier parameters */
1544b72b91aSCy Schubert 		switch (tclas_elem->classifier_type) {
1554b72b91aSCy Schubert 		case 4:
1564b72b91aSCy Schubert 			ret = wpas_populate_type4_classifier(
1574b72b91aSCy Schubert 				&tclas_elem->frame_classifier.type4_param,
1584b72b91aSCy Schubert 				buf);
1594b72b91aSCy Schubert 			break;
1604b72b91aSCy Schubert 		case 10:
1614b72b91aSCy Schubert 			ret = wpas_populate_type10_classifier(
1624b72b91aSCy Schubert 				&tclas_elem->frame_classifier.type10_param,
1634b72b91aSCy Schubert 				buf);
1644b72b91aSCy Schubert 			break;
1654b72b91aSCy Schubert 		default:
1664b72b91aSCy Schubert 			return -1;
1674b72b91aSCy Schubert 		}
1684b72b91aSCy Schubert 
1694b72b91aSCy Schubert 		if (ret == -1) {
1704b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
1714b72b91aSCy Schubert 				   "Failed to populate frame classifier");
1724b72b91aSCy Schubert 			return -1;
1734b72b91aSCy Schubert 		}
1744b72b91aSCy Schubert 
1754b72b91aSCy Schubert 		*len1 = (u8 *) wpabuf_put(buf, 0) - len1 - 1;
1764b72b91aSCy Schubert 	}
1774b72b91aSCy Schubert 
1784b72b91aSCy Schubert 	if (desc_elem->num_tclas_elem > 1) {
1794b72b91aSCy Schubert 		/* TCLAS Processing element */
1804b72b91aSCy Schubert 		wpabuf_put_u8(buf, WLAN_EID_TCLAS_PROCESSING);
1814b72b91aSCy Schubert 		wpabuf_put_u8(buf, 1);
1824b72b91aSCy Schubert 		wpabuf_put_u8(buf, desc_elem->tclas_processing);
1834b72b91aSCy Schubert 	}
1844b72b91aSCy Schubert 
185*a90b9d01SCy Schubert skip_tclas_elem:
186*a90b9d01SCy Schubert 	if (allow_scs_traffic_desc && desc_elem->qos_char_elem.available) {
187*a90b9d01SCy Schubert 		qos_elem = &desc_elem->qos_char_elem;
188*a90b9d01SCy Schubert 		/* Element ID, Length, and Element ID Extension */
189*a90b9d01SCy Schubert 		wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
190*a90b9d01SCy Schubert 		len1 = wpabuf_put(buf, 1);
191*a90b9d01SCy Schubert 		wpabuf_put_u8(buf, WLAN_EID_EXT_QOS_CHARACTERISTICS);
192*a90b9d01SCy Schubert 
193*a90b9d01SCy Schubert 		/* Remove invalid mask bits */
194*a90b9d01SCy Schubert 
195*a90b9d01SCy Schubert 		/* Medium Time is applicable only for direct link */
196*a90b9d01SCy Schubert 		if ((qos_elem->mask & SCS_QOS_BIT_MEDIUM_TIME) &&
197*a90b9d01SCy Schubert 		    qos_elem->direction != SCS_DIRECTION_DIRECT)
198*a90b9d01SCy Schubert 			qos_elem->mask &= ~SCS_QOS_BIT_MEDIUM_TIME;
199*a90b9d01SCy Schubert 
200*a90b9d01SCy Schubert 		/* Service Start Time LinkID is valid only when Service Start
201*a90b9d01SCy Schubert 		 * Time is present.
202*a90b9d01SCy Schubert 		 */
203*a90b9d01SCy Schubert 		if ((qos_elem->mask & SCS_QOS_BIT_SERVICE_START_TIME_LINKID) &&
204*a90b9d01SCy Schubert 		    !(qos_elem->mask & SCS_QOS_BIT_SERVICE_START_TIME))
205*a90b9d01SCy Schubert 			qos_elem->mask &=
206*a90b9d01SCy Schubert 				~SCS_QOS_BIT_SERVICE_START_TIME_LINKID;
207*a90b9d01SCy Schubert 
208*a90b9d01SCy Schubert 		/* IEEE P802.11be/D4.0, 9.4.2.316 QoS Characteristics element,
209*a90b9d01SCy Schubert 		 * Figure 9-1001av (Control Info field format)
210*a90b9d01SCy Schubert 		 */
211*a90b9d01SCy Schubert 		control_info = ((u32) qos_elem->direction <<
212*a90b9d01SCy Schubert 				EHT_QOS_CONTROL_INFO_DIRECTION_OFFSET);
213*a90b9d01SCy Schubert 		control_info |= ((u32) desc_elem->intra_access_priority <<
214*a90b9d01SCy Schubert 				 EHT_QOS_CONTROL_INFO_TID_OFFSET);
215*a90b9d01SCy Schubert 		control_info |= ((u32) desc_elem->intra_access_priority <<
216*a90b9d01SCy Schubert 				 EHT_QOS_CONTROL_INFO_USER_PRIORITY_OFFSET);
217*a90b9d01SCy Schubert 		control_info |= ((u32) qos_elem->mask <<
218*a90b9d01SCy Schubert 				 EHT_QOS_CONTROL_INFO_PRESENCE_MASK_OFFSET);
219*a90b9d01SCy Schubert 
220*a90b9d01SCy Schubert 		/* Control Info */
221*a90b9d01SCy Schubert 		wpabuf_put_le32(buf, control_info);
222*a90b9d01SCy Schubert 		/* Minimum Service Interval */
223*a90b9d01SCy Schubert 		wpabuf_put_le32(buf, qos_elem->min_si);
224*a90b9d01SCy Schubert 		/* Maximum Service Interval */
225*a90b9d01SCy Schubert 		wpabuf_put_le32(buf, qos_elem->max_si);
226*a90b9d01SCy Schubert 		/* Minimum Data Rate */
227*a90b9d01SCy Schubert 		wpabuf_put_le24(buf, qos_elem->min_data_rate);
228*a90b9d01SCy Schubert 		/* Delay Bound */
229*a90b9d01SCy Schubert 		wpabuf_put_le24(buf, qos_elem->delay_bound);
230*a90b9d01SCy Schubert 
231*a90b9d01SCy Schubert 		/* Maximum MSDU Size */
232*a90b9d01SCy Schubert 		if (qos_elem->mask & SCS_QOS_BIT_MAX_MSDU_SIZE)
233*a90b9d01SCy Schubert 			wpabuf_put_le16(buf, qos_elem->max_msdu_size);
234*a90b9d01SCy Schubert 		/* Start Service Time */
235*a90b9d01SCy Schubert 		if (qos_elem->mask & SCS_QOS_BIT_SERVICE_START_TIME)
236*a90b9d01SCy Schubert 			wpabuf_put_le32(buf, qos_elem->service_start_time);
237*a90b9d01SCy Schubert 		/* Service Start Time LinkID */
238*a90b9d01SCy Schubert 		if (qos_elem->mask & SCS_QOS_BIT_SERVICE_START_TIME_LINKID)
239*a90b9d01SCy Schubert 			wpabuf_put_u8(buf,
240*a90b9d01SCy Schubert 				      qos_elem->service_start_time_link_id);
241*a90b9d01SCy Schubert 		/* Mean Data Rate */
242*a90b9d01SCy Schubert 		if (qos_elem->mask & SCS_QOS_BIT_MEAN_DATA_RATE)
243*a90b9d01SCy Schubert 			wpabuf_put_le24(buf, qos_elem->mean_data_rate);
244*a90b9d01SCy Schubert 		/* Delayed Bounded Burst Size */
245*a90b9d01SCy Schubert 		if (qos_elem->mask & SCS_QOS_BIT_DELAYED_BOUNDED_BURST_SIZE)
246*a90b9d01SCy Schubert 			wpabuf_put_le32(buf, qos_elem->burst_size);
247*a90b9d01SCy Schubert 		/* MSDU Lifetime */
248*a90b9d01SCy Schubert 		if (qos_elem->mask & SCS_QOS_BIT_MSDU_LIFETIME)
249*a90b9d01SCy Schubert 			wpabuf_put_le16(buf, qos_elem->msdu_lifetime);
250*a90b9d01SCy Schubert 		/* MSDU Delivery Info */
251*a90b9d01SCy Schubert 		if (qos_elem->mask & SCS_QOS_BIT_MSDU_DELIVERY_INFO)
252*a90b9d01SCy Schubert 			wpabuf_put_u8(buf, qos_elem->msdu_delivery_info);
253*a90b9d01SCy Schubert 		/* Medium Time */
254*a90b9d01SCy Schubert 		if (qos_elem->mask & SCS_QOS_BIT_MEDIUM_TIME)
255*a90b9d01SCy Schubert 			wpabuf_put_le16(buf, qos_elem->medium_time);
256*a90b9d01SCy Schubert 
257*a90b9d01SCy Schubert 		*len1 = (u8 *) wpabuf_put(buf, 0) - len1 - 1;
258*a90b9d01SCy Schubert 	}
259*a90b9d01SCy Schubert 
2604b72b91aSCy Schubert end:
2614b72b91aSCy Schubert 	*len = (u8 *) wpabuf_put(buf, 0) - len - 1;
2624b72b91aSCy Schubert 	return 0;
2634b72b91aSCy Schubert }
2644b72b91aSCy Schubert 
2654b72b91aSCy Schubert 
wpas_send_mscs_req(struct wpa_supplicant * wpa_s)266c1d255d3SCy Schubert int wpas_send_mscs_req(struct wpa_supplicant *wpa_s)
267c1d255d3SCy Schubert {
268c1d255d3SCy Schubert 	struct wpabuf *buf;
269c1d255d3SCy Schubert 	size_t buf_len;
270c1d255d3SCy Schubert 	int ret;
271c1d255d3SCy Schubert 
272c1d255d3SCy Schubert 	if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid)
273c1d255d3SCy Schubert 		return 0;
274c1d255d3SCy Schubert 
275c1d255d3SCy Schubert 	if (!wpa_bss_ext_capab(wpa_s->current_bss, WLAN_EXT_CAPAB_MSCS)) {
276c1d255d3SCy Schubert 		wpa_dbg(wpa_s, MSG_INFO,
277c1d255d3SCy Schubert 			"AP does not support MSCS - could not send MSCS Req");
278c1d255d3SCy Schubert 		return -1;
279c1d255d3SCy Schubert 	}
280c1d255d3SCy Schubert 
281c1d255d3SCy Schubert 	if (!wpa_s->mscs_setup_done &&
282c1d255d3SCy Schubert 	    wpa_s->robust_av.request_type != SCS_REQ_ADD) {
283c1d255d3SCy Schubert 		wpa_msg(wpa_s, MSG_INFO,
284c1d255d3SCy Schubert 			"MSCS: Failed to send MSCS Request: request type invalid");
285c1d255d3SCy Schubert 		return -1;
286c1d255d3SCy Schubert 	}
287c1d255d3SCy Schubert 
288c1d255d3SCy Schubert 	buf_len = 3 +	/* Action frame header */
289c1d255d3SCy Schubert 		  3 +	/* MSCS descriptor IE header */
290c1d255d3SCy Schubert 		  1 +	/* Request type */
291c1d255d3SCy Schubert 		  2 +	/* User priority control */
292c1d255d3SCy Schubert 		  4 +	/* Stream timeout */
293c1d255d3SCy Schubert 		  3 +	/* TCLAS Mask IE header */
294c1d255d3SCy Schubert 		  wpa_s->robust_av.frame_classifier_len;
295c1d255d3SCy Schubert 
296c1d255d3SCy Schubert 	buf = wpabuf_alloc(buf_len);
297c1d255d3SCy Schubert 	if (!buf) {
298c1d255d3SCy Schubert 		wpa_printf(MSG_ERROR, "Failed to allocate MSCS req");
299c1d255d3SCy Schubert 		return -1;
300c1d255d3SCy Schubert 	}
301c1d255d3SCy Schubert 
302c1d255d3SCy Schubert 	wpabuf_put_u8(buf, WLAN_ACTION_ROBUST_AV_STREAMING);
303c1d255d3SCy Schubert 	wpabuf_put_u8(buf, ROBUST_AV_MSCS_REQ);
304c1d255d3SCy Schubert 	wpa_s->robust_av.dialog_token++;
305c1d255d3SCy Schubert 	wpabuf_put_u8(buf, wpa_s->robust_av.dialog_token);
306c1d255d3SCy Schubert 
307c1d255d3SCy Schubert 	/* MSCS descriptor element */
308c1d255d3SCy Schubert 	wpas_populate_mscs_descriptor_ie(&wpa_s->robust_av, buf);
309c1d255d3SCy Schubert 
310c1d255d3SCy Schubert 	wpa_hexdump_buf(MSG_MSGDUMP, "MSCS Request", buf);
311c1d255d3SCy Schubert 	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
312c1d255d3SCy Schubert 				  wpa_s->own_addr, wpa_s->bssid,
313c1d255d3SCy Schubert 				  wpabuf_head(buf), wpabuf_len(buf), 0);
314c1d255d3SCy Schubert 	if (ret < 0)
315c1d255d3SCy Schubert 		wpa_dbg(wpa_s, MSG_INFO, "MSCS: Failed to send MSCS Request");
316c1d255d3SCy Schubert 
317c1d255d3SCy Schubert 	wpabuf_free(buf);
318c1d255d3SCy Schubert 	return ret;
319c1d255d3SCy Schubert }
320c1d255d3SCy Schubert 
321c1d255d3SCy Schubert 
tclas_elem_len(const struct tclas_element * elem)3224b72b91aSCy Schubert static size_t tclas_elem_len(const struct tclas_element *elem)
3234b72b91aSCy Schubert {
3244b72b91aSCy Schubert 	size_t buf_len = 0;
3254b72b91aSCy Schubert 
3264b72b91aSCy Schubert 	buf_len += 2 +	/* TCLAS element header */
3274b72b91aSCy Schubert 		1 +	/* User Priority */
3284b72b91aSCy Schubert 		1 ;	/* Classifier Type */
3294b72b91aSCy Schubert 
3304b72b91aSCy Schubert 	if (elem->classifier_type == 4) {
3314b72b91aSCy Schubert 		enum ip_version ip_ver;
3324b72b91aSCy Schubert 
3334b72b91aSCy Schubert 		buf_len += 1 +	/* Classifier mask */
3344b72b91aSCy Schubert 			1 +	/* IP version */
3354b72b91aSCy Schubert 			1 +	/* user priority */
3364b72b91aSCy Schubert 			2 +	/* src_port */
3374b72b91aSCy Schubert 			2 +	/* dst_port */
3384b72b91aSCy Schubert 			1 ;	/* dscp */
3394b72b91aSCy Schubert 		ip_ver = elem->frame_classifier.type4_param.ip_version;
3404b72b91aSCy Schubert 		if (ip_ver == IPV4) {
3414b72b91aSCy Schubert 			buf_len += 4 +  /* src_ip */
3424b72b91aSCy Schubert 				4 +	/* dst_ip */
3434b72b91aSCy Schubert 				1 +	/* protocol */
3444b72b91aSCy Schubert 				1 ;  /* Reserved */
3454b72b91aSCy Schubert 		} else if (ip_ver == IPV6) {
3464b72b91aSCy Schubert 			buf_len += 16 +  /* src_ip */
3474b72b91aSCy Schubert 				16 +  /* dst_ip */
3484b72b91aSCy Schubert 				1  +  /* next_header */
3494b72b91aSCy Schubert 				3  ;  /* flow_label */
3504b72b91aSCy Schubert 		} else {
3514b72b91aSCy Schubert 			wpa_printf(MSG_ERROR, "%s: Incorrect IP version %d",
3524b72b91aSCy Schubert 				   __func__, ip_ver);
3534b72b91aSCy Schubert 			return 0;
3544b72b91aSCy Schubert 		}
3554b72b91aSCy Schubert 	} else if (elem->classifier_type == 10) {
3564b72b91aSCy Schubert 		buf_len += 1 +	/* protocol instance */
3574b72b91aSCy Schubert 			1 +	/* protocol number */
3584b72b91aSCy Schubert 			2 * elem->frame_classifier.type10_param.filter_len;
3594b72b91aSCy Schubert 	} else {
3604b72b91aSCy Schubert 		wpa_printf(MSG_ERROR, "%s: Incorrect classifier type %u",
3614b72b91aSCy Schubert 			   __func__, elem->classifier_type);
3624b72b91aSCy Schubert 		return 0;
3634b72b91aSCy Schubert 	}
3644b72b91aSCy Schubert 
3654b72b91aSCy Schubert 	return buf_len;
3664b72b91aSCy Schubert }
3674b72b91aSCy Schubert 
3684b72b91aSCy Schubert 
qos_char_len(const struct qos_characteristics * qos_elem)369*a90b9d01SCy Schubert static size_t qos_char_len(const struct qos_characteristics *qos_elem)
370*a90b9d01SCy Schubert {
371*a90b9d01SCy Schubert 	size_t buf_len = 0;
372*a90b9d01SCy Schubert 
373*a90b9d01SCy Schubert 	buf_len += 1 +	/* Element ID */
374*a90b9d01SCy Schubert 		1 +	/* Length */
375*a90b9d01SCy Schubert 		1 +	/* Element ID Extension */
376*a90b9d01SCy Schubert 		4 +	/* Control Info */
377*a90b9d01SCy Schubert 		4 +	/* Minimum Service Interval */
378*a90b9d01SCy Schubert 		4 +	/* Maximum Service Interval */
379*a90b9d01SCy Schubert 		3 +	/* Minimum Data Rate */
380*a90b9d01SCy Schubert 		3;	/* Delay Bound */
381*a90b9d01SCy Schubert 
382*a90b9d01SCy Schubert 	if (qos_elem->mask & SCS_QOS_BIT_MAX_MSDU_SIZE)
383*a90b9d01SCy Schubert 		buf_len += 2;	 /* Maximum MSDU Size */
384*a90b9d01SCy Schubert 
385*a90b9d01SCy Schubert 	if (qos_elem->mask & SCS_QOS_BIT_SERVICE_START_TIME) {
386*a90b9d01SCy Schubert 		buf_len += 4;	 /* Service Start Time */
387*a90b9d01SCy Schubert 		if (qos_elem->mask & SCS_QOS_BIT_SERVICE_START_TIME_LINKID)
388*a90b9d01SCy Schubert 			buf_len++;	/* Service Start Time LinkID */
389*a90b9d01SCy Schubert 	}
390*a90b9d01SCy Schubert 
391*a90b9d01SCy Schubert 	if (qos_elem->mask & SCS_QOS_BIT_MEAN_DATA_RATE)
392*a90b9d01SCy Schubert 		buf_len += 3;	 /* Mean Data Rate */
393*a90b9d01SCy Schubert 
394*a90b9d01SCy Schubert 	if (qos_elem->mask & SCS_QOS_BIT_DELAYED_BOUNDED_BURST_SIZE)
395*a90b9d01SCy Schubert 		buf_len += 4;	 /* Delayed Bounded Burst Size */
396*a90b9d01SCy Schubert 
397*a90b9d01SCy Schubert 	if (qos_elem->mask & SCS_QOS_BIT_MSDU_LIFETIME)
398*a90b9d01SCy Schubert 		buf_len += 2;	 /* MSDU Lifetime */
399*a90b9d01SCy Schubert 
400*a90b9d01SCy Schubert 	if (qos_elem->mask & SCS_QOS_BIT_MSDU_DELIVERY_INFO)
401*a90b9d01SCy Schubert 		buf_len++;	 /* MSDU Delivery Info */
402*a90b9d01SCy Schubert 
403*a90b9d01SCy Schubert 	if (qos_elem->mask & SCS_QOS_BIT_MEDIUM_TIME &&
404*a90b9d01SCy Schubert 	    qos_elem->direction == SCS_DIRECTION_DIRECT)
405*a90b9d01SCy Schubert 		buf_len += 2;	 /* Medium Time */
406*a90b9d01SCy Schubert 
407*a90b9d01SCy Schubert 	return buf_len;
408*a90b9d01SCy Schubert }
409*a90b9d01SCy Schubert 
410*a90b9d01SCy Schubert 
allocate_scs_buf(struct scs_desc_elem * desc_elem,unsigned int num_scs_desc,bool allow_scs_traffic_desc)4114b72b91aSCy Schubert static struct wpabuf * allocate_scs_buf(struct scs_desc_elem *desc_elem,
412*a90b9d01SCy Schubert 					unsigned int num_scs_desc,
413*a90b9d01SCy Schubert 					bool allow_scs_traffic_desc)
4144b72b91aSCy Schubert {
4154b72b91aSCy Schubert 	struct wpabuf *buf;
4164b72b91aSCy Schubert 	size_t buf_len = 0;
4174b72b91aSCy Schubert 	unsigned int i, j;
4184b72b91aSCy Schubert 
4194b72b91aSCy Schubert 	buf_len = 3; /* Action frame header */
4204b72b91aSCy Schubert 
4214b72b91aSCy Schubert 	for (i = 0; i < num_scs_desc; i++, desc_elem++) {
4224b72b91aSCy Schubert 		struct tclas_element *tclas_elem;
4234b72b91aSCy Schubert 
4244b72b91aSCy Schubert 		buf_len += 2 +	/* SCS descriptor IE header */
4254b72b91aSCy Schubert 			   1 +	/* SCSID */
4264b72b91aSCy Schubert 			   1 ;	/* Request type */
4274b72b91aSCy Schubert 
4284b72b91aSCy Schubert 		if (desc_elem->request_type == SCS_REQ_REMOVE)
4294b72b91aSCy Schubert 			continue;
4304b72b91aSCy Schubert 
431*a90b9d01SCy Schubert 		if (allow_scs_traffic_desc &&
432*a90b9d01SCy Schubert 		    desc_elem->qos_char_elem.available)
433*a90b9d01SCy Schubert 			buf_len += qos_char_len(&desc_elem->qos_char_elem);
434*a90b9d01SCy Schubert 
435*a90b9d01SCy Schubert 		if (!tclas_elem_required(&desc_elem->qos_char_elem))
436*a90b9d01SCy Schubert 			continue;
437*a90b9d01SCy Schubert 
4384b72b91aSCy Schubert 		if (desc_elem->intra_access_priority || desc_elem->scs_up_avail)
4394b72b91aSCy Schubert 			buf_len += 3;
4404b72b91aSCy Schubert 
4414b72b91aSCy Schubert 		tclas_elem = desc_elem->tclas_elems;
4424b72b91aSCy Schubert 		if (!tclas_elem) {
4434b72b91aSCy Schubert 			wpa_printf(MSG_ERROR, "%s: TCLAS element null",
4444b72b91aSCy Schubert 				   __func__);
4454b72b91aSCy Schubert 			return NULL;
4464b72b91aSCy Schubert 		}
4474b72b91aSCy Schubert 
4484b72b91aSCy Schubert 		for (j = 0; j < desc_elem->num_tclas_elem; j++, tclas_elem++) {
4494b72b91aSCy Schubert 			size_t elen;
4504b72b91aSCy Schubert 
4514b72b91aSCy Schubert 			elen = tclas_elem_len(tclas_elem);
4524b72b91aSCy Schubert 			if (elen == 0)
4534b72b91aSCy Schubert 				return NULL;
4544b72b91aSCy Schubert 			buf_len += elen;
4554b72b91aSCy Schubert 		}
4564b72b91aSCy Schubert 
4574b72b91aSCy Schubert 		if (desc_elem->num_tclas_elem > 1) {
4584b72b91aSCy Schubert 			buf_len += 1 +	/* TCLAS Processing eid */
4594b72b91aSCy Schubert 				   1 +	/* length */
4604b72b91aSCy Schubert 				   1 ;	/* processing */
4614b72b91aSCy Schubert 		}
4624b72b91aSCy Schubert 	}
4634b72b91aSCy Schubert 
4644b72b91aSCy Schubert 	buf = wpabuf_alloc(buf_len);
4654b72b91aSCy Schubert 	if (!buf) {
4664b72b91aSCy Schubert 		wpa_printf(MSG_ERROR, "Failed to allocate SCS req");
4674b72b91aSCy Schubert 		return NULL;
4684b72b91aSCy Schubert 	}
4694b72b91aSCy Schubert 
4704b72b91aSCy Schubert 	return buf;
4714b72b91aSCy Schubert }
4724b72b91aSCy Schubert 
4734b72b91aSCy Schubert 
scs_request_timer(void * eloop_ctx,void * timeout_ctx)4744b72b91aSCy Schubert static void scs_request_timer(void *eloop_ctx, void *timeout_ctx)
4754b72b91aSCy Schubert {
4764b72b91aSCy Schubert 	struct wpa_supplicant *wpa_s = eloop_ctx;
4774b72b91aSCy Schubert 	struct active_scs_elem *scs_desc, *prev;
4784b72b91aSCy Schubert 
4794b72b91aSCy Schubert 	if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid)
4804b72b91aSCy Schubert 		return;
4814b72b91aSCy Schubert 
4824b72b91aSCy Schubert 	/* Once timeout is over, remove all SCS descriptors with no response */
4834b72b91aSCy Schubert 	dl_list_for_each_safe(scs_desc, prev, &wpa_s->active_scs_ids,
4844b72b91aSCy Schubert 			      struct active_scs_elem, list) {
4854b72b91aSCy Schubert 		u8 bssid[ETH_ALEN] = { 0 };
4864b72b91aSCy Schubert 		const u8 *src;
4874b72b91aSCy Schubert 
4884b72b91aSCy Schubert 		if (scs_desc->status == SCS_DESC_SUCCESS)
4894b72b91aSCy Schubert 			continue;
4904b72b91aSCy Schubert 
4914b72b91aSCy Schubert 		if (wpa_s->current_bss)
4924b72b91aSCy Schubert 			src = wpa_s->current_bss->bssid;
4934b72b91aSCy Schubert 		else
4944b72b91aSCy Schubert 			src = bssid;
4954b72b91aSCy Schubert 
4964b72b91aSCy Schubert 		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCS_RESULT "bssid=" MACSTR
4974b72b91aSCy Schubert 			" SCSID=%u status_code=timedout", MAC2STR(src),
4984b72b91aSCy Schubert 			scs_desc->scs_id);
4994b72b91aSCy Schubert 
5004b72b91aSCy Schubert 		dl_list_del(&scs_desc->list);
5014b72b91aSCy Schubert 		wpa_printf(MSG_INFO, "%s: SCSID %d removed after timeout",
5024b72b91aSCy Schubert 			   __func__, scs_desc->scs_id);
5034b72b91aSCy Schubert 		os_free(scs_desc);
5044b72b91aSCy Schubert 	}
5054b72b91aSCy Schubert 
5064b72b91aSCy Schubert 	eloop_cancel_timeout(scs_request_timer, wpa_s, NULL);
5074b72b91aSCy Schubert 	wpa_s->ongoing_scs_req = false;
5084b72b91aSCy Schubert }
5094b72b91aSCy Schubert 
5104b72b91aSCy Schubert 
wpas_send_scs_req(struct wpa_supplicant * wpa_s)5114b72b91aSCy Schubert int wpas_send_scs_req(struct wpa_supplicant *wpa_s)
5124b72b91aSCy Schubert {
5134b72b91aSCy Schubert 	struct wpabuf *buf = NULL;
5144b72b91aSCy Schubert 	struct scs_desc_elem *desc_elem = NULL;
515*a90b9d01SCy Schubert 	const struct ieee80211_eht_capabilities *eht;
516*a90b9d01SCy Schubert 	const u8 *eht_ie;
5174b72b91aSCy Schubert 	int ret = -1;
5184b72b91aSCy Schubert 	unsigned int i;
519*a90b9d01SCy Schubert 	bool allow_scs_traffic_desc = false;
5204b72b91aSCy Schubert 
5214b72b91aSCy Schubert 	if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid)
5224b72b91aSCy Schubert 		return -1;
5234b72b91aSCy Schubert 
5244b72b91aSCy Schubert 	if (!wpa_bss_ext_capab(wpa_s->current_bss, WLAN_EXT_CAPAB_SCS)) {
5254b72b91aSCy Schubert 		wpa_dbg(wpa_s, MSG_INFO,
5264b72b91aSCy Schubert 			"AP does not support SCS - could not send SCS Request");
5274b72b91aSCy Schubert 		return -1;
5284b72b91aSCy Schubert 	}
5294b72b91aSCy Schubert 
5304b72b91aSCy Schubert 	desc_elem = wpa_s->scs_robust_av_req.scs_desc_elems;
5314b72b91aSCy Schubert 	if (!desc_elem)
5324b72b91aSCy Schubert 		return -1;
5334b72b91aSCy Schubert 
534*a90b9d01SCy Schubert 	if (wpa_is_non_eht_scs_traffic_desc_supported(wpa_s->current_bss))
535*a90b9d01SCy Schubert 		allow_scs_traffic_desc = true;
536*a90b9d01SCy Schubert 
537*a90b9d01SCy Schubert 	/* Allow SCS Traffic descriptor support for EHT connection */
538*a90b9d01SCy Schubert 	eht_ie = wpa_bss_get_ie_ext(wpa_s->current_bss,
539*a90b9d01SCy Schubert 				    WLAN_EID_EXT_EHT_CAPABILITIES);
540*a90b9d01SCy Schubert 	if (wpa_s->connection_eht && eht_ie &&
541*a90b9d01SCy Schubert 	    eht_ie[1] >= 1 + IEEE80211_EHT_CAPAB_MIN_LEN) {
542*a90b9d01SCy Schubert 		eht = (const struct ieee80211_eht_capabilities *) &eht_ie[3];
543*a90b9d01SCy Schubert 		if (eht->mac_cap & EHT_MACCAP_SCS_TRAFFIC_DESC)
544*a90b9d01SCy Schubert 			allow_scs_traffic_desc = true;
545*a90b9d01SCy Schubert 	}
546*a90b9d01SCy Schubert 
547*a90b9d01SCy Schubert 	if (!allow_scs_traffic_desc && desc_elem->qos_char_elem.available) {
548*a90b9d01SCy Schubert 		wpa_dbg(wpa_s, MSG_INFO,
549*a90b9d01SCy Schubert 			"Connection does not support EHT/non-EHT SCS Traffic Description - could not send SCS Request with QoS Characteristics");
550*a90b9d01SCy Schubert 		return -1;
551*a90b9d01SCy Schubert 	}
552*a90b9d01SCy Schubert 
5534b72b91aSCy Schubert 	buf = allocate_scs_buf(desc_elem,
554*a90b9d01SCy Schubert 			       wpa_s->scs_robust_av_req.num_scs_desc,
555*a90b9d01SCy Schubert 			       allow_scs_traffic_desc);
5564b72b91aSCy Schubert 	if (!buf)
5574b72b91aSCy Schubert 		return -1;
5584b72b91aSCy Schubert 
5594b72b91aSCy Schubert 	wpabuf_put_u8(buf, WLAN_ACTION_ROBUST_AV_STREAMING);
5604b72b91aSCy Schubert 	wpabuf_put_u8(buf, ROBUST_AV_SCS_REQ);
5614b72b91aSCy Schubert 	wpa_s->scs_dialog_token++;
5624b72b91aSCy Schubert 	if (wpa_s->scs_dialog_token == 0)
5634b72b91aSCy Schubert 		wpa_s->scs_dialog_token++;
5644b72b91aSCy Schubert 	wpabuf_put_u8(buf, wpa_s->scs_dialog_token);
5654b72b91aSCy Schubert 
5664b72b91aSCy Schubert 	for (i = 0; i < wpa_s->scs_robust_av_req.num_scs_desc;
5674b72b91aSCy Schubert 	     i++, desc_elem++) {
5684b72b91aSCy Schubert 		/* SCS Descriptor element */
569*a90b9d01SCy Schubert 		if (wpas_populate_scs_descriptor_ie(desc_elem, buf,
570*a90b9d01SCy Schubert 						    allow_scs_traffic_desc) < 0)
5714b72b91aSCy Schubert 			goto end;
5724b72b91aSCy Schubert 	}
5734b72b91aSCy Schubert 
5744b72b91aSCy Schubert 	wpa_hexdump_buf(MSG_DEBUG, "SCS Request", buf);
5754b72b91aSCy Schubert 	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
5764b72b91aSCy Schubert 				  wpa_s->own_addr, wpa_s->bssid,
5774b72b91aSCy Schubert 				  wpabuf_head(buf), wpabuf_len(buf), 0);
5784b72b91aSCy Schubert 	if (ret < 0) {
5794b72b91aSCy Schubert 		wpa_dbg(wpa_s, MSG_ERROR, "SCS: Failed to send SCS Request");
5804b72b91aSCy Schubert 		wpa_s->scs_dialog_token--;
5814b72b91aSCy Schubert 		goto end;
5824b72b91aSCy Schubert 	}
5834b72b91aSCy Schubert 
5844b72b91aSCy Schubert 	desc_elem = wpa_s->scs_robust_av_req.scs_desc_elems;
5854b72b91aSCy Schubert 	for (i = 0; i < wpa_s->scs_robust_av_req.num_scs_desc;
5864b72b91aSCy Schubert 	     i++, desc_elem++) {
5874b72b91aSCy Schubert 		struct active_scs_elem *active_scs_elem;
5884b72b91aSCy Schubert 
5894b72b91aSCy Schubert 		if (desc_elem->request_type != SCS_REQ_ADD)
5904b72b91aSCy Schubert 			continue;
5914b72b91aSCy Schubert 
5924b72b91aSCy Schubert 		active_scs_elem = os_malloc(sizeof(struct active_scs_elem));
5934b72b91aSCy Schubert 		if (!active_scs_elem)
5944b72b91aSCy Schubert 			break;
5954b72b91aSCy Schubert 		active_scs_elem->scs_id = desc_elem->scs_id;
5964b72b91aSCy Schubert 		active_scs_elem->status = SCS_DESC_SENT;
5974b72b91aSCy Schubert 		dl_list_add(&wpa_s->active_scs_ids, &active_scs_elem->list);
5984b72b91aSCy Schubert 	}
5994b72b91aSCy Schubert 
6004b72b91aSCy Schubert 	/*
6014b72b91aSCy Schubert 	 * Register a timeout after which this request will be removed from
6024b72b91aSCy Schubert 	 * the cache.
6034b72b91aSCy Schubert 	 */
6044b72b91aSCy Schubert 	eloop_register_timeout(SCS_RESP_TIMEOUT, 0, scs_request_timer, wpa_s,
6054b72b91aSCy Schubert 			       NULL);
6064b72b91aSCy Schubert 	wpa_s->ongoing_scs_req = true;
6074b72b91aSCy Schubert 
6084b72b91aSCy Schubert end:
6094b72b91aSCy Schubert 	wpabuf_free(buf);
6104b72b91aSCy Schubert 	free_up_scs_desc(&wpa_s->scs_robust_av_req);
6114b72b91aSCy Schubert 
6124b72b91aSCy Schubert 	return ret;
6134b72b91aSCy Schubert }
6144b72b91aSCy Schubert 
6154b72b91aSCy Schubert 
free_up_tclas_elem(struct scs_desc_elem * elem)6164b72b91aSCy Schubert void free_up_tclas_elem(struct scs_desc_elem *elem)
6174b72b91aSCy Schubert {
6184b72b91aSCy Schubert 	struct tclas_element *tclas_elems = elem->tclas_elems;
6194b72b91aSCy Schubert 	unsigned int num_tclas_elem = elem->num_tclas_elem;
6204b72b91aSCy Schubert 	struct tclas_element *tclas_data;
6214b72b91aSCy Schubert 	unsigned int j;
6224b72b91aSCy Schubert 
6234b72b91aSCy Schubert 	elem->tclas_elems = NULL;
6244b72b91aSCy Schubert 	elem->num_tclas_elem = 0;
6254b72b91aSCy Schubert 
6264b72b91aSCy Schubert 	if (!tclas_elems)
6274b72b91aSCy Schubert 		return;
6284b72b91aSCy Schubert 
6294b72b91aSCy Schubert 	tclas_data = tclas_elems;
6304b72b91aSCy Schubert 	for (j = 0; j < num_tclas_elem; j++, tclas_data++) {
6314b72b91aSCy Schubert 		if (tclas_data->classifier_type != 10)
6324b72b91aSCy Schubert 			continue;
6334b72b91aSCy Schubert 
6344b72b91aSCy Schubert 		os_free(tclas_data->frame_classifier.type10_param.filter_value);
6354b72b91aSCy Schubert 		os_free(tclas_data->frame_classifier.type10_param.filter_mask);
6364b72b91aSCy Schubert 	}
6374b72b91aSCy Schubert 
6384b72b91aSCy Schubert 	os_free(tclas_elems);
6394b72b91aSCy Schubert }
6404b72b91aSCy Schubert 
6414b72b91aSCy Schubert 
free_up_scs_desc(struct scs_robust_av_data * data)6424b72b91aSCy Schubert void free_up_scs_desc(struct scs_robust_av_data *data)
6434b72b91aSCy Schubert {
6444b72b91aSCy Schubert 	struct scs_desc_elem *desc_elems = data->scs_desc_elems;
6454b72b91aSCy Schubert 	unsigned int num_scs_desc = data->num_scs_desc;
6464b72b91aSCy Schubert 	struct scs_desc_elem *desc_data;
6474b72b91aSCy Schubert 	unsigned int i;
6484b72b91aSCy Schubert 
6494b72b91aSCy Schubert 	data->scs_desc_elems = NULL;
6504b72b91aSCy Schubert 	data->num_scs_desc = 0;
6514b72b91aSCy Schubert 
6524b72b91aSCy Schubert 	if (!desc_elems)
6534b72b91aSCy Schubert 		return;
6544b72b91aSCy Schubert 
6554b72b91aSCy Schubert 	desc_data = desc_elems;
6564b72b91aSCy Schubert 	for (i = 0; i < num_scs_desc; i++, desc_data++) {
6574b72b91aSCy Schubert 		if (desc_data->request_type == SCS_REQ_REMOVE ||
6584b72b91aSCy Schubert 		    !desc_data->tclas_elems)
6594b72b91aSCy Schubert 			continue;
6604b72b91aSCy Schubert 
6614b72b91aSCy Schubert 		free_up_tclas_elem(desc_data);
6624b72b91aSCy Schubert 	}
6634b72b91aSCy Schubert 	os_free(desc_elems);
6644b72b91aSCy Schubert }
6654b72b91aSCy Schubert 
6664b72b91aSCy Schubert 
667*a90b9d01SCy Schubert /* Element ID Extension(1) + Request Type(1) + User Priority Control(2) +
668*a90b9d01SCy Schubert  * Stream Timeout(4) */
669*a90b9d01SCy Schubert #define MSCS_DESCRIPTOR_FIXED_LEN 8
670*a90b9d01SCy Schubert 
wpas_parse_mscs_resp(struct wpa_supplicant * wpa_s,u16 status,const u8 * bssid,const u8 * mscs_desc_ie)671*a90b9d01SCy Schubert static void wpas_parse_mscs_resp(struct wpa_supplicant *wpa_s,
672*a90b9d01SCy Schubert 				 u16 status, const u8 *bssid,
673*a90b9d01SCy Schubert 				 const u8 *mscs_desc_ie)
674*a90b9d01SCy Schubert {
675*a90b9d01SCy Schubert 	struct robust_av_data robust_av;
676*a90b9d01SCy Schubert 	const u8 *pos;
677*a90b9d01SCy Schubert 
678*a90b9d01SCy Schubert 	/* The MSCS Descriptor element is optional in the MSCS Response frame */
679*a90b9d01SCy Schubert 	if (!mscs_desc_ie)
680*a90b9d01SCy Schubert 		goto event_mscs_result;
681*a90b9d01SCy Schubert 
682*a90b9d01SCy Schubert 	if (mscs_desc_ie[1] < MSCS_DESCRIPTOR_FIXED_LEN) {
683*a90b9d01SCy Schubert 		wpa_printf(MSG_INFO,
684*a90b9d01SCy Schubert 			   "MSCS: Drop received frame: invalid MSCS Descriptor element length: %d",
685*a90b9d01SCy Schubert 			   mscs_desc_ie[1]);
686*a90b9d01SCy Schubert 		return;
687*a90b9d01SCy Schubert 	}
688*a90b9d01SCy Schubert 
689*a90b9d01SCy Schubert 	os_memset(&robust_av, 0, sizeof(struct robust_av_data));
690*a90b9d01SCy Schubert 
691*a90b9d01SCy Schubert 	/* Skip Element ID, Length, and Element ID Extension */
692*a90b9d01SCy Schubert 	pos = &mscs_desc_ie[3];
693*a90b9d01SCy Schubert 
694*a90b9d01SCy Schubert 	robust_av.request_type = *pos++;
695*a90b9d01SCy Schubert 
696*a90b9d01SCy Schubert 	switch (robust_av.request_type) {
697*a90b9d01SCy Schubert 	case SCS_REQ_CHANGE:
698*a90b9d01SCy Schubert 		/*
699*a90b9d01SCy Schubert 		 * Inform the suggested set of parameters that could be accepted
700*a90b9d01SCy Schubert 		 * by the AP in response to a subsequent request by the station.
701*a90b9d01SCy Schubert 		 */
702*a90b9d01SCy Schubert 		robust_av.up_bitmap = *pos++;
703*a90b9d01SCy Schubert 		robust_av.up_limit = *pos++ & 0x07;
704*a90b9d01SCy Schubert 		robust_av.stream_timeout = WPA_GET_LE32(pos);
705*a90b9d01SCy Schubert 		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_MSCS_RESULT "bssid=" MACSTR
706*a90b9d01SCy Schubert 			" status_code=%u change up_bitmap=%u up_limit=%u stream_timeout=%u",
707*a90b9d01SCy Schubert 			MAC2STR(bssid), status, robust_av.up_bitmap,
708*a90b9d01SCy Schubert 			robust_av.up_limit, robust_av.stream_timeout);
709*a90b9d01SCy Schubert 		wpa_s->mscs_setup_done = false;
710*a90b9d01SCy Schubert 		return;
711*a90b9d01SCy Schubert 	case SCS_REQ_ADD:
712*a90b9d01SCy Schubert 		/*
713*a90b9d01SCy Schubert 		 * This type is used in (Re)Association Response frame MSCS
714*a90b9d01SCy Schubert 		 * Descriptor element if no change is required.
715*a90b9d01SCy Schubert 		 */
716*a90b9d01SCy Schubert 		break;
717*a90b9d01SCy Schubert 	default:
718*a90b9d01SCy Schubert 		wpa_printf(MSG_INFO,
719*a90b9d01SCy Schubert 			   "MSCS: Drop received frame with unknown Request Type: %u",
720*a90b9d01SCy Schubert 			   robust_av.request_type);
721*a90b9d01SCy Schubert 		return;
722*a90b9d01SCy Schubert 	}
723*a90b9d01SCy Schubert 
724*a90b9d01SCy Schubert event_mscs_result:
725*a90b9d01SCy Schubert 	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_MSCS_RESULT "bssid=" MACSTR
726*a90b9d01SCy Schubert 		" status_code=%u", MAC2STR(bssid), status);
727*a90b9d01SCy Schubert 	wpa_s->mscs_setup_done = status == WLAN_STATUS_SUCCESS;
728*a90b9d01SCy Schubert }
729*a90b9d01SCy Schubert 
730*a90b9d01SCy Schubert 
wpas_handle_robust_av_recv_action(struct wpa_supplicant * wpa_s,const u8 * src,const u8 * buf,size_t len)731c1d255d3SCy Schubert void wpas_handle_robust_av_recv_action(struct wpa_supplicant *wpa_s,
732c1d255d3SCy Schubert 				       const u8 *src, const u8 *buf, size_t len)
733c1d255d3SCy Schubert {
734c1d255d3SCy Schubert 	u8 dialog_token;
735c1d255d3SCy Schubert 	u16 status_code;
736*a90b9d01SCy Schubert 	const u8 *mscs_desc_ie;
737c1d255d3SCy Schubert 
738c1d255d3SCy Schubert 	if (len < 3)
739c1d255d3SCy Schubert 		return;
740c1d255d3SCy Schubert 
741c1d255d3SCy Schubert 	dialog_token = *buf++;
742*a90b9d01SCy Schubert 	len--;
743*a90b9d01SCy Schubert 
744*a90b9d01SCy Schubert 	/* AP sets dialog token to 0 for unsolicited response */
745*a90b9d01SCy Schubert 	if (!dialog_token && !wpa_s->mscs_setup_done) {
746*a90b9d01SCy Schubert 		wpa_printf(MSG_INFO,
747*a90b9d01SCy Schubert 			   "MSCS: Drop unsolicited received frame: inactive");
748*a90b9d01SCy Schubert 		return;
749*a90b9d01SCy Schubert 	}
750*a90b9d01SCy Schubert 
751*a90b9d01SCy Schubert 	if (dialog_token && dialog_token != wpa_s->robust_av.dialog_token) {
752c1d255d3SCy Schubert 		wpa_printf(MSG_INFO,
753c1d255d3SCy Schubert 			   "MSCS: Drop received frame due to dialog token mismatch: received:%u expected:%u",
754c1d255d3SCy Schubert 			   dialog_token, wpa_s->robust_av.dialog_token);
755c1d255d3SCy Schubert 		return;
756c1d255d3SCy Schubert 	}
757c1d255d3SCy Schubert 
758c1d255d3SCy Schubert 	status_code = WPA_GET_LE16(buf);
759*a90b9d01SCy Schubert 	buf += 2;
760*a90b9d01SCy Schubert 	len -= 2;
761*a90b9d01SCy Schubert 
762*a90b9d01SCy Schubert 	mscs_desc_ie = get_ie_ext(buf, len, WLAN_EID_EXT_MSCS_DESCRIPTOR);
763*a90b9d01SCy Schubert 	wpas_parse_mscs_resp(wpa_s, status_code, src, mscs_desc_ie);
764c1d255d3SCy Schubert }
765c1d255d3SCy Schubert 
766c1d255d3SCy Schubert 
wpas_handle_assoc_resp_mscs(struct wpa_supplicant * wpa_s,const u8 * bssid,const u8 * ies,size_t ies_len)767c1d255d3SCy Schubert void wpas_handle_assoc_resp_mscs(struct wpa_supplicant *wpa_s, const u8 *bssid,
768c1d255d3SCy Schubert 				 const u8 *ies, size_t ies_len)
769c1d255d3SCy Schubert {
770c1d255d3SCy Schubert 	const u8 *mscs_desc_ie, *mscs_status;
771c1d255d3SCy Schubert 	u16 status;
772c1d255d3SCy Schubert 
773c1d255d3SCy Schubert 	/* Process optional MSCS Status subelement when MSCS IE is in
774c1d255d3SCy Schubert 	 * (Re)Association Response frame */
775c1d255d3SCy Schubert 	if (!ies || ies_len == 0 || !wpa_s->robust_av.valid_config)
776c1d255d3SCy Schubert 		return;
777c1d255d3SCy Schubert 
778c1d255d3SCy Schubert 	mscs_desc_ie = get_ie_ext(ies, ies_len, WLAN_EID_EXT_MSCS_DESCRIPTOR);
779*a90b9d01SCy Schubert 	if (!mscs_desc_ie || mscs_desc_ie[1] <= MSCS_DESCRIPTOR_FIXED_LEN)
780c1d255d3SCy Schubert 		return;
781c1d255d3SCy Schubert 
782*a90b9d01SCy Schubert 	/* Subelements start after element header and fixed fields */
783*a90b9d01SCy Schubert 	mscs_status = get_ie(&mscs_desc_ie[2 + MSCS_DESCRIPTOR_FIXED_LEN],
784*a90b9d01SCy Schubert 			     mscs_desc_ie[1] - MSCS_DESCRIPTOR_FIXED_LEN,
785c1d255d3SCy Schubert 			     MCSC_SUBELEM_STATUS);
786c1d255d3SCy Schubert 	if (!mscs_status || mscs_status[1] < 2)
787c1d255d3SCy Schubert 		return;
788c1d255d3SCy Schubert 
789c1d255d3SCy Schubert 	status = WPA_GET_LE16(mscs_status + 2);
790*a90b9d01SCy Schubert 
791*a90b9d01SCy Schubert 	wpas_parse_mscs_resp(wpa_s, status, bssid, mscs_desc_ie);
792c1d255d3SCy Schubert }
7934b72b91aSCy Schubert 
7944b72b91aSCy Schubert 
wpas_wait_for_dscp_req_timer(void * eloop_ctx,void * timeout_ctx)7954b72b91aSCy Schubert static void wpas_wait_for_dscp_req_timer(void *eloop_ctx, void *timeout_ctx)
7964b72b91aSCy Schubert {
7974b72b91aSCy Schubert 	struct wpa_supplicant *wpa_s = eloop_ctx;
7984b72b91aSCy Schubert 
7994b72b91aSCy Schubert 	/* Once timeout is over, reset wait flag and allow sending DSCP query */
8004b72b91aSCy Schubert 	wpa_printf(MSG_DEBUG,
8014b72b91aSCy Schubert 		   "QM: Wait time over for sending DSCP request - allow DSCP query");
8024b72b91aSCy Schubert 	wpa_s->wait_for_dscp_req = 0;
8034b72b91aSCy Schubert 	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "request_wait end");
8044b72b91aSCy Schubert }
8054b72b91aSCy Schubert 
8064b72b91aSCy Schubert 
wpas_handle_assoc_resp_qos_mgmt(struct wpa_supplicant * wpa_s,const u8 * ies,size_t ies_len)8074b72b91aSCy Schubert void wpas_handle_assoc_resp_qos_mgmt(struct wpa_supplicant *wpa_s,
8084b72b91aSCy Schubert 				     const u8 *ies, size_t ies_len)
8094b72b91aSCy Schubert {
8104b72b91aSCy Schubert 	const u8 *wfa_capa;
8114b72b91aSCy Schubert 
8124b72b91aSCy Schubert 	wpa_s->connection_dscp = 0;
8134b72b91aSCy Schubert 	if (wpa_s->wait_for_dscp_req)
8144b72b91aSCy Schubert 		eloop_cancel_timeout(wpas_wait_for_dscp_req_timer, wpa_s, NULL);
8154b72b91aSCy Schubert 
8164b72b91aSCy Schubert 	if (!ies || ies_len == 0 || !wpa_s->enable_dscp_policy_capa)
8174b72b91aSCy Schubert 		return;
8184b72b91aSCy Schubert 
8194b72b91aSCy Schubert 	wfa_capa = get_vendor_ie(ies, ies_len, WFA_CAPA_IE_VENDOR_TYPE);
8204b72b91aSCy Schubert 	if (!wfa_capa || wfa_capa[1] < 6 || wfa_capa[6] < 1 ||
8214b72b91aSCy Schubert 	    !(wfa_capa[7] & WFA_CAPA_QM_DSCP_POLICY))
8224b72b91aSCy Schubert 		return; /* AP does not enable QM DSCP Policy */
8234b72b91aSCy Schubert 
8244b72b91aSCy Schubert 	wpa_s->connection_dscp = 1;
8254b72b91aSCy Schubert 	wpa_s->wait_for_dscp_req = !!(wfa_capa[7] &
8264b72b91aSCy Schubert 				      WFA_CAPA_QM_UNSOLIC_DSCP);
8274b72b91aSCy Schubert 	if (!wpa_s->wait_for_dscp_req)
8284b72b91aSCy Schubert 		return;
8294b72b91aSCy Schubert 
8304b72b91aSCy Schubert 	/* Register a timeout after which dscp query can be sent to AP. */
8314b72b91aSCy Schubert 	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "request_wait start");
8324b72b91aSCy Schubert 	eloop_register_timeout(DSCP_REQ_TIMEOUT, 0,
8334b72b91aSCy Schubert 			       wpas_wait_for_dscp_req_timer, wpa_s, NULL);
8344b72b91aSCy Schubert }
8354b72b91aSCy Schubert 
8364b72b91aSCy Schubert 
wpas_handle_robust_av_scs_recv_action(struct wpa_supplicant * wpa_s,const u8 * src,const u8 * buf,size_t len)8374b72b91aSCy Schubert void wpas_handle_robust_av_scs_recv_action(struct wpa_supplicant *wpa_s,
8384b72b91aSCy Schubert 					   const u8 *src, const u8 *buf,
8394b72b91aSCy Schubert 					   size_t len)
8404b72b91aSCy Schubert {
8414b72b91aSCy Schubert 	u8 dialog_token;
8424b72b91aSCy Schubert 	unsigned int i, count;
8434b72b91aSCy Schubert 	struct active_scs_elem *scs_desc, *prev;
8444b72b91aSCy Schubert 
8454b72b91aSCy Schubert 	if (len < 2)
8464b72b91aSCy Schubert 		return;
8474b72b91aSCy Schubert 	if (!wpa_s->ongoing_scs_req) {
8484b72b91aSCy Schubert 		wpa_printf(MSG_INFO,
8494b72b91aSCy Schubert 			   "SCS: Drop received response due to no ongoing request");
8504b72b91aSCy Schubert 		return;
8514b72b91aSCy Schubert 	}
8524b72b91aSCy Schubert 
8534b72b91aSCy Schubert 	dialog_token = *buf++;
8544b72b91aSCy Schubert 	len--;
8554b72b91aSCy Schubert 	if (dialog_token != wpa_s->scs_dialog_token) {
8564b72b91aSCy Schubert 		wpa_printf(MSG_INFO,
8574b72b91aSCy Schubert 			   "SCS: Drop received frame due to dialog token mismatch: received:%u expected:%u",
8584b72b91aSCy Schubert 			   dialog_token, wpa_s->scs_dialog_token);
8594b72b91aSCy Schubert 		return;
8604b72b91aSCy Schubert 	}
8614b72b91aSCy Schubert 
8624b72b91aSCy Schubert 	/* This Count field does not exist in the IEEE Std 802.11-2020
8634b72b91aSCy Schubert 	 * definition of the SCS Response frame. However, it was accepted to
8644b72b91aSCy Schubert 	 * be added into REVme per REVme/D0.0 CC35 CID 49 (edits in document
8654b72b91aSCy Schubert 	 * 11-21-0688-07). */
8664b72b91aSCy Schubert 	count = *buf++;
8674b72b91aSCy Schubert 	len--;
8684b72b91aSCy Schubert 	if (count == 0 || count * 3 > len) {
8694b72b91aSCy Schubert 		wpa_printf(MSG_INFO,
8704b72b91aSCy Schubert 			   "SCS: Drop received frame due to invalid count: %u (remaining %zu octets)",
8714b72b91aSCy Schubert 			   count, len);
8724b72b91aSCy Schubert 		return;
8734b72b91aSCy Schubert 	}
8744b72b91aSCy Schubert 
8754b72b91aSCy Schubert 	for (i = 0; i < count; i++) {
8764b72b91aSCy Schubert 		u8 id;
8774b72b91aSCy Schubert 		u16 status;
8784b72b91aSCy Schubert 		bool scs_desc_found = false;
8794b72b91aSCy Schubert 
8804b72b91aSCy Schubert 		id = *buf++;
8814b72b91aSCy Schubert 		status = WPA_GET_LE16(buf);
8824b72b91aSCy Schubert 		buf += 2;
8834b72b91aSCy Schubert 		len -= 3;
8844b72b91aSCy Schubert 
8854b72b91aSCy Schubert 		dl_list_for_each(scs_desc, &wpa_s->active_scs_ids,
8864b72b91aSCy Schubert 				 struct active_scs_elem, list) {
8874b72b91aSCy Schubert 			if (id == scs_desc->scs_id) {
8884b72b91aSCy Schubert 				scs_desc_found = true;
8894b72b91aSCy Schubert 				break;
8904b72b91aSCy Schubert 			}
8914b72b91aSCy Schubert 		}
8924b72b91aSCy Schubert 
8934b72b91aSCy Schubert 		if (!scs_desc_found) {
8944b72b91aSCy Schubert 			wpa_printf(MSG_INFO, "SCS: SCS ID invalid %u", id);
8954b72b91aSCy Schubert 			continue;
8964b72b91aSCy Schubert 		}
8974b72b91aSCy Schubert 
8984b72b91aSCy Schubert 		if (status != WLAN_STATUS_SUCCESS) {
8994b72b91aSCy Schubert 			dl_list_del(&scs_desc->list);
9004b72b91aSCy Schubert 			os_free(scs_desc);
9014b72b91aSCy Schubert 		} else if (status == WLAN_STATUS_SUCCESS) {
9024b72b91aSCy Schubert 			scs_desc->status = SCS_DESC_SUCCESS;
9034b72b91aSCy Schubert 		}
9044b72b91aSCy Schubert 
9054b72b91aSCy Schubert 		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCS_RESULT "bssid=" MACSTR
9064b72b91aSCy Schubert 			" SCSID=%u status_code=%u", MAC2STR(src), id, status);
9074b72b91aSCy Schubert 	}
9084b72b91aSCy Schubert 
9094b72b91aSCy Schubert 	eloop_cancel_timeout(scs_request_timer, wpa_s, NULL);
9104b72b91aSCy Schubert 	wpa_s->ongoing_scs_req = false;
9114b72b91aSCy Schubert 
9124b72b91aSCy Schubert 	dl_list_for_each_safe(scs_desc, prev, &wpa_s->active_scs_ids,
9134b72b91aSCy Schubert 			      struct active_scs_elem, list) {
9144b72b91aSCy Schubert 		if (scs_desc->status != SCS_DESC_SUCCESS) {
9154b72b91aSCy Schubert 			wpa_msg(wpa_s, MSG_INFO,
9164b72b91aSCy Schubert 				WPA_EVENT_SCS_RESULT "bssid=" MACSTR
9174b72b91aSCy Schubert 				" SCSID=%u status_code=response_not_received",
9184b72b91aSCy Schubert 				MAC2STR(src), scs_desc->scs_id);
9194b72b91aSCy Schubert 			dl_list_del(&scs_desc->list);
9204b72b91aSCy Schubert 			os_free(scs_desc);
9214b72b91aSCy Schubert 		}
9224b72b91aSCy Schubert 	}
9234b72b91aSCy Schubert }
9244b72b91aSCy Schubert 
9254b72b91aSCy Schubert 
wpas_clear_active_scs_ids(struct wpa_supplicant * wpa_s)9264b72b91aSCy Schubert static void wpas_clear_active_scs_ids(struct wpa_supplicant *wpa_s)
9274b72b91aSCy Schubert {
9284b72b91aSCy Schubert 	struct active_scs_elem *scs_elem;
9294b72b91aSCy Schubert 
9304b72b91aSCy Schubert 	while ((scs_elem = dl_list_first(&wpa_s->active_scs_ids,
9314b72b91aSCy Schubert 					 struct active_scs_elem, list))) {
9324b72b91aSCy Schubert 		dl_list_del(&scs_elem->list);
9334b72b91aSCy Schubert 		os_free(scs_elem);
9344b72b91aSCy Schubert 	}
9354b72b91aSCy Schubert }
9364b72b91aSCy Schubert 
9374b72b91aSCy Schubert 
wpas_scs_deinit(struct wpa_supplicant * wpa_s)9384b72b91aSCy Schubert void wpas_scs_deinit(struct wpa_supplicant *wpa_s)
9394b72b91aSCy Schubert {
9404b72b91aSCy Schubert 	free_up_scs_desc(&wpa_s->scs_robust_av_req);
9414b72b91aSCy Schubert 	wpa_s->scs_dialog_token = 0;
9424b72b91aSCy Schubert 	wpas_clear_active_scs_ids(wpa_s);
9434b72b91aSCy Schubert 	eloop_cancel_timeout(scs_request_timer, wpa_s, NULL);
9444b72b91aSCy Schubert 	wpa_s->ongoing_scs_req = false;
9454b72b91aSCy Schubert }
9464b72b91aSCy Schubert 
9474b72b91aSCy Schubert 
write_ipv4_info(char * pos,int total_len,const struct ipv4_params * v4,u8 classifier_mask)9484b72b91aSCy Schubert static int write_ipv4_info(char *pos, int total_len,
949*a90b9d01SCy Schubert 			   const struct ipv4_params *v4,
950*a90b9d01SCy Schubert 			   u8 classifier_mask)
9514b72b91aSCy Schubert {
9524b72b91aSCy Schubert 	int res, rem_len;
9534b72b91aSCy Schubert 	char addr[INET_ADDRSTRLEN];
9544b72b91aSCy Schubert 
9554b72b91aSCy Schubert 	rem_len = total_len;
9564b72b91aSCy Schubert 
957*a90b9d01SCy Schubert 	if (classifier_mask & BIT(1)) {
9584b72b91aSCy Schubert 		if (!inet_ntop(AF_INET, &v4->src_ip, addr, INET_ADDRSTRLEN)) {
9594b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
9604b72b91aSCy Schubert 				   "QM: Failed to set IPv4 source address");
9614b72b91aSCy Schubert 			return -1;
9624b72b91aSCy Schubert 		}
9634b72b91aSCy Schubert 
9644b72b91aSCy Schubert 		res = os_snprintf(pos, rem_len, " src_ip=%s", addr);
9654b72b91aSCy Schubert 		if (os_snprintf_error(rem_len, res))
9664b72b91aSCy Schubert 			return -1;
9674b72b91aSCy Schubert 
9684b72b91aSCy Schubert 		pos += res;
9694b72b91aSCy Schubert 		rem_len -= res;
9704b72b91aSCy Schubert 	}
9714b72b91aSCy Schubert 
972*a90b9d01SCy Schubert 	if (classifier_mask & BIT(2)) {
9734b72b91aSCy Schubert 		if (!inet_ntop(AF_INET, &v4->dst_ip, addr, INET_ADDRSTRLEN)) {
9744b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
9754b72b91aSCy Schubert 				   "QM: Failed to set IPv4 destination address");
9764b72b91aSCy Schubert 			return -1;
9774b72b91aSCy Schubert 		}
9784b72b91aSCy Schubert 
9794b72b91aSCy Schubert 		res = os_snprintf(pos, rem_len, " dst_ip=%s", addr);
9804b72b91aSCy Schubert 		if (os_snprintf_error(rem_len, res))
9814b72b91aSCy Schubert 			return -1;
9824b72b91aSCy Schubert 
9834b72b91aSCy Schubert 		pos += res;
9844b72b91aSCy Schubert 		rem_len -= res;
9854b72b91aSCy Schubert 	}
9864b72b91aSCy Schubert 
987*a90b9d01SCy Schubert 	if (classifier_mask & BIT(3)) {
9884b72b91aSCy Schubert 		res = os_snprintf(pos, rem_len, " src_port=%d", v4->src_port);
9894b72b91aSCy Schubert 		if (os_snprintf_error(rem_len, res))
9904b72b91aSCy Schubert 			return -1;
9914b72b91aSCy Schubert 
9924b72b91aSCy Schubert 		pos += res;
9934b72b91aSCy Schubert 		rem_len -= res;
9944b72b91aSCy Schubert 	}
9954b72b91aSCy Schubert 
996*a90b9d01SCy Schubert 	if (classifier_mask & BIT(4)) {
9974b72b91aSCy Schubert 		res = os_snprintf(pos, rem_len, " dst_port=%d", v4->dst_port);
9984b72b91aSCy Schubert 		if (os_snprintf_error(rem_len, res))
9994b72b91aSCy Schubert 			return -1;
10004b72b91aSCy Schubert 
10014b72b91aSCy Schubert 		pos += res;
10024b72b91aSCy Schubert 		rem_len -= res;
10034b72b91aSCy Schubert 	}
10044b72b91aSCy Schubert 
1005*a90b9d01SCy Schubert 	if (classifier_mask & BIT(6)) {
10064b72b91aSCy Schubert 		res = os_snprintf(pos, rem_len, " protocol=%d", v4->protocol);
10074b72b91aSCy Schubert 		if (os_snprintf_error(rem_len, res))
10084b72b91aSCy Schubert 			return -1;
10094b72b91aSCy Schubert 
10104b72b91aSCy Schubert 		pos += res;
10114b72b91aSCy Schubert 		rem_len -= res;
10124b72b91aSCy Schubert 	}
10134b72b91aSCy Schubert 
10144b72b91aSCy Schubert 	return total_len - rem_len;
10154b72b91aSCy Schubert }
10164b72b91aSCy Schubert 
10174b72b91aSCy Schubert 
write_ipv6_info(char * pos,int total_len,const struct ipv6_params * v6,u8 classifier_mask)10184b72b91aSCy Schubert static int write_ipv6_info(char *pos, int total_len,
1019*a90b9d01SCy Schubert 			   const struct ipv6_params *v6,
1020*a90b9d01SCy Schubert 			   u8 classifier_mask)
10214b72b91aSCy Schubert {
10224b72b91aSCy Schubert 	int res, rem_len;
10234b72b91aSCy Schubert 	char addr[INET6_ADDRSTRLEN];
10244b72b91aSCy Schubert 
10254b72b91aSCy Schubert 	rem_len = total_len;
10264b72b91aSCy Schubert 
1027*a90b9d01SCy Schubert 	if (classifier_mask & BIT(1)) {
10284b72b91aSCy Schubert 		if (!inet_ntop(AF_INET6, &v6->src_ip, addr, INET6_ADDRSTRLEN)) {
10294b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
10304b72b91aSCy Schubert 				   "QM: Failed to set IPv6 source addr");
10314b72b91aSCy Schubert 			return -1;
10324b72b91aSCy Schubert 		}
10334b72b91aSCy Schubert 
10344b72b91aSCy Schubert 		res = os_snprintf(pos, rem_len, " src_ip=%s", addr);
10354b72b91aSCy Schubert 		if (os_snprintf_error(rem_len, res))
10364b72b91aSCy Schubert 			return -1;
10374b72b91aSCy Schubert 
10384b72b91aSCy Schubert 		pos += res;
10394b72b91aSCy Schubert 		rem_len -= res;
10404b72b91aSCy Schubert 	}
10414b72b91aSCy Schubert 
1042*a90b9d01SCy Schubert 	if (classifier_mask & BIT(2)) {
10434b72b91aSCy Schubert 		if (!inet_ntop(AF_INET6, &v6->dst_ip, addr, INET6_ADDRSTRLEN)) {
10444b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
10454b72b91aSCy Schubert 				   "QM: Failed to set IPv6 destination addr");
10464b72b91aSCy Schubert 			return -1;
10474b72b91aSCy Schubert 		}
10484b72b91aSCy Schubert 
10494b72b91aSCy Schubert 		res = os_snprintf(pos, rem_len, " dst_ip=%s", addr);
10504b72b91aSCy Schubert 		if (os_snprintf_error(rem_len, res))
10514b72b91aSCy Schubert 			return -1;
10524b72b91aSCy Schubert 
10534b72b91aSCy Schubert 		pos += res;
10544b72b91aSCy Schubert 		rem_len -= res;
10554b72b91aSCy Schubert 	}
10564b72b91aSCy Schubert 
1057*a90b9d01SCy Schubert 	if (classifier_mask & BIT(3)) {
10584b72b91aSCy Schubert 		res = os_snprintf(pos, rem_len, " src_port=%d", v6->src_port);
10594b72b91aSCy Schubert 		if (os_snprintf_error(rem_len, res))
10604b72b91aSCy Schubert 			return -1;
10614b72b91aSCy Schubert 
10624b72b91aSCy Schubert 		pos += res;
10634b72b91aSCy Schubert 		rem_len -= res;
10644b72b91aSCy Schubert 	}
10654b72b91aSCy Schubert 
1066*a90b9d01SCy Schubert 	if (classifier_mask & BIT(4)) {
10674b72b91aSCy Schubert 		res = os_snprintf(pos, rem_len, " dst_port=%d", v6->dst_port);
10684b72b91aSCy Schubert 		if (os_snprintf_error(rem_len, res))
10694b72b91aSCy Schubert 			return -1;
10704b72b91aSCy Schubert 
10714b72b91aSCy Schubert 		pos += res;
10724b72b91aSCy Schubert 		rem_len -= res;
10734b72b91aSCy Schubert 	}
10744b72b91aSCy Schubert 
1075*a90b9d01SCy Schubert 	if (classifier_mask & BIT(6)) {
10764b72b91aSCy Schubert 		res = os_snprintf(pos, rem_len, " protocol=%d",
10774b72b91aSCy Schubert 				  v6->next_header);
10784b72b91aSCy Schubert 		if (os_snprintf_error(rem_len, res))
10794b72b91aSCy Schubert 			return -1;
10804b72b91aSCy Schubert 
10814b72b91aSCy Schubert 		pos += res;
10824b72b91aSCy Schubert 		rem_len -= res;
10834b72b91aSCy Schubert 	}
10844b72b91aSCy Schubert 
10854b72b91aSCy Schubert 	return total_len - rem_len;
10864b72b91aSCy Schubert }
10874b72b91aSCy Schubert 
10884b72b91aSCy Schubert 
10894b72b91aSCy Schubert struct dscp_policy_data {
10904b72b91aSCy Schubert 	u8 policy_id;
10914b72b91aSCy Schubert 	u8 req_type;
10924b72b91aSCy Schubert 	u8 dscp;
10934b72b91aSCy Schubert 	bool dscp_info;
10944b72b91aSCy Schubert 	const u8 *frame_classifier;
10954b72b91aSCy Schubert 	u8 frame_classifier_len;
10964b72b91aSCy Schubert 	struct type4_params type4_param;
10974b72b91aSCy Schubert 	const u8 *domain_name;
10984b72b91aSCy Schubert 	u8 domain_name_len;
10994b72b91aSCy Schubert 	u16 start_port;
11004b72b91aSCy Schubert 	u16 end_port;
11014b72b91aSCy Schubert 	bool port_range_info;
11024b72b91aSCy Schubert };
11034b72b91aSCy Schubert 
11044b72b91aSCy Schubert 
set_frame_classifier_type4_ipv4(struct dscp_policy_data * policy)11054b72b91aSCy Schubert static int set_frame_classifier_type4_ipv4(struct dscp_policy_data *policy)
11064b72b91aSCy Schubert {
11074b72b91aSCy Schubert 	u8 classifier_mask;
11084b72b91aSCy Schubert 	const u8 *frame_classifier = policy->frame_classifier;
11094b72b91aSCy Schubert 	struct type4_params *type4_param = &policy->type4_param;
11104b72b91aSCy Schubert 
11114b72b91aSCy Schubert 	if (policy->frame_classifier_len < 18) {
11124b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
11134b72b91aSCy Schubert 			   "QM: Received IPv4 frame classifier with insufficient length %d",
11144b72b91aSCy Schubert 			   policy->frame_classifier_len);
11154b72b91aSCy Schubert 		return -1;
11164b72b91aSCy Schubert 	}
11174b72b91aSCy Schubert 
11184b72b91aSCy Schubert 	classifier_mask = frame_classifier[1];
11194b72b91aSCy Schubert 
11204b72b91aSCy Schubert 	/* Classifier Mask - bit 1 = Source IP Address */
11214b72b91aSCy Schubert 	if (classifier_mask & BIT(1)) {
1122*a90b9d01SCy Schubert 		type4_param->classifier_mask |= BIT(1);
11234b72b91aSCy Schubert 		os_memcpy(&type4_param->ip_params.v4.src_ip,
11244b72b91aSCy Schubert 			  &frame_classifier[3], 4);
11254b72b91aSCy Schubert 	}
11264b72b91aSCy Schubert 
11274b72b91aSCy Schubert 	/* Classifier Mask - bit 2 = Destination IP Address */
11284b72b91aSCy Schubert 	if (classifier_mask & BIT(2)) {
11294b72b91aSCy Schubert 		if (policy->domain_name) {
11304b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
11314b72b91aSCy Schubert 				   "QM: IPv4: Both domain name and destination IP address not expected");
11324b72b91aSCy Schubert 			return -1;
11334b72b91aSCy Schubert 		}
11344b72b91aSCy Schubert 
1135*a90b9d01SCy Schubert 		type4_param->classifier_mask |= BIT(2);
11364b72b91aSCy Schubert 		os_memcpy(&type4_param->ip_params.v4.dst_ip,
11374b72b91aSCy Schubert 			  &frame_classifier[7], 4);
11384b72b91aSCy Schubert 	}
11394b72b91aSCy Schubert 
11404b72b91aSCy Schubert 	/* Classifier Mask - bit 3 = Source Port */
11414b72b91aSCy Schubert 	if (classifier_mask & BIT(3)) {
1142*a90b9d01SCy Schubert 		type4_param->classifier_mask |= BIT(3);
11434b72b91aSCy Schubert 		type4_param->ip_params.v4.src_port =
11444b72b91aSCy Schubert 			WPA_GET_BE16(&frame_classifier[11]);
11454b72b91aSCy Schubert 	}
11464b72b91aSCy Schubert 
11474b72b91aSCy Schubert 	/* Classifier Mask - bit 4 = Destination Port */
11484b72b91aSCy Schubert 	if (classifier_mask & BIT(4)) {
11494b72b91aSCy Schubert 		if (policy->port_range_info) {
11504b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
11514b72b91aSCy Schubert 				   "QM: IPv4: Both port range and destination port not expected");
11524b72b91aSCy Schubert 			return -1;
11534b72b91aSCy Schubert 		}
11544b72b91aSCy Schubert 
1155*a90b9d01SCy Schubert 		type4_param->classifier_mask |= BIT(4);
11564b72b91aSCy Schubert 		type4_param->ip_params.v4.dst_port =
11574b72b91aSCy Schubert 			WPA_GET_BE16(&frame_classifier[13]);
11584b72b91aSCy Schubert 	}
11594b72b91aSCy Schubert 
11604b72b91aSCy Schubert 	/* Classifier Mask - bit 5 = DSCP (ignored) */
11614b72b91aSCy Schubert 
11624b72b91aSCy Schubert 	/* Classifier Mask - bit 6 = Protocol */
11634b72b91aSCy Schubert 	if (classifier_mask & BIT(6)) {
1164*a90b9d01SCy Schubert 		type4_param->classifier_mask |= BIT(6);
11654b72b91aSCy Schubert 		type4_param->ip_params.v4.protocol = frame_classifier[16];
11664b72b91aSCy Schubert 	}
11674b72b91aSCy Schubert 
11684b72b91aSCy Schubert 	return 0;
11694b72b91aSCy Schubert }
11704b72b91aSCy Schubert 
11714b72b91aSCy Schubert 
set_frame_classifier_type4_ipv6(struct dscp_policy_data * policy)11724b72b91aSCy Schubert static int set_frame_classifier_type4_ipv6(struct dscp_policy_data *policy)
11734b72b91aSCy Schubert {
11744b72b91aSCy Schubert 	u8 classifier_mask;
11754b72b91aSCy Schubert 	const u8 *frame_classifier = policy->frame_classifier;
11764b72b91aSCy Schubert 	struct type4_params *type4_param = &policy->type4_param;
11774b72b91aSCy Schubert 
11784b72b91aSCy Schubert 	if (policy->frame_classifier_len < 44) {
11794b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
11804b72b91aSCy Schubert 			   "QM: Received IPv6 frame classifier with insufficient length %d",
11814b72b91aSCy Schubert 			   policy->frame_classifier_len);
11824b72b91aSCy Schubert 		return -1;
11834b72b91aSCy Schubert 	}
11844b72b91aSCy Schubert 
11854b72b91aSCy Schubert 	classifier_mask = frame_classifier[1];
11864b72b91aSCy Schubert 
11874b72b91aSCy Schubert 	/* Classifier Mask - bit 1 = Source IP Address */
11884b72b91aSCy Schubert 	if (classifier_mask & BIT(1)) {
1189*a90b9d01SCy Schubert 		type4_param->classifier_mask |= BIT(1);
11904b72b91aSCy Schubert 		os_memcpy(&type4_param->ip_params.v6.src_ip,
11914b72b91aSCy Schubert 			  &frame_classifier[3], 16);
11924b72b91aSCy Schubert 	}
11934b72b91aSCy Schubert 
11944b72b91aSCy Schubert 	/* Classifier Mask - bit 2 = Destination IP Address */
11954b72b91aSCy Schubert 	if (classifier_mask & BIT(2)) {
11964b72b91aSCy Schubert 		if (policy->domain_name) {
11974b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
11984b72b91aSCy Schubert 				   "QM: IPv6: Both domain name and destination IP address not expected");
11994b72b91aSCy Schubert 			return -1;
12004b72b91aSCy Schubert 		}
1201*a90b9d01SCy Schubert 		type4_param->classifier_mask |= BIT(2);
12024b72b91aSCy Schubert 		os_memcpy(&type4_param->ip_params.v6.dst_ip,
12034b72b91aSCy Schubert 			  &frame_classifier[19], 16);
12044b72b91aSCy Schubert 	}
12054b72b91aSCy Schubert 
12064b72b91aSCy Schubert 	/* Classifier Mask - bit 3 = Source Port */
12074b72b91aSCy Schubert 	if (classifier_mask & BIT(3)) {
1208*a90b9d01SCy Schubert 		type4_param->classifier_mask |= BIT(3);
12094b72b91aSCy Schubert 		type4_param->ip_params.v6.src_port =
12104b72b91aSCy Schubert 				WPA_GET_BE16(&frame_classifier[35]);
12114b72b91aSCy Schubert 	}
12124b72b91aSCy Schubert 
12134b72b91aSCy Schubert 	/* Classifier Mask - bit 4 = Destination Port */
12144b72b91aSCy Schubert 	if (classifier_mask & BIT(4)) {
12154b72b91aSCy Schubert 		if (policy->port_range_info) {
12164b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
12174b72b91aSCy Schubert 				   "IPv6: Both port range and destination port not expected");
12184b72b91aSCy Schubert 			return -1;
12194b72b91aSCy Schubert 		}
12204b72b91aSCy Schubert 
1221*a90b9d01SCy Schubert 		type4_param->classifier_mask |= BIT(4);
12224b72b91aSCy Schubert 		type4_param->ip_params.v6.dst_port =
12234b72b91aSCy Schubert 				WPA_GET_BE16(&frame_classifier[37]);
12244b72b91aSCy Schubert 	}
12254b72b91aSCy Schubert 
12264b72b91aSCy Schubert 	/* Classifier Mask - bit 5 = DSCP (ignored) */
12274b72b91aSCy Schubert 
12284b72b91aSCy Schubert 	/* Classifier Mask - bit 6 = Next Header */
12294b72b91aSCy Schubert 	if (classifier_mask & BIT(6)) {
1230*a90b9d01SCy Schubert 		type4_param->classifier_mask |= BIT(6);
12314b72b91aSCy Schubert 		type4_param->ip_params.v6.next_header = frame_classifier[40];
12324b72b91aSCy Schubert 	}
12334b72b91aSCy Schubert 
12344b72b91aSCy Schubert 	return 0;
12354b72b91aSCy Schubert }
12364b72b91aSCy Schubert 
12374b72b91aSCy Schubert 
wpas_set_frame_classifier_params(struct dscp_policy_data * policy)12384b72b91aSCy Schubert static int wpas_set_frame_classifier_params(struct dscp_policy_data *policy)
12394b72b91aSCy Schubert {
12404b72b91aSCy Schubert 	const u8 *frame_classifier = policy->frame_classifier;
12414b72b91aSCy Schubert 	u8 frame_classifier_len = policy->frame_classifier_len;
12424b72b91aSCy Schubert 
12434b72b91aSCy Schubert 	if (frame_classifier_len < 3) {
12444b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
12454b72b91aSCy Schubert 			   "QM: Received frame classifier with insufficient length %d",
12464b72b91aSCy Schubert 			   frame_classifier_len);
12474b72b91aSCy Schubert 		return -1;
12484b72b91aSCy Schubert 	}
12494b72b91aSCy Schubert 
12504b72b91aSCy Schubert 	/* Only allowed Classifier Type: IP and higher layer parameters (4) */
12514b72b91aSCy Schubert 	if (frame_classifier[0] != 4) {
12524b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
12534b72b91aSCy Schubert 			   "QM: Received frame classifier with invalid classifier type %d",
12544b72b91aSCy Schubert 			   frame_classifier[0]);
12554b72b91aSCy Schubert 		return -1;
12564b72b91aSCy Schubert 	}
12574b72b91aSCy Schubert 
12584b72b91aSCy Schubert 	/* Classifier Mask - bit 0 = Version */
12594b72b91aSCy Schubert 	if (!(frame_classifier[1] & BIT(0))) {
12604b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
12614b72b91aSCy Schubert 			   "QM: Received frame classifier without IP version");
12624b72b91aSCy Schubert 		return -1;
12634b72b91aSCy Schubert 	}
12644b72b91aSCy Schubert 
12654b72b91aSCy Schubert 	/* Version (4 or 6) */
12664b72b91aSCy Schubert 	if (frame_classifier[2] == 4) {
12674b72b91aSCy Schubert 		if (set_frame_classifier_type4_ipv4(policy)) {
12684b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
12694b72b91aSCy Schubert 				   "QM: Failed to set IPv4 parameters");
12704b72b91aSCy Schubert 			return -1;
12714b72b91aSCy Schubert 		}
12724b72b91aSCy Schubert 
12734b72b91aSCy Schubert 		policy->type4_param.ip_version = IPV4;
12744b72b91aSCy Schubert 	} else if (frame_classifier[2] == 6) {
12754b72b91aSCy Schubert 		if (set_frame_classifier_type4_ipv6(policy)) {
12764b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
12774b72b91aSCy Schubert 				   "QM: Failed to set IPv6 parameters");
12784b72b91aSCy Schubert 			return -1;
12794b72b91aSCy Schubert 		}
12804b72b91aSCy Schubert 
12814b72b91aSCy Schubert 		policy->type4_param.ip_version = IPV6;
12824b72b91aSCy Schubert 	} else {
12834b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
12844b72b91aSCy Schubert 			   "QM: Received unknown IP version %d",
12854b72b91aSCy Schubert 			   frame_classifier[2]);
12864b72b91aSCy Schubert 		return -1;
12874b72b91aSCy Schubert 	}
12884b72b91aSCy Schubert 
12894b72b91aSCy Schubert 	return 0;
12904b72b91aSCy Schubert }
12914b72b91aSCy Schubert 
12924b72b91aSCy Schubert 
dscp_valid_domain_name(const char * str)12934b72b91aSCy Schubert static bool dscp_valid_domain_name(const char *str)
12944b72b91aSCy Schubert {
12954b72b91aSCy Schubert 	if (!str[0])
12964b72b91aSCy Schubert 		return false;
12974b72b91aSCy Schubert 
12984b72b91aSCy Schubert 	while (*str) {
12994b72b91aSCy Schubert 		if (is_ctrl_char(*str) || *str == ' ' || *str == '=')
13004b72b91aSCy Schubert 			return false;
13014b72b91aSCy Schubert 		str++;
13024b72b91aSCy Schubert 	}
13034b72b91aSCy Schubert 
13044b72b91aSCy Schubert 	return true;
13054b72b91aSCy Schubert }
13064b72b91aSCy Schubert 
13074b72b91aSCy Schubert 
wpas_add_dscp_policy(struct wpa_supplicant * wpa_s,struct dscp_policy_data * policy)13084b72b91aSCy Schubert static void wpas_add_dscp_policy(struct wpa_supplicant *wpa_s,
13094b72b91aSCy Schubert 				 struct dscp_policy_data *policy)
13104b72b91aSCy Schubert {
13114b72b91aSCy Schubert 	int ip_ver = 0, res;
13124b72b91aSCy Schubert 	char policy_str[1000], *pos;
13134b72b91aSCy Schubert 	int len;
13144b72b91aSCy Schubert 
13154b72b91aSCy Schubert 	if (!policy->frame_classifier && !policy->domain_name &&
13164b72b91aSCy Schubert 	    !policy->port_range_info) {
13174b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
13184b72b91aSCy Schubert 			   "QM: Invalid DSCP policy - no attributes present");
13194b72b91aSCy Schubert 		goto fail;
13204b72b91aSCy Schubert 	}
13214b72b91aSCy Schubert 
13224b72b91aSCy Schubert 	policy_str[0] = '\0';
13234b72b91aSCy Schubert 	pos = policy_str;
13244b72b91aSCy Schubert 	len = sizeof(policy_str);
13254b72b91aSCy Schubert 
13264b72b91aSCy Schubert 	if (policy->frame_classifier) {
13274b72b91aSCy Schubert 		struct type4_params *type4 = &policy->type4_param;
13284b72b91aSCy Schubert 
13294b72b91aSCy Schubert 		if (wpas_set_frame_classifier_params(policy)) {
13304b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
13314b72b91aSCy Schubert 				   "QM: Failed to set frame classifier parameters");
13324b72b91aSCy Schubert 			goto fail;
13334b72b91aSCy Schubert 		}
13344b72b91aSCy Schubert 
13354b72b91aSCy Schubert 		if (type4->ip_version == IPV4)
1336*a90b9d01SCy Schubert 			res = write_ipv4_info(pos, len, &type4->ip_params.v4,
1337*a90b9d01SCy Schubert 					      type4->classifier_mask);
13384b72b91aSCy Schubert 		else
1339*a90b9d01SCy Schubert 			res = write_ipv6_info(pos, len, &type4->ip_params.v6,
1340*a90b9d01SCy Schubert 					      type4->classifier_mask);
13414b72b91aSCy Schubert 
13424b72b91aSCy Schubert 		if (res <= 0) {
13434b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
13444b72b91aSCy Schubert 				   "QM: Failed to write IP parameters");
13454b72b91aSCy Schubert 			goto fail;
13464b72b91aSCy Schubert 		}
13474b72b91aSCy Schubert 
13484b72b91aSCy Schubert 		ip_ver = type4->ip_version;
13494b72b91aSCy Schubert 
13504b72b91aSCy Schubert 		pos += res;
13514b72b91aSCy Schubert 		len -= res;
13524b72b91aSCy Schubert 	}
13534b72b91aSCy Schubert 
13544b72b91aSCy Schubert 	if (policy->port_range_info) {
13554b72b91aSCy Schubert 		res = os_snprintf(pos, len, " start_port=%u end_port=%u",
13564b72b91aSCy Schubert 				  policy->start_port, policy->end_port);
13574b72b91aSCy Schubert 		if (os_snprintf_error(len, res)) {
13584b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
13594b72b91aSCy Schubert 				   "QM: Failed to write port range attributes for policy id = %d",
13604b72b91aSCy Schubert 				   policy->policy_id);
13614b72b91aSCy Schubert 			goto fail;
13624b72b91aSCy Schubert 		}
13634b72b91aSCy Schubert 
13644b72b91aSCy Schubert 		pos += res;
13654b72b91aSCy Schubert 		len -= res;
13664b72b91aSCy Schubert 	}
13674b72b91aSCy Schubert 
13684b72b91aSCy Schubert 	if (policy->domain_name) {
13694b72b91aSCy Schubert 		char domain_name_str[250];
13704b72b91aSCy Schubert 
13714b72b91aSCy Schubert 		if (policy->domain_name_len >= sizeof(domain_name_str)) {
13724b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
13734b72b91aSCy Schubert 				   "QM: Domain name length higher than max expected");
13744b72b91aSCy Schubert 			goto fail;
13754b72b91aSCy Schubert 		}
13764b72b91aSCy Schubert 		os_memcpy(domain_name_str, policy->domain_name,
13774b72b91aSCy Schubert 			  policy->domain_name_len);
13784b72b91aSCy Schubert 		domain_name_str[policy->domain_name_len] = '\0';
13794b72b91aSCy Schubert 		if (!dscp_valid_domain_name(domain_name_str)) {
13804b72b91aSCy Schubert 			wpa_printf(MSG_ERROR, "QM: Invalid domain name string");
13814b72b91aSCy Schubert 			goto fail;
13824b72b91aSCy Schubert 		}
13834b72b91aSCy Schubert 		res = os_snprintf(pos, len, " domain_name=%s", domain_name_str);
13844b72b91aSCy Schubert 		if (os_snprintf_error(len, res)) {
13854b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
13864b72b91aSCy Schubert 				   "QM: Failed to write domain name attribute for policy id = %d",
13874b72b91aSCy Schubert 				   policy->policy_id);
13884b72b91aSCy Schubert 			goto fail;
13894b72b91aSCy Schubert 		}
13904b72b91aSCy Schubert 	}
13914b72b91aSCy Schubert 
13924b72b91aSCy Schubert 	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY
13934b72b91aSCy Schubert 		"add policy_id=%u dscp=%u ip_version=%d%s",
13944b72b91aSCy Schubert 		policy->policy_id, policy->dscp, ip_ver, policy_str);
13954b72b91aSCy Schubert 	return;
13964b72b91aSCy Schubert fail:
13974b72b91aSCy Schubert 	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "reject policy_id=%u",
13984b72b91aSCy Schubert 		policy->policy_id);
13994b72b91aSCy Schubert }
14004b72b91aSCy Schubert 
14014b72b91aSCy Schubert 
wpas_dscp_deinit(struct wpa_supplicant * wpa_s)14024b72b91aSCy Schubert void wpas_dscp_deinit(struct wpa_supplicant *wpa_s)
14034b72b91aSCy Schubert {
14044b72b91aSCy Schubert 	wpa_printf(MSG_DEBUG, "QM: Clear all active DSCP policies");
14054b72b91aSCy Schubert 	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "clear_all");
14064b72b91aSCy Schubert 	wpa_s->dscp_req_dialog_token = 0;
14074b72b91aSCy Schubert 	wpa_s->dscp_query_dialog_token = 0;
14084b72b91aSCy Schubert 	wpa_s->connection_dscp = 0;
14094b72b91aSCy Schubert 	if (wpa_s->wait_for_dscp_req) {
14104b72b91aSCy Schubert 		wpa_s->wait_for_dscp_req = 0;
14114b72b91aSCy Schubert 		eloop_cancel_timeout(wpas_wait_for_dscp_req_timer, wpa_s, NULL);
14124b72b91aSCy Schubert 	}
14134b72b91aSCy Schubert }
14144b72b91aSCy Schubert 
14154b72b91aSCy Schubert 
wpas_fill_dscp_policy(struct dscp_policy_data * policy,u8 attr_id,u8 attr_len,const u8 * attr_data)14164b72b91aSCy Schubert static void wpas_fill_dscp_policy(struct dscp_policy_data *policy, u8 attr_id,
14174b72b91aSCy Schubert 				  u8 attr_len, const u8 *attr_data)
14184b72b91aSCy Schubert {
14194b72b91aSCy Schubert 	switch (attr_id) {
14204b72b91aSCy Schubert 	case QM_ATTR_PORT_RANGE:
14214b72b91aSCy Schubert 		if (attr_len < 4) {
14224b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
14234b72b91aSCy Schubert 				   "QM: Received Port Range attribute with insufficient length %d",
14244b72b91aSCy Schubert 				    attr_len);
14254b72b91aSCy Schubert 			break;
14264b72b91aSCy Schubert 		}
14274b72b91aSCy Schubert 		policy->start_port = WPA_GET_BE16(attr_data);
14284b72b91aSCy Schubert 		policy->end_port = WPA_GET_BE16(attr_data + 2);
14294b72b91aSCy Schubert 		policy->port_range_info = true;
14304b72b91aSCy Schubert 		break;
14314b72b91aSCy Schubert 	case QM_ATTR_DSCP_POLICY:
14324b72b91aSCy Schubert 		if (attr_len < 3) {
14334b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
14344b72b91aSCy Schubert 				   "QM: Received DSCP Policy attribute with insufficient length %d",
14354b72b91aSCy Schubert 				   attr_len);
14364b72b91aSCy Schubert 			return;
14374b72b91aSCy Schubert 		}
14384b72b91aSCy Schubert 		policy->policy_id = attr_data[0];
14394b72b91aSCy Schubert 		policy->req_type = attr_data[1];
14404b72b91aSCy Schubert 		policy->dscp = attr_data[2];
14414b72b91aSCy Schubert 		policy->dscp_info = true;
14424b72b91aSCy Schubert 		break;
14434b72b91aSCy Schubert 	case QM_ATTR_TCLAS:
14444b72b91aSCy Schubert 		if (attr_len < 1) {
14454b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
14464b72b91aSCy Schubert 				   "QM: Received TCLAS attribute with insufficient length %d",
14474b72b91aSCy Schubert 				   attr_len);
14484b72b91aSCy Schubert 			return;
14494b72b91aSCy Schubert 		}
14504b72b91aSCy Schubert 		policy->frame_classifier = attr_data;
14514b72b91aSCy Schubert 		policy->frame_classifier_len = attr_len;
14524b72b91aSCy Schubert 		break;
14534b72b91aSCy Schubert 	case QM_ATTR_DOMAIN_NAME:
14544b72b91aSCy Schubert 		if (attr_len < 1) {
14554b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
14564b72b91aSCy Schubert 				   "QM: Received domain name attribute with insufficient length %d",
14574b72b91aSCy Schubert 				   attr_len);
14584b72b91aSCy Schubert 			return;
14594b72b91aSCy Schubert 		}
14604b72b91aSCy Schubert 		policy->domain_name = attr_data;
14614b72b91aSCy Schubert 		policy->domain_name_len = attr_len;
14624b72b91aSCy Schubert 		break;
14634b72b91aSCy Schubert 	default:
14644b72b91aSCy Schubert 		wpa_printf(MSG_ERROR, "QM: Received invalid QoS attribute %d",
14654b72b91aSCy Schubert 			   attr_id);
14664b72b91aSCy Schubert 		break;
14674b72b91aSCy Schubert 	}
14684b72b91aSCy Schubert }
14694b72b91aSCy Schubert 
14704b72b91aSCy Schubert 
wpas_handle_qos_mgmt_recv_action(struct wpa_supplicant * wpa_s,const u8 * src,const u8 * buf,size_t len)14714b72b91aSCy Schubert void wpas_handle_qos_mgmt_recv_action(struct wpa_supplicant *wpa_s,
14724b72b91aSCy Schubert 				      const u8 *src,
14734b72b91aSCy Schubert 				      const u8 *buf, size_t len)
14744b72b91aSCy Schubert {
14754b72b91aSCy Schubert 	int rem_len;
14764b72b91aSCy Schubert 	const u8 *qos_ie, *attr;
14774b72b91aSCy Schubert 	int more, reset;
14784b72b91aSCy Schubert 
14794b72b91aSCy Schubert 	if (!wpa_s->enable_dscp_policy_capa) {
14804b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
14814b72b91aSCy Schubert 			   "QM: Ignore DSCP Policy frame since the capability is not enabled");
14824b72b91aSCy Schubert 		return;
14834b72b91aSCy Schubert 	}
14844b72b91aSCy Schubert 
14854b72b91aSCy Schubert 	if (!pmf_in_use(wpa_s, src)) {
14864b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
14874b72b91aSCy Schubert 			   "QM: Ignore DSCP Policy frame since PMF is not in use");
14884b72b91aSCy Schubert 		return;
14894b72b91aSCy Schubert 	}
14904b72b91aSCy Schubert 
14914b72b91aSCy Schubert 	if (!wpa_s->connection_dscp) {
14924b72b91aSCy Schubert 		 wpa_printf(MSG_DEBUG,
14934b72b91aSCy Schubert 			    "QM: DSCP Policy capability not enabled for the current association - ignore QoS Management Action frames");
14944b72b91aSCy Schubert 		return;
14954b72b91aSCy Schubert 	}
14964b72b91aSCy Schubert 
14974b72b91aSCy Schubert 	if (len < 1)
14984b72b91aSCy Schubert 		return;
14994b72b91aSCy Schubert 
15004b72b91aSCy Schubert 	/* Handle only DSCP Policy Request frame */
15014b72b91aSCy Schubert 	if (buf[0] != QM_DSCP_POLICY_REQ) {
15024b72b91aSCy Schubert 		wpa_printf(MSG_ERROR, "QM: Received unexpected QoS action frame %d",
15034b72b91aSCy Schubert 			   buf[0]);
15044b72b91aSCy Schubert 		return;
15054b72b91aSCy Schubert 	}
15064b72b91aSCy Schubert 
15074b72b91aSCy Schubert 	if (len < 3) {
15084b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
15094b72b91aSCy Schubert 			   "Received QoS Management DSCP Policy Request frame with invalid length %zu",
15104b72b91aSCy Schubert 			   len);
15114b72b91aSCy Schubert 		return;
15124b72b91aSCy Schubert 	}
15134b72b91aSCy Schubert 
15144b72b91aSCy Schubert 	/* Clear wait_for_dscp_req on receiving first DSCP request from AP */
15154b72b91aSCy Schubert 	if (wpa_s->wait_for_dscp_req) {
15164b72b91aSCy Schubert 		wpa_s->wait_for_dscp_req = 0;
15174b72b91aSCy Schubert 		eloop_cancel_timeout(wpas_wait_for_dscp_req_timer, wpa_s, NULL);
15184b72b91aSCy Schubert 	}
15194b72b91aSCy Schubert 
15204b72b91aSCy Schubert 	wpa_s->dscp_req_dialog_token = buf[1];
15214b72b91aSCy Schubert 	more = buf[2] & DSCP_POLICY_CTRL_MORE;
15224b72b91aSCy Schubert 	reset = buf[2] & DSCP_POLICY_CTRL_RESET;
15234b72b91aSCy Schubert 
15244b72b91aSCy Schubert 	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "request_start%s%s",
15254b72b91aSCy Schubert 		reset ? " clear_all" : "", more ? " more" : "");
15264b72b91aSCy Schubert 
15274b72b91aSCy Schubert 	qos_ie = buf + 3;
15284b72b91aSCy Schubert 	rem_len = len - 3;
15294b72b91aSCy Schubert 	while (rem_len > 2) {
15304b72b91aSCy Schubert 		struct dscp_policy_data policy;
15314b72b91aSCy Schubert 		int rem_attrs_len, ie_len;
15324b72b91aSCy Schubert 
15334b72b91aSCy Schubert 		ie_len = 2 + qos_ie[1];
15344b72b91aSCy Schubert 		if (rem_len < ie_len)
15354b72b91aSCy Schubert 			break;
15364b72b91aSCy Schubert 
15374b72b91aSCy Schubert 		if (rem_len < 6 || qos_ie[0] != WLAN_EID_VENDOR_SPECIFIC ||
15384b72b91aSCy Schubert 		    qos_ie[1] < 4 ||
15394b72b91aSCy Schubert 		    WPA_GET_BE32(&qos_ie[2]) != QM_IE_VENDOR_TYPE) {
15404b72b91aSCy Schubert 			rem_len -= ie_len;
15414b72b91aSCy Schubert 			qos_ie += ie_len;
15424b72b91aSCy Schubert 			continue;
15434b72b91aSCy Schubert 		}
15444b72b91aSCy Schubert 
15454b72b91aSCy Schubert 		os_memset(&policy, 0, sizeof(struct dscp_policy_data));
15464b72b91aSCy Schubert 		attr = qos_ie + 6;
15474b72b91aSCy Schubert 		rem_attrs_len = qos_ie[1] - 4;
15484b72b91aSCy Schubert 
1549*a90b9d01SCy Schubert 		while (rem_attrs_len > 2) {
1550*a90b9d01SCy Schubert 			u8 attr_id, attr_len;
1551*a90b9d01SCy Schubert 
1552*a90b9d01SCy Schubert 			attr_id = *attr++;
1553*a90b9d01SCy Schubert 			attr_len = *attr++;
1554*a90b9d01SCy Schubert 			rem_attrs_len -= 2;
1555*a90b9d01SCy Schubert 			if (attr_len > rem_attrs_len)
1556*a90b9d01SCy Schubert 				break;
1557*a90b9d01SCy Schubert 			wpas_fill_dscp_policy(&policy, attr_id, attr_len, attr);
1558*a90b9d01SCy Schubert 			rem_attrs_len -= attr_len;
1559*a90b9d01SCy Schubert 			attr += attr_len;
15604b72b91aSCy Schubert 		}
15614b72b91aSCy Schubert 
15624b72b91aSCy Schubert 		rem_len -= ie_len;
15634b72b91aSCy Schubert 		qos_ie += ie_len;
15644b72b91aSCy Schubert 
15654b72b91aSCy Schubert 		if (!policy.dscp_info) {
15664b72b91aSCy Schubert 			wpa_printf(MSG_ERROR,
15674b72b91aSCy Schubert 				   "QM: Received QoS IE without DSCP Policy attribute");
15684b72b91aSCy Schubert 			continue;
15694b72b91aSCy Schubert 		}
15704b72b91aSCy Schubert 
15714b72b91aSCy Schubert 		if (policy.req_type == DSCP_POLICY_REQ_ADD)
15724b72b91aSCy Schubert 			wpas_add_dscp_policy(wpa_s, &policy);
15734b72b91aSCy Schubert 		else if (policy.req_type == DSCP_POLICY_REQ_REMOVE)
15744b72b91aSCy Schubert 			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY
15754b72b91aSCy Schubert 				"remove policy_id=%u", policy.policy_id);
15764b72b91aSCy Schubert 		else
15774b72b91aSCy Schubert 			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY
15784b72b91aSCy Schubert 				"reject policy_id=%u", policy.policy_id);
15794b72b91aSCy Schubert 	}
15804b72b91aSCy Schubert 
15814b72b91aSCy Schubert 	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "request_end");
15824b72b91aSCy Schubert }
15834b72b91aSCy Schubert 
15844b72b91aSCy Schubert 
wpas_send_dscp_response(struct wpa_supplicant * wpa_s,struct dscp_resp_data * resp_data)15854b72b91aSCy Schubert int wpas_send_dscp_response(struct wpa_supplicant *wpa_s,
15864b72b91aSCy Schubert 			    struct dscp_resp_data *resp_data)
15874b72b91aSCy Schubert {
15884b72b91aSCy Schubert 	struct wpabuf *buf = NULL;
15894b72b91aSCy Schubert 	size_t buf_len;
15904b72b91aSCy Schubert 	int ret = -1, i;
15914b72b91aSCy Schubert 	u8 resp_control = 0;
15924b72b91aSCy Schubert 
15934b72b91aSCy Schubert 	if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid) {
15944b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
15954b72b91aSCy Schubert 			   "QM: Failed to send DSCP response - not connected to AP");
15964b72b91aSCy Schubert 		return -1;
15974b72b91aSCy Schubert 	}
15984b72b91aSCy Schubert 
15994b72b91aSCy Schubert 	if (resp_data->solicited && !wpa_s->dscp_req_dialog_token) {
16004b72b91aSCy Schubert 		wpa_printf(MSG_ERROR, "QM: No ongoing DSCP request");
16014b72b91aSCy Schubert 		return -1;
16024b72b91aSCy Schubert 	}
16034b72b91aSCy Schubert 
16044b72b91aSCy Schubert 	if (!wpa_s->connection_dscp) {
16054b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
16064b72b91aSCy Schubert 			   "QM: Failed to send DSCP response - DSCP capability not enabled for the current association");
16074b72b91aSCy Schubert 		return -1;
16084b72b91aSCy Schubert 
16094b72b91aSCy Schubert 	}
16104b72b91aSCy Schubert 
16114b72b91aSCy Schubert 	buf_len = 1 +	/* Category */
16124b72b91aSCy Schubert 		  3 +	/* OUI */
16134b72b91aSCy Schubert 		  1 +	/* OUI Type */
16144b72b91aSCy Schubert 		  1 +	/* OUI Subtype */
16154b72b91aSCy Schubert 		  1 +	/* Dialog Token */
16164b72b91aSCy Schubert 		  1 +	/* Response Control */
16174b72b91aSCy Schubert 		  1 +	/* Count */
16184b72b91aSCy Schubert 		  2 * resp_data->num_policies;  /* Status list */
16194b72b91aSCy Schubert 	buf = wpabuf_alloc(buf_len);
16204b72b91aSCy Schubert 	if (!buf) {
16214b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
16224b72b91aSCy Schubert 			   "QM: Failed to allocate DSCP policy response");
16234b72b91aSCy Schubert 		return -1;
16244b72b91aSCy Schubert 	}
16254b72b91aSCy Schubert 
16264b72b91aSCy Schubert 	wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED);
16274b72b91aSCy Schubert 	wpabuf_put_be24(buf, OUI_WFA);
16284b72b91aSCy Schubert 	wpabuf_put_u8(buf, QM_ACTION_OUI_TYPE);
16294b72b91aSCy Schubert 	wpabuf_put_u8(buf, QM_DSCP_POLICY_RESP);
16304b72b91aSCy Schubert 
16314b72b91aSCy Schubert 	wpabuf_put_u8(buf, resp_data->solicited ?
16324b72b91aSCy Schubert 		      wpa_s->dscp_req_dialog_token : 0);
16334b72b91aSCy Schubert 
16344b72b91aSCy Schubert 	if (resp_data->more)
16354b72b91aSCy Schubert 		resp_control |= DSCP_POLICY_CTRL_MORE;
16364b72b91aSCy Schubert 	if (resp_data->reset)
16374b72b91aSCy Schubert 		resp_control |= DSCP_POLICY_CTRL_RESET;
16384b72b91aSCy Schubert 	wpabuf_put_u8(buf, resp_control);
16394b72b91aSCy Schubert 
16404b72b91aSCy Schubert 	wpabuf_put_u8(buf, resp_data->num_policies);
16414b72b91aSCy Schubert 	for (i = 0; i < resp_data->num_policies; i++) {
16424b72b91aSCy Schubert 		wpabuf_put_u8(buf, resp_data->policy[i].id);
16434b72b91aSCy Schubert 		wpabuf_put_u8(buf, resp_data->policy[i].status);
16444b72b91aSCy Schubert 	}
16454b72b91aSCy Schubert 
16464b72b91aSCy Schubert 	wpa_hexdump_buf(MSG_MSGDUMP, "DSCP response frame: ", buf);
16474b72b91aSCy Schubert 	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
16484b72b91aSCy Schubert 				  wpa_s->own_addr, wpa_s->bssid,
16494b72b91aSCy Schubert 				  wpabuf_head(buf), wpabuf_len(buf), 0);
16504b72b91aSCy Schubert 	if (ret < 0) {
16514b72b91aSCy Schubert 		wpa_msg(wpa_s, MSG_INFO, "QM: Failed to send DSCP response");
16524b72b91aSCy Schubert 		goto fail;
16534b72b91aSCy Schubert 	}
16544b72b91aSCy Schubert 
16554b72b91aSCy Schubert 	/*
16564b72b91aSCy Schubert 	 * Mark DSCP request complete whether response sent is solicited or
16574b72b91aSCy Schubert 	 * unsolicited
16584b72b91aSCy Schubert 	 */
16594b72b91aSCy Schubert 	wpa_s->dscp_req_dialog_token = 0;
16604b72b91aSCy Schubert 
16614b72b91aSCy Schubert fail:
16624b72b91aSCy Schubert 	wpabuf_free(buf);
16634b72b91aSCy Schubert 	return ret;
16644b72b91aSCy Schubert }
16654b72b91aSCy Schubert 
16664b72b91aSCy Schubert 
wpas_send_dscp_query(struct wpa_supplicant * wpa_s,const char * domain_name,size_t domain_name_length)16674b72b91aSCy Schubert int wpas_send_dscp_query(struct wpa_supplicant *wpa_s, const char *domain_name,
16684b72b91aSCy Schubert 			 size_t domain_name_length)
16694b72b91aSCy Schubert {
16704b72b91aSCy Schubert 	struct wpabuf *buf = NULL;
16714b72b91aSCy Schubert 	int ret, dscp_query_size;
16724b72b91aSCy Schubert 
16734b72b91aSCy Schubert 	if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid)
16744b72b91aSCy Schubert 		return -1;
16754b72b91aSCy Schubert 
16764b72b91aSCy Schubert 	if (!wpa_s->connection_dscp) {
16774b72b91aSCy Schubert 		wpa_printf(MSG_ERROR,
16784b72b91aSCy Schubert 			   "QM: Failed to send DSCP query - DSCP capability not enabled for the current association");
16794b72b91aSCy Schubert 		return -1;
16804b72b91aSCy Schubert 	}
16814b72b91aSCy Schubert 
16824b72b91aSCy Schubert 	if (wpa_s->wait_for_dscp_req) {
16834b72b91aSCy Schubert 		wpa_printf(MSG_INFO, "QM: Wait until AP sends a DSCP request");
16844b72b91aSCy Schubert 		return -1;
16854b72b91aSCy Schubert 	}
16864b72b91aSCy Schubert 
16874b72b91aSCy Schubert #define DOMAIN_NAME_OFFSET (4 /* OUI */ + 1 /* Attr Id */ + 1 /* Attr len */)
16884b72b91aSCy Schubert 
16894b72b91aSCy Schubert 	if (domain_name_length > 255 - DOMAIN_NAME_OFFSET) {
16904b72b91aSCy Schubert 		wpa_printf(MSG_ERROR, "QM: Too long domain name");
16914b72b91aSCy Schubert 		return -1;
16924b72b91aSCy Schubert 	}
16934b72b91aSCy Schubert 
16944b72b91aSCy Schubert 	dscp_query_size = 1 + /* Category */
16954b72b91aSCy Schubert 			  4 + /* OUI Type */
16964b72b91aSCy Schubert 			  1 + /* OUI subtype */
16974b72b91aSCy Schubert 			  1; /* Dialog Token */
16984b72b91aSCy Schubert 	if (domain_name && domain_name_length)
16994b72b91aSCy Schubert 		dscp_query_size += 1 + /* Element ID */
17004b72b91aSCy Schubert 			1 + /* IE Length */
17014b72b91aSCy Schubert 			DOMAIN_NAME_OFFSET + domain_name_length;
17024b72b91aSCy Schubert 
17034b72b91aSCy Schubert 	buf = wpabuf_alloc(dscp_query_size);
17044b72b91aSCy Schubert 	if (!buf) {
17054b72b91aSCy Schubert 		wpa_printf(MSG_ERROR, "QM: Failed to allocate DSCP query");
17064b72b91aSCy Schubert 		return -1;
17074b72b91aSCy Schubert 	}
17084b72b91aSCy Schubert 
17094b72b91aSCy Schubert 	wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED);
17104b72b91aSCy Schubert 	wpabuf_put_be32(buf, QM_ACTION_VENDOR_TYPE);
17114b72b91aSCy Schubert 	wpabuf_put_u8(buf, QM_DSCP_POLICY_QUERY);
17124b72b91aSCy Schubert 	wpa_s->dscp_query_dialog_token++;
17134b72b91aSCy Schubert 	if (wpa_s->dscp_query_dialog_token == 0)
17144b72b91aSCy Schubert 		wpa_s->dscp_query_dialog_token++;
17154b72b91aSCy Schubert 	wpabuf_put_u8(buf, wpa_s->dscp_query_dialog_token);
17164b72b91aSCy Schubert 
17174b72b91aSCy Schubert 	if (domain_name && domain_name_length) {
17184b72b91aSCy Schubert 		/* Domain Name attribute */
17194b72b91aSCy Schubert 		wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
17204b72b91aSCy Schubert 		wpabuf_put_u8(buf, DOMAIN_NAME_OFFSET + domain_name_length);
17214b72b91aSCy Schubert 		wpabuf_put_be32(buf, QM_IE_VENDOR_TYPE);
17224b72b91aSCy Schubert 		wpabuf_put_u8(buf, QM_ATTR_DOMAIN_NAME);
17234b72b91aSCy Schubert 		wpabuf_put_u8(buf, domain_name_length);
17244b72b91aSCy Schubert 		wpabuf_put_data(buf, domain_name, domain_name_length);
17254b72b91aSCy Schubert 	}
17264b72b91aSCy Schubert #undef DOMAIN_NAME_OFFSET
17274b72b91aSCy Schubert 
17284b72b91aSCy Schubert 	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
17294b72b91aSCy Schubert 				  wpa_s->own_addr, wpa_s->bssid,
17304b72b91aSCy Schubert 				  wpabuf_head(buf), wpabuf_len(buf), 0);
17314b72b91aSCy Schubert 	if (ret < 0) {
17324b72b91aSCy Schubert 		wpa_dbg(wpa_s, MSG_ERROR, "QM: Failed to send DSCP query");
17334b72b91aSCy Schubert 		wpa_s->dscp_query_dialog_token--;
17344b72b91aSCy Schubert 	}
17354b72b91aSCy Schubert 
17364b72b91aSCy Schubert 	wpabuf_free(buf);
17374b72b91aSCy Schubert 	return ret;
17384b72b91aSCy Schubert }
1739