]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
MBO: Add support for transition reject reason code
authorKanchanapally, Vidyullatha <vkanchan@qti.qualcomm.com>
Mon, 6 Mar 2017 11:46:00 +0000 (17:16 +0530)
committerJouni Malinen <j@w1.fi>
Mon, 6 Mar 2017 22:34:14 +0000 (00:34 +0200)
Add support for rejecting a BSS transition request using MBO reject
reason codes. A candidate is selected or rejected based on whether it is
found acceptable by both wpa_supplicant and the driver. Also accept any
candidate meeting a certain threshold if disassoc imminent is set in BTM
Request frame.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
wpa_supplicant/config.c
wpa_supplicant/config.h
wpa_supplicant/config_file.c
wpa_supplicant/mbo.c
wpa_supplicant/wnm_sta.c
wpa_supplicant/wnm_sta.h
wpa_supplicant/wpa_supplicant_i.h

index 58ecf19fd5fbe4dbd13ac9c0e0197afa8e160aec..00c5ff0dbfdd3e2d8b40a96b6511e0a5b76ada95 100644 (file)
@@ -3742,6 +3742,8 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
 
 #ifdef CONFIG_MBO
        config->mbo_cell_capa = DEFAULT_MBO_CELL_CAPA;
+       config->disassoc_imminent_rssi_threshold =
+               DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD;
 #endif /* CONFIG_MBO */
 
        if (ctrl_interface)
@@ -4453,6 +4455,7 @@ static const struct global_parse_data global_fields[] = {
        { STR(non_pref_chan), 0 },
        { INT_RANGE(mbo_cell_capa, MBO_CELL_CAPA_AVAILABLE,
                    MBO_CELL_CAPA_NOT_SUPPORTED), 0 },
+       { INT_RANGE(disassoc_imminent_rssi_threshold, -120, 0), 0 },
 #endif /*CONFIG_MBO */
        { INT(gas_address3), 0 },
        { INT_RANGE(ftm_responder, 0, 1), 0 },
index 2f2bb01c6de68438fe3d33dee2ec432baca77629..3f84b0e1c502cb5018283c5013b736e2c159f41f 100644 (file)
@@ -41,6 +41,7 @@
 #define DEFAULT_P2P_GO_CTWINDOW 0
 #define DEFAULT_WPA_RSC_RELAXATION 1
 #define DEFAULT_MBO_CELL_CAPA MBO_CELL_CAPA_NOT_SUPPORTED
+#define DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD -75
 
 #include "config_ssid.h"
 #include "wps/wps.h"
@@ -1291,6 +1292,12 @@ struct wpa_config {
         * mbo_cell_capa - Cellular capabilities for MBO
         */
        enum mbo_cellular_capa mbo_cell_capa;
+
+       /**
+        * disassoc_imminent_rssi_threshold - RSSI threshold of candidate AP
+        * when disassociation imminent is set.
+        */
+        int disassoc_imminent_rssi_threshold;
 #endif /* CONFIG_MBO */
 
        /**
index 84a1ee945bb4d30829ef3321aa318375e0d3f388..160a5da2cfca3d3699f868cb6a1b17687e671a61 100644 (file)
@@ -1394,6 +1394,10 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
                fprintf(f, "non_pref_chan=%s\n", config->non_pref_chan);
        if (config->mbo_cell_capa != DEFAULT_MBO_CELL_CAPA)
                fprintf(f, "mbo_cell_capa=%u\n", config->mbo_cell_capa);
+       if (config->disassoc_imminent_rssi_threshold !=
+           DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD)
+               fprintf(f, "disassoc_imminent_rssi_threshold=%d\n",
+                       config->disassoc_imminent_rssi_threshold);
 #endif /* CONFIG_MBO */
 
        if (config->gas_address3)
index 3edfd27c6b9955e275ba6e7b481d4325f93b1c21..5488f446afe1ce908d9d2789bdbf3438a200f6a9 100644 (file)
@@ -382,7 +382,7 @@ void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie)
 void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie,
                           size_t len)
 {
-       const u8 *pos, *cell_pref = NULL, *reason = NULL;
+       const u8 *pos, *cell_pref = NULL;
        u8 id, elen;
        u16 disallowed_sec = 0;
 
@@ -417,7 +417,8 @@ void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie,
                        if (elen != 1)
                                goto fail;
 
-                       reason = pos;
+                       wpa_s->wnm_mbo_trans_reason_present = 1;
+                       wpa_s->wnm_mbo_transition_reason = *pos;
                        break;
                case MBO_ATTR_ID_ASSOC_RETRY_DELAY:
                        if (elen != 2)
@@ -460,9 +461,9 @@ void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie,
                wpa_msg(wpa_s, MSG_INFO, MBO_CELL_PREFERENCE "preference=%u",
                        *cell_pref);
 
-       if (reason)
+       if (wpa_s->wnm_mbo_trans_reason_present)
                wpa_msg(wpa_s, MSG_INFO, MBO_TRANSITION_REASON "reason=%u",
-                       *reason);
+                       wpa_s->wnm_mbo_transition_reason);
 
        if (disallowed_sec && wpa_s->current_bss)
                wpa_bss_tmp_disallow(wpa_s, wpa_s->current_bss->bssid,
index 0b1a2db8e10e958bcbe201d1805c22c9ed9fffee..9bcb039a4690f81ec5786b1628c7185fa6a4e2b8 100644 (file)
@@ -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, &params);
+       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);
        }
 }
 
index 81d815359634ecaeedfaf20bf748494365438001..78b87d05532a37c5d43c94a57df56b903d5262ee 100644 (file)
@@ -43,6 +43,10 @@ struct neighbor_report {
        unsigned int rm_capab_present:1;
        unsigned int bearing_present:1;
        unsigned int bss_term_present:1;
+       unsigned int acceptable:1;
+#ifdef CONFIG_MBO
+       unsigned int is_first:1;
+#endif /* CONFIG_MBO */
        struct measurement_pilot *meas_pilot;
        struct multiple_bssid *mul_bssid;
        int freq;
index 95e19b8feadbd3f908ebf5b510c89498bafa6839..87065cf9e7462e442fe4a619e8a56544e0c1d9bd 100644 (file)
@@ -1044,6 +1044,10 @@ struct wpa_supplicant {
        struct neighbor_report *wnm_neighbor_report_elements;
        struct os_reltime wnm_cand_valid_until;
        u8 wnm_cand_from_bss[ETH_ALEN];
+#ifdef CONFIG_MBO
+       unsigned int wnm_mbo_trans_reason_present:1;
+       u8 wnm_mbo_transition_reason;
+#endif /* CONFIG_MBO */
 #endif /* CONFIG_WNM */
 
 #ifdef CONFIG_TESTING_GET_GTK