From e05e130837c86a0d60d2f70bb03fcd468d79450d Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 7 Jan 2014 09:39:30 +0200 Subject: [PATCH] P2P: Use radio work to protect offchannel Action frame exchanges Signed-hostap: Jouni Malinen --- src/p2p/p2p_go_neg.c | 1 + wpa_supplicant/p2p_supplicant.c | 134 ++++++++++++++++++++++++++++++ wpa_supplicant/wpa_supplicant_i.h | 1 + 3 files changed, 136 insertions(+) diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c index 008651e47..0e688a96b 100644 --- a/src/p2p/p2p_go_neg.c +++ b/src/p2p/p2p_go_neg.c @@ -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) { diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index 81b31db01..719e54983 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -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 */ diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 5231be8f0..9558495ab 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -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;