source: rtems-libbsd/freebsd/contrib/wpa/wpa_supplicant/wmm_ac.c @ 9c9d11b

55-freebsd-126-freebsd-12
Last change on this file since 9c9d11b was 9c9d11b, checked in by Sichen Zhao <1473996754@…>, on 08/01/17 at 12:43:41

Import wpa from FreeBSD

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