P2P: Use radio work to protect offchannel Action frame exchanges
Signed-hostap: Jouni Malinen <j@w1.fi>
This commit is contained in:
parent
e1d1c8e223
commit
e05e130837
3 changed files with 136 additions and 0 deletions
|
@ -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) {
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue