GAS: Use off-channel operations for requests

This separates off-channel Action frame TX/RX from P2P into a generic
implementation that can now be used both for P2P and GAS needs.
This commit is contained in:
Jouni Malinen 2011-09-29 19:22:08 +03:00 committed by Jouni Malinen
parent 04ea7b7947
commit 24f6497c34
8 changed files with 452 additions and 262 deletions

View file

@ -202,6 +202,7 @@ OBJS += ../src/p2p/p2p_group.o
OBJS += ../src/ap/p2p_hostapd.o OBJS += ../src/ap/p2p_hostapd.o
CFLAGS += -DCONFIG_P2P CFLAGS += -DCONFIG_P2P
NEED_GAS=y NEED_GAS=y
NEED_OFFCHANNEL=y
NEED_80211_COMMON=y NEED_80211_COMMON=y
ifdef CONFIG_P2P_STRICT ifdef CONFIG_P2P_STRICT
CFLAGS += -DCONFIG_P2P_STRICT CFLAGS += -DCONFIG_P2P_STRICT
@ -1287,6 +1288,12 @@ ifdef NEED_GAS
OBJS += ../src/common/gas.o OBJS += ../src/common/gas.o
OBJS += gas_query.o OBJS += gas_query.o
CFLAGS += -DCONFIG_GAS CFLAGS += -DCONFIG_GAS
NEED_OFFCHANNEL=y
endif
ifdef NEED_OFFCHANNEL
OBJS += offchannel.o
CFLAGS += -DCONFIG_OFFCHANNEL
endif endif
OBJS_wpa_rm := ctrl_iface.o mlme.o ctrl_iface_unix.o OBJS_wpa_rm := ctrl_iface.o mlme.o ctrl_iface_unix.o

View file

@ -45,6 +45,7 @@
#include "bss.h" #include "bss.h"
#include "mlme.h" #include "mlme.h"
#include "scan.h" #include "scan.h"
#include "offchannel.h"
static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s)
@ -1914,27 +1915,28 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
sme_event_assoc_timed_out(wpa_s, data); sme_event_assoc_timed_out(wpa_s, data);
break; break;
#ifdef CONFIG_AP
case EVENT_TX_STATUS: case EVENT_TX_STATUS:
wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS dst=" MACSTR wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS dst=" MACSTR
" type=%d stype=%d", " type=%d stype=%d",
MAC2STR(data->tx_status.dst), MAC2STR(data->tx_status.dst),
data->tx_status.type, data->tx_status.stype); data->tx_status.type, data->tx_status.stype);
#ifdef CONFIG_AP
if (wpa_s->ap_iface == NULL) { if (wpa_s->ap_iface == NULL) {
#ifdef CONFIG_P2P #ifdef CONFIG_OFFCHANNEL
if (data->tx_status.type == WLAN_FC_TYPE_MGMT && if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
data->tx_status.stype == WLAN_FC_STYPE_ACTION) data->tx_status.stype == WLAN_FC_STYPE_ACTION)
wpas_send_action_tx_status( offchannel_send_action_tx_status(
wpa_s, data->tx_status.dst, wpa_s, data->tx_status.dst,
data->tx_status.data, data->tx_status.data,
data->tx_status.data_len, data->tx_status.data_len,
data->tx_status.ack ? data->tx_status.ack ?
P2P_SEND_ACTION_SUCCESS : OFFCHANNEL_SEND_ACTION_SUCCESS :
P2P_SEND_ACTION_NO_ACK); OFFCHANNEL_SEND_ACTION_NO_ACK);
#endif /* CONFIG_P2P */ #endif /* CONFIG_OFFCHANNEL */
break; break;
} }
#ifdef CONFIG_P2P #endif /* CONFIG_AP */
#ifdef CONFIG_OFFCHANNEL
wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS pending_dst=" wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS pending_dst="
MACSTR, MAC2STR(wpa_s->parent->pending_action_dst)); MACSTR, MAC2STR(wpa_s->parent->pending_action_dst));
/* /*
@ -1945,16 +1947,17 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
data->tx_status.stype == WLAN_FC_STYPE_ACTION && data->tx_status.stype == WLAN_FC_STYPE_ACTION &&
os_memcmp(wpa_s->parent->pending_action_dst, os_memcmp(wpa_s->parent->pending_action_dst,
data->tx_status.dst, ETH_ALEN) == 0) { data->tx_status.dst, ETH_ALEN) == 0) {
wpas_send_action_tx_status( offchannel_send_action_tx_status(
wpa_s->parent, data->tx_status.dst, wpa_s->parent, data->tx_status.dst,
data->tx_status.data, data->tx_status.data,
data->tx_status.data_len, data->tx_status.data_len,
data->tx_status.ack ? data->tx_status.ack ?
P2P_SEND_ACTION_SUCCESS : OFFCHANNEL_SEND_ACTION_SUCCESS :
P2P_SEND_ACTION_NO_ACK); OFFCHANNEL_SEND_ACTION_NO_ACK);
break; break;
} }
#endif /* CONFIG_P2P */ #endif /* CONFIG_OFFCHANNEL */
#ifdef CONFIG_AP
switch (data->tx_status.type) { switch (data->tx_status.type) {
case WLAN_FC_TYPE_MGMT: case WLAN_FC_TYPE_MGMT:
ap_mgmt_tx_cb(wpa_s, data->tx_status.data, ap_mgmt_tx_cb(wpa_s, data->tx_status.data,
@ -1969,7 +1972,9 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
data->tx_status.ack); data->tx_status.ack);
break; break;
} }
#endif /* CONFIG_AP */
break; break;
#ifdef CONFIG_AP
case EVENT_RX_FROM_UNKNOWN: case EVENT_RX_FROM_UNKNOWN:
if (wpa_s->ap_iface == NULL) if (wpa_s->ap_iface == NULL)
break; break;
@ -2067,16 +2072,29 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
data->rx_probe_req.ie_len); data->rx_probe_req.ie_len);
#endif /* CONFIG_P2P */ #endif /* CONFIG_P2P */
break; break;
#ifdef CONFIG_P2P
case EVENT_REMAIN_ON_CHANNEL: case EVENT_REMAIN_ON_CHANNEL:
#ifdef CONFIG_OFFCHANNEL
offchannel_remain_on_channel_cb(
wpa_s, data->remain_on_channel.freq,
data->remain_on_channel.duration);
#endif /* CONFIG_OFFCHANNEL */
#ifdef CONFIG_P2P
wpas_p2p_remain_on_channel_cb( wpas_p2p_remain_on_channel_cb(
wpa_s, data->remain_on_channel.freq, wpa_s, data->remain_on_channel.freq,
data->remain_on_channel.duration); data->remain_on_channel.duration);
#endif /* CONFIG_P2P */
break; break;
case EVENT_CANCEL_REMAIN_ON_CHANNEL: case EVENT_CANCEL_REMAIN_ON_CHANNEL:
#ifdef CONFIG_OFFCHANNEL
offchannel_cancel_remain_on_channel_cb(
wpa_s, data->remain_on_channel.freq);
#endif /* CONFIG_OFFCHANNEL */
#ifdef CONFIG_P2P
wpas_p2p_cancel_remain_on_channel_cb( wpas_p2p_cancel_remain_on_channel_cb(
wpa_s, data->remain_on_channel.freq); wpa_s, data->remain_on_channel.freq);
#endif /* CONFIG_P2P */
break; break;
#ifdef CONFIG_P2P
case EVENT_P2P_DEV_FOUND: { case EVENT_P2P_DEV_FOUND: {
struct p2p_peer_info peer_info; struct p2p_peer_info peer_info;

View file

@ -21,6 +21,7 @@
#include "common/gas.h" #include "common/gas.h"
#include "wpa_supplicant_i.h" #include "wpa_supplicant_i.h"
#include "driver_i.h" #include "driver_i.h"
#include "offchannel.h"
#include "gas_query.h" #include "gas_query.h"
@ -32,7 +33,8 @@ struct gas_query_pending {
u8 addr[ETH_ALEN]; u8 addr[ETH_ALEN];
u8 dialog_token; u8 dialog_token;
u8 next_frag_id; u8 next_frag_id;
int wait_comeback; int wait_comeback:1;
int offchannel_tx_started:1;
int freq; int freq;
u16 status_code; u16 status_code;
struct wpabuf *adv_proto; struct wpabuf *adv_proto;
@ -73,6 +75,8 @@ static void gas_query_done(struct gas_query *gas,
struct gas_query_pending *query, struct gas_query_pending *query,
enum gas_query_result result) enum gas_query_result result)
{ {
if (query->offchannel_tx_started)
offchannel_send_action_done(gas->wpa_s);
eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query); eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
eloop_cancel_timeout(gas_query_timeout, gas, query); eloop_cancel_timeout(gas_query_timeout, gas, query);
dl_list_del(&query->list); dl_list_del(&query->list);
@ -127,12 +131,17 @@ static int gas_query_append(struct gas_query_pending *query, const u8 *data,
static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query, static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query,
struct wpabuf *req) struct wpabuf *req)
{ {
int res;
wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u " wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
"freq=%d", MAC2STR(query->addr), "freq=%d", MAC2STR(query->addr),
(unsigned int) wpabuf_len(req), query->freq); (unsigned int) wpabuf_len(req), query->freq);
return wpa_drv_send_action(gas->wpa_s, query->freq, 0, query->addr, res = offchannel_send_action(gas->wpa_s, query->freq, query->addr,
gas->wpa_s->own_addr, query->addr, gas->wpa_s->own_addr, query->addr,
wpabuf_head(req), wpabuf_len(req)); wpabuf_head(req), wpabuf_len(req), 1000,
NULL);
if (res == 0)
query->offchannel_tx_started = 1;
return res;
} }

