diff options
Diffstat (limited to 'contrib/wpa/wpa_supplicant/wnm_sta.c')
-rw-r--r-- | contrib/wpa/wpa_supplicant/wnm_sta.c | 530 |
1 files changed, 421 insertions, 109 deletions
diff --git a/contrib/wpa/wpa_supplicant/wnm_sta.c b/contrib/wpa/wpa_supplicant/wnm_sta.c index 67a07ff7b1e7..6b68fc9e3772 100644 --- a/contrib/wpa/wpa_supplicant/wnm_sta.c +++ b/contrib/wpa/wpa_supplicant/wnm_sta.c @@ -13,6 +13,7 @@ #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" #include "rsn_supp/wpa.h" +#include "config.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "scan.h" @@ -84,12 +85,11 @@ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, /* TFS IE(s) */ if (tfs_req) { wnmtfs_ie_len = wpabuf_len(tfs_req); - wnmtfs_ie = os_malloc(wnmtfs_ie_len); + wnmtfs_ie = os_memdup(wpabuf_head(tfs_req), wnmtfs_ie_len); if (wnmtfs_ie == NULL) { os_free(wnmsleep_ie); return -1; } - os_memcpy(wnmtfs_ie, wpabuf_head(tfs_req), wnmtfs_ie_len); } else { wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN); if (wnmtfs_ie == NULL) { @@ -338,6 +338,9 @@ void wnm_deallocate_memory(struct wpa_supplicant *wpa_s) wpa_s->wnm_num_neighbor_report = 0; os_free(wpa_s->wnm_neighbor_report_elements); wpa_s->wnm_neighbor_report_elements = NULL; + + wpabuf_free(wpa_s->coloc_intf_elems); + wpa_s->coloc_intf_elems = NULL; } @@ -501,10 +504,128 @@ static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s, } +static void wnm_clear_acceptable(struct wpa_supplicant *wpa_s) +{ + unsigned int i; + + for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) + wpa_s->wnm_neighbor_report_elements[i].acceptable = 0; +} + + +static struct wpa_bss * get_first_acceptable(struct wpa_supplicant *wpa_s) +{ + unsigned int i; + struct neighbor_report *nei; + + for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { + nei = &wpa_s->wnm_neighbor_report_elements[i]; + if (nei->acceptable) + return wpa_bss_get_bssid(wpa_s, nei->bssid); + } + + return NULL; +} + + +#ifdef CONFIG_MBO static struct wpa_bss * -compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs) +get_mbo_transition_candidate(struct wpa_supplicant *wpa_s, + enum mbo_transition_reject_reason *reason) { + struct wpa_bss *target = NULL; + struct wpa_bss_trans_info params; + struct wpa_bss_candidate_info *info = NULL; + struct neighbor_report *nei = wpa_s->wnm_neighbor_report_elements; + u8 *first_candidate_bssid = NULL, *pos; + unsigned int i; + + params.mbo_transition_reason = wpa_s->wnm_mbo_transition_reason; + params.n_candidates = 0; + params.bssid = os_calloc(wpa_s->wnm_num_neighbor_report, ETH_ALEN); + if (!params.bssid) + return NULL; + + pos = params.bssid; + for (i = 0; i < wpa_s->wnm_num_neighbor_report; nei++, i++) { + if (nei->is_first) + first_candidate_bssid = nei->bssid; + if (!nei->acceptable) + continue; + os_memcpy(pos, nei->bssid, ETH_ALEN); + pos += ETH_ALEN; + params.n_candidates++; + } + + if (!params.n_candidates) + goto end; + + info = wpa_drv_get_bss_trans_status(wpa_s, ¶ms); + if (!info) { + /* If failed to get candidate BSS transition status from driver, + * get the first acceptable candidate from wpa_supplicant. + */ + target = wpa_bss_get_bssid(wpa_s, params.bssid); + goto end; + } + + /* Get the first acceptable candidate from driver */ + for (i = 0; i < info->num; i++) { + if (info->candidates[i].is_accept) { + target = wpa_bss_get_bssid(wpa_s, + info->candidates[i].bssid); + goto end; + } + } + + /* If Disassociation Imminent is set and driver rejects all the + * candidate select first acceptable candidate which has + * rssi > disassoc_imminent_rssi_threshold + */ + if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) { + for (i = 0; i < info->num; i++) { + target = wpa_bss_get_bssid(wpa_s, + info->candidates[i].bssid); + if (target && + (target->level < + wpa_s->conf->disassoc_imminent_rssi_threshold)) + continue; + goto end; + } + } + + /* While sending BTM reject use reason code of the first candidate + * received in BTM request frame + */ + if (reason) { + for (i = 0; i < info->num; i++) { + if (first_candidate_bssid && + os_memcmp(first_candidate_bssid, + info->candidates[i].bssid, ETH_ALEN) == 0) + { + *reason = info->candidates[i].reject_reason; + break; + } + } + } + + target = NULL; + +end: + os_free(params.bssid); + if (info) { + os_free(info->candidates); + os_free(info); + } + return target; +} +#endif /* CONFIG_MBO */ + +static struct wpa_bss * +compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs, + enum mbo_transition_reject_reason *reason) +{ u8 i; struct wpa_bss *bss = wpa_s->current_bss; struct wpa_bss *target; @@ -515,6 +636,8 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs) wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d", MAC2STR(wpa_s->bssid), bss->level); + wnm_clear_acceptable(wpa_s); + for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { struct neighbor_report *nei; @@ -564,7 +687,7 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs) if (wpa_s->current_ssid && !wpa_scan_res_match(wpa_s, 0, target, wpa_s->current_ssid, - 1)) { + 1, 0)) { wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR " (pref %d) does not match the current network profile", MAC2STR(nei->bssid), @@ -591,14 +714,26 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs) continue; } + nei->acceptable = 1; + } + +#ifdef CONFIG_MBO + if (wpa_s->wnm_mbo_trans_reason_present) + target = get_mbo_transition_candidate(wpa_s, reason); + else + target = get_first_acceptable(wpa_s); +#else /* CONFIG_MBO */ + target = get_first_acceptable(wpa_s); +#endif /* CONFIG_MBO */ + + if (target) { wpa_printf(MSG_DEBUG, "WNM: Found an acceptable preferred transition candidate BSS " MACSTR " (RSSI %d)", - MAC2STR(nei->bssid), target->level); - return target; + MAC2STR(target->bssid), target->level); } - return NULL; + return target; } @@ -651,36 +786,40 @@ static u32 wnm_get_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) } -static int wnm_add_nei_rep(u8 *buf, size_t len, const u8 *bssid, u32 bss_info, - u8 op_class, u8 chan, u8 phy_type, u8 pref) +static int wnm_add_nei_rep(struct wpabuf **buf, const u8 *bssid, + u32 bss_info, u8 op_class, u8 chan, u8 phy_type, + u8 pref) { - u8 *pos = buf; + if (wpabuf_len(*buf) + 18 > + IEEE80211_MAX_MMPDU_SIZE - IEEE80211_HDRLEN) { + wpa_printf(MSG_DEBUG, + "WNM: No room in frame for Neighbor Report element"); + return -1; + } - if (len < 18) { + if (wpabuf_resize(buf, 18) < 0) { wpa_printf(MSG_DEBUG, - "WNM: Not enough room for Neighbor Report element"); + "WNM: Failed to allocate memory for Neighbor Report element"); return -1; } - *pos++ = WLAN_EID_NEIGHBOR_REPORT; + wpabuf_put_u8(*buf, WLAN_EID_NEIGHBOR_REPORT); /* length: 13 for basic neighbor report + 3 for preference subelement */ - *pos++ = 16; - os_memcpy(pos, bssid, ETH_ALEN); - pos += ETH_ALEN; - WPA_PUT_LE32(pos, bss_info); - pos += 4; - *pos++ = op_class; - *pos++ = chan; - *pos++ = phy_type; - *pos++ = WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE; - *pos++ = 1; - *pos++ = pref; - return pos - buf; + wpabuf_put_u8(*buf, 16); + wpabuf_put_data(*buf, bssid, ETH_ALEN); + wpabuf_put_le32(*buf, bss_info); + wpabuf_put_u8(*buf, op_class); + wpabuf_put_u8(*buf, chan); + wpabuf_put_u8(*buf, phy_type); + wpabuf_put_u8(*buf, WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE); + wpabuf_put_u8(*buf, 1); + wpabuf_put_u8(*buf, pref); + return 0; } static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s, - struct wpa_bss *bss, u8 *buf, size_t len, + struct wpa_bss *bss, struct wpabuf **buf, u8 pref) { const u8 *ie; @@ -729,20 +868,19 @@ static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s, info = wnm_get_bss_info(wpa_s, bss); - return wnm_add_nei_rep(buf, len, bss->bssid, info, op_class, chan, - phy_type, pref); + return wnm_add_nei_rep(buf, bss->bssid, info, op_class, chan, phy_type, + pref); } -static int wnm_add_cand_list(struct wpa_supplicant *wpa_s, u8 *buf, size_t len) +static void wnm_add_cand_list(struct wpa_supplicant *wpa_s, struct wpabuf **buf) { - u8 *pos = buf; unsigned int i, pref = 255; struct os_reltime now; struct wpa_ssid *ssid = wpa_s->current_ssid; if (!ssid) - return 0; + return; /* * TODO: Define when scan results are no longer valid for the candidate @@ -750,7 +888,7 @@ static int wnm_add_cand_list(struct wpa_supplicant *wpa_s, u8 *buf, size_t len) */ os_get_reltime(&now); if (os_reltime_expired(&now, &wpa_s->last_scan, 10)) - return 0; + return; wpa_printf(MSG_DEBUG, "WNM: Add candidate list to BSS Transition Management Response frame"); @@ -758,93 +896,100 @@ static int wnm_add_cand_list(struct wpa_supplicant *wpa_s, u8 *buf, size_t len) struct wpa_bss *bss = wpa_s->last_scan_res[i]; int res; - if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1)) { - res = wnm_nei_rep_add_bss(wpa_s, bss, pos, len, pref--); + if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1, 0)) { + res = wnm_nei_rep_add_bss(wpa_s, bss, buf, pref--); if (res == -2) continue; /* could not build entry for BSS */ if (res < 0) break; /* no more room for candidates */ if (pref == 1) break; - - pos += res; - len -= res; } } - wpa_hexdump(MSG_DEBUG, - "WNM: BSS Transition Management Response candidate list", - buf, pos - buf); - - return pos - buf; + wpa_hexdump_buf(MSG_DEBUG, + "WNM: BSS Transition Management Response candidate list", + *buf); } +#define BTM_RESP_MIN_SIZE 5 + ETH_ALEN + static void wnm_send_bss_transition_mgmt_resp( struct wpa_supplicant *wpa_s, u8 dialog_token, - enum bss_trans_mgmt_status_code status, u8 delay, - const u8 *target_bssid) + enum bss_trans_mgmt_status_code status, + enum mbo_transition_reject_reason reason, + u8 delay, const u8 *target_bssid) { - u8 buf[2000], *pos; - struct ieee80211_mgmt *mgmt; - size_t len; + struct wpabuf *buf; int res; - wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response " - "to " MACSTR " dialog_token=%u status=%u delay=%d", - MAC2STR(wpa_s->bssid), dialog_token, status, delay); + wpa_printf(MSG_DEBUG, + "WNM: Send BSS Transition Management Response to " MACSTR + " dialog_token=%u status=%u reason=%u delay=%d", + MAC2STR(wpa_s->bssid), dialog_token, status, reason, delay); if (!wpa_s->current_bss) { wpa_printf(MSG_DEBUG, "WNM: Current BSS not known - drop response"); return; } - mgmt = (struct ieee80211_mgmt *) buf; - os_memset(&buf, 0, sizeof(buf)); - os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); - os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); - os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); - mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_ACTION); - mgmt->u.action.category = WLAN_ACTION_WNM; - mgmt->u.action.u.bss_tm_resp.action = WNM_BSS_TRANS_MGMT_RESP; - mgmt->u.action.u.bss_tm_resp.dialog_token = dialog_token; - mgmt->u.action.u.bss_tm_resp.status_code = status; - mgmt->u.action.u.bss_tm_resp.bss_termination_delay = delay; - pos = mgmt->u.action.u.bss_tm_resp.variable; + buf = wpabuf_alloc(BTM_RESP_MIN_SIZE); + if (!buf) { + wpa_printf(MSG_DEBUG, + "WNM: Failed to allocate memory for BTM response"); + return; + } + + wpabuf_put_u8(buf, WLAN_ACTION_WNM); + wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_RESP); + wpabuf_put_u8(buf, dialog_token); + wpabuf_put_u8(buf, status); + wpabuf_put_u8(buf, delay); if (target_bssid) { - os_memcpy(pos, target_bssid, ETH_ALEN); - pos += ETH_ALEN; + wpabuf_put_data(buf, target_bssid, ETH_ALEN); } else if (status == WNM_BSS_TM_ACCEPT) { /* * P802.11-REVmc clarifies that the Target BSSID field is always * present when status code is zero, so use a fake value here if * no BSSID is yet known. */ - os_memset(pos, 0, ETH_ALEN); - pos += ETH_ALEN; + wpabuf_put_data(buf, "\0\0\0\0\0\0", ETH_ALEN); } if (status == WNM_BSS_TM_ACCEPT) - pos += wnm_add_cand_list(wpa_s, pos, buf + sizeof(buf) - pos); + wnm_add_cand_list(wpa_s, &buf); #ifdef CONFIG_MBO - if (status != WNM_BSS_TM_ACCEPT) { - pos += wpas_mbo_ie_bss_trans_reject( - wpa_s, pos, buf + sizeof(buf) - pos, - MBO_TRANSITION_REJECT_REASON_UNSPECIFIED); + if (status != WNM_BSS_TM_ACCEPT && + wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) { + u8 mbo[10]; + size_t ret; + + ret = wpas_mbo_ie_bss_trans_reject(wpa_s, mbo, sizeof(mbo), + reason); + if (ret) { + if (wpabuf_resize(&buf, ret) < 0) { + wpabuf_free(buf); + wpa_printf(MSG_DEBUG, + "WNM: Failed to allocate memory for MBO IE"); + return; + } + + wpabuf_put_data(buf, mbo, ret); + } } #endif /* CONFIG_MBO */ - len = pos - (u8 *) &mgmt->u.action.category; - res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid, - &mgmt->u.action.category, len, 0); + wpabuf_head_u8(buf), wpabuf_len(buf), 0); if (res < 0) { wpa_printf(MSG_DEBUG, "WNM: Failed to send BSS Transition Management Response"); } + + wpabuf_free(buf); } @@ -863,10 +1008,10 @@ static void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s, wpa_s->wnm_reply = 0; wpa_printf(MSG_DEBUG, "WNM: Sending successful BSS Transition Management Response"); - wnm_send_bss_transition_mgmt_resp(wpa_s, - wpa_s->wnm_dialog_token, - WNM_BSS_TM_ACCEPT, - 0, bss->bssid); + wnm_send_bss_transition_mgmt_resp( + wpa_s, wpa_s->wnm_dialog_token, WNM_BSS_TM_ACCEPT, + MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, + bss->bssid); } if (bss == wpa_s->current_bss) { @@ -888,6 +1033,8 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail) struct wpa_bss *bss; struct wpa_ssid *ssid = wpa_s->current_ssid; enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED; + enum mbo_transition_reject_reason reason = + MBO_TRANSITION_REJECT_REASON_UNSPECIFIED; if (!wpa_s->wnm_neighbor_report_elements) return 0; @@ -909,7 +1056,7 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail) } /* Compare the Neighbor Report and scan results */ - bss = compare_scan_neighbor_results(wpa_s, 0); + bss = compare_scan_neighbor_results(wpa_s, 0, &reason); if (!bss) { wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found"); status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES; @@ -930,7 +1077,7 @@ send_bss_resp_fail: wpa_s->wnm_reply = 0; wnm_send_bss_transition_mgmt_resp(wpa_s, wpa_s->wnm_dialog_token, - status, 0, NULL); + status, reason, 0, NULL); } wnm_deallocate_memory(wpa_s); @@ -1118,7 +1265,7 @@ static int wnm_fetch_scan_results(struct wpa_supplicant *wpa_s) return 0; } - bss = compare_scan_neighbor_results(wpa_s, WNM_SCAN_RESULT_AGE); + bss = compare_scan_neighbor_results(wpa_s, WNM_SCAN_RESULT_AGE, NULL); if (!bss) { wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Comparison of scan results against transition candidates did not find matches"); @@ -1144,6 +1291,11 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, if (end - pos < 5) return; +#ifdef CONFIG_MBO + wpa_s->wnm_mbo_trans_reason_present = 0; + wpa_s->wnm_mbo_transition_reason = 0; +#endif /* CONFIG_MBO */ + if (wpa_s->current_bss) beacon_int = wpa_s->current_bss->beacon_int; else @@ -1166,10 +1318,10 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wpa_printf(MSG_INFO, "WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d", wpa_s->reject_btm_req_reason); - wnm_send_bss_transition_mgmt_resp(wpa_s, - wpa_s->wnm_dialog_token, - wpa_s->reject_btm_req_reason, - 0, NULL); + wnm_send_bss_transition_mgmt_resp( + wpa_s, wpa_s->wnm_dialog_token, + wpa_s->reject_btm_req_reason, + MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL); return; } #endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */ @@ -1248,6 +1400,15 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wpa_s->wnm_num_neighbor_report]; wnm_parse_neighbor_report(wpa_s, pos, len, rep); wpa_s->wnm_num_neighbor_report++; +#ifdef CONFIG_MBO + if (wpa_s->wnm_mbo_trans_reason_present && + wpa_s->wnm_num_neighbor_report == 1) { + rep->is_first = 1; + wpa_printf(MSG_DEBUG, + "WNM: First transition candidate is " + MACSTR, MAC2STR(rep->bssid)); + } +#endif /* CONFIG_MBO */ } pos += len; @@ -1259,7 +1420,8 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wnm_send_bss_transition_mgmt_resp( wpa_s, wpa_s->wnm_dialog_token, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES, - 0, NULL); + MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, + NULL); return; } @@ -1322,19 +1484,21 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates"); status = WNM_BSS_TM_REJECT_UNSPECIFIED; } - wnm_send_bss_transition_mgmt_resp(wpa_s, - wpa_s->wnm_dialog_token, - status, 0, NULL); + wnm_send_bss_transition_mgmt_resp( + wpa_s, wpa_s->wnm_dialog_token, status, + MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL); } } +#define BTM_QUERY_MIN_SIZE 4 + int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, - u8 query_reason, int cand_list) + u8 query_reason, + const char *btm_candidates, + int cand_list) { - u8 buf[2000], *pos; - struct ieee80211_mgmt *mgmt; - size_t len; + struct wpabuf *buf; int ret; wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to " @@ -1342,28 +1506,43 @@ int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, MAC2STR(wpa_s->bssid), query_reason, cand_list ? " candidate list" : ""); - mgmt = (struct ieee80211_mgmt *) buf; - os_memset(&buf, 0, sizeof(buf)); - os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); - os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); - os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); - mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_ACTION); - mgmt->u.action.category = WLAN_ACTION_WNM; - mgmt->u.action.u.bss_tm_query.action = WNM_BSS_TRANS_MGMT_QUERY; - mgmt->u.action.u.bss_tm_query.dialog_token = 1; - mgmt->u.action.u.bss_tm_query.query_reason = query_reason; - pos = mgmt->u.action.u.bss_tm_query.variable; + buf = wpabuf_alloc(BTM_QUERY_MIN_SIZE); + if (!buf) + return -1; + + wpabuf_put_u8(buf, WLAN_ACTION_WNM); + wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_QUERY); + wpabuf_put_u8(buf, 1); + wpabuf_put_u8(buf, query_reason); if (cand_list) - pos += wnm_add_cand_list(wpa_s, pos, buf + sizeof(buf) - pos); + wnm_add_cand_list(wpa_s, &buf); + + if (btm_candidates) { + const size_t max_len = 1000; + + ret = wpabuf_resize(&buf, max_len); + if (ret < 0) { + wpabuf_free(buf); + return ret; + } + + ret = ieee802_11_parse_candidate_list(btm_candidates, + wpabuf_put(buf, 0), + max_len); + if (ret < 0) { + wpabuf_free(buf); + return ret; + } - len = pos - (u8 *) &mgmt->u.action.category; + wpabuf_put(buf, ret); + } ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid, - &mgmt->u.action.category, len, 0); + wpabuf_head_u8(buf), wpabuf_len(buf), 0); + wpabuf_free(buf); return ret; } @@ -1468,6 +1647,32 @@ static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s, pos = next; continue; } + + if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 && + WPA_GET_BE24(pos) == OUI_WFA && + pos[3] == HS20_WNM_T_C_ACCEPTANCE) { + const u8 *ie_end; + u8 url_len; + char *url; + + ie_end = pos + ie_len; + pos += 4; + url_len = *pos++; + wpa_printf(MSG_DEBUG, + "WNM: HS 2.0 Terms and Conditions Acceptance (URL Length %u)", + url_len); + if (url_len > ie_end - pos) + break; + url = os_malloc(url_len + 1); + if (!url) + break; + os_memcpy(url, pos, url_len); + url[url_len] = '\0'; + hs20_rx_t_c_acceptance(wpa_s, url); + os_free(url); + pos = next; + continue; + } #endif /* CONFIG_HS20 */ pos = next; @@ -1515,6 +1720,46 @@ static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s, } +static void ieee802_11_rx_wnm_coloc_intf_req(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *frm, + int len) +{ + u8 dialog_token, req_info, auto_report, timeout; + + if (!wpa_s->conf->coloc_intf_reporting) + return; + + /* Dialog Token [1] | Request Info [1] */ + + if (len < 2) + return; + dialog_token = frm[0]; + req_info = frm[1]; + auto_report = req_info & 0x03; + timeout = req_info >> 2; + + wpa_dbg(wpa_s, MSG_DEBUG, + "WNM: Received Collocated Interference Request (dialog_token %u auto_report %u timeout %u sa " MACSTR ")", + dialog_token, auto_report, timeout, MAC2STR(sa)); + + if (dialog_token == 0) + return; /* only nonzero values are used for request */ + + if (wpa_s->wpa_state != WPA_COMPLETED || + os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) { + wpa_dbg(wpa_s, MSG_DEBUG, + "WNM: Collocated Interference Request frame not from current AP - ignore it"); + return; + } + + wpa_msg(wpa_s, MSG_INFO, COLOC_INTF_REQ "%u %u %u", + dialog_token, auto_report, timeout); + wpa_s->coloc_intf_dialog_token = dialog_token; + wpa_s->coloc_intf_auto_report = auto_report; + wpa_s->coloc_intf_timeout = timeout; +} + + void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, const struct ieee80211_mgmt *mgmt, size_t len) { @@ -1548,8 +1793,75 @@ void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, case WNM_NOTIFICATION_REQ: ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos); break; + case WNM_COLLOCATED_INTERFERENCE_REQ: + ieee802_11_rx_wnm_coloc_intf_req(wpa_s, mgmt->sa, pos, + end - pos); + break; default: wpa_printf(MSG_ERROR, "WNM: Unknown request"); break; } } + + +int wnm_send_coloc_intf_report(struct wpa_supplicant *wpa_s, u8 dialog_token, + const struct wpabuf *elems) +{ + struct wpabuf *buf; + int ret; + + if (wpa_s->wpa_state < WPA_ASSOCIATED || !elems) + return -1; + + wpa_printf(MSG_DEBUG, "WNM: Send Collocated Interference Report to " + MACSTR " (dialog token %u)", + MAC2STR(wpa_s->bssid), dialog_token); + + buf = wpabuf_alloc(3 + wpabuf_len(elems)); + if (!buf) + return -1; + + wpabuf_put_u8(buf, WLAN_ACTION_WNM); + wpabuf_put_u8(buf, WNM_COLLOCATED_INTERFERENCE_REPORT); + wpabuf_put_u8(buf, dialog_token); + wpabuf_put_buf(buf, elems); + + ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head_u8(buf), wpabuf_len(buf), 0); + wpabuf_free(buf); + return ret; +} + + +void wnm_set_coloc_intf_elems(struct wpa_supplicant *wpa_s, + struct wpabuf *elems) +{ + wpabuf_free(wpa_s->coloc_intf_elems); + if (elems && wpabuf_len(elems) == 0) { + wpabuf_free(elems); + elems = NULL; + } + wpa_s->coloc_intf_elems = elems; + + if (wpa_s->conf->coloc_intf_reporting && wpa_s->coloc_intf_elems && + wpa_s->coloc_intf_dialog_token && + (wpa_s->coloc_intf_auto_report == 1 || + wpa_s->coloc_intf_auto_report == 3)) { + /* TODO: Check that there has not been less than + * wpa_s->coloc_intf_timeout * 200 TU from the last report. + */ + wnm_send_coloc_intf_report(wpa_s, + wpa_s->coloc_intf_dialog_token, + wpa_s->coloc_intf_elems); + } +} + + +void wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s) +{ +#ifdef CONFIG_WNM + wpa_s->coloc_intf_dialog_token = 0; + wpa_s->coloc_intf_auto_report = 0; +#endif /* CONFIG_WNM */ +} |