diff mbox series

[15/25] OCV: Include and verify OCI in WNM-Sleep Exit frames

Message ID 20180806194643.1328-16-Mathy.Vanhoef@cs.kuleuven.be
State Accepted
Headers show
Series Add support for Operating Channel Validation (OCV) | expand

Commit Message

Mathy Vanhoef Aug. 6, 2018, 7:46 p.m. UTC
Include and verify the OCI element in WNM-Sleep Exit Request and
Response frames. In case verification fails, the frame is silently
ignored.

Signed-off-by: Mathy Vanhoef <Mathy.Vanhoef@cs.kuleuven.be>
---
 src/ap/wnm_ap.c          | 78 ++++++++++++++++++++++++++++++++++++---
 wpa_supplicant/wnm_sta.c | 79 +++++++++++++++++++++++++++++++++++++---
 2 files changed, 147 insertions(+), 10 deletions(-)
diff mbox series

Patch

diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 710fe502b..1850df531 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -12,6 +12,7 @@ 
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
 #include "common/wpa_ctrl.h"
+#include "common/ocv.h"
 #include "ap/hostapd.h"
 #include "ap/sta_info.h"
 #include "ap/ap_config.h"
@@ -54,8 +55,8 @@  static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
 	size_t gtk_elem_len = 0;
 	size_t igtk_elem_len = 0;
 	struct wnm_sleep_element wnmsleep_ie;
-	u8 *wnmtfs_ie;
-	u8 wnmsleep_ie_len;
+	u8 *wnmtfs_ie, *oci_ie;
+	u8 wnmsleep_ie_len, oci_ie_len;
 	u16 wnmtfs_ie_len;
 	u8 *pos;
 	struct sta_info *sta;
@@ -88,10 +89,41 @@  static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
 		wnmtfs_ie = NULL;
 	}
 
+	oci_ie = NULL;
+	oci_ie_len = 0;
+#ifdef CONFIG_OCV
+	if (action_type == WNM_SLEEP_MODE_EXIT &&
+	    sta != NULL && wpa_auth_uses_ocv(sta->wpa_sm)) {
+		struct wpa_channel_info ci;
+
+		if (hostapd_drv_channel_info(hapd, &ci) != 0) {
+			wpa_printf(MSG_WARNING, "Failed to get channel info "
+				   "for OCI element in WNM-Sleep Mode frame");
+			os_free(wnmtfs_ie);
+			return -1;
+		}
+
+		oci_ie_len = OCV_OCI_EXTENDED_LEN;
+		oci_ie = os_zalloc(oci_ie_len);
+		if (oci_ie == NULL) {
+			wpa_printf(MSG_WARNING, "Failed to allocate buffer for "
+				   "for OCI element in WNM-Sleep Mode frame");
+			os_free(wnmtfs_ie);
+			return -1;
+		}
+
+		if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
+			os_free(wnmtfs_ie);
+			os_free(oci_ie);
+			return -1;
+		}
+	}
+#endif /* CONFIG_OCV */
+
 #define MAX_GTK_SUBELEM_LEN 45
 #define MAX_IGTK_SUBELEM_LEN 26
-	mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len +
-			 MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN);
+	mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + MAX_GTK_SUBELEM_LEN +
+			 MAX_IGTK_SUBELEM_LEN + oci_ie_len);
 	if (mgmt == NULL) {
 		wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
 			   "WNM-Sleep Response action frame");
@@ -136,9 +168,15 @@  static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
 	pos += wnmsleep_ie_len;
 	if (wnmtfs_ie)
 		os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len);
+	pos += wnmtfs_ie_len;
+#ifdef CONFIG_OCV
+	/* copy OCV OCI here */
+	if (oci_ie_len > 0)
+		os_memcpy(pos, oci_ie, oci_ie_len);
+#endif /* CONFIG_OCV */
 
 	len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len +
