From 6f19cc4d7887c7c4c718bc746b661f9a9aadcff4 Mon Sep 17 00:00:00 2001 From: Markus Theil Date: Wed, 10 Jun 2020 10:32:57 +0200 Subject: [PATCH] nl80211: Handle control port TX status events over nl80211 In order to retransmit faster in AP mode, hostapd can handle TX status notifications. When using nl80211, this is currently only possible with socket control messages. Add support for receiving such events directly over nl80211 and detecting, if this feature is supported. This finally allows for a clean separation between management/control path (over nl80211) and in-kernel data path. A follow up commit enables the feature in AP mode. Control port TX status contains the original frame content for matching with the current hostapd code. Furthermore, a cookie is included, which allows for matching against outstanding cookies in the future. This commit only prints the cookie value for debugging purposes on TX status receive. Signed-off-by: Markus Theil --- src/drivers/driver.h | 2 ++ src/drivers/driver_common.c | 1 + src/drivers/driver_nl80211_capa.c | 4 ++++ src/drivers/driver_nl80211_event.c | 36 ++++++++++++++++++++++++++++++ 4 files changed, 43 insertions(+) diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 0ecda49dd..cb412cbfe 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -1931,6 +1931,8 @@ struct wpa_driver_capa { /** Driver supports a separate control port RX for EAPOL frames */ #define WPA_DRIVER_FLAGS2_CONTROL_PORT_RX 0x0000000000000001ULL +/** Driver supports TX status reports for EAPOL frames through control port */ +#define WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS 0x0000000000000002ULL u64 flags2; #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \ diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c index 23a6a4293..a7ebe9566 100644 --- a/src/drivers/driver_common.c +++ b/src/drivers/driver_common.c @@ -328,6 +328,7 @@ const char * driver_flag2_to_string(u64 flag2) #define DF2S(x) case WPA_DRIVER_FLAGS2_ ## x: return #x switch (flag2) { DF2S(CONTROL_PORT_RX); + DF2S(CONTROL_PORT_TX_STATUS); } return "UNKNOWN"; #undef DF2S diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c index 5d2558153..46f61fdbf 100644 --- a/src/drivers/driver_nl80211_capa.c +++ b/src/drivers/driver_nl80211_capa.c @@ -620,6 +620,10 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info, if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_CONTROL_PORT_NO_PREAUTH)) capa->flags2 |= WPA_DRIVER_FLAGS2_CONTROL_PORT_RX; + if (ext_feature_isset( + ext_features, len, + NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_TX_STATUS)) + capa->flags2 |= WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS; if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_VLAN_OFFLOAD)) diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c index 6a2de1f3c..1873b1826 100644 --- a/src/drivers/driver_nl80211_event.c +++ b/src/drivers/driver_nl80211_event.c @@ -138,6 +138,8 @@ static const char * nl80211_command_to_string(enum nl80211_commands cmd) C2S(NL80211_CMD_CONTROL_PORT_FRAME) C2S(NL80211_CMD_UPDATE_OWE_INFO) C2S(NL80211_CMD_UNPROT_BEACON) + C2S(NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS) + default: return "NL80211_CMD_UNKNOWN"; } @@ -2557,6 +2559,37 @@ 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) +{ + 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) + 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])); + + 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; + wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event); +} + + static void do_process_drv_event(struct i802_bss *bss, int cmd, struct nlattr **tb) { @@ -2775,6 +2808,9 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, mlme_event_unprot_beacon(drv, nla_data(frame), nla_len(frame)); break; + case NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS: + nl80211_control_port_frame_tx_status(drv, tb); + break; default: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event " "(cmd=%d)", cmd);