1 /* 2 * Wi-Fi Multimedia Admission Control (WMM-AC) 3 * Copyright(c) 2014, Intel Mobile Communication GmbH. 4 * Copyright(c) 2014, Intel Corporation. All rights reserved. 5 * 6 * This software may be distributed under the terms of the BSD license. 7 * See README for more details. 8 */ 9 10 #include "includes.h" 11 12 #include "utils/common.h" 13 #include "utils/list.h" 14 #include "utils/eloop.h" 15 #include "common/ieee802_11_common.h" 16 #include "wpa_supplicant_i.h" 17 #include "bss.h" 18 #include "driver_i.h" 19 #include "wmm_ac.h" 20 21 static void wmm_ac_addts_req_timeout(void *eloop_ctx, void *timeout_ctx); 22 23 static const enum wmm_ac up_to_ac[8] = { 24 WMM_AC_BK, 25 WMM_AC_BE, 26 WMM_AC_BE, 27 WMM_AC_BK, 28 WMM_AC_VI, 29 WMM_AC_VI, 30 WMM_AC_VO, 31 WMM_AC_VO 32 }; 33 34 35 static inline u8 wmm_ac_get_tsid(const struct wmm_tspec_element *tspec) 36 { 37 return (tspec->ts_info[0] >> 1) & 0x0f; 38 } 39 40 41 static u8 wmm_ac_get_direction(const struct wmm_tspec_element *tspec) 42 { 43 return (tspec->ts_info[0] >> 5) & 0x03; 44 } 45 46 47 static u8 wmm_ac_get_user_priority(const struct wmm_tspec_element *tspec) 48 { 49 return (tspec->ts_info[1] >> 3) & 0x07; 50 } 51 52 53 static u8 wmm_ac_direction_to_idx(u8 direction) 54 { 55 switch (direction) { 56 case WMM_AC_DIR_UPLINK: 57 return TS_DIR_IDX_UPLINK; 58 case WMM_AC_DIR_DOWNLINK: 59 return TS_DIR_IDX_DOWNLINK; 60 case WMM_AC_DIR_BIDIRECTIONAL: 61 return TS_DIR_IDX_BIDI; 62 default: 63 wpa_printf(MSG_ERROR, "Invalid direction: %d", direction); 64 return WMM_AC_DIR_UPLINK; 65 } 66 } 67 68 69 static int wmm_ac_add_ts(struct wpa_supplicant *wpa_s, const u8 *addr, 70 const struct wmm_tspec_element *tspec) 71 { 72 struct wmm_tspec_element *_tspec; 73 int ret; 74 u16 admitted_time = le_to_host16(tspec->medium_time); 75 u8 up = wmm_ac_get_user_priority(tspec); 76 u8 ac = up_to_ac[up]; 77 u8 dir = wmm_ac_get_direction(tspec); 78 u8 tsid = wmm_ac_get_tsid(tspec); 79 enum ts_dir_idx idx = wmm_ac_direction_to_idx(dir); 80 81 /* should have been verified before, but double-check here */ 82 if (wpa_s->tspecs[ac][idx]) { 83 wpa_printf(MSG_ERROR, 84 "WMM AC: tspec (ac=%d, dir=%d) already exists!", 85 ac, dir); 86 return -1; 87 } 88 89 /* copy tspec */ 90 _tspec = os_memdup(tspec, sizeof(*_tspec)); 91 if (!_tspec) 92 return -1; 93 94 if (dir != WMM_AC_DIR_DOWNLINK) { 95 ret = wpa_drv_add_ts(wpa_s, tsid, addr, up, admitted_time); 96 wpa_printf(MSG_DEBUG, 97 "WMM AC: Add TS: addr=" MACSTR 98 " TSID=%u admitted time=%u, ret=%d", 99 MAC2STR(addr), tsid, admitted_time, ret); 100 if (ret < 0) { 101 os_free(_tspec); 102 return -1; 103 } 104 } 105 106 wpa_s->tspecs[ac][idx] = _tspec; 107 108 wpa_printf(MSG_DEBUG, "Traffic stream was created successfully"); 109 110 wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_ADDED 111 "tsid=%d addr=" MACSTR " admitted_time=%d", 112 tsid, MAC2STR(addr), admitted_time); 113 114 return 0; 115 } 116 117 118 static void wmm_ac_del_ts_idx(struct wpa_supplicant *wpa_s, u8 ac, 119 enum ts_dir_idx dir) 120 { 121 struct wmm_tspec_element *tspec = wpa_s->tspecs[ac][dir]; 122 u8 tsid; 123 124 if (!tspec) 125 return; 126 127 tsid = wmm_ac_get_tsid(tspec); 128 wpa_printf(MSG_DEBUG, "WMM AC: Del TS ac=%d tsid=%d", ac, tsid); 129 130 /* update the driver in case of uplink/bidi */ 131 if (wmm_ac_get_direction(tspec) != WMM_AC_DIR_DOWNLINK) 132 wpa_drv_del_ts(wpa_s, tsid, wpa_s->bssid); 133 134 wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REMOVED 135 "tsid=%d addr=" MACSTR, tsid, MAC2STR(wpa_s->bssid)); 136 137 os_free(wpa_s->tspecs[ac][dir]); 138 wpa_s->tspecs[ac][dir] = NULL; 139 } 140 141 142 static void wmm_ac_del_req(struct wpa_supplicant *wpa_s, int failed) 143 { 144 struct wmm_ac_addts_request *req = wpa_s->addts_request; 145 146 if (!req) 147 return; 148 149 if (failed) 150 wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REQ_FAILED 151 "tsid=%u", wmm_ac_get_tsid(&req->tspec)); 152 153 eloop_cancel_timeout(wmm_ac_addts_req_timeout, wpa_s, req); 154 wpa_s->addts_request = NULL; 155 os_free(req); 156 } 157 158 159 static void wmm_ac_addts_req_timeout(void *eloop_ctx, void *timeout_ctx) 160 { 161 struct wpa_supplicant *wpa_s = eloop_ctx; 162 struct wmm_ac_addts_request *addts_req = timeout_ctx; 163 164 wpa_printf(MSG_DEBUG, 165 "Timeout getting ADDTS response (tsid=%d up=%d)", 166 wmm_ac_get_tsid(&addts_req->tspec), 167 wmm_ac_get_user_priority(&addts_req->tspec)); 168 169 wmm_ac_del_req(wpa_s, 1); 170 } 171 172 173 static int wmm_ac_send_addts_request(struct wpa_supplicant *wpa_s, 174 const struct wmm_ac_addts_request *req) 175 { 176 struct wpabuf *buf; 177 int ret; 178 179 wpa_printf(MSG_DEBUG, "Sending ADDTS Request to " MACSTR, 180 MAC2STR(req->address)); 181 182 /* category + action code + dialog token + status + sizeof(tspec) */ 183 buf = wpabuf_alloc(4 + sizeof(req->tspec)); 184 if (!buf) { 185 wpa_printf(MSG_ERROR, "WMM AC: Allocation error"); 186 return -1; 187 } 188 189 wpabuf_put_u8(buf, WLAN_ACTION_WMM); 190 wpabuf_put_u8(buf, WMM_ACTION_CODE_ADDTS_REQ); 191 wpabuf_put_u8(buf, req->dialog_token); 192 wpabuf_put_u8(buf, 0); /* status code */ 193 wpabuf_put_data(buf, &req->tspec, sizeof(req->tspec)); 194 195 ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, req->address, 196 wpa_s->own_addr, wpa_s->bssid, 197 wpabuf_head(buf), wpabuf_len(buf), 0); 198 if (ret) { 199 wpa_printf(MSG_WARNING, 200 "WMM AC: Failed to send ADDTS Request"); 201 } 202 203 wpabuf_free(buf); 204 return ret; 205 } 206 207 208 static int wmm_ac_send_delts(struct wpa_supplicant *wpa_s, 209 const struct wmm_tspec_element *tspec, 210 const u8 *address) 211 { 212 struct wpabuf *buf; 213 int ret; 214 215 /* category + action code + dialog token + status + sizeof(tspec) */ 216 buf = wpabuf_alloc(4 + sizeof(*tspec)); 217 if (!buf) 218 return -1; 219 220 wpa_printf(MSG_DEBUG, "Sending DELTS to " MACSTR, MAC2STR(address)); 221 222 /* category + action code + dialog token + status + sizeof(tspec) */ 223 wpabuf_put_u8(buf, WLAN_ACTION_WMM); 224 wpabuf_put_u8(buf, WMM_ACTION_CODE_DELTS); 225 wpabuf_put_u8(buf, 0); /* Dialog Token (not used) */ 226 wpabuf_put_u8(buf, 0); /* Status Code (not used) */ 227 wpabuf_put_data(buf, tspec, sizeof(*tspec)); 228 229 ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, address, 230 wpa_s->own_addr, wpa_s->bssid, 231 wpabuf_head(buf), wpabuf_len(buf), 0); 232 if (ret) 233 wpa_printf(MSG_WARNING, "Failed to send DELTS frame"); 234 235 wpabuf_free(buf); 236 return ret; 237 } 238 239 240 /* return the AC using the given TSPEC tid */ 241 static int wmm_ac_find_tsid(struct wpa_supplicant *wpa_s, u8 tsid, 242 enum ts_dir_idx *dir) 243 { 244 int ac; 245 enum ts_dir_idx idx; 246 247 for (ac = 0; ac < WMM_AC_NUM; ac++) { 248 for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) { 249 if (wpa_s->tspecs[ac][idx] && 250 wmm_ac_get_tsid(wpa_s->tspecs[ac][idx]) == tsid) { 251 if (dir) 252 *dir = idx; 253 return ac; 254 } 255 } 256 } 257 258 return -1; 259 } 260 261 262 static struct wmm_ac_addts_request * 263 wmm_ac_build_addts_req(struct wpa_supplicant *wpa_s, 264 const struct wmm_ac_ts_setup_params *params, 265 const u8 *address) 266 { 267 struct wmm_ac_addts_request *addts_req; 268 struct wmm_tspec_element *tspec; 269 u8 ac = up_to_ac[params->user_priority]; 270 u8 uapsd = wpa_s->wmm_ac_assoc_info->ac_params[ac].uapsd; 271 272 addts_req = os_zalloc(sizeof(*addts_req)); 273 if (!addts_req) 274 return NULL; 275 276 tspec = &addts_req->tspec; 277 os_memcpy(addts_req->address, address, ETH_ALEN); 278 279 /* The dialog token cannot be zero */ 280 if (++wpa_s->wmm_ac_last_dialog_token == 0) 281 wpa_s->wmm_ac_last_dialog_token++; 282 283 addts_req->dialog_token = wpa_s->wmm_ac_last_dialog_token; 284 tspec->eid = WLAN_EID_VENDOR_SPECIFIC; 285 tspec->length = sizeof(*tspec) - 2; /* reduce eid and length */ 286 tspec->oui[0] = 0x00; 287 tspec->oui[1] = 0x50; 288 tspec->oui[2] = 0xf2; 289 tspec->oui_type = WMM_OUI_TYPE; 290 tspec->oui_subtype = WMM_OUI_SUBTYPE_TSPEC_ELEMENT; 291 tspec->version = WMM_VERSION; 292 293 tspec->ts_info[0] = params->tsid << 1; 294 tspec->ts_info[0] |= params->direction << 5; 295 tspec->ts_info[0] |= WMM_AC_ACCESS_POLICY_EDCA << 7; 296 tspec->ts_info[1] = uapsd << 2; 297 tspec->ts_info[1] |= params->user_priority << 3; 298 tspec->ts_info[2] = 0; 299 300 tspec->nominal_msdu_size = host_to_le16(params->nominal_msdu_size); 301 if (params->fixed_nominal_msdu) 302 tspec->nominal_msdu_size |= 303 host_to_le16(WMM_AC_FIXED_MSDU_SIZE); 304 305 tspec->mean_data_rate = host_to_le32(params->mean_data_rate); 306 tspec->minimum_phy_rate = host_to_le32(params->minimum_phy_rate); 307 tspec->surplus_bandwidth_allowance = 308 host_to_le16(params->surplus_bandwidth_allowance); 309 310 return addts_req; 311 } 312 313 314 static int param_in_range(const char *name, long value, 315 long min_val, long max_val) 316 { 317 if (value < min_val || (max_val >= 0 && value > max_val)) { 318 wpa_printf(MSG_DEBUG, 319 "WMM AC: param %s (%ld) is out of range (%ld-%ld)", 320 name, value, min_val, max_val); 321 return 0; 322 } 323 324 return 1; 325 } 326 327 328 static int wmm_ac_should_replace_ts(struct wpa_supplicant *wpa_s, 329 u8 tsid, u8 ac, u8 dir) 330 { 331 enum ts_dir_idx idx; 332 int cur_ac, existing_ts = 0, replace_ts = 0; 333 334 cur_ac = wmm_ac_find_tsid(wpa_s, tsid, &idx); 335 if (cur_ac >= 0) { 336 if (cur_ac != ac) { 337 wpa_printf(MSG_DEBUG, 338 "WMM AC: TSID %i already exists on different ac (%d)", 339 tsid, cur_ac); 340 return -1; 341 } 342 343 /* same tsid - this tspec will replace the current one */ 344 replace_ts |= BIT(idx); 345 } 346 347 for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) { 348 if (wpa_s->tspecs[ac][idx]) 349 existing_ts |= BIT(idx); 350 } 351 352 switch (dir) { 353 case WMM_AC_DIR_UPLINK: 354 /* replace existing uplink/bidi tspecs */ 355 replace_ts |= existing_ts & (BIT(TS_DIR_IDX_UPLINK) | 356 BIT(TS_DIR_IDX_BIDI)); 357 break; 358 case WMM_AC_DIR_DOWNLINK: 359 /* replace existing downlink/bidi tspecs */ 360 replace_ts |= existing_ts & (BIT(TS_DIR_IDX_DOWNLINK) | 361 BIT(TS_DIR_IDX_BIDI)); 362 break; 363 case WMM_AC_DIR_BIDIRECTIONAL: 364 /* replace all existing tspecs */ 365 replace_ts |= existing_ts; 366 break; 367 default: 368 return -1; 369 } 370 371 return replace_ts; 372 } 373 374 375 static int wmm_ac_ts_req_is_valid(struct wpa_supplicant *wpa_s, 376 const struct wmm_ac_ts_setup_params *params) 377 { 378 enum wmm_ac req_ac; 379 380 #define PARAM_IN_RANGE(field, min_value, max_value) \ 381 param_in_range(#field, params->field, min_value, max_value) 382 383 if (!PARAM_IN_RANGE(tsid, 0, WMM_AC_MAX_TID) || 384 !PARAM_IN_RANGE(user_priority, 0, WMM_AC_MAX_USER_PRIORITY) || 385 !PARAM_IN_RANGE(nominal_msdu_size, 1, WMM_AC_MAX_NOMINAL_MSDU) || 386 !PARAM_IN_RANGE(mean_data_rate, 1, -1) || 387 !PARAM_IN_RANGE(minimum_phy_rate, 1, -1) || 388 !PARAM_IN_RANGE(surplus_bandwidth_allowance, WMM_AC_MIN_SBA_UNITY, 389 -1)) 390 return 0; 391 #undef PARAM_IN_RANGE 392 393 if (!(params->direction == WMM_TSPEC_DIRECTION_UPLINK || 394 params->direction == WMM_TSPEC_DIRECTION_DOWNLINK || 395 params->direction == WMM_TSPEC_DIRECTION_BI_DIRECTIONAL)) { 396 wpa_printf(MSG_DEBUG, "WMM AC: invalid TS direction: %d", 397 params->direction); 398 return 0; 399 } 400 401 req_ac = up_to_ac[params->user_priority]; 402 403 /* Requested accesss category must have acm */ 404 if (!wpa_s->wmm_ac_assoc_info->ac_params[req_ac].acm) { 405 wpa_printf(MSG_DEBUG, "WMM AC: AC %d is not ACM", req_ac); 406 return 0; 407 } 408 409 if (wmm_ac_should_replace_ts(wpa_s, params->tsid, req_ac, 410 params->direction) < 0) 411 return 0; 412 413 return 1; 414 } 415 416 417 static struct wmm_ac_assoc_data * 418 wmm_ac_process_param_elem(struct wpa_supplicant *wpa_s, const u8 *ies, 419 size_t ies_len) 420 { 421 struct ieee802_11_elems elems; 422 struct wmm_parameter_element *wmm_params; 423 struct wmm_ac_assoc_data *assoc_data; 424 int i; 425 426 /* Parsing WMM Parameter Element */ 427 if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) { 428 wpa_printf(MSG_DEBUG, "WMM AC: could not parse assoc ies"); 429 return NULL; 430 } 431 432 if (!elems.wmm) { 433 wpa_printf(MSG_DEBUG, "WMM AC: No WMM IE"); 434 return NULL; 435 } 436 437 if (elems.wmm_len != sizeof(*wmm_params)) { 438 wpa_printf(MSG_DEBUG, "WMM AC: Invalid WMM ie length"); 439 return NULL; 440 } 441 442 wmm_params = (struct wmm_parameter_element *)(elems.wmm); 443 444 assoc_data = os_zalloc(sizeof(*assoc_data)); 445 if (!assoc_data) 446 return NULL; 447 448 for (i = 0; i < WMM_AC_NUM; i++) 449 assoc_data->ac_params[i].acm = 450 !!(wmm_params->ac[i].aci_aifsn & WMM_AC_ACM); 451 452 wpa_printf(MSG_DEBUG, 453 "WMM AC: AC mandatory: AC_BE=%u AC_BK=%u AC_VI=%u AC_VO=%u", 454 assoc_data->ac_params[WMM_AC_BE].acm, 455 assoc_data->ac_params[WMM_AC_BK].acm, 456 assoc_data->ac_params[WMM_AC_VI].acm, 457 assoc_data->ac_params[WMM_AC_VO].acm); 458 459 return assoc_data; 460 } 461 462 463 static int wmm_ac_init(struct wpa_supplicant *wpa_s, const u8 *ies, 464 size_t ies_len, const struct wmm_params *wmm_params) 465 { 466 struct wmm_ac_assoc_data *assoc_data; 467 u8 ac; 468 469 if (wpa_s->wmm_ac_assoc_info) { 470 wpa_printf(MSG_ERROR, "WMM AC: Already initialized"); 471 return -1; 472 } 473 474 if (!ies) { 475 wpa_printf(MSG_ERROR, "WMM AC: Missing IEs"); 476 return -1; 477 } 478 479 if (!(wmm_params->info_bitmap & WMM_PARAMS_UAPSD_QUEUES_INFO)) { 480 wpa_printf(MSG_DEBUG, "WMM AC: Missing U-APSD configuration"); 481 return -1; 482 } 483 484 os_memset(wpa_s->tspecs, 0, sizeof(wpa_s->tspecs)); 485 wpa_s->wmm_ac_last_dialog_token = 0; 486 wpa_s->addts_request = NULL; 487 488 assoc_data = wmm_ac_process_param_elem(wpa_s, ies, ies_len); 489 if (!assoc_data) 490 return -1; 491 492 wpa_printf(MSG_DEBUG, "WMM AC: U-APSD queues=0x%x", 493 wmm_params->uapsd_queues); 494 495 for (ac = 0; ac < WMM_AC_NUM; ac++) { 496 assoc_data->ac_params[ac].uapsd = 497 !!(wmm_params->uapsd_queues & BIT(ac)); 498 } 499 500 wpa_s->wmm_ac_assoc_info = assoc_data; 501 return 0; 502 } 503 504 505 static void wmm_ac_del_ts(struct wpa_supplicant *wpa_s, u8 ac, int dir_bitmap) 506 { 507 enum ts_dir_idx idx; 508 509 for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) { 510 if (!(dir_bitmap & BIT(idx))) 511 continue; 512 513 wmm_ac_del_ts_idx(wpa_s, ac, idx); 514 } 515 } 516 517 518 static void wmm_ac_deinit(struct wpa_supplicant *wpa_s) 519 { 520 int i; 521 522 for (i = 0; i < WMM_AC_NUM; i++) 523 wmm_ac_del_ts(wpa_s, i, TS_DIR_IDX_ALL); 524 525 /* delete pending add_ts requset */ 526 wmm_ac_del_req(wpa_s, 1); 527 528 os_free(wpa_s->wmm_ac_assoc_info); 529 wpa_s->wmm_ac_assoc_info = NULL; 530 } 531 532 533 void wmm_ac_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *ies, 534 size_t ies_len, const struct wmm_params *wmm_params) 535 { 536 if (wmm_ac_init(wpa_s, ies, ies_len, wmm_params)) 537 return; 538 539 wpa_printf(MSG_DEBUG, 540 "WMM AC: Valid WMM association, WMM AC is enabled"); 541 } 542 543 544 void wmm_ac_notify_disassoc(struct wpa_supplicant *wpa_s) 545 { 546 if (!wpa_s->wmm_ac_assoc_info) 547 return; 548 549 wmm_ac_deinit(wpa_s); 550 wpa_printf(MSG_DEBUG, "WMM AC: WMM AC is disabled"); 551 } 552 553 554 int wpas_wmm_ac_delts(struct wpa_supplicant *wpa_s, u8 tsid) 555 { 556 struct wmm_tspec_element tspec; 557 int ac; 558 enum ts_dir_idx dir; 559 560 if (!wpa_s->wmm_ac_assoc_info) { 561 wpa_printf(MSG_DEBUG, 562 "WMM AC: Failed to delete TS, WMM AC is disabled"); 563 return -1; 564 } 565 566 ac = wmm_ac_find_tsid(wpa_s, tsid, &dir); 567 if (ac < 0) { 568 wpa_printf(MSG_DEBUG, "WMM AC: TS does not exist"); 569 return -1; 570 } 571 572 tspec = *wpa_s->tspecs[ac][dir]; 573 574 wmm_ac_del_ts_idx(wpa_s, ac, dir); 575 576 wmm_ac_send_delts(wpa_s, &tspec, wpa_s->bssid); 577 578 return 0; 579 } 580 581 582 int wpas_wmm_ac_addts(struct wpa_supplicant *wpa_s, 583 struct wmm_ac_ts_setup_params *params) 584 { 585 struct wmm_ac_addts_request *addts_req; 586 587 if (!wpa_s->wmm_ac_assoc_info) { 588 wpa_printf(MSG_DEBUG, 589 "WMM AC: Cannot add TS - missing assoc data"); 590 return -1; 591 } 592 593 if (wpa_s->addts_request) { 594 wpa_printf(MSG_DEBUG, 595 "WMM AC: can't add TS - ADDTS request is already pending"); 596 return -1; 597 } 598 599 /* 600 * we can setup downlink TS even without driver support. 601 * however, we need driver support for the other directions. 602 */ 603 if (params->direction != WMM_AC_DIR_DOWNLINK && 604 !wpa_s->wmm_ac_supported) { 605 wpa_printf(MSG_DEBUG, 606 "Cannot set uplink/bidi TS without driver support"); 607 return -1; 608 } 609 610 if (!wmm_ac_ts_req_is_valid(wpa_s, params)) 611 return -1; 612 613 wpa_printf(MSG_DEBUG, "WMM AC: TS setup request (addr=" MACSTR 614 " tsid=%u user priority=%u direction=%d)", 615 MAC2STR(wpa_s->bssid), params->tsid, 616 params->user_priority, params->direction); 617 618 addts_req = wmm_ac_build_addts_req(wpa_s, params, wpa_s->bssid); 619 if (!addts_req) 620 return -1; 621 622 if (wmm_ac_send_addts_request(wpa_s, addts_req)) 623 goto err; 624 625 /* save as pending and set ADDTS resp timeout to 1 second */ 626 wpa_s->addts_request = addts_req; 627 eloop_register_timeout(1, 0, wmm_ac_addts_req_timeout, 628 wpa_s, addts_req); 629 return 0; 630 err: 631 os_free(addts_req); 632 return -1; 633 } 634 635 636 static void wmm_ac_handle_delts(struct wpa_supplicant *wpa_s, const u8 *sa, 637 const struct wmm_tspec_element *tspec) 638 { 639 int ac; 640 u8 tsid; 641 enum ts_dir_idx idx; 642 643 tsid = wmm_ac_get_tsid(tspec); 644 645 wpa_printf(MSG_DEBUG, 646 "WMM AC: DELTS frame has been received TSID=%u addr=" 647 MACSTR, tsid, MAC2STR(sa)); 648 649 ac = wmm_ac_find_tsid(wpa_s, tsid, &idx); 650 if (ac < 0) { 651 wpa_printf(MSG_DEBUG, 652 "WMM AC: Ignoring DELTS frame - TSID does not exist"); 653 return; 654 } 655 656 wmm_ac_del_ts_idx(wpa_s, ac, idx); 657 658 wpa_printf(MSG_DEBUG, 659 "TS was deleted successfully (tsid=%u address=" MACSTR ")", 660 tsid, MAC2STR(sa)); 661 } 662 663 664 static void wmm_ac_handle_addts_resp(struct wpa_supplicant *wpa_s, const u8 *sa, 665 const u8 resp_dialog_token, const u8 status_code, 666 const struct wmm_tspec_element *tspec) 667 { 668 struct wmm_ac_addts_request *req = wpa_s->addts_request; 669 u8 ac, tsid, up, dir; 670 int replace_tspecs; 671 672 tsid = wmm_ac_get_tsid(tspec); 673 dir = wmm_ac_get_direction(tspec); 674 up = wmm_ac_get_user_priority(tspec); 675 ac = up_to_ac[up]; 676 677 /* make sure we have a matching addts request */ 678 if (!req || req->dialog_token != resp_dialog_token) { 679 wpa_printf(MSG_DEBUG, 680 "WMM AC: no req with dialog=%u, ignoring frame", 681 resp_dialog_token); 682 return; 683 } 684 685 /* make sure the params are the same */ 686 if (os_memcmp(req->address, sa, ETH_ALEN) != 0 || 687 tsid != wmm_ac_get_tsid(&req->tspec) || 688 up != wmm_ac_get_user_priority(&req->tspec) || 689 dir != wmm_ac_get_direction(&req->tspec)) { 690 wpa_printf(MSG_DEBUG, 691 "WMM AC: ADDTS params do not match, ignoring frame"); 692 return; 693 } 694 695 /* delete pending request */ 696 wmm_ac_del_req(wpa_s, 0); 697 698 wpa_printf(MSG_DEBUG, 699 "ADDTS response status=%d tsid=%u up=%u direction=%u", 700 status_code, tsid, up, dir); 701 702 if (status_code != WMM_ADDTS_STATUS_ADMISSION_ACCEPTED) { 703 wpa_printf(MSG_INFO, "WMM AC: ADDTS request was rejected"); 704 goto err_msg; 705 } 706 707 replace_tspecs = wmm_ac_should_replace_ts(wpa_s, tsid, ac, dir); 708 if (replace_tspecs < 0) 709 goto err_delts; 710 711 wpa_printf(MSG_DEBUG, "ts idx replace bitmap: 0x%x", replace_tspecs); 712 713 /* when replacing tspecs - delete first */ 714 wmm_ac_del_ts(wpa_s, ac, replace_tspecs); 715 716 /* Creating a new traffic stream */ 717 wpa_printf(MSG_DEBUG, 718 "WMM AC: adding a new TS with TSID=%u address="MACSTR 719 " medium time=%u access category=%d dir=%d ", 720 tsid, MAC2STR(sa), 721 le_to_host16(tspec->medium_time), ac, dir); 722 723 if (wmm_ac_add_ts(wpa_s, sa, tspec)) 724 goto err_delts; 725 726 return; 727 728 err_delts: 729 /* ask the ap to delete the tspec */ 730 wmm_ac_send_delts(wpa_s, tspec, sa); 731 err_msg: 732 wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REQ_FAILED "tsid=%u", 733 tsid); 734 } 735 736 737 void wmm_ac_rx_action(struct wpa_supplicant *wpa_s, const u8 *da, 738 const u8 *sa, const u8 *data, size_t len) 739 { 740 u8 action; 741 u8 dialog_token; 742 u8 status_code; 743 struct ieee802_11_elems elems; 744 struct wmm_tspec_element *tspec; 745 746 if (wpa_s->wmm_ac_assoc_info == NULL) { 747 wpa_printf(MSG_DEBUG, 748 "WMM AC: WMM AC is disabled, ignoring action frame"); 749 return; 750 } 751 752 action = data[0]; 753 754 if (action != WMM_ACTION_CODE_ADDTS_RESP && 755 action != WMM_ACTION_CODE_DELTS) { 756 wpa_printf(MSG_DEBUG, 757 "WMM AC: Unknown action (%d), ignoring action frame", 758 action); 759 return; 760 } 761 762 /* WMM AC action frame */ 763 if (os_memcmp(da, wpa_s->own_addr, ETH_ALEN) != 0) { 764 wpa_printf(MSG_DEBUG, "WMM AC: frame destination addr="MACSTR 765 " is other than ours, ignoring frame", MAC2STR(da)); 766 return; 767 } 768 769 if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) { 770 wpa_printf(MSG_DEBUG, "WMM AC: ignore frame with sa " MACSTR 771 " different other than our bssid", MAC2STR(da)); 772 return; 773 } 774 775 if (len < 2 + sizeof(struct wmm_tspec_element)) { 776 wpa_printf(MSG_DEBUG, 777 "WMM AC: Short ADDTS response ignored (len=%lu)", 778 (unsigned long) len); 779 return; 780 } 781 782 data++; 783 len--; 784 dialog_token = data[0]; 785 status_code = data[1]; 786 787 if (ieee802_11_parse_elems(data + 2, len - 2, &elems, 1) != ParseOK) { 788 wpa_printf(MSG_DEBUG, 789 "WMM AC: Could not parse WMM AC action from " MACSTR, 790 MAC2STR(sa)); 791 return; 792 } 793 794 /* the struct also contains the type and value, so decrease it */ 795 if (elems.wmm_tspec_len != sizeof(struct wmm_tspec_element) - 2) { 796 wpa_printf(MSG_DEBUG, "WMM AC: missing or wrong length TSPEC"); 797 return; 798 } 799 800 tspec = (struct wmm_tspec_element *)(elems.wmm_tspec - 2); 801 802 wpa_printf(MSG_DEBUG, "WMM AC: RX WMM AC Action from " MACSTR, 803 MAC2STR(sa)); 804 wpa_hexdump(MSG_MSGDUMP, "WMM AC: WMM AC Action content", data, len); 805 806 switch (action) { 807 case WMM_ACTION_CODE_ADDTS_RESP: 808 wmm_ac_handle_addts_resp(wpa_s, sa, dialog_token, status_code, 809 tspec); 810 break; 811 case WMM_ACTION_CODE_DELTS: 812 wmm_ac_handle_delts(wpa_s, sa, tspec); 813 break; 814 default: 815 break; 816 } 817 } 818 819 820 static const char * get_ac_str(u8 ac) 821 { 822 switch (ac) { 823 case WMM_AC_BE: 824 return "BE"; 825 case WMM_AC_BK: 826 return "BK"; 827 case WMM_AC_VI: 828 return "VI"; 829 case WMM_AC_VO: 830 return "VO"; 831 default: 832 return "N/A"; 833 } 834 } 835 836 837 static const char * get_direction_str(u8 direction) 838 { 839 switch (direction) { 840 case WMM_AC_DIR_DOWNLINK: 841 return "Downlink"; 842 case WMM_AC_DIR_UPLINK: 843 return "Uplink"; 844 case WMM_AC_DIR_BIDIRECTIONAL: 845 return "Bi-directional"; 846 default: 847 return "N/A"; 848 } 849 } 850 851 852 int wpas_wmm_ac_status(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) 853 { 854 struct wmm_ac_assoc_data *assoc_info = wpa_s->wmm_ac_assoc_info; 855 enum ts_dir_idx idx; 856 int pos = 0; 857 u8 ac, up; 858 859 if (!assoc_info) { 860 return wpa_scnprintf(buf, buflen - pos, 861 "Not associated to a WMM AP, WMM AC is Disabled\n"); 862 } 863 864 pos += wpa_scnprintf(buf + pos, buflen - pos, "WMM AC is Enabled\n"); 865 866 for (ac = 0; ac < WMM_AC_NUM; ac++) { 867 int ts_count = 0; 868 869 pos += wpa_scnprintf(buf + pos, buflen - pos, 870 "%s: acm=%d uapsd=%d\n", 871 get_ac_str(ac), 872 assoc_info->ac_params[ac].acm, 873 assoc_info->ac_params[ac].uapsd); 874 875 for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) { 876 struct wmm_tspec_element *tspec; 877 u8 dir, tsid; 878 const char *dir_str; 879 880 tspec = wpa_s->tspecs[ac][idx]; 881 if (!tspec) 882 continue; 883 884 ts_count++; 885 886 dir = wmm_ac_get_direction(tspec); 887 dir_str = get_direction_str(dir); 888 tsid = wmm_ac_get_tsid(tspec); 889 up = wmm_ac_get_user_priority(tspec); 890 891 pos += wpa_scnprintf(buf + pos, buflen - pos, 892 "\tTSID=%u UP=%u\n" 893 "\tAddress = "MACSTR"\n" 894 "\tWMM AC dir = %s\n" 895 "\tTotal admitted time = %u\n\n", 896 tsid, up, 897 MAC2STR(wpa_s->bssid), 898 dir_str, 899 le_to_host16(tspec->medium_time)); 900 } 901 902 if (!ts_count) { 903 pos += wpa_scnprintf(buf + pos, buflen - pos, 904 "\t(No Traffic Stream)\n\n"); 905 } 906 } 907 908 return pos; 909 } 910 911 912 static u8 wmm_ac_get_tspecs_count(struct wpa_supplicant *wpa_s) 913 { 914 int ac, dir, tspecs_count = 0; 915 916 for (ac = 0; ac < WMM_AC_NUM; ac++) { 917 for (dir = 0; dir < TS_DIR_IDX_COUNT; dir++) { 918 if (wpa_s->tspecs[ac][dir]) 919 tspecs_count++; 920 } 921 } 922 923 return tspecs_count; 924 } 925 926 927 void wmm_ac_save_tspecs(struct wpa_supplicant *wpa_s) 928 { 929 int ac, dir, tspecs_count; 930 931 wpa_printf(MSG_DEBUG, "WMM AC: Save last configured tspecs"); 932 933 if (!wpa_s->wmm_ac_assoc_info) 934 return; 935 936 tspecs_count = wmm_ac_get_tspecs_count(wpa_s); 937 if (!tspecs_count) { 938 wpa_printf(MSG_DEBUG, "WMM AC: No configured TSPECs"); 939 return; 940 } 941 942 wpa_printf(MSG_DEBUG, "WMM AC: Saving tspecs"); 943 944 wmm_ac_clear_saved_tspecs(wpa_s); 945 wpa_s->last_tspecs = os_calloc(tspecs_count, 946 sizeof(*wpa_s->last_tspecs)); 947 if (!wpa_s->last_tspecs) { 948 wpa_printf(MSG_ERROR, "WMM AC: Failed to save tspecs!"); 949 return; 950 } 951 952 for (ac = 0; ac < WMM_AC_NUM; ac++) { 953 for (dir = 0; dir < TS_DIR_IDX_COUNT; dir++) { 954 if (!wpa_s->tspecs[ac][dir]) 955 continue; 956 957 wpa_s->last_tspecs[wpa_s->last_tspecs_count++] = 958 *wpa_s->tspecs[ac][dir]; 959 } 960 } 961 962 wpa_printf(MSG_DEBUG, "WMM AC: Successfully saved %d TSPECs", 963 wpa_s->last_tspecs_count); 964 } 965 966 967 void wmm_ac_clear_saved_tspecs(struct wpa_supplicant *wpa_s) 968 { 969 if (wpa_s->last_tspecs) { 970 wpa_printf(MSG_DEBUG, "WMM AC: Clear saved tspecs"); 971 os_free(wpa_s->last_tspecs); 972 wpa_s->last_tspecs = NULL; 973 wpa_s->last_tspecs_count = 0; 974 } 975 } 976 977 978 int wmm_ac_restore_tspecs(struct wpa_supplicant *wpa_s) 979 { 980 unsigned int i; 981 982 if (!wpa_s->wmm_ac_assoc_info || !wpa_s->last_tspecs_count) 983 return 0; 984 985 wpa_printf(MSG_DEBUG, "WMM AC: Restore %u saved tspecs", 986 wpa_s->last_tspecs_count); 987 988 for (i = 0; i < wpa_s->last_tspecs_count; i++) 989 wmm_ac_add_ts(wpa_s, wpa_s->bssid, &wpa_s->last_tspecs[i]); 990 991 return 0; 992 } 993