P2P: Use radio work to protect offchannel Action frame exchanges
authorJouni Malinen <j@w1.fi>
Tue, 7 Jan 2014 07:39:30 +0000 (09:39 +0200)
committerJouni Malinen <j@w1.fi>
Tue, 7 Jan 2014 08:45:09 +0000 (10:45 +0200)
Signed-hostap: Jouni Malinen <j@w1.fi>

src/p2p/p2p_go_neg.c
wpa_supplicant/p2p_supplicant.c
wpa_supplicant/wpa_supplicant_i.h

index 008651e..0e688a9 100644 (file)
@@ -1040,6 +1040,7 @@ fail:
                            wpabuf_head(conf), wpabuf_len(conf), 0) < 0) {
                p2p_dbg(p2p, "Failed to send Action frame");
                p2p_go_neg_failed(p2p, dev, -1);
+               p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
        }
        wpabuf_free(conf);
        if (status != P2P_SC_SUCCESS) {
index 81b31db..719e549 100644 (file)
@@ -897,6 +897,32 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
 }
 
 
+struct send_action_work {
+       unsigned int freq;
+       u8 dst[ETH_ALEN];
+       u8 src[ETH_ALEN];
+       u8 bssid[ETH_ALEN];
+       size_t len;
+       unsigned int wait_time;
+       u8 buf[0];
+};
+
+
+static void wpas_p2p_send_action_work_timeout(void *eloop_ctx,
+                                             void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+
+       if (!wpa_s->p2p_send_action_work)
+               return;
+
+       wpa_printf(MSG_DEBUG, "P2P: Send Action frame radio work timed out");
+       os_free(wpa_s->p2p_send_action_work->ctx);
+       radio_work_done(wpa_s->p2p_send_action_work);
+       wpa_s->p2p_send_action_work = NULL;
+}
+
+
 static void wpas_p2p_send_action_tx_status(struct wpa_supplicant *wpa_s,
                                           unsigned int freq,
                                           const u8 *dst, const u8 *src,
@@ -907,6 +933,29 @@ static void wpas_p2p_send_action_tx_status(struct wpa_supplicant *wpa_s,
 {
        enum p2p_send_action_result res = P2P_SEND_ACTION_SUCCESS;
 
+       if (wpa_s->p2p_send_action_work) {
+               struct send_action_work *awork;
+               awork = wpa_s->p2p_send_action_work->ctx;
+               if (awork->wait_time == 0) {
+                       os_free(awork);
+                       radio_work_done(wpa_s->p2p_send_action_work);
+                       wpa_s->p2p_send_action_work = NULL;
+               } else {
+                       /*
+                        * In theory, this should not be needed, but number of
+                        * places in the P2P code is still using non-zero wait
+                        * time for the last Action frame in the sequence and
+                        * some of these do not call send_action_done().
+                        */
+                       eloop_cancel_timeout(wpas_p2p_send_action_work_timeout,
+                                            wpa_s, NULL);
+                       eloop_register_timeout(
+                               0, awork->wait_time * 1000,
+                               wpas_p2p_send_action_work_timeout,
+                               wpa_s, NULL);
+               }
+       }
+
        if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled)
                return;
 
@@ -938,11 +987,81 @@ static void wpas_p2p_send_action_tx_status(struct wpa_supplicant *wpa_s,
 }
 
 
+static void wpas_send_action_cb(struct wpa_radio_work *work, int deinit)
+{
+       struct wpa_supplicant *wpa_s = work->wpa_s;
+       struct send_action_work *awork = work->ctx;
+
+       if (deinit) {
+               os_free(awork);
+               return;
+       }
+
+       if (offchannel_send_action(wpa_s, awork->freq, awork->dst, awork->src,
+                                  awork->bssid, awork->buf, awork->len,
+                                  awork->wait_time,
+                                  wpas_p2p_send_action_tx_status, 1) < 0) {
+               os_free(awork);
+               radio_work_done(work);
+               return;
+       }
+       wpa_s->p2p_send_action_work = work;
+}
+
+
+static int wpas_send_action_work(struct wpa_supplicant *wpa_s,
+                                unsigned int freq, const u8 *dst,
+                                const u8 *src, const u8 *bssid, const u8 *buf,
+                                size_t len, unsigned int wait_time)
+{
+       struct send_action_work *awork;
+
+       if (wpa_s->p2p_send_action_work) {
+               wpa_printf(MSG_DEBUG, "P2P: Cannot schedule new p2p-send-action work since one is already pending");
+               return -1;
+       }
+
+       awork = os_zalloc(sizeof(*awork) + len);
+       if (awork == NULL)
+               return -1;
+
+       awork->freq = freq;
+       os_memcpy(awork->dst, dst, ETH_ALEN);
+       os_memcpy(awork->src, src, ETH_ALEN);
+       os_memcpy(awork->bssid, bssid, ETH_ALEN);
+       awork->len = len;
+       awork->wait_time = wait_time;
+       os_memcpy(awork->buf, buf, len);
+
+       if (radio_add_work(wpa_s, freq, "p2p-send-action", 0,
+                          wpas_send_action_cb, awork) < 0) {
+               os_free(awork);
+               return -1;
+       }
+
+       return 0;
+}
+
+
 static int wpas_send_action(void *ctx, unsigned int freq, const u8 *dst,
                            const u8 *src, const u8 *bssid, const u8 *buf,
                            size_t len, unsigned int wait_time)
 {
        struct wpa_supplicant *wpa_s = ctx;
+       int listen_freq = -1, send_freq = -1;
+
+       if (wpa_s->p2p_listen_work)
+               listen_freq = wpa_s->p2p_listen_work->freq;
+       if (wpa_s->p2p_send_action_work)
+               send_freq = wpa_s->p2p_send_action_work->freq;
+       if (listen_freq != (int) freq && send_freq != (int) freq) {
+               wpa_printf(MSG_DEBUG, "P2P: Schedule new radio work for Action frame TX (listen_freq=%d send_freq=%d)",
+                          listen_freq, send_freq);
+               return wpas_send_action_work(wpa_s, freq, dst, src, bssid, buf,
+                                            len, wait_time);
+       }
+
+       wpa_printf(MSG_DEBUG, "P2P: Use ongoing radio work for Action frame TX");
        return offchannel_send_action(wpa_s, freq, dst, src, bssid, buf, len,
                                      wait_time,
                                      wpas_p2p_send_action_tx_status, 1);
@@ -952,6 +1071,15 @@ static int wpas_send_action(void *ctx, unsigned int freq, const u8 *dst,
 static void wpas_send_action_done(void *ctx)
 {
        struct wpa_supplicant *wpa_s = ctx;
+
+       if (wpa_s->p2p_send_action_work) {
+               eloop_cancel_timeout(wpas_p2p_send_action_work_timeout,
+                                    wpa_s, NULL);
+               os_free(wpa_s->p2p_send_action_work->ctx);
+               radio_work_done(wpa_s->p2p_send_action_work);
+               wpa_s->p2p_send_action_work = NULL;
+       }
+
        offchannel_send_action_done(wpa_s);
 }
 
@@ -3680,6 +3808,12 @@ void wpas_p2p_deinit(struct wpa_supplicant *wpa_s)
        wpas_p2p_remove_pending_group_interface(wpa_s);
        eloop_cancel_timeout(wpas_p2p_group_freq_conflict, wpa_s, NULL);
        wpas_p2p_listen_work_done(wpa_s);
+       if (wpa_s->p2p_send_action_work) {
+               os_free(wpa_s->p2p_send_action_work->ctx);
+               radio_work_done(wpa_s->p2p_send_action_work);
+               wpa_s->p2p_send_action_work = NULL;
+       }
+       eloop_cancel_timeout(wpas_p2p_send_action_work_timeout, wpa_s, NULL);
 
        /* TODO: remove group interface from the driver if this wpa_s instance
         * is on top of a P2P group interface */
index 5231be8..9558495 100644 (file)
@@ -698,6 +698,7 @@ struct wpa_supplicant {
        struct wpa_ssid *p2p_last_4way_hs_fail;
        struct wpa_radio_work *p2p_scan_work;
        struct wpa_radio_work *p2p_listen_work;
+       struct wpa_radio_work *p2p_send_action_work;
 #endif /* CONFIG_P2P */
 
        struct wpa_ssid *bgscan_ssid;