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