P2P: Avoid truncation of long listen operation due to offchan tx
[mech_eap.git] / wpa_supplicant / offchannel.c
index 7ec7656..7a86347 100644 (file)
@@ -12,6 +12,7 @@
 #include "common.h"
 #include "utils/eloop.h"
 #include "wpa_supplicant_i.h"
+#include "p2p_supplicant.h"
 #include "driver_i.h"
 #include "offchannel.h"
 
@@ -30,8 +31,7 @@ wpas_get_tx_interface(struct wpa_supplicant *wpa_s, const u8 *src)
         */
        iface = wpa_s->global->ifaces;
        while (iface) {
-               if (os_memcmp(wpa_s->pending_action_src,
-                             iface->own_addr, ETH_ALEN) == 0)
+               if (os_memcmp(src, iface->own_addr, ETH_ALEN) == 0)
                        break;
                iface = iface->next;
        }
@@ -55,11 +55,12 @@ static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx)
 
        without_roc = wpa_s->pending_action_without_roc;
        wpa_s->pending_action_without_roc = 0;
-       wpa_printf(MSG_DEBUG, "Off-channel: Send Action callback "
-                  "(without_roc=%d pending_action_tx=%p)",
-                  without_roc, wpa_s->pending_action_tx);
+       wpa_printf(MSG_DEBUG,
+                  "Off-channel: Send Action callback (without_roc=%d pending_action_tx=%p pending_action_tx_done=%d)",
+                  without_roc, wpa_s->pending_action_tx,
+                  !!wpa_s->pending_action_tx_done);
 
-       if (wpa_s->pending_action_tx == NULL)
+       if (wpa_s->pending_action_tx == NULL || wpa_s->pending_action_tx_done)
                return;
 
        /*
@@ -83,6 +84,7 @@ static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx)
                           wpa_s->off_channel_freq,
                           iface->assoc_freq);
                if (without_roc && wpa_s->off_channel_freq == 0) {
+                       unsigned int duration = 200;
                        /*
                         * We may get here if wpas_send_action() found us to be
                         * on the correct channel, but remain-on-channel cancel
@@ -90,9 +92,18 @@ static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx)
                         */
                        wpa_printf(MSG_DEBUG, "Off-channel: Schedule "
                                   "remain-on-channel to send Action frame");
+#ifdef CONFIG_TESTING_OPTIONS
+                       if (wpa_s->extra_roc_dur) {
+                               wpa_printf(MSG_DEBUG,
+                                          "TESTING: Increase ROC duration %u -> %u",
+                                          duration,
+                                          duration + wpa_s->extra_roc_dur);
+                               duration += wpa_s->extra_roc_dur;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
                        if (wpa_drv_remain_on_channel(
-                                   wpa_s, wpa_s->pending_action_freq, 200) <
-                           0) {
+                                   wpa_s, wpa_s->pending_action_freq,
+                                   duration) < 0) {
                                wpa_printf(MSG_DEBUG, "Off-channel: Failed to "
                                           "request driver to remain on "
                                           "channel (%u MHz) for Action Frame "
@@ -159,6 +170,21 @@ void offchannel_send_action_tx_status(
                return;
        }
 
+       /* Accept report only if the contents of the frame matches */
+       if (data_len - wpabuf_len(wpa_s->pending_action_tx) != 24 ||
+           os_memcmp(data + 24, wpabuf_head(wpa_s->pending_action_tx),
+                     wpabuf_len(wpa_s->pending_action_tx)) != 0) {
+               wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - "
+                                  "mismatching contents with pending frame");
+               wpa_hexdump(MSG_MSGDUMP, "TX status frame data",
+                           data, data_len);
+               wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame",
+                               wpa_s->pending_action_tx);
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "Off-channel: Delete matching pending action frame");
+
        wpabuf_free(wpa_s->pending_action_tx);
        wpa_s->pending_action_tx = NULL;
 
@@ -172,6 +198,14 @@ void offchannel_send_action_tx_status(
                        wpa_s->pending_action_bssid,
                        data, data_len, result);
        }
+
+#ifdef CONFIG_P2P
+       if (wpa_s->p2p_long_listen > 0) {
+               /* Continue the listen */
+               wpa_printf(MSG_DEBUG, "P2P: Continuing long Listen state");
+               wpas_p2p_listen_start(wpa_s, wpa_s->p2p_long_listen);
+       }
+#endif /* CONFIG_P2P */
 }
 
 
@@ -220,6 +254,7 @@ int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq,
                           MAC2STR(wpa_s->pending_action_dst));
                wpabuf_free(wpa_s->pending_action_tx);
        }
+       wpa_s->pending_action_tx_done = 0;
        wpa_s->pending_action_tx = wpabuf_alloc(len);
        if (wpa_s->pending_action_tx == NULL) {
                wpa_printf(MSG_DEBUG, "Off-channel: Failed to allocate Action "
@@ -236,18 +271,21 @@ int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq,
 
        if (freq != 0 && wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) {
                struct wpa_supplicant *iface;
+               int ret;
 
-               iface = wpas_get_tx_interface(wpa_s,
-                                             wpa_s->pending_action_src);
+               iface = wpas_get_tx_interface(wpa_s, src);
                wpa_s->action_tx_wait_time = wait_time;
 
-               return wpa_drv_send_action(
+               ret = wpa_drv_send_action(
                        iface, wpa_s->pending_action_freq,
                        wait_time, wpa_s->pending_action_dst,
                        wpa_s->pending_action_src, wpa_s->pending_action_bssid,
                        wpabuf_head(wpa_s->pending_action_tx),
                        wpabuf_len(wpa_s->pending_action_tx),
                        wpa_s->pending_action_no_cck);
+               if (ret == 0)
+                       wpa_s->pending_action_tx_done = 1;
+               return ret;
        }
 
        if (freq) {
@@ -285,6 +323,15 @@ int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq,
                   "channel");
        if (wait_time > wpa_s->max_remain_on_chan)
                wait_time = wpa_s->max_remain_on_chan;
+       else if (wait_time == 0)
+               wait_time = 20;
+#ifdef CONFIG_TESTING_OPTIONS
+       if (wpa_s->extra_roc_dur) {
+               wpa_printf(MSG_DEBUG, "TESTING: Increase ROC duration %u -> %u",
+                          wait_time, wait_time + wpa_s->extra_roc_dur);
+               wait_time += wpa_s->extra_roc_dur;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
        if (wpa_drv_remain_on_channel(wpa_s, freq, wait_time) < 0) {
                wpa_printf(MSG_DEBUG, "Off-channel: Failed to request driver "
                           "to remain on channel (%u MHz) for Action "
@@ -357,6 +404,33 @@ void offchannel_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
 
 
 /**
+ * offchannel_pending_action_tx - Check whether there is a pending Action TX
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: Pointer to pending frame or %NULL if no pending operation
+ *
+ * This function can be used to check whether there is a pending Action frame TX
+ * operation. The returned pointer should be used only for checking whether it
+ * is %NULL (no pending frame) or to print the pointer value in debug
+ * information (i.e., the pointer should not be dereferenced).
+ */
+const void * offchannel_pending_action_tx(struct wpa_supplicant *wpa_s)
+{
+       return wpa_s->pending_action_tx;
+}
+
+
+/**
+ * offchannel_clear_pending_action_tx - Clear pending Action frame TX
+ * @wpa_s: Pointer to wpa_supplicant data
+ */
+void offchannel_clear_pending_action_tx(struct wpa_supplicant *wpa_s)
+{
+       wpabuf_free(wpa_s->pending_action_tx);
+       wpa_s->pending_action_tx = NULL;
+}
+
+
+/**
  * offchannel_deinit - Deinit off-channel operations
  * @wpa_s: Pointer to wpa_supplicant data
  *
@@ -365,7 +439,6 @@ void offchannel_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
  */
 void offchannel_deinit(struct wpa_supplicant *wpa_s)
 {
-       wpabuf_free(wpa_s->pending_action_tx);
-       wpa_s->pending_action_tx = NULL;
+       offchannel_clear_pending_action_tx(wpa_s);
        eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL);
 }