aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrendan Jackman <brendan.jackman@bluwireless.co.uk>2020-01-03 15:17:41 (GMT)
committerJouni Malinen <j@w1.fi>2020-01-05 17:43:52 (GMT)
commit8759e9116aa6c18d1f68caf1b5bc1cd338f6c142 (patch)
tree0c87bfaa56708b18d912e7b556ca2bb60018196a
parentccaabeaa036c74d3f93635ab53409a44b3a295b2 (diff)
downloadhostap-8759e9116aa6c18d1f68caf1b5bc1cd338f6c142.zip
hostap-8759e9116aa6c18d1f68caf1b5bc1cd338f6c142.tar.gz
hostap-8759e9116aa6c18d1f68caf1b5bc1cd338f6c142.tar.bz2
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>
-rw-r--r--src/drivers/driver.h27
-rw-r--r--src/drivers/driver_nl80211.c32
-rw-r--r--src/drivers/driver_nl80211_capa.c4
-rw-r--r--wpa_supplicant/driver_i.h10
4 files changed, 73 insertions, 0 deletions
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 08c82a0..09867f2 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) \
@@ -2870,6 +2872,31 @@ struct wpa_driver_ops {
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
* @addr: Destination MAC address
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index e4bc44f..f940ac5 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 9a82cd1..e1cf9f7 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 f8d63a0..d091c87 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,