]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
nl80211: Work around misdelivered control port TX status
authorJouni Malinen <j@w1.fi>
Sun, 21 Jun 2020 14:32:00 +0000 (17:32 +0300)
committerJouni Malinen <j@w1.fi>
Sun, 21 Jun 2020 14:56:04 +0000 (17:56 +0300)
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>
src/drivers/driver_nl80211.c
src/drivers/driver_nl80211.h
src/drivers/driver_nl80211_event.c

index da7038fea2b2639492e80a91e1661d64a10bbc7d..7e3b3f40dd362b989f690eee34d604f7ccd1b7e5 100644 (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;
 }
index e4cbcc6e2f4567f1f19bc152eeb7936299778383..017c025a0d75d37c16d130ffe327c6c6d3f30d35 100644 (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;
 
index 1873b1826dfc4eee67c489e3e085e4e435793eeb..ce95e9cd39759ce429c05080f4949a9728906c04 100644 (file)
 #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 "