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@xxxxxxxxxxxxxx> --- src/ap/wnm_ap.c | 78 ++++++++++++++++++++++++++++++++++++--- wpa_supplicant/wnm_sta.c | 79 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 147 insertions(+), 10 deletions(-) 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 || -- 2.18.0 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap