]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
wpa_supplicant: Add support for Neighbor Report
authorAssaf Krauss <assaf.krauss@intel.com>
Wed, 5 Nov 2014 08:42:50 +0000 (03:42 -0500)
committerJouni Malinen <j@w1.fi>
Sat, 22 Nov 2014 19:23:14 +0000 (21:23 +0200)
Add the ability to send a Neighbor Report Request (part of
RRM). Requester is then notified once the report arrives.

Signed-off-by: Assaf Krauss <assaf.krauss@intel.com>
src/common/ieee802_11_defs.h
wpa_supplicant/events.c
wpa_supplicant/wpa_supplicant.c
wpa_supplicant/wpa_supplicant_i.h

index bcef88abbb0809df192f64c5de1e44cbc457edf5..0566e9378663bde01b0d585609df529010829eca 100644 (file)
 #define WLAN_TDLS_PEER_TRAFFIC_RESPONSE 9
 #define WLAN_TDLS_DISCOVERY_REQUEST 10
 
+/* Radio Measurement Action codes */
+#define WLAN_RRM_RADIO_MEASUREMENT_REQUEST 0
+#define WLAN_RRM_RADIO_MEASUREMENT_REPORT 1
+#define WLAN_RRM_LINK_MEASUREMENT_REQUEST 2
+#define WLAN_RRM_LINK_MEASUREMENT_REPORT 3
+#define WLAN_RRM_NEIGHBOR_REPORT_REQUEST 4
+#define WLAN_RRM_NEIGHBOR_REPORT_RESPONSE 5
+
+/* Radio Measurement capabilities (from RRM Capabilities IE) */
+/* byte 1 (out of 5) */
+#define WLAN_RRM_CAPS_NEIGHBOR_REPORT BIT(1)
+
 /* Timeout Interval Type */
 #define WLAN_TIMEOUT_REASSOC_DEADLINE 1
 #define WLAN_TIMEOUT_KEY_LIFETIME 2
index ffa980d77abc8b15b06e2855725179ac22aaff80..66098162101987bcdbaaaea9174ff56a121e0dfd 100644 (file)
@@ -2866,6 +2866,12 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s,
        }
 #endif /* CONFIG_INTERWORKING */
 
+       if (category == WLAN_ACTION_RADIO_MEASUREMENT &&
+           payload[0] == WLAN_RRM_NEIGHBOR_REPORT_RESPONSE) {
+               wpas_rrm_process_neighbor_rep(wpa_s, payload + 1, plen - 1);
+               return;
+       }
+
        wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
                           category, payload, plen, freq);
        if (wpa_s->ifmsh)
index 18d2e8e318486d0c7be0aeeb9f3fd106c43072e8..ef80c6b5f4e1b5a3cfa3314dfaca172b3885e4a2 100644 (file)
@@ -4913,6 +4913,24 @@ int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
 }
 
 
+static void wpas_rrm_neighbor_rep_timeout_handler(void *data, void *user_ctx)
+{
+       struct rrm_data *rrm = data;
+
+       if (!rrm->notify_neighbor_rep) {
+               wpa_printf(MSG_ERROR,
+                          "RRM: Unexpected neighbor report timeout");
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report - NONE");
+       rrm->notify_neighbor_rep(rrm->neighbor_rep_cb_ctx, NULL);
+
+       rrm->notify_neighbor_rep = NULL;
+       rrm->neighbor_rep_cb_ctx = NULL;
+}
+
+
 /*
  * wpas_rrm_reset - Clear and reset all RRM data in wpa_supplicant
  * @wpa_s: Pointer to wpa_supplicant
@@ -4920,4 +4938,153 @@ int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
 void wpas_rrm_reset(struct wpa_supplicant *wpa_s)
 {
        wpa_s->rrm.rrm_used = 0;
+
+       eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm,
+                            NULL);
+       if (wpa_s->rrm.notify_neighbor_rep)
+               wpas_rrm_neighbor_rep_timeout_handler(&wpa_s->rrm, NULL);
+       wpa_s->rrm.next_neighbor_rep_token = 1;
+}
+
+
+/*
+ * wpas_rrm_process_neighbor_rep - Handle incoming neighbor report
+ * @wpa_s: Pointer to wpa_supplicant
+ * @report: Neighbor report buffer, prefixed by a 1-byte dialog token
+ * @report_len: Length of neighbor report buffer
+ */
+void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s,
+                                  const u8 *report, size_t report_len)
+{
+       struct wpabuf *neighbor_rep;
+
+       wpa_hexdump(MSG_DEBUG, "RRM: New Neighbor Report", report, report_len);
+       if (report_len < 1)
+               return;
+
+       if (report[0] != wpa_s->rrm.next_neighbor_rep_token - 1) {
+               wpa_printf(MSG_DEBUG,
+                          "RRM: Discarding neighbor report with token %d (expected %d)",
+                          report[0], wpa_s->rrm.next_neighbor_rep_token - 1);
+               return;
+       }
+
+       eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm,
+                            NULL);
+
+       if (!wpa_s->rrm.notify_neighbor_rep) {
+               wpa_printf(MSG_ERROR, "RRM: Unexpected neighbor report");
+               return;
+       }
+
+       /* skipping the first byte, which is only an id (dialog token) */
+       neighbor_rep = wpabuf_alloc(report_len - 1);
+       if (neighbor_rep == NULL)
+               return;
+       wpabuf_put_data(neighbor_rep, report + 1, report_len - 1);
+       wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report (token = %d)",
+                  report[0]);
+       wpa_s->rrm.notify_neighbor_rep(wpa_s->rrm.neighbor_rep_cb_ctx,
+                                      neighbor_rep);
+       wpa_s->rrm.notify_neighbor_rep = NULL;
+       wpa_s->rrm.neighbor_rep_cb_ctx = NULL;
+}
+
+
+/**
+ * wpas_rrm_send_neighbor_rep_request - Request a neighbor report from our AP
+ * @wpa_s: Pointer to wpa_supplicant
+ * @cb: Callback function to be called once the requested report arrives, or
+ *     timed out after RRM_NEIGHBOR_REPORT_TIMEOUT seconds.
+ *     In the former case, 'neighbor_rep' is a newly allocated wpabuf, and it's
+ *     the requester's responsibility to free it.
+ *     In the latter case NULL will be sent in 'neighbor_rep'.
+ * @cb_ctx: Context value to send the callback function
+ * Returns: 0 in case of success, negative error code otherwise
+ *
+ * In case there is a previous request which has not been answered yet, the
+ * new request fails. The caller may retry after RRM_NEIGHBOR_REPORT_TIMEOUT.
+ * Request must contain a callback function.
+ * The Neighbor Report Request sent to the AP will specify the current SSID.
+ */
+int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s,
+                                      void (*cb)(void *ctx,
+                                                 struct wpabuf *neighbor_rep),
+                                      void *cb_ctx)
+{
+       struct wpabuf *buf;
+       const u8 *rrm_ie;
+
+       if (wpa_s->wpa_state != WPA_COMPLETED || wpa_s->current_ssid == NULL) {
+               wpa_printf(MSG_DEBUG, "RRM: No connection, no RRM.");
+               return -ENOTCONN;
+       }
+
+       if (!wpa_s->rrm.rrm_used) {
+               wpa_printf(MSG_DEBUG, "RRM: No RRM in current connection.");
+               return -EOPNOTSUPP;
+       }
+
+       rrm_ie = wpa_bss_get_ie(wpa_s->current_bss,
+                               WLAN_EID_RRM_ENABLED_CAPABILITIES);
+       if (!rrm_ie || !(wpa_s->current_bss->caps & IEEE80211_CAP_RRM) ||
+           !(rrm_ie[2] & WLAN_RRM_CAPS_NEIGHBOR_REPORT)) {
+               wpa_printf(MSG_DEBUG,
+                          "RRM: No network support for Neighbor Report.");
+               return -EOPNOTSUPP;
+       }
+
+       if (!cb) {
+               wpa_printf(MSG_DEBUG,
+                          "RRM: Neighbor Report request must provide a callback.");
+               return -EINVAL;
+       }
+
+       /* Refuse if there's a live request */
+       if (wpa_s->rrm.notify_neighbor_rep) {
+               wpa_printf(MSG_DEBUG,
+                          "RRM: Currently handling previous Neighbor Report.");
+               return -EBUSY;
+       }
+
+       /* 5 = action category + action code + dialog token + IE hdr */
+       buf = wpabuf_alloc(5 + wpa_s->current_ssid->ssid_len);
+       if (buf == NULL) {
+               wpa_printf(MSG_DEBUG,
+                          "RRM: Failed to allocate Neighbor Report Request");
+               return -ENOMEM;
+       }
+
+       wpa_printf(MSG_DEBUG, "RRM: Neighbor report request (for %s), token=%d",
+                  wpa_ssid_txt(wpa_s->current_ssid->ssid,
+                               wpa_s->current_ssid->ssid_len),
+                  wpa_s->rrm.next_neighbor_rep_token);
+
+       wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+       wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_REQUEST);
+       wpabuf_put_u8(buf, wpa_s->rrm.next_neighbor_rep_token);
+       wpabuf_put_u8(buf, WLAN_EID_SSID);
+       wpabuf_put_u8(buf, wpa_s->current_ssid->ssid_len);
+       wpabuf_put_data(buf, wpa_s->current_ssid->ssid,
+                       wpa_s->current_ssid->ssid_len);
+
+       wpa_s->rrm.next_neighbor_rep_token++;
+
+       if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+                               wpa_s->own_addr, wpa_s->bssid,
+                               wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
+               wpa_printf(MSG_DEBUG,
+                          "RRM: Failed to send Neighbor Report Request");
+               wpabuf_free(buf);
+               return -ECANCELED;
+       }
+
+       wpa_s->rrm.neighbor_rep_cb_ctx = cb_ctx;
+       wpa_s->rrm.notify_neighbor_rep = cb;
+       eloop_register_timeout(RRM_NEIGHBOR_REPORT_TIMEOUT, 0,
+                              wpas_rrm_neighbor_rep_timeout_handler,
+                              &wpa_s->rrm, NULL);
+
+       wpabuf_free(buf);
+       return 0;
 }
index 071941e7de1814042eeaf48322455b77ec6f8ef3..a1155070e5846b7161d870ba50b228a45ffcd9a7 100644 (file)
@@ -378,12 +378,29 @@ struct wpa_used_freq_data {
        unsigned int flags;
 };
 
+#define RRM_NEIGHBOR_REPORT_TIMEOUT 1 /* 1 second for AP to send a report */
+
 /*
  * struct rrm_data - Data used for managing RRM features
  */
 struct rrm_data {
        /* rrm_used - indication regarding the current connection */
        unsigned int rrm_used:1;
+
+       /*
+        * notify_neighbor_rep - Callback for notifying report requester
+        */
+       void (*notify_neighbor_rep)(void *ctx, struct wpabuf *neighbor_rep);
+
+       /*
+        * neighbor_rep_cb_ctx - Callback context
+        * Received in the callback registration, and sent to the callback
+        * function as a parameter.
+        */
+       void *neighbor_rep_cb_ctx;
+
+       /* next_neighbor_rep_token - Next request's dialog token */
+       u8 next_neighbor_rep_token;
 };
 
 /**
@@ -1006,6 +1023,12 @@ int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s);
 void add_freq(int *freqs, int *num_freqs, int freq);
 
 void wpas_rrm_reset(struct wpa_supplicant *wpa_s);
+void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s,
+                                  const u8 *report, size_t report_len);
+int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s,
+                                      void (*cb)(void *ctx,
+                                                 struct wpabuf *neighbor_rep),
+                                      void *cb_ctx);
 
 /**
  * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response