310
wpa_supplicant/offchannel.c Normal file
View file

@ -0,0 +1,310 @@
/*
* wpa_supplicant - Off-channel Action frame TX/RX
* Copyright (c) 2009-2010, Atheros Communications
* Copyright (c) 2011, Qualcomm Atheros
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "utils/eloop.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "offchannel.h"
static struct wpa_supplicant *
wpas_get_tx_interface(struct wpa_supplicant *wpa_s, const u8 *src)
{
struct wpa_supplicant *iface;
if (os_memcmp(src, wpa_s->own_addr, ETH_ALEN) == 0)
return wpa_s;
/*
* Try to find a group interface that matches with the source address.
*/
iface = wpa_s->global->ifaces;
while (iface) {
if (os_memcmp(wpa_s->pending_action_src,
iface->own_addr, ETH_ALEN) == 0)
break;
iface = iface->next;
}
if (iface) {
wpa_printf(MSG_DEBUG, "P2P: Use group interface %s "
"instead of interface %s for Action TX",
iface->ifname, wpa_s->ifname);
return iface;
}
return wpa_s;
}
static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
struct wpa_supplicant *iface;
int res;
int without_roc;
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);
if (wpa_s->pending_action_tx == NULL)
return;
/*
* This call is likely going to be on the P2P device instance if the
* driver uses a separate interface for that purpose. However, some
* Action frames are actually sent within a P2P Group and when that is
* the case, we need to follow power saving (e.g., GO buffering the
* frame for a client in PS mode or a client following the advertised
* NoA from its GO). To make that easier for the driver, select the
* correct group interface here.
*/
iface = wpas_get_tx_interface(wpa_s, wpa_s->pending_action_src);
if (wpa_s->off_channel_freq != wpa_s->pending_action_freq &&
wpa_s->pending_action_freq != 0 &&
wpa_s->pending_action_freq != iface->assoc_freq) {
wpa_printf(MSG_DEBUG, "Off-channel: Pending Action frame TX "
"waiting for another freq=%u (off_channel_freq=%u "
"assoc_freq=%u)",
wpa_s->pending_action_freq,
wpa_s->off_channel_freq,
iface->assoc_freq);
if (without_roc && wpa_s->off_channel_freq == 0) {
/*
* We may get here if wpas_send_action() found us to be
* on the correct channel, but remain-on-channel cancel
* event was received before getting here.
*/
wpa_printf(MSG_DEBUG, "Off-channel: Schedule "
"remain-on-channel to send Action frame");
if (wpa_drv_remain_on_channel(
wpa_s, wpa_s->pending_action_freq, 200) <
0) {
wpa_printf(MSG_DEBUG, "Off-channel: Failed to "
"request driver to remain on "
"channel (%u MHz) for Action Frame "
"TX", wpa_s->pending_action_freq);
} else {
wpa_s->off_channel_freq = 0;
wpa_s->roc_waiting_drv_freq =
wpa_s->pending_action_freq;
}
}
return;
}
wpa_printf(MSG_DEBUG, "Off-channel: Sending pending Action frame to "
MACSTR " using interface %s",
MAC2STR(wpa_s->pending_action_dst), iface->ifname);
res = wpa_drv_send_action(iface, wpa_s->pending_action_freq, 0,
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));
if (res) {
wpa_printf(MSG_DEBUG, "Off-channel: Failed to send the "
"pending Action frame");
/*
* Use fake TX status event to allow state machines to
* continue.
*/
offchannel_send_action_tx_status(
wpa_s, wpa_s->pending_action_dst,
wpabuf_head(wpa_s->pending_action_tx),
wpabuf_len(wpa_s->pending_action_tx),
OFFCHANNEL_SEND_ACTION_FAILED);
}
}
void offchannel_send_action_tx_status(
struct wpa_supplicant *wpa_s, const u8 *dst, const u8 *data,
size_t data_len, enum offchannel_send_action_result result)
{
if (wpa_s->pending_action_tx == NULL) {
wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - "
"no pending operation");
return;
}
if (os_memcmp(dst, wpa_s->pending_action_dst, ETH_ALEN) != 0) {
wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - "
"unknown destination address");
return;
}
wpabuf_free(wpa_s->pending_action_tx);
wpa_s->pending_action_tx = NULL;
wpa_printf(MSG_DEBUG, "Off-channel: TX status result=%d cb=%p",
result, wpa_s->pending_action_tx_status_cb);
if (wpa_s->pending_action_tx_status_cb) {
wpa_s->pending_action_tx_status_cb(
wpa_s, wpa_s->pending_action_freq,
wpa_s->pending_action_dst, wpa_s->pending_action_src,
wpa_s->pending_action_bssid,
data, data_len, result);
}
}
int offchannel_send_action(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,
void (*tx_cb)(struct wpa_supplicant *wpa_s,
unsigned int freq, const u8 *dst,
const u8 *src, const u8 *bssid,
const u8 *data, size_t data_len,
enum offchannel_send_action_result
result))
{
wpa_printf(MSG_DEBUG, "Off-channel: Send action frame: freq=%d dst="
MACSTR " src=" MACSTR " bssid=" MACSTR " len=%d",
freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
(int) len);
wpa_s->pending_action_tx_status_cb = tx_cb;
if (wpa_s->pending_action_tx) {
wpa_printf(MSG_DEBUG, "Off-channel: Dropped pending Action "
"frame TX to " MACSTR,
MAC2STR(wpa_s->pending_action_dst));
wpabuf_free(wpa_s->pending_action_tx);
}
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 "
"frame TX buffer (len=%llu)",
(unsigned long long) len);
return -1;
}
wpabuf_put_data(wpa_s->pending_action_tx, buf, len);
os_memcpy(wpa_s->pending_action_src, src, ETH_ALEN);
os_memcpy(wpa_s->pending_action_dst, dst, ETH_ALEN);
os_memcpy(wpa_s->pending_action_bssid, bssid, ETH_ALEN);
wpa_s->pending_action_freq = freq;
if (freq != 0 && wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) {
struct wpa_supplicant *iface;
iface = wpas_get_tx_interface(wpa_s,
wpa_s->pending_action_src);
wpa_s->action_tx_wait_time = wait_time;
return 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));
}
if (freq) {
struct wpa_supplicant *tx_iface;
tx_iface = wpas_get_tx_interface(wpa_s, src);
if (tx_iface->assoc_freq == freq) {
wpa_printf(MSG_DEBUG, "Off-channel: Already on "
"requested channel (TX interface operating "
"channel)");
freq = 0;
}
}
if (wpa_s->off_channel_freq == freq || freq == 0) {
wpa_printf(MSG_DEBUG, "Off-channel: Already on requested "
"channel; send Action frame immediately");
/* TODO: Would there ever be need to extend the current
* duration on the channel? */
wpa_s->pending_action_without_roc = 1;
eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL);
eloop_register_timeout(0, 0, wpas_send_action_cb, wpa_s, NULL);
return 0;
}
wpa_s->pending_action_without_roc = 0;
if (wpa_s->roc_waiting_drv_freq == freq) {
wpa_printf(MSG_DEBUG, "Off-channel: Already waiting for "
"driver to get to frequency %u MHz; continue "
"waiting to send the Action frame", freq);
return 0;
}
wpa_printf(MSG_DEBUG, "Off-channel: Schedule Action frame to be "
"transmitted once the driver gets to the requested "
"channel");
if (wait_time > wpa_s->max_remain_on_chan)
wait_time = wpa_s->max_remain_on_chan;
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 "
"Frame TX", freq);
return -1;
}
wpa_s->off_channel_freq = 0;
wpa_s->roc_waiting_drv_freq = freq;
return 0;
}
void offchannel_send_action_done(struct wpa_supplicant *wpa_s)
{
wpa_printf(MSG_DEBUG, "Off-channel: Action frame sequence done "
"notification");
wpabuf_free(wpa_s->pending_action_tx);
wpa_s->pending_action_tx = NULL;
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX &&
wpa_s->action_tx_wait_time)
wpa_drv_send_action_cancel_wait(wpa_s);
if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
wpa_drv_cancel_remain_on_channel(wpa_s);
wpa_s->off_channel_freq = 0;
wpa_s->roc_waiting_drv_freq = 0;
}
}
void offchannel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
unsigned int freq, unsigned int duration)
{
wpa_s->roc_waiting_drv_freq = 0;
wpa_s->off_channel_freq = freq;
wpas_send_action_cb(wpa_s, NULL);
}
void offchannel_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
unsigned int freq)
{
wpa_s->off_channel_freq = 0;
}
void offchannel_deinit(struct wpa_supplicant *wpa_s)
{
wpabuf_free(wpa_s->pending_action_tx);
wpa_s->pending_action_tx = NULL;
eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL);
}

