nl80211: Control port over nl80211 helpers

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>
This commit is contained in:
Brendan Jackman 2020-01-03 16:17:41 +01:00 committed by Jouni Malinen
parent ccaabeaa03
commit 8759e9116a
4 changed files with 73 additions and 0 deletions

View 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

View 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,

View 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;
}

View 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,