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);