nl80211: Work around misdelivered control port TX status

The kernel commit "mac80211: support control port TX status reporting"
seems to be delivering the TX status events for EAPOL frames over
control port using NL80211_CMD_FRAME_TX_STATUS due to incorrect check on
whether the frame is a Management or Data frame. Use the pending cookie
value from EAPOL TX operation to detect this incorrect behavior and
redirect the event internally to allow it to be used to get full TX
control port functionality available for AP mode.

Signed-off-by: Jouni Malinen <j@w1.fi>
This commit is contained in:
Jouni Malinen 2020-06-21 17:32:00 +03:00
parent 87065881b1
commit 569497bf4f
3 changed files with 36 additions and 17 deletions

View file

@ -5363,14 +5363,18 @@ static int nl80211_tx_control_port(void *priv, const u8 *dest,
ext_arg.ext_data = &cookie;
ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL,
ack_handler_cookie, &ext_arg);
if (ret)
if (ret) {
wpa_printf(MSG_DEBUG,
"nl80211: tx_control_port failed: ret=%d (%s)",
ret, strerror(-ret));
else
} else {
struct wpa_driver_nl80211_data *drv = bss->drv;
wpa_printf(MSG_DEBUG,
"nl80211: tx_control_port cookie=0x%llx",
(long long unsigned int) cookie);
drv->eapol_tx_cookie = cookie;
}
return ret;
}

View file

@ -181,6 +181,7 @@ struct wpa_driver_nl80211_data {
#define MAX_SEND_FRAME_COOKIES 20
u64 send_frame_cookies[MAX_SEND_FRAME_COOKIES];
unsigned int num_send_frame_cookies;
u64 eapol_tx_cookie;
unsigned int last_mgmt_freq;

View file

@ -20,6 +20,12 @@
#include "driver_nl80211.h"
static void
nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
const u8 *frame, size_t len,
struct nlattr *ack, struct nlattr *cookie);
static const char * nl80211_command_to_string(enum nl80211_commands cmd)
{
#define C2S(x) case x: return #x;
@ -700,6 +706,16 @@ static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv,
WLAN_FC_GET_STYPE(fc), (long long unsigned int) cookie_val,
cookie ? "" : "(N/A)", ack != NULL);
if (cookie_val && cookie_val == drv->eapol_tx_cookie &&
len >= ETH_HLEN &&
WPA_GET_BE16(frame + 2 * ETH_ALEN) == ETH_P_PAE) {
wpa_printf(MSG_DEBUG,
"nl80211: Work around misdelivered control port TX status for EAPOL");
nl80211_control_port_frame_tx_status(drv, frame, len, ack,
cookie);
return;
}
if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT)
return;
@ -2561,31 +2577,23 @@ static void nl80211_control_port_frame(struct wpa_driver_nl80211_data *drv,
static void
nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
struct nlattr **tb)
const u8 *frame, size_t len,
struct nlattr *ack, struct nlattr *cookie)
{
bool acked = tb[NL80211_ATTR_ACK];
union wpa_event_data event;
const u8 *frame;
size_t frame_len;
if (!tb[NL80211_ATTR_FRAME] || !tb[NL80211_ATTR_COOKIE])
return;
frame = nla_data(tb[NL80211_ATTR_FRAME]);
frame_len = nla_len(tb[NL80211_ATTR_FRAME]);
if (frame_len < ETH_HLEN)
if (!cookie || len < ETH_HLEN)
return;
wpa_printf(MSG_DEBUG,
"nl80211: Control port TX status (ack=%d), cookie=%llu",
acked, (long long unsigned int)
nla_get_u64(tb[NL80211_ATTR_COOKIE]));
ack != NULL, (long long unsigned int) nla_get_u64(cookie));
os_memset(&event, 0, sizeof(event));
event.eapol_tx_status.dst = frame;
event.eapol_tx_status.data = frame + ETH_HLEN;
event.eapol_tx_status.data_len = frame_len - ETH_HLEN;
event.eapol_tx_status.ack = acked;
event.eapol_tx_status.data_len = len - ETH_HLEN;
event.eapol_tx_status.ack = ack != NULL;
wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event);
}
@ -2809,7 +2817,13 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
nla_len(frame));
break;
case NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS:
nl80211_control_port_frame_tx_status(drv, tb);
if (!frame)
break;
nl80211_control_port_frame_tx_status(drv,
nla_data(frame),
nla_len(frame),
tb[NL80211_ATTR_ACK],
tb[NL80211_ATTR_COOKIE]);
break;
default:
wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event "