View file

@ -0,0 +1,38 @@
/*
* wpa_supplicant - Off-channel Action frame TX/RX
* Copyright (c) 2009-2010, Atheros Communications
* Copyright (c) 2011, Qualcomm Atheros
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef OFFCHANNEL_H
#define OFFCHANNEL_H
int offchannel_send_action(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,
void (*tx_cb)(struct wpa_supplicant *wpa_s,
unsigned int freq, const u8 *dst,
const u8 *src, const u8 *bssid,
const u8 *data, size_t data_len,
enum offchannel_send_action_result
result));
void offchannel_send_action_done(struct wpa_supplicant *wpa_s);
void offchannel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
unsigned int freq, unsigned int duration);
void offchannel_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
unsigned int freq);
void offchannel_deinit(struct wpa_supplicant *wpa_s);
void offchannel_send_action_tx_status(
struct wpa_supplicant *wpa_s, const u8 *dst, const u8 *data,
size_t data_len, enum offchannel_send_action_result result);
#endif /* OFFCHANNEL_H */

View file

@ -34,6 +34,7 @@
#include "notify.h" #include "notify.h"
#include "scan.h" #include "scan.h"
#include "bss.h" #include "bss.h"
#include "offchannel.h"
#include "wps_supplicant.h" #include "wps_supplicant.h"
#include "p2p_supplicant.h" #include "p2p_supplicant.h"
@ -570,155 +571,38 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
} }
static struct wpa_supplicant * static void wpas_p2p_send_action_tx_status(struct wpa_supplicant *wpa_s,
wpas_get_tx_interface(struct wpa_supplicant *wpa_s, const u8 *src) unsigned int freq,
{ const u8 *dst, const u8 *src,
struct wpa_supplicant *iface; const u8 *bssid,
if (os_memcmp(src, wpa_s->own_addr, ETH_ALEN) == 0)
return wpa_s;
/*
* Try to find a group interface that matches with the source address.
*/
iface = wpa_s->global->ifaces;
while (iface) {
if (os_memcmp(wpa_s->pending_action_src,
iface->own_addr, ETH_ALEN) == 0)
break;
iface = iface->next;
}
if (iface) {
wpa_printf(MSG_DEBUG, "P2P: Use group interface %s "
"instead of interface %s for Action TX",
iface->ifname, wpa_s->ifname);
return iface;
}
return wpa_s;
}
static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
struct wpa_supplicant *iface;
int res;
int without_roc;
without_roc = wpa_s->pending_action_without_roc;
wpa_s->pending_action_without_roc = 0;
wpa_printf(MSG_DEBUG, "P2P: Send Action callback (without_roc=%d "
"pending_action_tx=%p)",
without_roc, wpa_s->pending_action_tx);
if (wpa_s->pending_action_tx == NULL)
return;
/*
* This call is likely going to be on the P2P device instance if the
* driver uses a separate interface for that purpose. However, some
* Action frames are actually sent within a P2P Group and when that is
* the case, we need to follow power saving (e.g., GO buffering the
* frame for a client in PS mode or a client following the advertised
* NoA from its GO). To make that easier for the driver, select the
* correct group interface here.
*/
iface = wpas_get_tx_interface(wpa_s, wpa_s->pending_action_src);
if (wpa_s->off_channel_freq != wpa_s->pending_action_freq &&
wpa_s->pending_action_freq != 0 &&
wpa_s->pending_action_freq != iface->assoc_freq) {
wpa_printf(MSG_DEBUG, "P2P: Pending Action frame TX "
"waiting for another freq=%u (off_channel_freq=%u "
"assoc_freq=%u)",
wpa_s->pending_action_freq,
wpa_s->off_channel_freq,
iface->assoc_freq);
if (without_roc && wpa_s->off_channel_freq == 0) {
/*
* We may get here if wpas_send_action() found us to be
* on the correct channel, but remain-on-channel cancel
* event was received before getting here.
*/
wpa_printf(MSG_DEBUG, "P2P: Schedule "
"remain-on-channel to send Action frame");
if (wpa_drv_remain_on_channel(
wpa_s, wpa_s->pending_action_freq, 200) <
0) {
wpa_printf(MSG_DEBUG, "P2P: Failed to request "
"driver to remain on channel (%u "
"MHz) for Action Frame TX",
wpa_s->pending_action_freq);
} else {
wpa_s->off_channel_freq = 0;
wpa_s->roc_waiting_drv_freq =
wpa_s->pending_action_freq;
}
}
return;
}
wpa_printf(MSG_DEBUG, "P2P: Sending pending Action frame to "
MACSTR " using interface %s",
MAC2STR(wpa_s->pending_action_dst), iface->ifname);
res = wpa_drv_send_action(iface, wpa_s->pending_action_freq, 0,
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));
if (res) {
wpa_printf(MSG_DEBUG, "P2P: Failed to send the pending "
"Action frame");
/*
* Use fake TX status event to allow P2P state machine to
* continue.
*/
wpas_send_action_tx_status(
wpa_s, wpa_s->pending_action_dst,
wpabuf_head(wpa_s->pending_action_tx),
wpabuf_len(wpa_s->pending_action_tx),
P2P_SEND_ACTION_FAILED);
}
}
void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst,
const u8 *data, size_t data_len, const u8 *data, size_t data_len,
enum p2p_send_action_result result) enum offchannel_send_action_result
result)
{ {
enum p2p_send_action_result res = P2P_SEND_ACTION_SUCCESS;
if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled)
return; return;
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
return; return;
if (wpa_s->pending_action_tx == NULL) { switch (result) {
wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - no " case OFFCHANNEL_SEND_ACTION_SUCCESS:
"pending operation"); res = P2P_SEND_ACTION_SUCCESS;
return; break;
case OFFCHANNEL_SEND_ACTION_NO_ACK:
res = P2P_SEND_ACTION_NO_ACK;
break;
case OFFCHANNEL_SEND_ACTION_FAILED:
res = P2P_SEND_ACTION_FAILED;
break;
} }
if (os_memcmp(dst, wpa_s->pending_action_dst, ETH_ALEN) != 0) { p2p_send_action_cb(wpa_s->global->p2p, freq, dst, src, bssid, res);
wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - unknown "
"destination address");
return;
}
wpabuf_free(wpa_s->pending_action_tx);
wpa_s->pending_action_tx = NULL;
p2p_send_action_cb(wpa_s->global->p2p, wpa_s->pending_action_freq,
wpa_s->pending_action_dst,
wpa_s->pending_action_src,
wpa_s->pending_action_bssid,
result);
if (wpa_s->pending_pd_before_join && if (wpa_s->pending_pd_before_join &&
(os_memcmp(wpa_s->pending_action_dst, wpa_s->pending_join_dev_addr, (os_memcmp(dst, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 ||
ETH_ALEN) == 0 || os_memcmp(dst, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0)) {
os_memcmp(wpa_s->pending_action_dst,
wpa_s->pending_join_iface_addr, ETH_ALEN) == 0)) {
wpa_s->pending_pd_before_join = 0; wpa_s->pending_pd_before_join = 0;
wpa_printf(MSG_DEBUG, "P2P: Starting pending " wpa_printf(MSG_DEBUG, "P2P: Starting pending "
"join-existing-group operation"); "join-existing-group operation");
@ -732,104 +616,16 @@ static int wpas_send_action(void *ctx, unsigned int freq, const u8 *dst,
size_t len, unsigned int wait_time) size_t len, unsigned int wait_time)
{ {
struct wpa_supplicant *wpa_s = ctx; struct wpa_supplicant *wpa_s = ctx;
return offchannel_send_action(wpa_s, freq, dst, src, bssid, buf, len,
wpa_printf(MSG_DEBUG, "P2P: Send action frame: freq=%d dst=" MACSTR wait_time,
" src=" MACSTR " bssid=" MACSTR " len=%d", wpas_p2p_send_action_tx_status);
freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
(int) len);
if (wpa_s->pending_action_tx) {
wpa_printf(MSG_DEBUG, "P2P: Dropped pending Action frame TX "
"to " MACSTR, MAC2STR(wpa_s->pending_action_dst));
wpabuf_free(wpa_s->pending_action_tx);
}
wpa_s->pending_action_tx = wpabuf_alloc(len);
if (wpa_s->pending_action_tx == NULL) {
wpa_printf(MSG_DEBUG, "P2P: Failed to allocate Action frame "
"TX buffer (len=%llu)", (unsigned long long) len);
return -1;
}
wpabuf_put_data(wpa_s->pending_action_tx, buf, len);
os_memcpy(wpa_s->pending_action_src, src, ETH_ALEN);
os_memcpy(wpa_s->pending_action_dst, dst, ETH_ALEN);
os_memcpy(wpa_s->pending_action_bssid, bssid, ETH_ALEN);
wpa_s->pending_action_freq = freq;
if (freq != 0 && wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) {
struct wpa_supplicant *iface;
iface = wpas_get_tx_interface(wpa_s, wpa_s->pending_action_src);
wpa_s->action_tx_wait_time = wait_time;
return 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));
}
if (freq) {
struct wpa_supplicant *tx_iface;
tx_iface = wpas_get_tx_interface(wpa_s, src);
if (tx_iface->assoc_freq == freq) {
wpa_printf(MSG_DEBUG, "P2P: Already on requested "
"channel (TX interface operating channel)");
freq = 0;
}
}
if (wpa_s->off_channel_freq == freq || freq == 0) {
wpa_printf(MSG_DEBUG, "P2P: Already on requested channel; "
"send Action frame immediately");
/* TODO: Would there ever be need to extend the current
* duration on the channel? */
wpa_s->pending_action_without_roc = 1;
eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL);
eloop_register_timeout(0, 0, wpas_send_action_cb, wpa_s, NULL);
return 0;
}
wpa_s->pending_action_without_roc = 0;
if (wpa_s->roc_waiting_drv_freq == freq) {
wpa_printf(MSG_DEBUG, "P2P: Already waiting for driver to get "
"to frequency %u MHz; continue waiting to send the "
"Action frame", freq);
return 0;
}
wpa_printf(MSG_DEBUG, "P2P: Schedule Action frame to be transmitted "
"once the driver gets to the requested channel");
if (wait_time > wpa_s->max_remain_on_chan)
wait_time = wpa_s->max_remain_on_chan;
if (wpa_drv_remain_on_channel(wpa_s, freq, wait_time) < 0) {
wpa_printf(MSG_DEBUG, "P2P: Failed to request driver "
"to remain on channel (%u MHz) for Action "
"Frame TX", freq);
return -1;
}
wpa_s->off_channel_freq = 0;
wpa_s->roc_waiting_drv_freq = freq;
return 0;
} }
static void wpas_send_action_done(void *ctx) static void wpas_send_action_done(void *ctx)
{ {
struct wpa_supplicant *wpa_s = ctx; struct wpa_supplicant *wpa_s = ctx;
wpa_printf(MSG_DEBUG, "P2P: Action frame sequence done notification"); offchannel_send_action_done(wpa_s);
wpabuf_free(wpa_s->pending_action_tx);
wpa_s->pending_action_tx = NULL;
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX &&
wpa_s->action_tx_wait_time)
wpa_drv_send_action_cancel_wait(wpa_s);
if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
wpa_drv_cancel_remain_on_channel(wpa_s);
wpa_s->off_channel_freq = 0;
wpa_s->roc_waiting_drv_freq = 0;
}
} }
@ -2529,9 +2325,6 @@ void wpas_p2p_deinit(struct wpa_supplicant *wpa_s)
wpa_drv_probe_req_report(wpa_s, 0); wpa_drv_probe_req_report(wpa_s, 0);
os_free(wpa_s->go_params); os_free(wpa_s->go_params);
wpa_s->go_params = NULL; wpa_s->go_params = NULL;
wpabuf_free(wpa_s->pending_action_tx);
wpa_s->pending_action_tx = NULL;
eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL);
eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
wpa_s->p2p_long_listen = 0; wpa_s->p2p_long_listen = 0;
@ -3056,9 +2849,6 @@ void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
{ {
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return; return;
wpa_s->roc_waiting_drv_freq = 0;
wpa_s->off_channel_freq = freq;
wpas_send_action_cb(wpa_s, NULL);
if (wpa_s->off_channel_freq == wpa_s->pending_listen_freq) { if (wpa_s->off_channel_freq == wpa_s->pending_listen_freq) {
p2p_listen_cb(wpa_s->global->p2p, wpa_s->pending_listen_freq, p2p_listen_cb(wpa_s->global->p2p, wpa_s->pending_listen_freq,
wpa_s->pending_listen_duration); wpa_s->pending_listen_duration);
@ -3096,7 +2886,6 @@ void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel callback " wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel callback "
"(p2p_long_listen=%d ms pending_action_tx=%p)", "(p2p_long_listen=%d ms pending_action_tx=%p)",
wpa_s->p2p_long_listen, wpa_s->pending_action_tx); wpa_s->p2p_long_listen, wpa_s->pending_action_tx);
wpa_s->off_channel_freq = 0;
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return; return;
if (p2p_listen_end(wpa_s->global->p2p, freq) > 0) if (p2p_listen_end(wpa_s->global->p2p, freq) > 0)

View file

@ -50,6 +50,7 @@
#include "bgscan.h" #include "bgscan.h"
#include "bss.h" #include "bss.h"
#include "scan.h" #include "scan.h"
#include "offchannel.h"
const char *wpa_supplicant_version = const char *wpa_supplicant_version =
"wpa_supplicant v" VERSION_STR "\n" "wpa_supplicant v" VERSION_STR "\n"
@ -440,6 +441,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
wpas_p2p_deinit(wpa_s); wpas_p2p_deinit(wpa_s);
#endif /* CONFIG_P2P */ #endif /* CONFIG_P2P */
#ifdef CONFIG_OFFCHANNEL
offchannel_deinit(wpa_s);
#endif /* CONFIG_OFFCHANNEL */
os_free(wpa_s->next_scan_freqs); os_free(wpa_s->next_scan_freqs);
wpa_s->next_scan_freqs = NULL; wpa_s->next_scan_freqs = NULL;

View file

@ -322,6 +322,13 @@ struct wpa_client_mlme {
#endif /* CONFIG_CLIENT_MLME */ #endif /* CONFIG_CLIENT_MLME */
}; };
enum offchannel_send_action_result {
OFFCHANNEL_SEND_ACTION_SUCCESS /* Frame was send and acknowledged */,
OFFCHANNEL_SEND_ACTION_NO_ACK /* Frame was sent, but not acknowledged
*/,
OFFCHANNEL_SEND_ACTION_FAILED /* Frame was not sent due to a failure */
};
/** /**
* struct wpa_supplicant - Internal data for wpa_supplicant interface * struct wpa_supplicant - Internal data for wpa_supplicant interface
* *
@ -478,13 +485,6 @@ struct wpa_supplicant {
void *ap_configured_cb_data; void *ap_configured_cb_data;
#endif /* CONFIG_AP */ #endif /* CONFIG_AP */
#ifdef CONFIG_P2P
struct p2p_go_neg_results *go_params;
int create_p2p_iface;
u8 pending_interface_addr[ETH_ALEN];
char pending_interface_name[100];
int pending_interface_type;
int p2p_group_idx;
unsigned int off_channel_freq; unsigned int off_channel_freq;
struct wpabuf *pending_action_tx; struct wpabuf *pending_action_tx;
u8 pending_action_src[ETH_ALEN]; u8 pending_action_src[ETH_ALEN];
@ -492,6 +492,22 @@ struct wpa_supplicant {
u8 pending_action_bssid[ETH_ALEN]; u8 pending_action_bssid[ETH_ALEN];
unsigned int pending_action_freq; unsigned int pending_action_freq;
int pending_action_without_roc; int pending_action_without_roc;
void (*pending_action_tx_status_cb)(struct wpa_supplicant *wpa_s,
unsigned int freq, const u8 *dst,
const u8 *src, const u8 *bssid,
const u8 *data, size_t data_len,
enum offchannel_send_action_result
result);
unsigned int roc_waiting_drv_freq;
int action_tx_wait_time;
#ifdef CONFIG_P2P
struct p2p_go_neg_results *go_params;
int create_p2p_iface;
u8 pending_interface_addr[ETH_ALEN];
char pending_interface_name[100];
int pending_interface_type;
int p2p_group_idx;
unsigned int pending_listen_freq; unsigned int pending_listen_freq;
unsigned int pending_listen_duration; unsigned int pending_listen_duration;
enum { enum {
@ -515,8 +531,6 @@ struct wpa_supplicant {
u8 pending_join_dev_addr[ETH_ALEN]; u8 pending_join_dev_addr[ETH_ALEN];
int pending_join_wps_method; int pending_join_wps_method;
int p2p_join_scan_count; int p2p_join_scan_count;
unsigned int roc_waiting_drv_freq;
int action_tx_wait_time;
int force_long_sd; int force_long_sd;
/* /*