-		igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len;
+		igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len + oci_ie_len;
 
 	/* In driver, response frame should be forced to sent when STA is in
 	 * PS mode */
@@ -185,6 +223,7 @@  static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
 #undef MAX_IGTK_SUBELEM_LEN
 fail:
 	os_free(wnmtfs_ie);
+	os_free(oci_ie);
 	os_free(mgmt);
 	return res;
 }
@@ -193,6 +232,7 @@  fail:
 static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
 				       const u8 *addr, const u8 *frm, int len)
 {
+	struct sta_info *sta;
 	/* Dialog Token [1] | WNM-Sleep Mode IE | TFS Response IE */
 	const u8 *pos = frm;
 	u8 dialog_token;
@@ -201,6 +241,8 @@  static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
 	u8 *tfsreq_ie_start = NULL;
 	u8 *tfsreq_ie_end = NULL;
 	u16 tfsreq_ie_len = 0;
+	const u8 *oci_ie = NULL;
+	u8 oci_ie_len = 0;
 
 	if (!hapd->conf->wnm_sleep_mode) {
 		wpa_printf(MSG_DEBUG, "Ignore WNM-Sleep Mode Request from "
@@ -221,6 +263,10 @@  static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
 			if (!tfsreq_ie_start)
 				tfsreq_ie_start = (u8 *) pos;
 			tfsreq_ie_end = (u8 *) pos;
+		} else if (*pos == WLAN_EID_EXTENSION && ie_len >= 1 &&
+				   pos[2] == WLAN_EID_EXT_OCV_OCI) {
+			oci_ie = pos + 3;
+			oci_ie_len = ie_len - 1;
 		} else
 			wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized",
 				   *pos);
@@ -232,6 +278,28 @@  static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
 		return;
 	}
 
+#ifdef CONFIG_OCV
+	sta = ap_get_sta(hapd, addr);
+	if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT &&
+			sta != NULL && wpa_auth_uses_ocv(sta->wpa_sm)) {
+		struct wpa_channel_info ci;
+
+		if (hostapd_drv_channel_info(hapd, &ci) != 0) {
+			wpa_printf(MSG_WARNING,
+				   "Failed to get channel info to validate "
+				   "received OCI in WNM-Sleep Mode frame");
+			return;
+		}
+
+		if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci,
+					 channel_width_to_int(ci.chanwidth),
+					 ci.seg1_idx) != 0) {
+			wpa_msg(hapd, MSG_WARNING, ocv_errorstr);
+			return;
+		}
+	}
+#endif /* CONFIG_OCV */
+
 	if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER &&
 	    tfsreq_ie_start && tfsreq_ie_end &&
 	    tfsreq_ie_end - tfsreq_ie_start >= 0) {
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 7c410e730..5577b0901 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -12,6 +12,7 @@ 
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "common/wpa_ctrl.h"
+#include "common/ocv.h"
 #include "rsn_supp/wpa.h"
 #include "config.h"
 #include "wpa_supplicant_i.h"
@@ -54,12 +55,13 @@  static int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s,
 int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
 				 u8 action, u16 intval, struct wpabuf *tfs_req)
 {
+	struct wpa_sm *sm = wpa_s->wpa;
 	struct ieee80211_mgmt *mgmt;
 	int res;
 	size_t len;
 	struct wnm_sleep_element *wnmsleep_ie;
-	u8 *wnmtfs_ie;
-	u8 wnmsleep_ie_len;
+	u8 *wnmtfs_ie, *oci_ie;
+	u8 wnmsleep_ie_len, oci_ie_len;
 	u16 wnmtfs_ie_len;  /* possibly multiple IE(s) */
 	enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD :
 		WNM_SLEEP_TFS_REQ_IE_NONE;
@@ -106,7 +108,40 @@  int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
 	wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element",
 		    (u8 *) wnmtfs_ie, wnmtfs_ie_len);
 
