MBO: Parse MBO IE in BSS Transition Management Request frames
authorAvraham Stern <avraham.stern@intel.com>
Mon, 15 Feb 2016 14:53:36 +0000 (16:53 +0200)
committerJouni Malinen <j@w1.fi>
Mon, 22 Feb 2016 17:53:04 +0000 (19:53 +0200)
Add parsing of MBO IE in BSS Transition Management Request frames. If
the MBO IE includes the association retry delay attribute, do not try to
reconnect to the current BSS until the delay time is over.

If the MBO IE includes the cellular data connection preference attribute
or the transition rejection reason attribute, send a message to upper
layers with the data.

Signed-off-by: David Spinadel <david.spinadel@intel.com>
Signed-off-by: Avraham Stern <avraham.stern@intel.com>
src/common/wpa_ctrl.h
wpa_supplicant/events.c
wpa_supplicant/mbo.c
wpa_supplicant/wnm_sta.c
wpa_supplicant/wpa_supplicant.c
wpa_supplicant/wpa_supplicant_i.h

index 3e0a7ec..d9641bb 100644 (file)
@@ -270,6 +270,12 @@ extern "C" {
 /* BSS Transition Management Response frame received */
 #define BSS_TM_RESP "BSS-TM-RESP "
 
+/* MBO IE with cellular data connection preference received */
+#define MBO_CELL_PREFERENCE "MBO-CELL-PREFERENCE "
+
+/* BSS Transition Management Request received with MBO transition reason */
+#define MBO_TRANSITION_REASON "MBO-TRANSITION-REASON "
+
 /* BSS command information masks */
 
 #define WPA_BSS_MASK_ALL               0xFFFDFFFF
index 76835de..00e4c04 100644 (file)
@@ -1076,6 +1076,12 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
                                assoc_disallow[2]);
                        continue;
                }
+
+               if (wpa_is_bss_tmp_disallowed(wpa_s, bss->bssid)) {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "   skip - MBO retry delay has not passed yet");
+                       continue;
+               }
 #endif /* CONFIG_MBO */
 
                /* Matching configuration found */
@@ -2541,7 +2547,8 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s,
            !disallowed_bssid(wpa_s, fast_reconnect->bssid) &&
            !disallowed_ssid(wpa_s, fast_reconnect->ssid,
                             fast_reconnect->ssid_len) &&
-           !wpas_temp_disabled(wpa_s, fast_reconnect_ssid)) {
+           !wpas_temp_disabled(wpa_s, fast_reconnect_ssid) &&
+           !wpa_is_bss_tmp_disallowed(wpa_s, fast_reconnect->bssid)) {
 #ifndef CONFIG_NO_SCAN_PROCESSING
                wpa_dbg(wpa_s, MSG_DEBUG, "Try to reconnect to the same BSS");
                if (wpa_supplicant_connect(wpa_s, fast_reconnect,
index 4663dd2..e0976c5 100644 (file)
@@ -620,3 +620,99 @@ int wpas_mbo_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos,
        wpabuf_free(buf);
        return res;
 }
+
+
+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;
+       u8 id, elen;
+       u16 disallowed_sec = 0;
+
+       if (len <= 4 || WPA_GET_BE24(mbo_ie) != OUI_WFA ||
+           mbo_ie[3] != MBO_OUI_TYPE)
+               return;
+
+       pos = mbo_ie + 4;
+       len -= 4;
+
+       while (len >= 2) {
+               id = *pos++;
+               elen = *pos++;
+               len -= 2;
+
+               if (elen > len)
+                       goto fail;
+
+               switch (id) {
+               case MBO_ATTR_ID_CELL_DATA_PREF:
+                       if (elen != 1)
+                               goto fail;
+
+                       if (wpa_s->conf->mbo_cell_capa ==
+                           MBO_CELL_CAPA_AVAILABLE)
+                               cell_pref = pos;
+                       else
+                               wpa_printf(MSG_DEBUG,
+                                          "MBO: Station does not support Cellular data connection");
+                       break;
+               case MBO_ATTR_ID_TRANSITION_REASON:
+                       if (elen != 1)
+                               goto fail;
+
+                       reason = pos;
+                       break;
+               case MBO_ATTR_ID_ASSOC_RETRY_DELAY:
+                       if (elen != 2)
+                               goto fail;
+
+                       if (wpa_s->wnm_mode &
+                           WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
+                               wpa_printf(MSG_DEBUG,
+                                          "MBO: Unexpected association retry delay, BSS is terminating");
+                               goto fail;
+                       } else if (wpa_s->wnm_mode &
+                                  WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
+                               disallowed_sec = WPA_GET_LE16(pos);
+                       } else {
+                               wpa_printf(MSG_DEBUG,
+                                          "MBO: Association retry delay attribute not in disassoc imminent mode");
+                       }
+
+                       break;
+               case MBO_ATTR_ID_AP_CAPA_IND:
+               case MBO_ATTR_ID_NON_PREF_CHAN_REPORT:
+               case MBO_ATTR_ID_CELL_DATA_CAPA:
+               case MBO_ATTR_ID_ASSOC_DISALLOW:
+               case MBO_ATTR_ID_TRANSITION_REJECT_REASON:
+                       wpa_printf(MSG_DEBUG,
+                                  "MBO: Attribute %d should not be included in BTM Request frame",
+                                  id);
+                       break;
+               default:
+                       wpa_printf(MSG_DEBUG, "MBO: Unknown attribute id %u",
+                                  id);
+                       return;
+               }
+
+               pos += elen;
+               len -= elen;
+       }
+
+       if (cell_pref)
+               wpa_msg(wpa_s, MSG_INFO, MBO_CELL_PREFERENCE "preference=%u",
+                       *cell_pref);
+
+       if (reason)
+               wpa_msg(wpa_s, MSG_INFO, MBO_TRANSITION_REASON "reason=%u",
+                       *reason);
+
+       if (disallowed_sec && wpa_s->current_bss)
+               wpa_bss_tmp_disallow(wpa_s, wpa_s->current_bss->bssid,
+                                    disallowed_sec);
+
+       return;
+fail:
+       wpa_printf(MSG_DEBUG, "MBO IE parsing failed (id=%u len=%u left=%zu)",
+                  id, elen, len);
+}
index ffc9052..31411e3 100644 (file)
@@ -546,6 +546,14 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s)
                        continue;
                }
 
+               if (wpa_is_bss_tmp_disallowed(wpa_s, target->bssid)) {
+                       wpa_printf(MSG_DEBUG,
+                                  "MBO: Candidate BSS " MACSTR
+                                  " retry delay is not over yet",
+                                  MAC2STR(nei->bssid));
+                       continue;
+               }
+
                if (target->level < bss->level && target->level < -80) {
                        wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
                                   " (pref %d) does not have sufficient signal level (%d)",
@@ -821,6 +829,9 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
 {
        unsigned int beacon_int;
        u8 valid_int;
+#ifdef CONFIG_MBO
+       const u8 *vendor;
+#endif /* CONFIG_MBO */
 
        if (end - pos < 5)
                return;
@@ -881,6 +892,12 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
                }
        }
 
+#ifdef CONFIG_MBO
+       vendor = get_ie(pos, end - pos, WLAN_EID_VENDOR_SPECIFIC);
+       if (vendor)
+               wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]);
+#endif /* CONFIG_MBO */
+
        if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
                unsigned int valid_ms;
 
index e2a48fb..2ffc238 100644 (file)
@@ -397,6 +397,18 @@ void free_hw_features(struct wpa_supplicant *wpa_s)
 }
 
 
+static void free_bss_tmp_disallowed(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_bss_tmp_disallowed *bss, *prev;
+
+       dl_list_for_each_safe(bss, prev, &wpa_s->bss_tmp_disallowed,
+                             struct wpa_bss_tmp_disallowed, list) {
+               dl_list_del(&bss->list);
+               os_free(bss);
+       }
+}
+
+
 static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
 {
        int i;
@@ -555,6 +567,8 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
        os_free(wpa_s->non_pref_chan);
        wpa_s->non_pref_chan = NULL;
 #endif /* CONFIG_MBO */
+
+       free_bss_tmp_disallowed(wpa_s);
 }
 
 
@@ -3497,6 +3511,8 @@ wpa_supplicant_alloc(struct wpa_supplicant *parent)
        wpa_s->parent = parent ? parent : wpa_s;
        wpa_s->sched_scanning = 0;
 
+       dl_list_init(&wpa_s->bss_tmp_disallowed);
+
        return wpa_s;
 }
 
@@ -6308,3 +6324,73 @@ struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
 
        return NULL;
 }
+
+
+static struct
+wpa_bss_tmp_disallowed * wpas_get_disallowed_bss(struct wpa_supplicant *wpa_s,
+                                                const u8 *bssid)
+{
+       struct wpa_bss_tmp_disallowed *bss;
+
+       dl_list_for_each(bss, &wpa_s->bss_tmp_disallowed,
+                        struct wpa_bss_tmp_disallowed, list) {
+               if (os_memcmp(bssid, bss->bssid, ETH_ALEN) == 0)
+                       return bss;
+       }
+
+       return NULL;
+}
+
+
+void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                         unsigned int sec)
+{
+       struct wpa_bss_tmp_disallowed *bss;
+       struct os_reltime until;
+
+       os_get_reltime(&until);
+       until.sec += sec;
+
+       bss = wpas_get_disallowed_bss(wpa_s, bssid);
+       if (bss) {
+               bss->disallowed_until = until;
+               return;
+       }
+
+       bss = os_malloc(sizeof(*bss));
+       if (!bss) {
+               wpa_printf(MSG_DEBUG,
+                          "Failed to allocate memory for temp disallow BSS");
+               return;
+       }
+
+       bss->disallowed_until = until;
+       os_memcpy(bss->bssid, bssid, ETH_ALEN);
+       dl_list_add(&wpa_s->bss_tmp_disallowed, &bss->list);
+}
+
+
+int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+       struct wpa_bss_tmp_disallowed *bss;
+       struct os_reltime now, age;
+
+       os_get_reltime(&now);
+
+       bss = wpas_get_disallowed_bss(wpa_s, bssid);
+       if (!bss)
+               return 0;
+
+       if (os_reltime_before(&now, &bss->disallowed_until)) {
+               os_reltime_sub(&bss->disallowed_until, &now, &age);
+               wpa_printf(MSG_DEBUG,
+                          "BSS " MACSTR " disabled for %ld.%0ld seconds",
+                          MAC2STR(bss->bssid), age.sec, age.usec);
+               return 1;
+       }
+
+       /* This BSS is not disallowed anymore */
+       dl_list_del(&bss->list);
+       os_free(bss);
+       return 0;
+}
index 202b9e6..07318fa 100644 (file)
@@ -434,6 +434,12 @@ struct icon_entry {
        size_t image_len;
 };
 
+struct wpa_bss_tmp_disallowed {
+       struct dl_list list;
+       u8 bssid[ETH_ALEN];
+       struct os_reltime disallowed_until;
+};
+
 /**
  * struct wpa_supplicant - Internal data for wpa_supplicant interface
  *
@@ -1029,6 +1035,12 @@ struct wpa_supplicant {
        size_t non_pref_chan_num;
        u8 mbo_wnm_token;
 #endif /* CONFIG_MBO */
+
+       /*
+        * This should be under CONFIG_MBO, but it is left out to allow using
+        * the bss_temp_disallowed list for other purposes as well.
+        */
+       struct dl_list bss_tmp_disallowed;
 };
 
 
@@ -1150,6 +1162,8 @@ int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s,
 void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie);
 int wpas_mbo_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos,
                              size_t len);
+void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *ie,
+                          size_t len);
 
 /**
  * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response
@@ -1232,4 +1246,8 @@ int wpas_sched_scan_plans_set(struct wpa_supplicant *wpa_s, const char *cmd);
 struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
                                   u16 num_modes, enum hostapd_hw_mode mode);
 
+void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                         unsigned int sec);
+int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid);
+
 #endif /* WPA_SUPPLICANT_I_H */