X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=wpa_supplicant%2Fwmm_ac.c;h=5625d36638b5c33dc60e1517b2f0a91c8365e9ee;hb=b84ce655d31a2236734c788fd3292c10d5d9f0b1;hp=0656699503b602e4e5b1b4de9987d78fa1a18568;hpb=674f6c073f6f7cd9e04e5f117710f03d5e09ad63;p=mech_eap.git diff --git a/wpa_supplicant/wmm_ac.c b/wpa_supplicant/wmm_ac.c index 0656699..5625d36 100644 --- a/wpa_supplicant/wmm_ac.c +++ b/wpa_supplicant/wmm_ac.c @@ -11,12 +11,14 @@ #include "utils/common.h" #include "utils/list.h" +#include "utils/eloop.h" #include "common/ieee802_11_common.h" #include "wpa_supplicant_i.h" #include "bss.h" #include "driver_i.h" #include "wmm_ac.h" +static void wmm_ac_addts_req_timeout(void *eloop_ctx, void *timeout_ctx); static const enum wmm_ac up_to_ac[8] = { WMM_AC_BK, @@ -36,6 +38,141 @@ static inline u8 wmm_ac_get_tsid(const struct wmm_tspec_element *tspec) } +static u8 wmm_ac_get_direction(const struct wmm_tspec_element *tspec) +{ + return (tspec->ts_info[0] >> 5) & 0x03; +} + + +static u8 wmm_ac_get_user_priority(const struct wmm_tspec_element *tspec) +{ + return (tspec->ts_info[1] >> 3) & 0x07; +} + + +static u8 wmm_ac_direction_to_idx(u8 direction) +{ + switch (direction) { + case WMM_AC_DIR_UPLINK: + return TS_DIR_IDX_UPLINK; + case WMM_AC_DIR_DOWNLINK: + return TS_DIR_IDX_DOWNLINK; + case WMM_AC_DIR_BIDIRECTIONAL: + return TS_DIR_IDX_BIDI; + default: + wpa_printf(MSG_ERROR, "Invalid direction: %d", direction); + return WMM_AC_DIR_UPLINK; + } +} + + +static int wmm_ac_add_ts(struct wpa_supplicant *wpa_s, const u8 *addr, + const struct wmm_tspec_element *tspec) +{ + struct wmm_tspec_element *_tspec; + int ret; + u16 admitted_time = le_to_host16(tspec->medium_time); + u8 up = wmm_ac_get_user_priority(tspec); + u8 ac = up_to_ac[up]; + u8 dir = wmm_ac_get_direction(tspec); + u8 tsid = wmm_ac_get_tsid(tspec); + enum ts_dir_idx idx = wmm_ac_direction_to_idx(dir); + + /* should have been verified before, but double-check here */ + if (wpa_s->tspecs[ac][idx]) { + wpa_printf(MSG_ERROR, + "WMM AC: tspec (ac=%d, dir=%d) already exists!", + ac, dir); + return -1; + } + + /* copy tspec */ + _tspec = os_malloc(sizeof(*_tspec)); + if (!_tspec) + return -1; + + /* store the admitted TSPEC */ + os_memcpy(_tspec, tspec, sizeof(*_tspec)); + + if (dir != WMM_AC_DIR_DOWNLINK) { + ret = wpa_drv_add_ts(wpa_s, tsid, addr, up, admitted_time); + wpa_printf(MSG_DEBUG, + "WMM AC: Add TS: addr=" MACSTR + " TSID=%u admitted time=%u, ret=%d", + MAC2STR(addr), tsid, admitted_time, ret); + if (ret < 0) { + os_free(_tspec); + return -1; + } + } + + wpa_s->tspecs[ac][idx] = _tspec; + + wpa_printf(MSG_DEBUG, "Traffic stream was created successfully"); + + wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_ADDED + "tsid=%d addr=" MACSTR " admitted_time=%d", + tsid, MAC2STR(addr), admitted_time); + + return 0; +} + + +static void wmm_ac_del_ts_idx(struct wpa_supplicant *wpa_s, u8 ac, + enum ts_dir_idx dir) +{ + struct wmm_tspec_element *tspec = wpa_s->tspecs[ac][dir]; + u8 tsid; + + if (!tspec) + return; + + tsid = wmm_ac_get_tsid(tspec); + wpa_printf(MSG_DEBUG, "WMM AC: Del TS ac=%d tsid=%d", ac, tsid); + + /* update the driver in case of uplink/bidi */ + if (wmm_ac_get_direction(tspec) != WMM_AC_DIR_DOWNLINK) + wpa_drv_del_ts(wpa_s, tsid, wpa_s->bssid); + + wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REMOVED + "tsid=%d addr=" MACSTR, tsid, MAC2STR(wpa_s->bssid)); + + os_free(wpa_s->tspecs[ac][dir]); + wpa_s->tspecs[ac][dir] = NULL; +} + + +static void wmm_ac_del_req(struct wpa_supplicant *wpa_s, int failed) +{ + struct wmm_ac_addts_request *req = wpa_s->addts_request; + + if (!req) + return; + + if (failed) + wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REQ_FAILED + "tsid=%u", wmm_ac_get_tsid(&req->tspec)); + + eloop_cancel_timeout(wmm_ac_addts_req_timeout, wpa_s, req); + wpa_s->addts_request = NULL; + os_free(req); +} + + +static void wmm_ac_addts_req_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wmm_ac_addts_request *addts_req = timeout_ctx; + + wpa_printf(MSG_DEBUG, + "Timeout getting ADDTS response (tsid=%d up=%d)", + wmm_ac_get_tsid(&addts_req->tspec), + wmm_ac_get_user_priority(&addts_req->tspec)); + + wmm_ac_del_req(wpa_s, 1); +} + + static int wmm_ac_send_addts_request(struct wpa_supplicant *wpa_s, const struct wmm_ac_addts_request *req) { @@ -290,7 +427,7 @@ wmm_ac_process_param_elem(struct wpa_supplicant *wpa_s, const u8 *ies, int i; /* Parsing WMM Parameter Element */ - if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) != ParseOK) { + if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) { wpa_printf(MSG_DEBUG, "WMM AC: could not parse assoc ies"); return NULL; } @@ -301,7 +438,7 @@ wmm_ac_process_param_elem(struct wpa_supplicant *wpa_s, const u8 *ies, } if (elems.wmm_len != sizeof(*wmm_params)) { - wpa_printf(MSG_WARNING, "WMM AC: Invalid WMM ie length"); + wpa_printf(MSG_DEBUG, "WMM AC: Invalid WMM ie length"); return NULL; } @@ -349,6 +486,7 @@ static int wmm_ac_init(struct wpa_supplicant *wpa_s, const u8 *ies, os_memset(wpa_s->tspecs, 0, sizeof(wpa_s->tspecs)); wpa_s->wmm_ac_last_dialog_token = 0; + wpa_s->addts_request = NULL; assoc_data = wmm_ac_process_param_elem(wpa_s, ies, ies_len); if (!assoc_data) @@ -375,11 +513,7 @@ static void wmm_ac_del_ts(struct wpa_supplicant *wpa_s, u8 ac, int dir_bitmap) if (!(dir_bitmap & BIT(idx))) continue; - if (!wpa_s->tspecs[ac][idx]) - continue; - - os_free(wpa_s->tspecs[ac][idx]); - wpa_s->tspecs[ac][idx] = NULL; + wmm_ac_del_ts_idx(wpa_s, ac, idx); } } @@ -391,6 +525,9 @@ static void wmm_ac_deinit(struct wpa_supplicant *wpa_s) for (i = 0; i < WMM_AC_NUM; i++) wmm_ac_del_ts(wpa_s, i, TS_DIR_IDX_ALL); + /* delete pending add_ts requset */ + wmm_ac_del_req(wpa_s, 1); + os_free(wpa_s->wmm_ac_assoc_info); wpa_s->wmm_ac_assoc_info = NULL; } @@ -419,7 +556,7 @@ void wmm_ac_notify_disassoc(struct wpa_supplicant *wpa_s) int wpas_wmm_ac_delts(struct wpa_supplicant *wpa_s, u8 tsid) { - struct wmm_tspec_element *tspec; + struct wmm_tspec_element tspec; int ac; enum ts_dir_idx dir; @@ -435,14 +572,11 @@ int wpas_wmm_ac_delts(struct wpa_supplicant *wpa_s, u8 tsid) return -1; } - tspec = wpa_s->tspecs[ac][dir]; - wmm_ac_send_delts(wpa_s, tspec, wpa_s->bssid); + tspec = *wpa_s->tspecs[ac][dir]; - os_free(tspec); - wpa_s->tspecs[ac][dir] = NULL; + wmm_ac_del_ts_idx(wpa_s, ac, dir); - wpa_printf(MSG_DEBUG, "WMM AC: TS was deleted (TSID=%u addr=" MACSTR - ")", tsid, MAC2STR(wpa_s->bssid)); + wmm_ac_send_delts(wpa_s, &tspec, wpa_s->bssid); return 0; } @@ -459,6 +593,12 @@ int wpas_wmm_ac_addts(struct wpa_supplicant *wpa_s, return -1; } + if (wpa_s->addts_request) { + wpa_printf(MSG_DEBUG, + "WMM AC: can't add TS - ADDTS request is already pending"); + return -1; + } + /* * we can setup downlink TS even without driver support. * however, we need driver support for the other directions. @@ -485,10 +625,371 @@ int wpas_wmm_ac_addts(struct wpa_supplicant *wpa_s, if (wmm_ac_send_addts_request(wpa_s, addts_req)) goto err; - /* TODO: wait for ADDTS response, etc. */ - os_free(addts_req); + /* save as pending and set ADDTS resp timeout to 1 second */ + wpa_s->addts_request = addts_req; + eloop_register_timeout(1, 0, wmm_ac_addts_req_timeout, + wpa_s, addts_req); return 0; err: os_free(addts_req); return -1; } + + +static void wmm_ac_handle_delts(struct wpa_supplicant *wpa_s, const u8 *sa, + const struct wmm_tspec_element *tspec) +{ + int ac; + u8 tsid; + enum ts_dir_idx idx; + + tsid = wmm_ac_get_tsid(tspec); + + wpa_printf(MSG_DEBUG, + "WMM AC: DELTS frame has been received TSID=%u addr=" + MACSTR, tsid, MAC2STR(sa)); + + ac = wmm_ac_find_tsid(wpa_s, tsid, &idx); + if (ac < 0) { + wpa_printf(MSG_DEBUG, + "WMM AC: Ignoring DELTS frame - TSID does not exist"); + return; + } + + wmm_ac_del_ts_idx(wpa_s, ac, idx); + + wpa_printf(MSG_DEBUG, + "TS was deleted successfully (tsid=%u address=" MACSTR ")", + tsid, MAC2STR(sa)); +} + + +static void wmm_ac_handle_addts_resp(struct wpa_supplicant *wpa_s, const u8 *sa, + const u8 resp_dialog_token, const u8 status_code, + const struct wmm_tspec_element *tspec) +{ + struct wmm_ac_addts_request *req = wpa_s->addts_request; + u8 ac, tsid, up, dir; + int replace_tspecs; + + tsid = wmm_ac_get_tsid(tspec); + dir = wmm_ac_get_direction(tspec); + up = wmm_ac_get_user_priority(tspec); + ac = up_to_ac[up]; + + /* make sure we have a matching addts request */ + if (!req || req->dialog_token != resp_dialog_token) { + wpa_printf(MSG_DEBUG, + "WMM AC: no req with dialog=%u, ignoring frame", + resp_dialog_token); + return; + } + + /* make sure the params are the same */ + if (os_memcmp(req->address, sa, ETH_ALEN) != 0 || + tsid != wmm_ac_get_tsid(&req->tspec) || + up != wmm_ac_get_user_priority(&req->tspec) || + dir != wmm_ac_get_direction(&req->tspec)) { + wpa_printf(MSG_DEBUG, + "WMM AC: ADDTS params do not match, ignoring frame"); + return; + } + + /* delete pending request */ + wmm_ac_del_req(wpa_s, 0); + + wpa_printf(MSG_DEBUG, + "ADDTS response status=%d tsid=%u up=%u direction=%u", + status_code, tsid, up, dir); + + if (status_code != WMM_ADDTS_STATUS_ADMISSION_ACCEPTED) { + wpa_printf(MSG_INFO, "WMM AC: ADDTS request was rejected"); + goto err_msg; + } + + replace_tspecs = wmm_ac_should_replace_ts(wpa_s, tsid, ac, dir); + if (replace_tspecs < 0) + goto err_delts; + + wpa_printf(MSG_DEBUG, "ts idx replace bitmap: 0x%x", replace_tspecs); + + /* when replacing tspecs - delete first */ + wmm_ac_del_ts(wpa_s, ac, replace_tspecs); + + /* Creating a new traffic stream */ + wpa_printf(MSG_DEBUG, + "WMM AC: adding a new TS with TSID=%u address="MACSTR + " medium time=%u access category=%d dir=%d ", + tsid, MAC2STR(sa), + le_to_host16(tspec->medium_time), ac, dir); + + if (wmm_ac_add_ts(wpa_s, sa, tspec)) + goto err_delts; + + return; + +err_delts: + /* ask the ap to delete the tspec */ + wmm_ac_send_delts(wpa_s, tspec, sa); +err_msg: + wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REQ_FAILED "tsid=%u", + tsid); +} + + +void wmm_ac_rx_action(struct wpa_supplicant *wpa_s, const u8 *da, + const u8 *sa, const u8 *data, size_t len) +{ + u8 action; + u8 dialog_token; + u8 status_code; + struct ieee802_11_elems elems; + struct wmm_tspec_element *tspec; + + if (wpa_s->wmm_ac_assoc_info == NULL) { + wpa_printf(MSG_DEBUG, + "WMM AC: WMM AC is disabled, ignoring action frame"); + return; + } + + action = data[0]; + + if (action != WMM_ACTION_CODE_ADDTS_RESP && + action != WMM_ACTION_CODE_DELTS) { + wpa_printf(MSG_DEBUG, + "WMM AC: Unknown action (%d), ignoring action frame", + action); + return; + } + + /* WMM AC action frame */ + if (os_memcmp(da, wpa_s->own_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "WMM AC: frame destination addr="MACSTR + " is other than ours, ignoring frame", MAC2STR(da)); + return; + } + + if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "WMM AC: ignore frame with sa " MACSTR + " different other than our bssid", MAC2STR(da)); + return; + } + + if (len < 2 + sizeof(struct wmm_tspec_element)) { + wpa_printf(MSG_DEBUG, + "WMM AC: Short ADDTS response ignored (len=%lu)", + (unsigned long) len); + return; + } + + data++; + len--; + dialog_token = data[0]; + status_code = data[1]; + + if (ieee802_11_parse_elems(data + 2, len - 2, &elems, 1) != ParseOK) { + wpa_printf(MSG_DEBUG, + "WMM AC: Could not parse WMM AC action from " MACSTR, + MAC2STR(sa)); + return; + } + + /* the struct also contains the type and value, so decrease it */ + if (elems.wmm_tspec_len != sizeof(struct wmm_tspec_element) - 2) { + wpa_printf(MSG_DEBUG, "WMM AC: missing or wrong length TSPEC"); + return; + } + + tspec = (struct wmm_tspec_element *)(elems.wmm_tspec - 2); + + wpa_printf(MSG_DEBUG, "WMM AC: RX WMM AC Action from " MACSTR, + MAC2STR(sa)); + wpa_hexdump(MSG_MSGDUMP, "WMM AC: WMM AC Action content", data, len); + + switch (action) { + case WMM_ACTION_CODE_ADDTS_RESP: + wmm_ac_handle_addts_resp(wpa_s, sa, dialog_token, status_code, + tspec); + break; + case WMM_ACTION_CODE_DELTS: + wmm_ac_handle_delts(wpa_s, sa, tspec); + break; + default: + break; + } +} + + +static const char * get_ac_str(u8 ac) +{ + switch (ac) { + case WMM_AC_BE: + return "BE"; + case WMM_AC_BK: + return "BK"; + case WMM_AC_VI: + return "VI"; + case WMM_AC_VO: + return "VO"; + default: + return "N/A"; + } +} + + +static const char * get_direction_str(u8 direction) +{ + switch (direction) { + case WMM_AC_DIR_DOWNLINK: + return "Downlink"; + case WMM_AC_DIR_UPLINK: + return "Uplink"; + case WMM_AC_DIR_BIDIRECTIONAL: + return "Bi-directional"; + default: + return "N/A"; + } +} + + +int wpas_wmm_ac_status(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) +{ + struct wmm_ac_assoc_data *assoc_info = wpa_s->wmm_ac_assoc_info; + enum ts_dir_idx idx; + int pos = 0; + u8 ac, up; + + if (!assoc_info) { + return wpa_scnprintf(buf, buflen - pos, + "Not associated to a WMM AP, WMM AC is Disabled\n"); + } + + pos += wpa_scnprintf(buf + pos, buflen - pos, "WMM AC is Enabled\n"); + + for (ac = 0; ac < WMM_AC_NUM; ac++) { + int ts_count = 0; + + pos += wpa_scnprintf(buf + pos, buflen - pos, + "%s: acm=%d uapsd=%d\n", + get_ac_str(ac), + assoc_info->ac_params[ac].acm, + assoc_info->ac_params[ac].uapsd); + + for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) { + struct wmm_tspec_element *tspec; + u8 dir, tsid; + const char *dir_str; + + tspec = wpa_s->tspecs[ac][idx]; + if (!tspec) + continue; + + ts_count++; + + dir = wmm_ac_get_direction(tspec); + dir_str = get_direction_str(dir); + tsid = wmm_ac_get_tsid(tspec); + up = wmm_ac_get_user_priority(tspec); + + pos += wpa_scnprintf(buf + pos, buflen - pos, + "\tTSID=%u UP=%u\n" + "\tAddress = "MACSTR"\n" + "\tWMM AC dir = %s\n" + "\tTotal admitted time = %u\n\n", + tsid, up, + MAC2STR(wpa_s->bssid), + dir_str, + le_to_host16(tspec->medium_time)); + } + + if (!ts_count) { + pos += wpa_scnprintf(buf + pos, buflen - pos, + "\t(No Traffic Stream)\n\n"); + } + } + + return pos; +} + + +static u8 wmm_ac_get_tspecs_count(struct wpa_supplicant *wpa_s) +{ + int ac, dir, tspecs_count = 0; + + for (ac = 0; ac < WMM_AC_NUM; ac++) { + for (dir = 0; dir < TS_DIR_IDX_COUNT; dir++) { + if (wpa_s->tspecs[ac][dir]) + tspecs_count++; + } + } + + return tspecs_count; +} + + +void wmm_ac_save_tspecs(struct wpa_supplicant *wpa_s) +{ + int ac, dir, tspecs_count; + + wpa_printf(MSG_DEBUG, "WMM AC: Save last configured tspecs"); + + if (!wpa_s->wmm_ac_assoc_info) + return; + + tspecs_count = wmm_ac_get_tspecs_count(wpa_s); + if (!tspecs_count) { + wpa_printf(MSG_DEBUG, "WMM AC: No configured TSPECs"); + return; + } + + wpa_printf(MSG_DEBUG, "WMM AC: Saving tspecs"); + + wmm_ac_clear_saved_tspecs(wpa_s); + wpa_s->last_tspecs = os_calloc(tspecs_count, + sizeof(*wpa_s->last_tspecs)); + if (!wpa_s->last_tspecs) { + wpa_printf(MSG_ERROR, "WMM AC: Failed to save tspecs!"); + return; + } + + for (ac = 0; ac < WMM_AC_NUM; ac++) { + for (dir = 0; dir < TS_DIR_IDX_COUNT; dir++) { + if (!wpa_s->tspecs[ac][dir]) + continue; + + wpa_s->last_tspecs[wpa_s->last_tspecs_count++] = + *wpa_s->tspecs[ac][dir]; + } + } + + wpa_printf(MSG_DEBUG, "WMM AC: Successfully saved %d TSPECs", + wpa_s->last_tspecs_count); +} + + +void wmm_ac_clear_saved_tspecs(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->last_tspecs) { + wpa_printf(MSG_DEBUG, "WMM AC: Clear saved tspecs"); + os_free(wpa_s->last_tspecs); + wpa_s->last_tspecs = NULL; + wpa_s->last_tspecs_count = 0; + } +} + + +int wmm_ac_restore_tspecs(struct wpa_supplicant *wpa_s) +{ + unsigned int i; + + if (!wpa_s->wmm_ac_assoc_info || !wpa_s->last_tspecs_count) + return 0; + + wpa_printf(MSG_DEBUG, "WMM AC: Restore %u saved tspecs", + wpa_s->last_tspecs_count); + + for (i = 0; i < wpa_s->last_tspecs_count; i++) + wmm_ac_add_ts(wpa_s, wpa_s->bssid, &wpa_s->last_tspecs[i]); + + return 0; +}