OCV: Include and verify OCI in WNM-Sleep Exit frames
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>
This commit is contained in:
parent
0059625b77
commit
fa97981265
2 changed files with 156 additions and 9 deletions
|
@ -12,6 +12,7 @@
|
||||||
#include "utils/eloop.h"
|
#include "utils/eloop.h"
|
||||||
#include "common/ieee802_11_defs.h"
|
#include "common/ieee802_11_defs.h"
|
||||||
#include "common/wpa_ctrl.h"
|
#include "common/wpa_ctrl.h"
|
||||||
|
#include "common/ocv.h"
|
||||||
#include "ap/hostapd.h"
|
#include "ap/hostapd.h"
|
||||||
#include "ap/sta_info.h"
|
#include "ap/sta_info.h"
|
||||||
#include "ap/ap_config.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 gtk_elem_len = 0;
|
||||||
size_t igtk_elem_len = 0;
|
size_t igtk_elem_len = 0;
|
||||||
struct wnm_sleep_element wnmsleep_ie;
|
struct wnm_sleep_element wnmsleep_ie;
|
||||||
u8 *wnmtfs_ie;
|
u8 *wnmtfs_ie, *oci_ie;
|
||||||
u8 wnmsleep_ie_len;
|
u8 wnmsleep_ie_len, oci_ie_len;
|
||||||
u16 wnmtfs_ie_len;
|
u16 wnmtfs_ie_len;
|
||||||
u8 *pos;
|
u8 *pos;
|
||||||
struct sta_info *sta;
|
struct sta_info *sta;
|
||||||
|
@ -88,10 +89,42 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
|
||||||
wnmtfs_ie = NULL;
|
wnmtfs_ie = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oci_ie = NULL;
|
||||||
|
oci_ie_len = 0;
|
||||||
|
#ifdef CONFIG_OCV
|
||||||
|
if (action_type == WNM_SLEEP_MODE_EXIT &&
|
||||||
|
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) {
|
||||||
|
wpa_printf(MSG_WARNING,
|
||||||
|
"Failed to allocate buffer 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_GTK_SUBELEM_LEN 45
|
||||||
#define MAX_IGTK_SUBELEM_LEN 26
|
#define MAX_IGTK_SUBELEM_LEN 26
|
||||||
mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len +
|
mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len +
|
||||||
MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN);
|
MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN +
|
||||||
|
oci_ie_len);
|
||||||
if (mgmt == NULL) {
|
if (mgmt == NULL) {
|
||||||
wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
|
wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
|
||||||
"WNM-Sleep Response action frame");
|
"WNM-Sleep Response action frame");
|
||||||
|
@ -134,11 +167,18 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
|
||||||
os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len);
|
os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len);
|
||||||
/* copy TFS IE here */
|
/* copy TFS IE here */
|
||||||
pos += wnmsleep_ie_len;
|
pos += wnmsleep_ie_len;
|
||||||
if (wnmtfs_ie)
|
if (wnmtfs_ie) {
|
||||||
os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len);
|
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 +
|
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
|
/* In driver, response frame should be forced to sent when STA is in
|
||||||
* PS mode */
|
* PS mode */
|
||||||
|
@ -185,6 +225,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
|
||||||
#undef MAX_IGTK_SUBELEM_LEN
|
#undef MAX_IGTK_SUBELEM_LEN
|
||||||
fail:
|
fail:
|
||||||
os_free(wnmtfs_ie);
|
os_free(wnmtfs_ie);
|
||||||
|
os_free(oci_ie);
|
||||||
os_free(mgmt);
|
os_free(mgmt);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -201,6 +242,11 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
|
||||||
u8 *tfsreq_ie_start = NULL;
|
u8 *tfsreq_ie_start = NULL;
|
||||||
u8 *tfsreq_ie_end = NULL;
|
u8 *tfsreq_ie_end = NULL;
|
||||||
u16 tfsreq_ie_len = 0;
|
u16 tfsreq_ie_len = 0;
|
||||||
|
#ifdef CONFIG_OCV
|
||||||
|
struct sta_info *sta;
|
||||||
|
const u8 *oci_ie = NULL;
|
||||||
|
u8 oci_ie_len = 0;
|
||||||
|
#endif /* CONFIG_OCV */
|
||||||
|
|
||||||
if (!hapd->conf->wnm_sleep_mode) {
|
if (!hapd->conf->wnm_sleep_mode) {
|
||||||
wpa_printf(MSG_DEBUG, "Ignore WNM-Sleep Mode Request from "
|
wpa_printf(MSG_DEBUG, "Ignore WNM-Sleep Mode Request from "
|
||||||
|
@ -221,6 +267,12 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
|
||||||
if (!tfsreq_ie_start)
|
if (!tfsreq_ie_start)
|
||||||
tfsreq_ie_start = (u8 *) pos;
|
tfsreq_ie_start = (u8 *) pos;
|
||||||
tfsreq_ie_end = (u8 *) pos;
|
tfsreq_ie_end = (u8 *) pos;
|
||||||
|
#ifdef CONFIG_OCV
|
||||||
|
} 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;
|
||||||
|
#endif /* CONFIG_OCV */
|
||||||
} else
|
} else
|
||||||
wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized",
|
wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized",
|
||||||
*pos);
|
*pos);
|
||||||
|
@ -232,6 +284,27 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_OCV
|
||||||
|
sta = ap_get_sta(hapd, addr);
|
||||||
|
if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT &&
|
||||||
|
sta && 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, "WNM: %s", ocv_errorstr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_OCV */
|
||||||
|
|
||||||
if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER &&
|
if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER &&
|
||||||
tfsreq_ie_start && tfsreq_ie_end &&
|
tfsreq_ie_start && tfsreq_ie_end &&
|
||||||
tfsreq_ie_end - tfsreq_ie_start >= 0) {
|
tfsreq_ie_end - tfsreq_ie_start >= 0) {
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "common/ieee802_11_defs.h"
|
#include "common/ieee802_11_defs.h"
|
||||||
#include "common/ieee802_11_common.h"
|
#include "common/ieee802_11_common.h"
|
||||||
#include "common/wpa_ctrl.h"
|
#include "common/wpa_ctrl.h"
|
||||||
|
#include "common/ocv.h"
|
||||||
#include "rsn_supp/wpa.h"
|
#include "rsn_supp/wpa.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "wpa_supplicant_i.h"
|
#include "wpa_supplicant_i.h"
|
||||||
|
@ -58,8 +59,8 @@ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
|
||||||
int res;
|
int res;
|
||||||
size_t len;
|
size_t len;
|
||||||
struct wnm_sleep_element *wnmsleep_ie;
|
struct wnm_sleep_element *wnmsleep_ie;
|
||||||
u8 *wnmtfs_ie;
|
u8 *wnmtfs_ie, *oci_ie;
|
||||||
u8 wnmsleep_ie_len;
|
u8 wnmsleep_ie_len, oci_ie_len;
|
||||||
u16 wnmtfs_ie_len; /* possibly multiple IE(s) */
|
u16 wnmtfs_ie_len; /* possibly multiple IE(s) */
|
||||||
enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD :
|
enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD :
|
||||||
WNM_SLEEP_TFS_REQ_IE_NONE;
|
WNM_SLEEP_TFS_REQ_IE_NONE;
|
||||||
|
@ -106,7 +107,41 @@ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
|
||||||
wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element",
|
wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element",
|
||||||
(u8 *) wnmtfs_ie, wnmtfs_ie_len);
|
(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(wpa_s->wpa)) {
|
||||||
|
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) {
|
||||||
|
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) {
|
if (mgmt == NULL) {
|
||||||
wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
|
wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
|
||||||
"WNM-Sleep Request action frame");
|
"WNM-Sleep Request action frame");
|
||||||
|
@ -131,8 +166,16 @@ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
|
||||||
wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len);
|
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 +
|
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,
|
res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
|
||||||
wpa_s->own_addr, wpa_s->bssid,
|
wpa_s->own_addr, wpa_s->bssid,
|
||||||
|
@ -145,6 +188,7 @@ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
|
||||||
|
|
||||||
os_free(wnmsleep_ie);
|
os_free(wnmsleep_ie);
|
||||||
os_free(wnmtfs_ie);
|
os_free(wnmtfs_ie);
|
||||||
|
os_free(oci_ie);
|
||||||
os_free(mgmt);
|
os_free(mgmt);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
@ -256,6 +300,10 @@ static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s,
|
||||||
/* multiple TFS Resp IE (assuming consecutive) */
|
/* multiple TFS Resp IE (assuming consecutive) */
|
||||||
const u8 *tfsresp_ie_start = NULL;
|
const u8 *tfsresp_ie_start = NULL;
|
||||||
const u8 *tfsresp_ie_end = NULL;
|
const u8 *tfsresp_ie_end = NULL;
|
||||||
|
#ifdef CONFIG_OCV
|
||||||
|
const u8 *oci_ie = NULL;
|
||||||
|
u8 oci_ie_len = 0;
|
||||||
|
#endif /* CONFIG_OCV */
|
||||||
size_t left;
|
size_t left;
|
||||||
|
|
||||||
if (!wpa_s->wnmsleep_used) {
|
if (!wpa_s->wnmsleep_used) {
|
||||||
|
@ -289,6 +337,12 @@ static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s,
|
||||||
if (!tfsresp_ie_start)
|
if (!tfsresp_ie_start)
|
||||||
tfsresp_ie_start = pos;
|
tfsresp_ie_start = pos;
|
||||||
tfsresp_ie_end = pos;
|
tfsresp_ie_end = pos;
|
||||||
|
#ifdef CONFIG_OCV
|
||||||
|
} 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;
|
||||||
|
#endif /* CONFIG_OCV */
|
||||||
} else
|
} else
|
||||||
wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos);
|
wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos);
|
||||||
pos += ie_len + 2;
|
pos += ie_len + 2;
|
||||||
|
@ -299,6 +353,26 @@ static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s,
|
||||||
return;
|
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, "WNM: %s", ocv_errorstr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_OCV */
|
||||||
|
|
||||||
wpa_s->wnmsleep_used = 0;
|
wpa_s->wnmsleep_used = 0;
|
||||||
|
|
||||||
if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT ||
|
if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT ||
|
||||||
|
|
Loading…
Reference in a new issue