diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 08c82a0fb..09867f2cd 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -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 diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index e4bc44ffb..f940ac552 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -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, diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c index 9a82cd1e5..e1cf9f7b1 100644 --- a/src/drivers/driver_nl80211_capa.c +++ b/src/drivers/driver_nl80211_capa.c @@ -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; } diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h index f8d63a07b..d091c8761 100644 --- a/wpa_supplicant/driver_i.h +++ b/wpa_supplicant/driver_i.h @@ -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,