]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
nl80211: Control port over nl80211 helpers
authorBrendan Jackman <brendan.jackman@bluwireless.co.uk>
Fri, 3 Jan 2020 15:17:41 +0000 (16:17 +0100)
committerJouni Malinen <j@w1.fi>
Sun, 5 Jan 2020 17:43:52 +0000 (19:43 +0200)
Linux kernel v4.17 added the ability to request sending controlled port
frames (e.g., IEEE 802.1X controlled port EAPOL frames) via nl80211
instead of a normal network socket. Doing this provides the device
driver with ordering information between the control port frames and the
installation of keys. This empowers it to avoid race conditions between,
for example, PTK replacement and the sending of frame 4 of the 4-way
rekeying handshake in an RSNA. The key difference between the specific
control port and normal socket send is that the device driver will
certainly get any EAPOL frames comprising a 4-way handshake before it
gets the key installation call for the derived key. By flushing its TX
buffers it can then ensure that no pending EAPOL frames are
inadvertently encrypted with a key that the peer will not yet have
installed.

Add a CONTROL_PORT flag to the hostap driver API to report driver
capability for using a separate control port for EAPOL frames. This
operation is exactly like an Ethernet send except for the extra ordering
information it provides for device drivers. The nl80211 driver is
updated to support this operation when the device reports support for
NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211. Also add a driver op
tx_control_port() for request a frame to be sent over the controlled
port.

Signed-off-by: Brendan Jackman <brendan.jackman@bluwireless.co.uk>
src/drivers/driver.h
src/drivers/driver_nl80211.c
src/drivers/driver_nl80211_capa.c
wpa_supplicant/driver_i.h

index 08c82a0fba70a54d960972cc7c2bc2ba258958fd..09867f2cd2dcdd3a228d82bc56c6c83710fb1db9 100644 (file)
@@ -1704,6 +1704,8 @@ struct wpa_driver_capa {
 #define WPA_DRIVER_FLAGS_FTM_RESPONDER         0x0100000000000000ULL
 /** Driver support 4-way handshake offload for WPA-Personal */
 #define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK    0x0200000000000000ULL
+/** Driver supports a separate control port for EAPOL frames */
+#define WPA_DRIVER_FLAGS_CONTROL_PORT          0x0400000000000000ULL
        u64 flags;
 
 #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
@@ -2869,6 +2871,31 @@ struct wpa_driver_ops {
        int (*read_sta_data)(void *priv, struct hostap_sta_driver_data *data,
                             const u8 *addr);
 
+       /**
+        * tx_control_port - Send a frame over the 802.1X controlled port
+        * @priv: Private driver interface data
+        * @dest: Destination MAC address
+        * @proto: Ethertype in host byte order
+        * @buf: Frame payload starting from IEEE 802.1X header
+        * @len: Frame payload length
+        *
+        * Returns 0 on success, else an error
+        *
+        * This is like a normal Ethernet send except that the driver is aware
+        * (by other means than the Ethertype) that this frame is special,
+        * and more importantly it gains an ordering between the transmission of
+        * the frame and other driver management operations such as key
+        * installations. This can be used to work around known limitations in
+        * IEEE 802.11 protocols such as race conditions between rekeying 4-way
+        * handshake message 4/4 and a PTK being overwritten.
+        *
+        * This function is only used for a given interface if the driver
+        * instance reports WPA_DRIVER_FLAGS_CONTROL_PORT capability. Otherwise,
+        * API users will fall back to sending the frame via a normal socket.
+        */
+       int (*tx_control_port)(void *priv, const u8 *dest,
+                              u16 proto, const u8 *buf, size_t len);
+
        /**
         * hapd_send_eapol - Send an EAPOL packet (AP only)
         * @priv: private driver interface data
index e4bc44ffba22aab219b8f339a5dacc12c4da96b1..f940ac5520b9cd3ea4c786babac11925848d1669 100644 (file)
@@ -5092,6 +5092,37 @@ static void nl80211_teardown_ap(struct i802_bss *bss)
 }
 
 
+static int nl80211_tx_control_port(void *priv, const u8 *dest,
+                                  u16 proto, const u8 *buf, size_t len)
+{
+       struct i802_bss *bss = priv;
+       struct nl_msg *msg;
+       int ret;
+
+       wpa_printf(MSG_DEBUG,
+                  "nl80211: Send over control port dest=" MACSTR
+                  " proto=0x%04x len=%u",
+                  MAC2STR(dest), proto, (unsigned int) len);
+
+       msg = nl80211_bss_msg(bss, 0, NL80211_CMD_CONTROL_PORT_FRAME);
+       if (!msg ||
+           nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, proto) ||
+           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dest) ||
+           nla_put(msg, NL80211_ATTR_FRAME, len, buf)) {
+               nlmsg_free(msg);
+               return -ENOBUFS;
+       }
+
+       ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+       if (ret)
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: tx_control_port failed: ret=%d (%s)",
+                          ret, strerror(ret));
+
+       return ret;
+}
+
+
 static int nl80211_send_eapol_data(struct i802_bss *bss,
                                   const u8 *addr, const u8 *data,
                                   size_t data_len)
@@ -11232,6 +11263,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .get_hw_feature_data = nl80211_get_hw_feature_data,
        .sta_add = wpa_driver_nl80211_sta_add,
        .sta_remove = driver_nl80211_sta_remove,
+       .tx_control_port = nl80211_tx_control_port,
        .hapd_send_eapol = wpa_driver_nl80211_hapd_send_eapol,
        .sta_set_flags = wpa_driver_nl80211_sta_set_flags,
        .sta_set_airtime_weight = driver_nl80211_sta_set_airtime_weight,
index 9a82cd1e52415bb554585fc49aae896b55a24517..e1cf9f7b1db85d80a4d7c0ea27d90586f731fa29 100644 (file)
@@ -433,6 +433,10 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info,
        if (ext_feature_isset(ext_features, len,
                              NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER))
                capa->flags |= WPA_DRIVER_FLAGS_FTM_RESPONDER;
+
+       if (ext_feature_isset(ext_features, len,
+                             NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211))
+               capa->flags |= WPA_DRIVER_FLAGS_CONTROL_PORT;
 }
 
 
index f8d63a07b0ef4ac3448e0e788fce100f2e9b685a..d091c8761a812a01402e2a8164be31e8670f20b4 100644 (file)
@@ -342,6 +342,16 @@ static inline int wpa_drv_sta_remove(struct wpa_supplicant *wpa_s,
        return -1;
 }
 
+static inline int wpa_drv_tx_control_port(struct wpa_supplicant *wpa_s,
+                                         const u8 *dest, u16 proto,
+                                         const u8 *buf, size_t len)
+{
+       if (!wpa_s->driver->tx_control_port)
+               return -1;
+       return wpa_s->driver->tx_control_port(wpa_s->drv_priv, dest, proto,
+                                             buf, len);
+}
+
 static inline int wpa_drv_hapd_send_eapol(struct wpa_supplicant *wpa_s,
                                          const u8 *addr, const u8 *data,
                                          size_t data_len, int encrypt,