-	mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len);
+	oci_ie = NULL;
+	oci_ie_len = 0;
+#ifdef CONFIG_OCV
+	if (action == WNM_SLEEP_MODE_EXIT && wpa_sm_ocv_enabled(sm)) {
+		struct wpa_channel_info ci;
+
+		if (wpa_drv_channel_info(wpa_s, &ci) != 0) {
+			wpa_printf(MSG_WARNING, "Failed to get channel info "
+				   "for OCI element in WNM-Sleep Mode frame");
+			os_free(wnmsleep_ie);
+			os_free(wnmtfs_ie);
+			return -1;
+		}
+
+		oci_ie_len = OCV_OCI_EXTENDED_LEN;
+		oci_ie = os_zalloc(oci_ie_len);
+		if (oci_ie == NULL) {
+			wpa_printf(MSG_WARNING, "Failed to allocate buffer for "
+				   "for OCI element in WNM-Sleep Mode frame");
+			os_free(wnmsleep_ie);
+			os_free(wnmtfs_ie);
+			return -1;
+		}
+
+		if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
+			os_free(wnmsleep_ie);
+			os_free(wnmtfs_ie);
+			os_free(oci_ie);
+			return -1;
+		}
+	}
+#endif /* CONFIG_OCV */
+
+	mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len + oci_ie_len);
 	if (mgmt == NULL) {
 		wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
 			   "WNM-Sleep Request action frame");
@@ -130,9 +165,15 @@  int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
 		os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable +
 			  wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len);
 	}
-
+#ifdef CONFIG_OCV
+	/* copy OCV OCI here */
+	if (oci_ie_len > 0) {
+		os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable +
+			  wnmsleep_ie_len + wnmtfs_ie_len, oci_ie, oci_ie_len);
+	}
+#endif /* CONFIG_OCV */
 	len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len +
-		wnmtfs_ie_len;
+		wnmtfs_ie_len + oci_ie_len;
 
 	res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
 				  wpa_s->own_addr, wpa_s->bssid,
@@ -145,6 +186,7 @@  int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
 
 	os_free(wnmsleep_ie);
 	os_free(wnmtfs_ie);
+	os_free(oci_ie);
 	os_free(mgmt);
 
 	return res;
@@ -256,6 +298,8 @@  static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s,
 	/* multiple TFS Resp IE (assuming consecutive) */
 	const u8 *tfsresp_ie_start = NULL;
 	const u8 *tfsresp_ie_end = NULL;
+	const u8 *oci_ie = NULL;
+	u8 oci_ie_len = 0;
 	size_t left;
 
 	if (!wpa_s->wnmsleep_used) {
@@ -289,6 +333,10 @@  static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s,
 			if (!tfsresp_ie_start)
 				tfsresp_ie_start = pos;
 			tfsresp_ie_end = pos;
+		} else if (*pos == WLAN_EID_EXTENSION && ie_len >= 1 &&
+			   pos[2] == WLAN_EID_EXT_OCV_OCI) {
+			oci_ie = pos + 3;
+			oci_ie_len = ie_len - 1;
 		} else
 			wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos);
 		pos += ie_len + 2;
@@ -299,6 +347,27 @@  static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s,
 		return;
 	}
 
+#ifdef CONFIG_OCV
+	if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT &&
+	    wpa_sm_ocv_enabled(wpa_s->wpa)) {
+		struct wpa_channel_info ci;
+
+		if (wpa_drv_channel_info(wpa_s, &ci) != 0) {
+			wpa_msg(wpa_s, MSG_WARNING,
+				"Failed to get channel info to validate "
+				"received OCI in WNM-Sleep Mode frame");
+			return;
+		}
+
+		if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci,
+					 channel_width_to_int(ci.chanwidth),
+					 ci.seg1_idx) != 0) {
+			wpa_msg(wpa_s, MSG_WARNING, ocv_errorstr);
+			return;
+		}
+	}
+#endif /* CONFIG_OCV */
+
 	wpa_s->wnmsleep_used = 0;
 
 	if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT ||