|
@@ -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"
|
|
@@ -499,10 +500,127 @@ 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->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;
|
|
@@ -513,6 +631,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;
|
|
|
|
|
@@ -589,14 +709,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;
|
|
|
}
|
|
|
|
|
|
|
|
@@ -780,17 +912,19 @@ static int wnm_add_cand_list(struct wpa_supplicant *wpa_s, u8 *buf, size_t len)
|
|
|
|
|
|
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;
|
|
|
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");
|
|
@@ -827,10 +961,10 @@ static void wnm_send_bss_transition_mgmt_resp(
|
|
|
pos += wnm_add_cand_list(wpa_s, pos, buf + sizeof(buf) - pos);
|
|
|
|
|
|
#ifdef CONFIG_MBO
|
|
|
- if (status != WNM_BSS_TM_ACCEPT) {
|
|
|
+ if (status != WNM_BSS_TM_ACCEPT &&
|
|
|
+ wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) {
|
|
|
pos += wpas_mbo_ie_bss_trans_reject(
|
|
|
- wpa_s, pos, buf + sizeof(buf) - pos,
|
|
|
- MBO_TRANSITION_REJECT_REASON_UNSPECIFIED);
|
|
|
+ wpa_s, pos, buf + sizeof(buf) - pos, reason);
|
|
|
}
|
|
|
#endif /* CONFIG_MBO */
|
|
|
|
|
@@ -861,10 +995,9 @@ 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_REASON_UNSPECIFIED, 0, bss->bssid);
|
|
|
}
|
|
|
|
|
|
if (bss == wpa_s->current_bss) {
|
|
@@ -886,6 +1019,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;
|
|
@@ -907,7 +1042,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;
|
|
@@ -928,7 +1063,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);
|
|
|
|
|
@@ -1116,7 +1251,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");
|
|
@@ -1142,6 +1277,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
|
|
@@ -1164,10 +1304,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_REASON_UNSPECIFIED, 0, NULL);
|
|
|
return;
|
|
|
}
|
|
|
#endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */
|
|
@@ -1246,6 +1386,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;
|
|
@@ -1257,7 +1406,7 @@ 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_REASON_UNSPECIFIED, 0, NULL);
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -1320,9 +1469,9 @@ 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_REASON_UNSPECIFIED, 0, NULL);
|
|
|
}
|
|
|
}
|
|
|
|