blob: 58edd9bc2aa7c1cb1fdc3fe0d04bfc78e5f21c02 [file] [log] [blame]
Hai Shalom899fcc72020-10-19 14:38:18 -07001/*
2 * wpa_supplicant - Robust AV procedures
3 * Copyright (c) 2020, The Linux Foundation
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9#include "utils/includes.h"
10#include "utils/common.h"
Hai Shalomc1a21442022-02-04 13:43:00 -080011#include "utils/eloop.h"
Hai Shalom899fcc72020-10-19 14:38:18 -070012#include "common/wpa_ctrl.h"
13#include "common/ieee802_11_common.h"
14#include "wpa_supplicant_i.h"
15#include "driver_i.h"
16#include "bss.h"
Shivani Baranwal84940f82022-02-02 10:21:47 +053017#include "notify.h"
Hai Shalom899fcc72020-10-19 14:38:18 -070018
19
Hai Shalomc1a21442022-02-04 13:43:00 -080020#define SCS_RESP_TIMEOUT 1
21#define DSCP_REQ_TIMEOUT 5
22
23
Hai Shalom899fcc72020-10-19 14:38:18 -070024void wpas_populate_mscs_descriptor_ie(struct robust_av_data *robust_av,
25 struct wpabuf *buf)
26{
27 u8 *len, *len1;
28
29 /* MSCS descriptor element */
30 wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
31 len = wpabuf_put(buf, 1);
32 wpabuf_put_u8(buf, WLAN_EID_EXT_MSCS_DESCRIPTOR);
33 wpabuf_put_u8(buf, robust_av->request_type);
34 wpabuf_put_u8(buf, robust_av->up_bitmap);
35 wpabuf_put_u8(buf, robust_av->up_limit);
36 wpabuf_put_le32(buf, robust_av->stream_timeout);
37
38 if (robust_av->request_type != SCS_REQ_REMOVE) {
39 /* TCLAS mask element */
40 wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
41 len1 = wpabuf_put(buf, 1);
42 wpabuf_put_u8(buf, WLAN_EID_EXT_TCLAS_MASK);
43
44 /* Frame classifier */
45 wpabuf_put_data(buf, robust_av->frame_classifier,
46 robust_av->frame_classifier_len);
47 *len1 = (u8 *) wpabuf_put(buf, 0) - len1 - 1;
48 }
49
50 *len = (u8 *) wpabuf_put(buf, 0) - len - 1;
51}
52
53
Hai Shalomc1a21442022-02-04 13:43:00 -080054static int wpas_populate_type4_classifier(struct type4_params *type4_param,
55 struct wpabuf *buf)
56{
57 /* classifier parameters */
58 wpabuf_put_u8(buf, type4_param->classifier_mask);
59 if (type4_param->ip_version == IPV4) {
60 wpabuf_put_u8(buf, IPV4); /* IP version */
61 wpabuf_put_data(buf, &type4_param->ip_params.v4.src_ip.s_addr,
62 4);
63 wpabuf_put_data(buf, &type4_param->ip_params.v4.dst_ip.s_addr,
64 4);
65 wpabuf_put_be16(buf, type4_param->ip_params.v4.src_port);
66 wpabuf_put_be16(buf, type4_param->ip_params.v4.dst_port);
67 wpabuf_put_u8(buf, type4_param->ip_params.v4.dscp);
68 wpabuf_put_u8(buf, type4_param->ip_params.v4.protocol);
69 wpabuf_put_u8(buf, 0); /* Reserved octet */
70 } else {
71 wpabuf_put_u8(buf, IPV6);
72 wpabuf_put_data(buf, &type4_param->ip_params.v6.src_ip.s6_addr,
73 16);
74 wpabuf_put_data(buf, &type4_param->ip_params.v6.dst_ip.s6_addr,
75 16);
76 wpabuf_put_be16(buf, type4_param->ip_params.v6.src_port);
77 wpabuf_put_be16(buf, type4_param->ip_params.v6.dst_port);
78 wpabuf_put_u8(buf, type4_param->ip_params.v6.dscp);
79 wpabuf_put_u8(buf, type4_param->ip_params.v6.next_header);
80 wpabuf_put_data(buf, type4_param->ip_params.v6.flow_label, 3);
81 }
82
83 return 0;
84}
85
86
87static int wpas_populate_type10_classifier(struct type10_params *type10_param,
88 struct wpabuf *buf)
89{
90 /* classifier parameters */
91 wpabuf_put_u8(buf, type10_param->prot_instance);
92 wpabuf_put_u8(buf, type10_param->prot_number);
93 wpabuf_put_data(buf, type10_param->filter_value,
94 type10_param->filter_len);
95 wpabuf_put_data(buf, type10_param->filter_mask,
96 type10_param->filter_len);
97 return 0;
98}
99
100
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000101static bool tclas_elem_required(const struct qos_characteristics *qos_elem)
102{
103 if (!qos_elem || !qos_elem->available)
104 return true;
105
106 if (qos_elem->direction == SCS_DIRECTION_DOWN)
107 return true;
108
109 return false;
110}
111
112
Hai Shalomc1a21442022-02-04 13:43:00 -0800113static int wpas_populate_scs_descriptor_ie(struct scs_desc_elem *desc_elem,
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000114 struct wpabuf *buf,
115 bool allow_scs_traffic_desc)
Hai Shalomc1a21442022-02-04 13:43:00 -0800116{
117 u8 *len, *len1;
118 struct tclas_element *tclas_elem;
119 unsigned int i;
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000120 struct qos_characteristics *qos_elem;
121 u32 control_info = 0;
Hai Shalomc1a21442022-02-04 13:43:00 -0800122
123 /* SCS Descriptor element */
124 wpabuf_put_u8(buf, WLAN_EID_SCS_DESCRIPTOR);
125 len = wpabuf_put(buf, 1);
126 wpabuf_put_u8(buf, desc_elem->scs_id);
127 wpabuf_put_u8(buf, desc_elem->request_type);
128 if (desc_elem->request_type == SCS_REQ_REMOVE)
129 goto end;
130
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000131 if (!tclas_elem_required(&desc_elem->qos_char_elem))
132 goto skip_tclas_elem;
133
Hai Shalomc1a21442022-02-04 13:43:00 -0800134 if (desc_elem->intra_access_priority || desc_elem->scs_up_avail) {
135 wpabuf_put_u8(buf, WLAN_EID_INTRA_ACCESS_CATEGORY_PRIORITY);
136 wpabuf_put_u8(buf, 1);
137 wpabuf_put_u8(buf, desc_elem->intra_access_priority);
138 }
139
140 tclas_elem = desc_elem->tclas_elems;
141
142 if (!tclas_elem)
143 return -1;
144
145 for (i = 0; i < desc_elem->num_tclas_elem; i++, tclas_elem++) {
146 int ret;
147
148 /* TCLAS element */
149 wpabuf_put_u8(buf, WLAN_EID_TCLAS);
150 len1 = wpabuf_put(buf, 1);
151 wpabuf_put_u8(buf, 255); /* User Priority: not compared */
152 /* Frame Classifier */
153 wpabuf_put_u8(buf, tclas_elem->classifier_type);
154 /* Frame classifier parameters */
155 switch (tclas_elem->classifier_type) {
156 case 4:
157 ret = wpas_populate_type4_classifier(
158 &tclas_elem->frame_classifier.type4_param,
159 buf);
160 break;
161 case 10:
162 ret = wpas_populate_type10_classifier(
163 &tclas_elem->frame_classifier.type10_param,
164 buf);
165 break;
166 default:
167 return -1;
168 }
169
170 if (ret == -1) {
171 wpa_printf(MSG_ERROR,
172 "Failed to populate frame classifier");
173 return -1;
174 }
175
176 *len1 = (u8 *) wpabuf_put(buf, 0) - len1 - 1;
177 }
178
179 if (desc_elem->num_tclas_elem > 1) {
180 /* TCLAS Processing element */
181 wpabuf_put_u8(buf, WLAN_EID_TCLAS_PROCESSING);
182 wpabuf_put_u8(buf, 1);
183 wpabuf_put_u8(buf, desc_elem->tclas_processing);
184 }
185
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000186skip_tclas_elem:
187 if (allow_scs_traffic_desc && desc_elem->qos_char_elem.available) {
188 qos_elem = &desc_elem->qos_char_elem;
189 /* Element ID, Length, and Element ID Extension */
190 wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
191 len1 = wpabuf_put(buf, 1);
192 wpabuf_put_u8(buf, WLAN_EID_EXT_QOS_CHARACTERISTICS);
193
194 /* Remove invalid mask bits */
195
196 /* Medium Time is applicable only for direct link */
197 if ((qos_elem->mask & SCS_QOS_BIT_MEDIUM_TIME) &&
198 qos_elem->direction != SCS_DIRECTION_DIRECT)
199 qos_elem->mask &= ~SCS_QOS_BIT_MEDIUM_TIME;
200
201 /* Service Start Time LinkID is valid only when Service Start
202 * Time is present.
203 */
204 if ((qos_elem->mask & SCS_QOS_BIT_SERVICE_START_TIME_LINKID) &&
205 !(qos_elem->mask & SCS_QOS_BIT_SERVICE_START_TIME))
206 qos_elem->mask &=
207 ~SCS_QOS_BIT_SERVICE_START_TIME_LINKID;
208
209 /* IEEE P802.11be/D4.0, 9.4.2.316 QoS Characteristics element,
210 * Figure 9-1001av (Control Info field format)
211 */
212 control_info = ((u32) qos_elem->direction <<
213 EHT_QOS_CONTROL_INFO_DIRECTION_OFFSET);
214 control_info |= ((u32) desc_elem->intra_access_priority <<
215 EHT_QOS_CONTROL_INFO_TID_OFFSET);
216 control_info |= ((u32) desc_elem->intra_access_priority <<
217 EHT_QOS_CONTROL_INFO_USER_PRIORITY_OFFSET);
218 control_info |= ((u32) qos_elem->mask <<
219 EHT_QOS_CONTROL_INFO_PRESENCE_MASK_OFFSET);
220
221 /* Control Info */
222 wpabuf_put_le32(buf, control_info);
223 /* Minimum Service Interval */
224 wpabuf_put_le32(buf, qos_elem->min_si);
225 /* Maximum Service Interval */
226 wpabuf_put_le32(buf, qos_elem->max_si);
227 /* Minimum Data Rate */
228 wpabuf_put_le24(buf, qos_elem->min_data_rate);
229 /* Delay Bound */
230 wpabuf_put_le24(buf, qos_elem->delay_bound);
231
232 /* Maximum MSDU Size */
233 if (qos_elem->mask & SCS_QOS_BIT_MAX_MSDU_SIZE)
234 wpabuf_put_le16(buf, qos_elem->max_msdu_size);
235 /* Start Service Time */
236 if (qos_elem->mask & SCS_QOS_BIT_SERVICE_START_TIME)
237 wpabuf_put_le32(buf, qos_elem->service_start_time);
238 /* Service Start Time LinkID */
239 if (qos_elem->mask & SCS_QOS_BIT_SERVICE_START_TIME_LINKID)
240 wpabuf_put_u8(buf,
241 qos_elem->service_start_time_link_id);
242 /* Mean Data Rate */
243 if (qos_elem->mask & SCS_QOS_BIT_MEAN_DATA_RATE)
244 wpabuf_put_le24(buf, qos_elem->mean_data_rate);
245 /* Delayed Bounded Burst Size */
246 if (qos_elem->mask & SCS_QOS_BIT_DELAYED_BOUNDED_BURST_SIZE)
247 wpabuf_put_le32(buf, qos_elem->burst_size);
248 /* MSDU Lifetime */
249 if (qos_elem->mask & SCS_QOS_BIT_MSDU_LIFETIME)
250 wpabuf_put_le16(buf, qos_elem->msdu_lifetime);
251 /* MSDU Delivery Info */
252 if (qos_elem->mask & SCS_QOS_BIT_MSDU_DELIVERY_INFO)
253 wpabuf_put_u8(buf, qos_elem->msdu_delivery_info);
254 /* Medium Time */
255 if (qos_elem->mask & SCS_QOS_BIT_MEDIUM_TIME)
256 wpabuf_put_le16(buf, qos_elem->medium_time);
257
258 *len1 = (u8 *) wpabuf_put(buf, 0) - len1 - 1;
259 }
260
Hai Shalomc1a21442022-02-04 13:43:00 -0800261end:
262 *len = (u8 *) wpabuf_put(buf, 0) - len - 1;
263 return 0;
264}
265
266
Hai Shalom899fcc72020-10-19 14:38:18 -0700267int wpas_send_mscs_req(struct wpa_supplicant *wpa_s)
268{
269 struct wpabuf *buf;
Hai Shalom899fcc72020-10-19 14:38:18 -0700270 size_t buf_len;
271 int ret;
272
273 if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid)
274 return 0;
275
Hai Shalom60840252021-02-19 19:02:11 -0800276 if (!wpa_bss_ext_capab(wpa_s->current_bss, WLAN_EXT_CAPAB_MSCS)) {
Hai Shalom899fcc72020-10-19 14:38:18 -0700277 wpa_dbg(wpa_s, MSG_INFO,
278 "AP does not support MSCS - could not send MSCS Req");
279 return -1;
280 }
281
282 if (!wpa_s->mscs_setup_done &&
283 wpa_s->robust_av.request_type != SCS_REQ_ADD) {
284 wpa_msg(wpa_s, MSG_INFO,
285 "MSCS: Failed to send MSCS Request: request type invalid");
286 return -1;
287 }
288
289 buf_len = 3 + /* Action frame header */
290 3 + /* MSCS descriptor IE header */
291 1 + /* Request type */
292 2 + /* User priority control */
293 4 + /* Stream timeout */
294 3 + /* TCLAS Mask IE header */
295 wpa_s->robust_av.frame_classifier_len;
296
297 buf = wpabuf_alloc(buf_len);
298 if (!buf) {
299 wpa_printf(MSG_ERROR, "Failed to allocate MSCS req");
300 return -1;
301 }
302
303 wpabuf_put_u8(buf, WLAN_ACTION_ROBUST_AV_STREAMING);
304 wpabuf_put_u8(buf, ROBUST_AV_MSCS_REQ);
305 wpa_s->robust_av.dialog_token++;
306 wpabuf_put_u8(buf, wpa_s->robust_av.dialog_token);
307
308 /* MSCS descriptor element */
309 wpas_populate_mscs_descriptor_ie(&wpa_s->robust_av, buf);
310
311 wpa_hexdump_buf(MSG_MSGDUMP, "MSCS Request", buf);
312 ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
313 wpa_s->own_addr, wpa_s->bssid,
314 wpabuf_head(buf), wpabuf_len(buf), 0);
315 if (ret < 0)
316 wpa_dbg(wpa_s, MSG_INFO, "MSCS: Failed to send MSCS Request");
317
318 wpabuf_free(buf);
319 return ret;
320}
321
322
Hai Shalomc1a21442022-02-04 13:43:00 -0800323static size_t tclas_elem_len(const struct tclas_element *elem)
324{
325 size_t buf_len = 0;
326
327 buf_len += 2 + /* TCLAS element header */
328 1 + /* User Priority */
329 1 ; /* Classifier Type */
330
331 if (elem->classifier_type == 4) {
332 enum ip_version ip_ver;
333
334 buf_len += 1 + /* Classifier mask */
335 1 + /* IP version */
336 1 + /* user priority */
337 2 + /* src_port */
338 2 + /* dst_port */
339 1 ; /* dscp */
340 ip_ver = elem->frame_classifier.type4_param.ip_version;
341 if (ip_ver == IPV4) {
342 buf_len += 4 + /* src_ip */
343 4 + /* dst_ip */
344 1 + /* protocol */
345 1 ; /* Reserved */
346 } else if (ip_ver == IPV6) {
347 buf_len += 16 + /* src_ip */
348 16 + /* dst_ip */
349 1 + /* next_header */
350 3 ; /* flow_label */
351 } else {
352 wpa_printf(MSG_ERROR, "%s: Incorrect IP version %d",
353 __func__, ip_ver);
354 return 0;
355 }
356 } else if (elem->classifier_type == 10) {
357 buf_len += 1 + /* protocol instance */
358 1 + /* protocol number */
359 2 * elem->frame_classifier.type10_param.filter_len;
360 } else {
361 wpa_printf(MSG_ERROR, "%s: Incorrect classifier type %u",
362 __func__, elem->classifier_type);
363 return 0;
364 }
365
366 return buf_len;
367}
368
369
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000370static size_t qos_char_len(const struct qos_characteristics *qos_elem)
371{
372 size_t buf_len = 0;
373
374 buf_len += 1 + /* Element ID */
375 1 + /* Length */
376 1 + /* Element ID Extension */
377 4 + /* Control Info */
378 4 + /* Minimum Service Interval */
379 4 + /* Maximum Service Interval */
380 3 + /* Minimum Data Rate */
381 3; /* Delay Bound */
382
383 if (qos_elem->mask & SCS_QOS_BIT_MAX_MSDU_SIZE)
384 buf_len += 2; /* Maximum MSDU Size */
385
386 if (qos_elem->mask & SCS_QOS_BIT_SERVICE_START_TIME) {
387 buf_len += 4; /* Service Start Time */
388 if (qos_elem->mask & SCS_QOS_BIT_SERVICE_START_TIME_LINKID)
389 buf_len++; /* Service Start Time LinkID */
390 }
391
392 if (qos_elem->mask & SCS_QOS_BIT_MEAN_DATA_RATE)
393 buf_len += 3; /* Mean Data Rate */
394
395 if (qos_elem->mask & SCS_QOS_BIT_DELAYED_BOUNDED_BURST_SIZE)
396 buf_len += 4; /* Delayed Bounded Burst Size */
397
398 if (qos_elem->mask & SCS_QOS_BIT_MSDU_LIFETIME)
399 buf_len += 2; /* MSDU Lifetime */
400
401 if (qos_elem->mask & SCS_QOS_BIT_MSDU_DELIVERY_INFO)
402 buf_len++; /* MSDU Delivery Info */
403
404 if (qos_elem->mask & SCS_QOS_BIT_MEDIUM_TIME &&
405 qos_elem->direction == SCS_DIRECTION_DIRECT)
406 buf_len += 2; /* Medium Time */
407
408 return buf_len;
409}
410
411
Hai Shalomc1a21442022-02-04 13:43:00 -0800412static struct wpabuf * allocate_scs_buf(struct scs_desc_elem *desc_elem,
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000413 unsigned int num_scs_desc,
414 bool allow_scs_traffic_desc)
Hai Shalomc1a21442022-02-04 13:43:00 -0800415{
416 struct wpabuf *buf;
417 size_t buf_len = 0;
418 unsigned int i, j;
419
420 buf_len = 3; /* Action frame header */
421
422 for (i = 0; i < num_scs_desc; i++, desc_elem++) {
423 struct tclas_element *tclas_elem;
424
425 buf_len += 2 + /* SCS descriptor IE header */
426 1 + /* SCSID */
427 1 ; /* Request type */
428
429 if (desc_elem->request_type == SCS_REQ_REMOVE)
430 continue;
431
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000432 if (allow_scs_traffic_desc &&
433 desc_elem->qos_char_elem.available)
434 buf_len += qos_char_len(&desc_elem->qos_char_elem);
435
436 if (!tclas_elem_required(&desc_elem->qos_char_elem))
437 continue;
438
Hai Shalomc1a21442022-02-04 13:43:00 -0800439 if (desc_elem->intra_access_priority || desc_elem->scs_up_avail)
440 buf_len += 3;
441
442 tclas_elem = desc_elem->tclas_elems;
443 if (!tclas_elem) {
444 wpa_printf(MSG_ERROR, "%s: TCLAS element null",
445 __func__);
446 return NULL;
447 }
448
449 for (j = 0; j < desc_elem->num_tclas_elem; j++, tclas_elem++) {
450 size_t elen;
451
452 elen = tclas_elem_len(tclas_elem);
453 if (elen == 0)
454 return NULL;
455 buf_len += elen;
456 }
457
458 if (desc_elem->num_tclas_elem > 1) {
459 buf_len += 1 + /* TCLAS Processing eid */
460 1 + /* length */
461 1 ; /* processing */
462 }
463 }
464
465 buf = wpabuf_alloc(buf_len);
466 if (!buf) {
467 wpa_printf(MSG_ERROR, "Failed to allocate SCS req");
468 return NULL;
469 }
470
471 return buf;
472}
473
474
475static void scs_request_timer(void *eloop_ctx, void *timeout_ctx)
476{
477 struct wpa_supplicant *wpa_s = eloop_ctx;
478 struct active_scs_elem *scs_desc, *prev;
479
480 if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid)
481 return;
482
483 /* Once timeout is over, remove all SCS descriptors with no response */
484 dl_list_for_each_safe(scs_desc, prev, &wpa_s->active_scs_ids,
485 struct active_scs_elem, list) {
486 u8 bssid[ETH_ALEN] = { 0 };
487 const u8 *src;
488
489 if (scs_desc->status == SCS_DESC_SUCCESS)
490 continue;
491
492 if (wpa_s->current_bss)
493 src = wpa_s->current_bss->bssid;
494 else
495 src = bssid;
496
497 wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCS_RESULT "bssid=" MACSTR
498 " SCSID=%u status_code=timedout", MAC2STR(src),
499 scs_desc->scs_id);
500
501 dl_list_del(&scs_desc->list);
502 wpa_printf(MSG_INFO, "%s: SCSID %d removed after timeout",
503 __func__, scs_desc->scs_id);
504 os_free(scs_desc);
505 }
506
507 eloop_cancel_timeout(scs_request_timer, wpa_s, NULL);
508 wpa_s->ongoing_scs_req = false;
509}
510
511
512int wpas_send_scs_req(struct wpa_supplicant *wpa_s)
513{
514 struct wpabuf *buf = NULL;
515 struct scs_desc_elem *desc_elem = NULL;
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000516 const struct ieee80211_eht_capabilities *eht;
517 const u8 *eht_ie;
Hai Shalomc1a21442022-02-04 13:43:00 -0800518 int ret = -1;
519 unsigned int i;
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000520 bool allow_scs_traffic_desc = false;
Hai Shalomc1a21442022-02-04 13:43:00 -0800521
522 if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid)
523 return -1;
524
525 if (!wpa_bss_ext_capab(wpa_s->current_bss, WLAN_EXT_CAPAB_SCS)) {
526 wpa_dbg(wpa_s, MSG_INFO,
527 "AP does not support SCS - could not send SCS Request");
528 return -1;
529 }
530
531 desc_elem = wpa_s->scs_robust_av_req.scs_desc_elems;
532 if (!desc_elem)
533 return -1;
534
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000535 if (wpa_is_non_eht_scs_traffic_desc_supported(wpa_s->current_bss))
536 allow_scs_traffic_desc = true;
537
538 /* Allow SCS Traffic descriptor support for EHT connection */
539 eht_ie = wpa_bss_get_ie_ext(wpa_s->current_bss,
540 WLAN_EID_EXT_EHT_CAPABILITIES);
541 if (wpa_s->connection_eht && eht_ie &&
542 eht_ie[1] >= 1 + IEEE80211_EHT_CAPAB_MIN_LEN) {
543 eht = (const struct ieee80211_eht_capabilities *) &eht_ie[3];
544 if (eht->mac_cap & EHT_MACCAP_SCS_TRAFFIC_DESC)
545 allow_scs_traffic_desc = true;
546 }
547
548 if (!allow_scs_traffic_desc && desc_elem->qos_char_elem.available) {
549 wpa_dbg(wpa_s, MSG_INFO,
550 "Connection does not support EHT/non-EHT SCS Traffic Description - could not send SCS Request with QoS Characteristics");
551 return -1;
552 }
553
Hai Shalomc1a21442022-02-04 13:43:00 -0800554 buf = allocate_scs_buf(desc_elem,
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000555 wpa_s->scs_robust_av_req.num_scs_desc,
556 allow_scs_traffic_desc);
Hai Shalomc1a21442022-02-04 13:43:00 -0800557 if (!buf)
558 return -1;
559
560 wpabuf_put_u8(buf, WLAN_ACTION_ROBUST_AV_STREAMING);
561 wpabuf_put_u8(buf, ROBUST_AV_SCS_REQ);
562 wpa_s->scs_dialog_token++;
563 if (wpa_s->scs_dialog_token == 0)
564 wpa_s->scs_dialog_token++;
565 wpabuf_put_u8(buf, wpa_s->scs_dialog_token);
566
567 for (i = 0; i < wpa_s->scs_robust_av_req.num_scs_desc;
568 i++, desc_elem++) {
569 /* SCS Descriptor element */
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000570 if (wpas_populate_scs_descriptor_ie(desc_elem, buf,
571 allow_scs_traffic_desc) < 0)
Hai Shalomc1a21442022-02-04 13:43:00 -0800572 goto end;
573 }
574
575 wpa_hexdump_buf(MSG_DEBUG, "SCS Request", buf);
576 ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
577 wpa_s->own_addr, wpa_s->bssid,
578 wpabuf_head(buf), wpabuf_len(buf), 0);
579 if (ret < 0) {
580 wpa_dbg(wpa_s, MSG_ERROR, "SCS: Failed to send SCS Request");
581 wpa_s->scs_dialog_token--;
582 goto end;
583 }
584
585 desc_elem = wpa_s->scs_robust_av_req.scs_desc_elems;
586 for (i = 0; i < wpa_s->scs_robust_av_req.num_scs_desc;
587 i++, desc_elem++) {
588 struct active_scs_elem *active_scs_elem;
589
590 if (desc_elem->request_type != SCS_REQ_ADD)
591 continue;
592
593 active_scs_elem = os_malloc(sizeof(struct active_scs_elem));
594 if (!active_scs_elem)
595 break;
596 active_scs_elem->scs_id = desc_elem->scs_id;
597 active_scs_elem->status = SCS_DESC_SENT;
598 dl_list_add(&wpa_s->active_scs_ids, &active_scs_elem->list);
599 }
600
601 /*
602 * Register a timeout after which this request will be removed from
603 * the cache.
604 */
605 eloop_register_timeout(SCS_RESP_TIMEOUT, 0, scs_request_timer, wpa_s,
606 NULL);
607 wpa_s->ongoing_scs_req = true;
608
609end:
610 wpabuf_free(buf);
611 free_up_scs_desc(&wpa_s->scs_robust_av_req);
612
613 return ret;
614}
615
616
617void free_up_tclas_elem(struct scs_desc_elem *elem)
618{
619 struct tclas_element *tclas_elems = elem->tclas_elems;
620 unsigned int num_tclas_elem = elem->num_tclas_elem;
621 struct tclas_element *tclas_data;
622 unsigned int j;
623
624 elem->tclas_elems = NULL;
625 elem->num_tclas_elem = 0;
626
627 if (!tclas_elems)
628 return;
629
630 tclas_data = tclas_elems;
631 for (j = 0; j < num_tclas_elem; j++, tclas_data++) {
632 if (tclas_data->classifier_type != 10)
633 continue;
634
635 os_free(tclas_data->frame_classifier.type10_param.filter_value);
636 os_free(tclas_data->frame_classifier.type10_param.filter_mask);
637 }
638
639 os_free(tclas_elems);
640}
641
642
643void free_up_scs_desc(struct scs_robust_av_data *data)
644{
645 struct scs_desc_elem *desc_elems = data->scs_desc_elems;
646 unsigned int num_scs_desc = data->num_scs_desc;
647 struct scs_desc_elem *desc_data;
648 unsigned int i;
649
650 data->scs_desc_elems = NULL;
651 data->num_scs_desc = 0;
652
653 if (!desc_elems)
654 return;
655
656 desc_data = desc_elems;
657 for (i = 0; i < num_scs_desc; i++, desc_data++) {
658 if (desc_data->request_type == SCS_REQ_REMOVE ||
659 !desc_data->tclas_elems)
660 continue;
661
662 free_up_tclas_elem(desc_data);
663 }
664 os_free(desc_elems);
665}
666
667
Hai Shalom899fcc72020-10-19 14:38:18 -0700668void wpas_handle_robust_av_recv_action(struct wpa_supplicant *wpa_s,
669 const u8 *src, const u8 *buf, size_t len)
670{
671 u8 dialog_token;
672 u16 status_code;
673
674 if (len < 3)
675 return;
676
677 dialog_token = *buf++;
678 if (dialog_token != wpa_s->robust_av.dialog_token) {
679 wpa_printf(MSG_INFO,
680 "MSCS: Drop received frame due to dialog token mismatch: received:%u expected:%u",
681 dialog_token, wpa_s->robust_av.dialog_token);
682 return;
683 }
684
Hai Shalomc1a21442022-02-04 13:43:00 -0800685 status_code = WPA_GET_LE16(buf);
Hai Shalom899fcc72020-10-19 14:38:18 -0700686 wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_MSCS_RESULT "bssid=" MACSTR
687 " status_code=%u", MAC2STR(src), status_code);
688 wpa_s->mscs_setup_done = status_code == WLAN_STATUS_SUCCESS;
689}
690
691
692void wpas_handle_assoc_resp_mscs(struct wpa_supplicant *wpa_s, const u8 *bssid,
693 const u8 *ies, size_t ies_len)
694{
695 const u8 *mscs_desc_ie, *mscs_status;
696 u16 status;
697
698 /* Process optional MSCS Status subelement when MSCS IE is in
699 * (Re)Association Response frame */
700 if (!ies || ies_len == 0 || !wpa_s->robust_av.valid_config)
701 return;
702
703 mscs_desc_ie = get_ie_ext(ies, ies_len, WLAN_EID_EXT_MSCS_DESCRIPTOR);
704 if (!mscs_desc_ie || mscs_desc_ie[1] <= 8)
705 return;
706
707 /* Subelements start after (ie_id(1) + ie_len(1) + ext_id(1) +
708 * request type(1) + upc(2) + stream timeout(4) =) 10.
709 */
710 mscs_status = get_ie(&mscs_desc_ie[10], mscs_desc_ie[1] - 8,
711 MCSC_SUBELEM_STATUS);
712 if (!mscs_status || mscs_status[1] < 2)
713 return;
714
715 status = WPA_GET_LE16(mscs_status + 2);
716 wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_MSCS_RESULT "bssid=" MACSTR
717 " status_code=%u", MAC2STR(bssid), status);
718 wpa_s->mscs_setup_done = status == WLAN_STATUS_SUCCESS;
719}
Hai Shalomc1a21442022-02-04 13:43:00 -0800720
721
722static void wpas_wait_for_dscp_req_timer(void *eloop_ctx, void *timeout_ctx)
723{
724 struct wpa_supplicant *wpa_s = eloop_ctx;
725
726 /* Once timeout is over, reset wait flag and allow sending DSCP query */
727 wpa_printf(MSG_DEBUG,
728 "QM: Wait time over for sending DSCP request - allow DSCP query");
729 wpa_s->wait_for_dscp_req = 0;
730 wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "request_wait end");
731}
732
733
734void wpas_handle_assoc_resp_qos_mgmt(struct wpa_supplicant *wpa_s,
735 const u8 *ies, size_t ies_len)
736{
737 const u8 *wfa_capa;
738
739 wpa_s->connection_dscp = 0;
740 if (wpa_s->wait_for_dscp_req)
741 eloop_cancel_timeout(wpas_wait_for_dscp_req_timer, wpa_s, NULL);
742
743 if (!ies || ies_len == 0 || !wpa_s->enable_dscp_policy_capa)
744 return;
745
746 wfa_capa = get_vendor_ie(ies, ies_len, WFA_CAPA_IE_VENDOR_TYPE);
747 if (!wfa_capa || wfa_capa[1] < 6 || wfa_capa[6] < 1 ||
748 !(wfa_capa[7] & WFA_CAPA_QM_DSCP_POLICY))
749 return; /* AP does not enable QM DSCP Policy */
750
751 wpa_s->connection_dscp = 1;
752 wpa_s->wait_for_dscp_req = !!(wfa_capa[7] &
753 WFA_CAPA_QM_UNSOLIC_DSCP);
754 if (!wpa_s->wait_for_dscp_req)
755 return;
756
757 /* Register a timeout after which dscp query can be sent to AP. */
758 wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "request_wait start");
759 eloop_register_timeout(DSCP_REQ_TIMEOUT, 0,
760 wpas_wait_for_dscp_req_timer, wpa_s, NULL);
761}
762
763
764void wpas_handle_robust_av_scs_recv_action(struct wpa_supplicant *wpa_s,
765 const u8 *src, const u8 *buf,
766 size_t len)
767{
768 u8 dialog_token;
Swarn Singhc450e7b2023-03-28 17:36:22 +0530769 unsigned int i, count, num_active_scs, j = 0;
Hai Shalomc1a21442022-02-04 13:43:00 -0800770 struct active_scs_elem *scs_desc, *prev;
Swarn Singhc450e7b2023-03-28 17:36:22 +0530771 int *scs_resp[2];
Hai Shalomc1a21442022-02-04 13:43:00 -0800772
773 if (len < 2)
774 return;
775 if (!wpa_s->ongoing_scs_req) {
776 wpa_printf(MSG_INFO,
777 "SCS: Drop received response due to no ongoing request");
778 return;
779 }
780
781 dialog_token = *buf++;
782 len--;
783 if (dialog_token != wpa_s->scs_dialog_token) {
784 wpa_printf(MSG_INFO,
785 "SCS: Drop received frame due to dialog token mismatch: received:%u expected:%u",
786 dialog_token, wpa_s->scs_dialog_token);
787 return;
788 }
789
790 /* This Count field does not exist in the IEEE Std 802.11-2020
791 * definition of the SCS Response frame. However, it was accepted to
792 * be added into REVme per REVme/D0.0 CC35 CID 49 (edits in document
793 * 11-21-0688-07). */
794 count = *buf++;
795 len--;
796 if (count == 0 || count * 3 > len) {
797 wpa_printf(MSG_INFO,
798 "SCS: Drop received frame due to invalid count: %u (remaining %zu octets)",
799 count, len);
800 return;
801 }
802
Swarn Singhc450e7b2023-03-28 17:36:22 +0530803 num_active_scs = dl_list_len(&wpa_s->active_scs_ids);
804 if (num_active_scs < count) {
805 wpa_printf(MSG_ERROR, "Unexpected number of SCS responses."
806 " Expected < %d, received %d", num_active_scs, count);
807 return;
808 }
809
810 scs_resp[0] = (int *) os_zalloc(num_active_scs);
811 if (!scs_resp[0]) {
812 wpa_printf(MSG_ERROR, "Failed to allocate memory for scs_resp");
813 return;
814 }
815
816 scs_resp[1] = (int *) os_zalloc(num_active_scs);
817 if (!scs_resp[1]) {
818 os_free(scs_resp[0]);
819 wpa_printf(MSG_ERROR, "Failed to allocate memory for scs_resp");
820 return;
821 }
822
Hai Shalomc1a21442022-02-04 13:43:00 -0800823 for (i = 0; i < count; i++) {
824 u8 id;
825 u16 status;
826 bool scs_desc_found = false;
827
828 id = *buf++;
829 status = WPA_GET_LE16(buf);
830 buf += 2;
831 len -= 3;
832
833 dl_list_for_each(scs_desc, &wpa_s->active_scs_ids,
834 struct active_scs_elem, list) {
835 if (id == scs_desc->scs_id) {
836 scs_desc_found = true;
837 break;
838 }
839 }
840
841 if (!scs_desc_found) {
842 wpa_printf(MSG_INFO, "SCS: SCS ID invalid %u", id);
843 continue;
844 }
845
846 if (status != WLAN_STATUS_SUCCESS) {
847 dl_list_del(&scs_desc->list);
848 os_free(scs_desc);
849 } else if (status == WLAN_STATUS_SUCCESS) {
850 scs_desc->status = SCS_DESC_SUCCESS;
851 }
852
853 wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCS_RESULT "bssid=" MACSTR
854 " SCSID=%u status_code=%u", MAC2STR(src), id, status);
Swarn Singhc450e7b2023-03-28 17:36:22 +0530855 scs_resp[0][j] = id;
856 scs_resp[1][j++] = status;
Hai Shalomc1a21442022-02-04 13:43:00 -0800857 }
858
859 eloop_cancel_timeout(scs_request_timer, wpa_s, NULL);
860 wpa_s->ongoing_scs_req = false;
861
862 dl_list_for_each_safe(scs_desc, prev, &wpa_s->active_scs_ids,
863 struct active_scs_elem, list) {
864 if (scs_desc->status != SCS_DESC_SUCCESS) {
865 wpa_msg(wpa_s, MSG_INFO,
866 WPA_EVENT_SCS_RESULT "bssid=" MACSTR
867 " SCSID=%u status_code=response_not_received",
868 MAC2STR(src), scs_desc->scs_id);
Swarn Singhc450e7b2023-03-28 17:36:22 +0530869 if (j < num_active_scs) {
870 scs_resp[0][j] = scs_desc->scs_id;
871 scs_resp[1][j++] = -1; /* TIMEOUT indicator for AIDL */
872 }
Hai Shalomc1a21442022-02-04 13:43:00 -0800873 dl_list_del(&scs_desc->list);
874 os_free(scs_desc);
875 }
876 }
Swarn Singhc450e7b2023-03-28 17:36:22 +0530877 wpas_notify_qos_policy_scs_response(wpa_s, j, scs_resp);
878 os_free(scs_resp[0]);
879 os_free(scs_resp[1]);
Hai Shalomc1a21442022-02-04 13:43:00 -0800880}
881
882
883static void wpas_clear_active_scs_ids(struct wpa_supplicant *wpa_s)
884{
885 struct active_scs_elem *scs_elem;
886
887 while ((scs_elem = dl_list_first(&wpa_s->active_scs_ids,
888 struct active_scs_elem, list))) {
889 dl_list_del(&scs_elem->list);
890 os_free(scs_elem);
891 }
892}
893
894
895void wpas_scs_deinit(struct wpa_supplicant *wpa_s)
896{
897 free_up_scs_desc(&wpa_s->scs_robust_av_req);
898 wpa_s->scs_dialog_token = 0;
899 wpas_clear_active_scs_ids(wpa_s);
900 eloop_cancel_timeout(scs_request_timer, wpa_s, NULL);
901 wpa_s->ongoing_scs_req = false;
902}
903
904
905static int write_ipv4_info(char *pos, int total_len,
Sunil Ravi89eba102022-09-13 21:04:37 -0700906 const struct ipv4_params *v4,
907 u8 classifier_mask)
Hai Shalomc1a21442022-02-04 13:43:00 -0800908{
909 int res, rem_len;
910 char addr[INET_ADDRSTRLEN];
911
912 rem_len = total_len;
913
Sunil Ravi89eba102022-09-13 21:04:37 -0700914 if (classifier_mask & BIT(1)) {
Hai Shalomc1a21442022-02-04 13:43:00 -0800915 if (!inet_ntop(AF_INET, &v4->src_ip, addr, INET_ADDRSTRLEN)) {
916 wpa_printf(MSG_ERROR,
917 "QM: Failed to set IPv4 source address");
918 return -1;
919 }
920
921 res = os_snprintf(pos, rem_len, " src_ip=%s", addr);
922 if (os_snprintf_error(rem_len, res))
923 return -1;
924
925 pos += res;
926 rem_len -= res;
927 }
928
Sunil Ravi89eba102022-09-13 21:04:37 -0700929 if (classifier_mask & BIT(2)) {
Hai Shalomc1a21442022-02-04 13:43:00 -0800930 if (!inet_ntop(AF_INET, &v4->dst_ip, addr, INET_ADDRSTRLEN)) {
931 wpa_printf(MSG_ERROR,
932 "QM: Failed to set IPv4 destination address");
933 return -1;
934 }
935
936 res = os_snprintf(pos, rem_len, " dst_ip=%s", addr);
937 if (os_snprintf_error(rem_len, res))
938 return -1;
939
940 pos += res;
941 rem_len -= res;
942 }
943
Sunil Ravi89eba102022-09-13 21:04:37 -0700944 if (classifier_mask & BIT(3)) {
Hai Shalomc1a21442022-02-04 13:43:00 -0800945 res = os_snprintf(pos, rem_len, " src_port=%d", v4->src_port);
946 if (os_snprintf_error(rem_len, res))
947 return -1;
948
949 pos += res;
950 rem_len -= res;
951 }
952
Sunil Ravi89eba102022-09-13 21:04:37 -0700953 if (classifier_mask & BIT(4)) {
Hai Shalomc1a21442022-02-04 13:43:00 -0800954 res = os_snprintf(pos, rem_len, " dst_port=%d", v4->dst_port);
955 if (os_snprintf_error(rem_len, res))
956 return -1;
957
958 pos += res;
959 rem_len -= res;
960 }
961
Sunil Ravi89eba102022-09-13 21:04:37 -0700962 if (classifier_mask & BIT(6)) {
Hai Shalomc1a21442022-02-04 13:43:00 -0800963 res = os_snprintf(pos, rem_len, " protocol=%d", v4->protocol);
964 if (os_snprintf_error(rem_len, res))
965 return -1;
966
967 pos += res;
968 rem_len -= res;
969 }
970
971 return total_len - rem_len;
972}
973
974
975static int write_ipv6_info(char *pos, int total_len,
Sunil Ravi89eba102022-09-13 21:04:37 -0700976 const struct ipv6_params *v6,
977 u8 classifier_mask)
Hai Shalomc1a21442022-02-04 13:43:00 -0800978{
979 int res, rem_len;
980 char addr[INET6_ADDRSTRLEN];
981
982 rem_len = total_len;
983
Sunil Ravi89eba102022-09-13 21:04:37 -0700984 if (classifier_mask & BIT(1)) {
Hai Shalomc1a21442022-02-04 13:43:00 -0800985 if (!inet_ntop(AF_INET6, &v6->src_ip, addr, INET6_ADDRSTRLEN)) {
986 wpa_printf(MSG_ERROR,
987 "QM: Failed to set IPv6 source addr");
988 return -1;
989 }
990
991 res = os_snprintf(pos, rem_len, " src_ip=%s", addr);
992 if (os_snprintf_error(rem_len, res))
993 return -1;
994
995 pos += res;
996 rem_len -= res;
997 }
998
Sunil Ravi89eba102022-09-13 21:04:37 -0700999 if (classifier_mask & BIT(2)) {
Hai Shalomc1a21442022-02-04 13:43:00 -08001000 if (!inet_ntop(AF_INET6, &v6->dst_ip, addr, INET6_ADDRSTRLEN)) {
1001 wpa_printf(MSG_ERROR,
1002 "QM: Failed to set IPv6 destination addr");
1003 return -1;
1004 }
1005
1006 res = os_snprintf(pos, rem_len, " dst_ip=%s", addr);
1007 if (os_snprintf_error(rem_len, res))
1008 return -1;
1009
1010 pos += res;
1011 rem_len -= res;
1012 }
1013
Sunil Ravi89eba102022-09-13 21:04:37 -07001014 if (classifier_mask & BIT(3)) {
Hai Shalomc1a21442022-02-04 13:43:00 -08001015 res = os_snprintf(pos, rem_len, " src_port=%d", v6->src_port);
1016 if (os_snprintf_error(rem_len, res))
1017 return -1;
1018
1019 pos += res;
1020 rem_len -= res;
1021 }
1022
Sunil Ravi89eba102022-09-13 21:04:37 -07001023 if (classifier_mask & BIT(4)) {
Hai Shalomc1a21442022-02-04 13:43:00 -08001024 res = os_snprintf(pos, rem_len, " dst_port=%d", v6->dst_port);
1025 if (os_snprintf_error(rem_len, res))
1026 return -1;
1027
1028 pos += res;
1029 rem_len -= res;
1030 }
1031
Sunil Ravi89eba102022-09-13 21:04:37 -07001032 if (classifier_mask & BIT(6)) {
Hai Shalomc1a21442022-02-04 13:43:00 -08001033 res = os_snprintf(pos, rem_len, " protocol=%d",
1034 v6->next_header);
1035 if (os_snprintf_error(rem_len, res))
1036 return -1;
1037
1038 pos += res;
1039 rem_len -= res;
1040 }
1041
1042 return total_len - rem_len;
1043}
1044
1045
Hai Shalomc1a21442022-02-04 13:43:00 -08001046static int set_frame_classifier_type4_ipv4(struct dscp_policy_data *policy)
1047{
1048 u8 classifier_mask;
1049 const u8 *frame_classifier = policy->frame_classifier;
1050 struct type4_params *type4_param = &policy->type4_param;
1051
1052 if (policy->frame_classifier_len < 18) {
1053 wpa_printf(MSG_ERROR,
1054 "QM: Received IPv4 frame classifier with insufficient length %d",
1055 policy->frame_classifier_len);
1056 return -1;
1057 }
1058
1059 classifier_mask = frame_classifier[1];
1060
1061 /* Classifier Mask - bit 1 = Source IP Address */
1062 if (classifier_mask & BIT(1)) {
Sunil Ravi89eba102022-09-13 21:04:37 -07001063 type4_param->classifier_mask |= BIT(1);
Hai Shalomc1a21442022-02-04 13:43:00 -08001064 os_memcpy(&type4_param->ip_params.v4.src_ip,
1065 &frame_classifier[3], 4);
1066 }
1067
1068 /* Classifier Mask - bit 2 = Destination IP Address */
1069 if (classifier_mask & BIT(2)) {
1070 if (policy->domain_name) {
1071 wpa_printf(MSG_ERROR,
1072 "QM: IPv4: Both domain name and destination IP address not expected");
1073 return -1;
1074 }
1075
Sunil Ravi89eba102022-09-13 21:04:37 -07001076 type4_param->classifier_mask |= BIT(2);
Hai Shalomc1a21442022-02-04 13:43:00 -08001077 os_memcpy(&type4_param->ip_params.v4.dst_ip,
1078 &frame_classifier[7], 4);
1079 }
1080
1081 /* Classifier Mask - bit 3 = Source Port */
1082 if (classifier_mask & BIT(3)) {
Sunil Ravi89eba102022-09-13 21:04:37 -07001083 type4_param->classifier_mask |= BIT(3);
Hai Shalomc1a21442022-02-04 13:43:00 -08001084 type4_param->ip_params.v4.src_port =
1085 WPA_GET_BE16(&frame_classifier[11]);
1086 }
1087
1088 /* Classifier Mask - bit 4 = Destination Port */
1089 if (classifier_mask & BIT(4)) {
1090 if (policy->port_range_info) {
1091 wpa_printf(MSG_ERROR,
1092 "QM: IPv4: Both port range and destination port not expected");
1093 return -1;
1094 }
1095
Sunil Ravi89eba102022-09-13 21:04:37 -07001096 type4_param->classifier_mask |= BIT(4);
Hai Shalomc1a21442022-02-04 13:43:00 -08001097 type4_param->ip_params.v4.dst_port =
1098 WPA_GET_BE16(&frame_classifier[13]);
1099 }
1100
1101 /* Classifier Mask - bit 5 = DSCP (ignored) */
1102
1103 /* Classifier Mask - bit 6 = Protocol */
1104 if (classifier_mask & BIT(6)) {
Sunil Ravi89eba102022-09-13 21:04:37 -07001105 type4_param->classifier_mask |= BIT(6);
Hai Shalomc1a21442022-02-04 13:43:00 -08001106 type4_param->ip_params.v4.protocol = frame_classifier[16];
1107 }
1108
1109 return 0;
1110}
1111
1112
1113static int set_frame_classifier_type4_ipv6(struct dscp_policy_data *policy)
1114{
1115 u8 classifier_mask;
1116 const u8 *frame_classifier = policy->frame_classifier;
1117 struct type4_params *type4_param = &policy->type4_param;
1118
1119 if (policy->frame_classifier_len < 44) {
1120 wpa_printf(MSG_ERROR,
1121 "QM: Received IPv6 frame classifier with insufficient length %d",
1122 policy->frame_classifier_len);
1123 return -1;
1124 }
1125
1126 classifier_mask = frame_classifier[1];
1127
1128 /* Classifier Mask - bit 1 = Source IP Address */
1129 if (classifier_mask & BIT(1)) {
Sunil Ravi89eba102022-09-13 21:04:37 -07001130 type4_param->classifier_mask |= BIT(1);
Hai Shalomc1a21442022-02-04 13:43:00 -08001131 os_memcpy(&type4_param->ip_params.v6.src_ip,
1132 &frame_classifier[3], 16);
1133 }
1134
1135 /* Classifier Mask - bit 2 = Destination IP Address */
1136 if (classifier_mask & BIT(2)) {
1137 if (policy->domain_name) {
1138 wpa_printf(MSG_ERROR,
1139 "QM: IPv6: Both domain name and destination IP address not expected");
1140 return -1;
1141 }
Sunil Ravi89eba102022-09-13 21:04:37 -07001142 type4_param->classifier_mask |= BIT(2);
Hai Shalomc1a21442022-02-04 13:43:00 -08001143 os_memcpy(&type4_param->ip_params.v6.dst_ip,
1144 &frame_classifier[19], 16);
1145 }
1146
1147 /* Classifier Mask - bit 3 = Source Port */
1148 if (classifier_mask & BIT(3)) {
Sunil Ravi89eba102022-09-13 21:04:37 -07001149 type4_param->classifier_mask |= BIT(3);
Hai Shalomc1a21442022-02-04 13:43:00 -08001150 type4_param->ip_params.v6.src_port =
1151 WPA_GET_BE16(&frame_classifier[35]);
1152 }
1153
1154 /* Classifier Mask - bit 4 = Destination Port */
1155 if (classifier_mask & BIT(4)) {
1156 if (policy->port_range_info) {
1157 wpa_printf(MSG_ERROR,
1158 "IPv6: Both port range and destination port not expected");
1159 return -1;
1160 }
1161
Sunil Ravi89eba102022-09-13 21:04:37 -07001162 type4_param->classifier_mask |= BIT(4);
Hai Shalomc1a21442022-02-04 13:43:00 -08001163 type4_param->ip_params.v6.dst_port =
1164 WPA_GET_BE16(&frame_classifier[37]);
1165 }
1166
1167 /* Classifier Mask - bit 5 = DSCP (ignored) */
1168
1169 /* Classifier Mask - bit 6 = Next Header */
1170 if (classifier_mask & BIT(6)) {
Sunil Ravi89eba102022-09-13 21:04:37 -07001171 type4_param->classifier_mask |= BIT(6);
Hai Shalomc1a21442022-02-04 13:43:00 -08001172 type4_param->ip_params.v6.next_header = frame_classifier[40];
1173 }
1174
1175 return 0;
1176}
1177
1178
1179static int wpas_set_frame_classifier_params(struct dscp_policy_data *policy)
1180{
1181 const u8 *frame_classifier = policy->frame_classifier;
1182 u8 frame_classifier_len = policy->frame_classifier_len;
1183
1184 if (frame_classifier_len < 3) {
1185 wpa_printf(MSG_ERROR,
1186 "QM: Received frame classifier with insufficient length %d",
1187 frame_classifier_len);
1188 return -1;
1189 }
1190
1191 /* Only allowed Classifier Type: IP and higher layer parameters (4) */
1192 if (frame_classifier[0] != 4) {
1193 wpa_printf(MSG_ERROR,
1194 "QM: Received frame classifier with invalid classifier type %d",
1195 frame_classifier[0]);
1196 return -1;
1197 }
1198
1199 /* Classifier Mask - bit 0 = Version */
1200 if (!(frame_classifier[1] & BIT(0))) {
1201 wpa_printf(MSG_ERROR,
1202 "QM: Received frame classifier without IP version");
1203 return -1;
1204 }
1205
1206 /* Version (4 or 6) */
1207 if (frame_classifier[2] == 4) {
1208 if (set_frame_classifier_type4_ipv4(policy)) {
1209 wpa_printf(MSG_ERROR,
1210 "QM: Failed to set IPv4 parameters");
1211 return -1;
1212 }
1213
1214 policy->type4_param.ip_version = IPV4;
1215 } else if (frame_classifier[2] == 6) {
1216 if (set_frame_classifier_type4_ipv6(policy)) {
1217 wpa_printf(MSG_ERROR,
1218 "QM: Failed to set IPv6 parameters");
1219 return -1;
1220 }
1221
1222 policy->type4_param.ip_version = IPV6;
1223 } else {
1224 wpa_printf(MSG_ERROR,
1225 "QM: Received unknown IP version %d",
1226 frame_classifier[2]);
1227 return -1;
1228 }
1229
1230 return 0;
1231}
1232
1233
1234static bool dscp_valid_domain_name(const char *str)
1235{
1236 if (!str[0])
1237 return false;
1238
1239 while (*str) {
1240 if (is_ctrl_char(*str) || *str == ' ' || *str == '=')
1241 return false;
1242 str++;
1243 }
1244
1245 return true;
1246}
1247
1248
Shivani Baranwal84940f82022-02-02 10:21:47 +05301249static int wpas_add_dscp_policy(struct wpa_supplicant *wpa_s,
Hai Shalomc1a21442022-02-04 13:43:00 -08001250 struct dscp_policy_data *policy)
1251{
1252 int ip_ver = 0, res;
1253 char policy_str[1000], *pos;
1254 int len;
1255
1256 if (!policy->frame_classifier && !policy->domain_name &&
1257 !policy->port_range_info) {
1258 wpa_printf(MSG_ERROR,
1259 "QM: Invalid DSCP policy - no attributes present");
1260 goto fail;
1261 }
1262
1263 policy_str[0] = '\0';
1264 pos = policy_str;
1265 len = sizeof(policy_str);
1266
1267 if (policy->frame_classifier) {
1268 struct type4_params *type4 = &policy->type4_param;
1269
1270 if (wpas_set_frame_classifier_params(policy)) {
1271 wpa_printf(MSG_ERROR,
1272 "QM: Failed to set frame classifier parameters");
1273 goto fail;
1274 }
1275
1276 if (type4->ip_version == IPV4)
Sunil Ravi89eba102022-09-13 21:04:37 -07001277 res = write_ipv4_info(pos, len, &type4->ip_params.v4,
1278 type4->classifier_mask);
Hai Shalomc1a21442022-02-04 13:43:00 -08001279 else
Sunil Ravi89eba102022-09-13 21:04:37 -07001280 res = write_ipv6_info(pos, len, &type4->ip_params.v6,
1281 type4->classifier_mask);
Hai Shalomc1a21442022-02-04 13:43:00 -08001282
1283 if (res <= 0) {
1284 wpa_printf(MSG_ERROR,
1285 "QM: Failed to write IP parameters");
1286 goto fail;
1287 }
1288
1289 ip_ver = type4->ip_version;
1290
1291 pos += res;
1292 len -= res;
1293 }
1294
1295 if (policy->port_range_info) {
1296 res = os_snprintf(pos, len, " start_port=%u end_port=%u",
1297 policy->start_port, policy->end_port);
1298 if (os_snprintf_error(len, res)) {
1299 wpa_printf(MSG_ERROR,
1300 "QM: Failed to write port range attributes for policy id = %d",
1301 policy->policy_id);
1302 goto fail;
1303 }
1304
1305 pos += res;
1306 len -= res;
1307 }
1308
1309 if (policy->domain_name) {
1310 char domain_name_str[250];
1311
1312 if (policy->domain_name_len >= sizeof(domain_name_str)) {
1313 wpa_printf(MSG_ERROR,
1314 "QM: Domain name length higher than max expected");
1315 goto fail;
1316 }
1317 os_memcpy(domain_name_str, policy->domain_name,
1318 policy->domain_name_len);
1319 domain_name_str[policy->domain_name_len] = '\0';
1320 if (!dscp_valid_domain_name(domain_name_str)) {
1321 wpa_printf(MSG_ERROR, "QM: Invalid domain name string");
1322 goto fail;
1323 }
1324 res = os_snprintf(pos, len, " domain_name=%s", domain_name_str);
1325 if (os_snprintf_error(len, res)) {
1326 wpa_printf(MSG_ERROR,
1327 "QM: Failed to write domain name attribute for policy id = %d",
1328 policy->policy_id);
1329 goto fail;
1330 }
1331 }
1332
1333 wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY
1334 "add policy_id=%u dscp=%u ip_version=%d%s",
1335 policy->policy_id, policy->dscp, ip_ver, policy_str);
Shivani Baranwal84940f82022-02-02 10:21:47 +05301336 return 0;
Hai Shalomc1a21442022-02-04 13:43:00 -08001337fail:
1338 wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "reject policy_id=%u",
1339 policy->policy_id);
Shivani Baranwal84940f82022-02-02 10:21:47 +05301340 return -1;
Hai Shalomc1a21442022-02-04 13:43:00 -08001341}
1342
1343
1344void wpas_dscp_deinit(struct wpa_supplicant *wpa_s)
1345{
1346 wpa_printf(MSG_DEBUG, "QM: Clear all active DSCP policies");
1347 wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "clear_all");
1348 wpa_s->dscp_req_dialog_token = 0;
1349 wpa_s->dscp_query_dialog_token = 0;
1350 wpa_s->connection_dscp = 0;
1351 if (wpa_s->wait_for_dscp_req) {
1352 wpa_s->wait_for_dscp_req = 0;
1353 eloop_cancel_timeout(wpas_wait_for_dscp_req_timer, wpa_s, NULL);
1354 }
1355}
1356
1357
1358static void wpas_fill_dscp_policy(struct dscp_policy_data *policy, u8 attr_id,
1359 u8 attr_len, const u8 *attr_data)
1360{
1361 switch (attr_id) {
1362 case QM_ATTR_PORT_RANGE:
1363 if (attr_len < 4) {
1364 wpa_printf(MSG_ERROR,
1365 "QM: Received Port Range attribute with insufficient length %d",
1366 attr_len);
1367 break;
1368 }
1369 policy->start_port = WPA_GET_BE16(attr_data);
1370 policy->end_port = WPA_GET_BE16(attr_data + 2);
1371 policy->port_range_info = true;
1372 break;
1373 case QM_ATTR_DSCP_POLICY:
1374 if (attr_len < 3) {
1375 wpa_printf(MSG_ERROR,
1376 "QM: Received DSCP Policy attribute with insufficient length %d",
1377 attr_len);
1378 return;
1379 }
1380 policy->policy_id = attr_data[0];
1381 policy->req_type = attr_data[1];
1382 policy->dscp = attr_data[2];
1383 policy->dscp_info = true;
1384 break;
1385 case QM_ATTR_TCLAS:
1386 if (attr_len < 1) {
1387 wpa_printf(MSG_ERROR,
1388 "QM: Received TCLAS attribute with insufficient length %d",
1389 attr_len);
1390 return;
1391 }
1392 policy->frame_classifier = attr_data;
1393 policy->frame_classifier_len = attr_len;
1394 break;
1395 case QM_ATTR_DOMAIN_NAME:
1396 if (attr_len < 1) {
1397 wpa_printf(MSG_ERROR,
1398 "QM: Received domain name attribute with insufficient length %d",
1399 attr_len);
1400 return;
1401 }
1402 policy->domain_name = attr_data;
1403 policy->domain_name_len = attr_len;
1404 break;
1405 default:
1406 wpa_printf(MSG_ERROR, "QM: Received invalid QoS attribute %d",
1407 attr_id);
1408 break;
1409 }
1410}
1411
1412
1413void wpas_handle_qos_mgmt_recv_action(struct wpa_supplicant *wpa_s,
1414 const u8 *src,
1415 const u8 *buf, size_t len)
1416{
1417 int rem_len;
1418 const u8 *qos_ie, *attr;
1419 int more, reset;
1420
Shivani Baranwal84940f82022-02-02 10:21:47 +05301421 struct dscp_policy_data *policies = NULL, *policies_temp;
1422 int num_dscp_policies = 0;
1423
Hai Shalomc1a21442022-02-04 13:43:00 -08001424 if (!wpa_s->enable_dscp_policy_capa) {
1425 wpa_printf(MSG_ERROR,
1426 "QM: Ignore DSCP Policy frame since the capability is not enabled");
1427 return;
1428 }
1429
1430 if (!pmf_in_use(wpa_s, src)) {
1431 wpa_printf(MSG_ERROR,
1432 "QM: Ignore DSCP Policy frame since PMF is not in use");
1433 return;
1434 }
1435
1436 if (!wpa_s->connection_dscp) {
1437 wpa_printf(MSG_DEBUG,
1438 "QM: DSCP Policy capability not enabled for the current association - ignore QoS Management Action frames");
1439 return;
1440 }
1441
1442 if (len < 1)
1443 return;
1444
1445 /* Handle only DSCP Policy Request frame */
1446 if (buf[0] != QM_DSCP_POLICY_REQ) {
1447 wpa_printf(MSG_ERROR, "QM: Received unexpected QoS action frame %d",
1448 buf[0]);
1449 return;
1450 }
1451
1452 if (len < 3) {
1453 wpa_printf(MSG_ERROR,
1454 "Received QoS Management DSCP Policy Request frame with invalid length %zu",
1455 len);
1456 return;
1457 }
1458
1459 /* Clear wait_for_dscp_req on receiving first DSCP request from AP */
1460 if (wpa_s->wait_for_dscp_req) {
1461 wpa_s->wait_for_dscp_req = 0;
1462 eloop_cancel_timeout(wpas_wait_for_dscp_req_timer, wpa_s, NULL);
1463 }
1464
1465 wpa_s->dscp_req_dialog_token = buf[1];
1466 more = buf[2] & DSCP_POLICY_CTRL_MORE;
1467 reset = buf[2] & DSCP_POLICY_CTRL_RESET;
1468
Shivani Baranwal84940f82022-02-02 10:21:47 +05301469 if (reset)
1470 wpas_notify_qos_policy_reset(wpa_s);
1471
Hai Shalomc1a21442022-02-04 13:43:00 -08001472 wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "request_start%s%s",
1473 reset ? " clear_all" : "", more ? " more" : "");
1474
1475 qos_ie = buf + 3;
1476 rem_len = len - 3;
1477 while (rem_len > 2) {
1478 struct dscp_policy_data policy;
Shivani Baranwal84940f82022-02-02 10:21:47 +05301479 int res = 0;
Hai Shalomc1a21442022-02-04 13:43:00 -08001480 int rem_attrs_len, ie_len;
1481
1482 ie_len = 2 + qos_ie[1];
1483 if (rem_len < ie_len)
1484 break;
1485
1486 if (rem_len < 6 || qos_ie[0] != WLAN_EID_VENDOR_SPECIFIC ||
1487 qos_ie[1] < 4 ||
1488 WPA_GET_BE32(&qos_ie[2]) != QM_IE_VENDOR_TYPE) {
1489 rem_len -= ie_len;
1490 qos_ie += ie_len;
1491 continue;
1492 }
1493
1494 os_memset(&policy, 0, sizeof(struct dscp_policy_data));
1495 attr = qos_ie + 6;
1496 rem_attrs_len = qos_ie[1] - 4;
1497
Sunil8cd6f4d2022-06-28 18:40:46 +00001498 while (rem_attrs_len > 2) {
1499 u8 attr_id, attr_len;
1500
1501 attr_id = *attr++;
1502 attr_len = *attr++;
1503 rem_attrs_len -= 2;
1504 if (attr_len > rem_attrs_len)
1505 break;
1506 wpas_fill_dscp_policy(&policy, attr_id, attr_len, attr);
1507 rem_attrs_len -= attr_len;
1508 attr += attr_len;
Hai Shalomc1a21442022-02-04 13:43:00 -08001509 }
1510
1511 rem_len -= ie_len;
1512 qos_ie += ie_len;
1513
1514 if (!policy.dscp_info) {
1515 wpa_printf(MSG_ERROR,
1516 "QM: Received QoS IE without DSCP Policy attribute");
1517 continue;
1518 }
1519
1520 if (policy.req_type == DSCP_POLICY_REQ_ADD)
Shivani Baranwal84940f82022-02-02 10:21:47 +05301521 res = wpas_add_dscp_policy(wpa_s, &policy);
Hai Shalomc1a21442022-02-04 13:43:00 -08001522 else if (policy.req_type == DSCP_POLICY_REQ_REMOVE)
1523 wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY
1524 "remove policy_id=%u", policy.policy_id);
Shivani Baranwal84940f82022-02-02 10:21:47 +05301525 else {
Hai Shalomc1a21442022-02-04 13:43:00 -08001526 wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY
1527 "reject policy_id=%u", policy.policy_id);
Shivani Baranwal84940f82022-02-02 10:21:47 +05301528 res = -1;
1529 }
1530
1531 if (res)
1532 continue;
1533
1534 policies_temp = os_realloc(policies,
1535 (num_dscp_policies + 1) *
1536 sizeof(struct dscp_policy_data));
1537 if (!policies_temp)
1538 goto fail;
1539
1540 policies = policies_temp;
1541 policies[num_dscp_policies] = policy;
1542 num_dscp_policies++;
Hai Shalomc1a21442022-02-04 13:43:00 -08001543 }
1544
Shivani Baranwal84940f82022-02-02 10:21:47 +05301545 wpas_notify_qos_policy_request(wpa_s, policies, num_dscp_policies);
1546
Hai Shalomc1a21442022-02-04 13:43:00 -08001547 wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "request_end");
Shivani Baranwal84940f82022-02-02 10:21:47 +05301548
1549fail:
1550 os_free(policies);
1551 return;
Hai Shalomc1a21442022-02-04 13:43:00 -08001552}
1553
1554
1555int wpas_send_dscp_response(struct wpa_supplicant *wpa_s,
1556 struct dscp_resp_data *resp_data)
1557{
1558 struct wpabuf *buf = NULL;
1559 size_t buf_len;
1560 int ret = -1, i;
1561 u8 resp_control = 0;
1562
1563 if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid) {
1564 wpa_printf(MSG_ERROR,
1565 "QM: Failed to send DSCP response - not connected to AP");
1566 return -1;
1567 }
1568
1569 if (resp_data->solicited && !wpa_s->dscp_req_dialog_token) {
1570 wpa_printf(MSG_ERROR, "QM: No ongoing DSCP request");
1571 return -1;
1572 }
1573
1574 if (!wpa_s->connection_dscp) {
1575 wpa_printf(MSG_ERROR,
1576 "QM: Failed to send DSCP response - DSCP capability not enabled for the current association");
1577 return -1;
1578
1579 }
1580
1581 buf_len = 1 + /* Category */
1582 3 + /* OUI */
1583 1 + /* OUI Type */
1584 1 + /* OUI Subtype */
1585 1 + /* Dialog Token */
1586 1 + /* Response Control */
1587 1 + /* Count */
1588 2 * resp_data->num_policies; /* Status list */
1589 buf = wpabuf_alloc(buf_len);
1590 if (!buf) {
1591 wpa_printf(MSG_ERROR,
1592 "QM: Failed to allocate DSCP policy response");
1593 return -1;
1594 }
1595
1596 wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED);
1597 wpabuf_put_be24(buf, OUI_WFA);
1598 wpabuf_put_u8(buf, QM_ACTION_OUI_TYPE);
1599 wpabuf_put_u8(buf, QM_DSCP_POLICY_RESP);
1600
1601 wpabuf_put_u8(buf, resp_data->solicited ?
1602 wpa_s->dscp_req_dialog_token : 0);
1603
1604 if (resp_data->more)
1605 resp_control |= DSCP_POLICY_CTRL_MORE;
1606 if (resp_data->reset)
1607 resp_control |= DSCP_POLICY_CTRL_RESET;
1608 wpabuf_put_u8(buf, resp_control);
1609
1610 wpabuf_put_u8(buf, resp_data->num_policies);
1611 for (i = 0; i < resp_data->num_policies; i++) {
1612 wpabuf_put_u8(buf, resp_data->policy[i].id);
1613 wpabuf_put_u8(buf, resp_data->policy[i].status);
1614 }
1615
1616 wpa_hexdump_buf(MSG_MSGDUMP, "DSCP response frame: ", buf);
1617 ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
1618 wpa_s->own_addr, wpa_s->bssid,
1619 wpabuf_head(buf), wpabuf_len(buf), 0);
1620 if (ret < 0) {
1621 wpa_msg(wpa_s, MSG_INFO, "QM: Failed to send DSCP response");
1622 goto fail;
1623 }
1624
1625 /*
1626 * Mark DSCP request complete whether response sent is solicited or
1627 * unsolicited
1628 */
1629 wpa_s->dscp_req_dialog_token = 0;
1630
1631fail:
1632 wpabuf_free(buf);
1633 return ret;
1634}
1635
1636
1637int wpas_send_dscp_query(struct wpa_supplicant *wpa_s, const char *domain_name,
1638 size_t domain_name_length)
1639{
1640 struct wpabuf *buf = NULL;
1641 int ret, dscp_query_size;
1642
1643 if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid)
1644 return -1;
1645
1646 if (!wpa_s->connection_dscp) {
1647 wpa_printf(MSG_ERROR,
1648 "QM: Failed to send DSCP query - DSCP capability not enabled for the current association");
1649 return -1;
1650 }
1651
1652 if (wpa_s->wait_for_dscp_req) {
1653 wpa_printf(MSG_INFO, "QM: Wait until AP sends a DSCP request");
1654 return -1;
1655 }
1656
1657#define DOMAIN_NAME_OFFSET (4 /* OUI */ + 1 /* Attr Id */ + 1 /* Attr len */)
1658
1659 if (domain_name_length > 255 - DOMAIN_NAME_OFFSET) {
1660 wpa_printf(MSG_ERROR, "QM: Too long domain name");
1661 return -1;
1662 }
1663
1664 dscp_query_size = 1 + /* Category */
1665 4 + /* OUI Type */
1666 1 + /* OUI subtype */
1667 1; /* Dialog Token */
1668 if (domain_name && domain_name_length)
1669 dscp_query_size += 1 + /* Element ID */
1670 1 + /* IE Length */
1671 DOMAIN_NAME_OFFSET + domain_name_length;
1672
1673 buf = wpabuf_alloc(dscp_query_size);
1674 if (!buf) {
1675 wpa_printf(MSG_ERROR, "QM: Failed to allocate DSCP query");
1676 return -1;
1677 }
1678
1679 wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED);
1680 wpabuf_put_be32(buf, QM_ACTION_VENDOR_TYPE);
1681 wpabuf_put_u8(buf, QM_DSCP_POLICY_QUERY);
1682 wpa_s->dscp_query_dialog_token++;
1683 if (wpa_s->dscp_query_dialog_token == 0)
1684 wpa_s->dscp_query_dialog_token++;
1685 wpabuf_put_u8(buf, wpa_s->dscp_query_dialog_token);
1686
1687 if (domain_name && domain_name_length) {
1688 /* Domain Name attribute */
1689 wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
1690 wpabuf_put_u8(buf, DOMAIN_NAME_OFFSET + domain_name_length);
1691 wpabuf_put_be32(buf, QM_IE_VENDOR_TYPE);
1692 wpabuf_put_u8(buf, QM_ATTR_DOMAIN_NAME);
1693 wpabuf_put_u8(buf, domain_name_length);
1694 wpabuf_put_data(buf, domain_name, domain_name_length);
1695 }
1696#undef DOMAIN_NAME_OFFSET
1697
1698 ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
1699 wpa_s->own_addr, wpa_s->bssid,
1700 wpabuf_head(buf), wpabuf_len(buf), 0);
1701 if (ret < 0) {
1702 wpa_dbg(wpa_s, MSG_ERROR, "QM: Failed to send DSCP query");
1703 wpa_s->dscp_query_dialog_token--;
1704 }
1705
1706 wpabuf_free(buf);
1707 return ret;
1708}