diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c index 1e8f58b4d..d9c1690ee 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,42 @@ 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 && + 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_IGTK_SUBELEM_LEN 26 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) { wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " "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); /* copy TFS IE here */ pos += wnmsleep_ie_len; - if (wnmtfs_ie) + 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 +225,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; } @@ -201,6 +242,11 @@ 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; +#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) { 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) tfsreq_ie_start = (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 wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized", *pos); @@ -232,6 +284,27 @@ 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 && 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 && 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 6b68fc9e3..a6ccd842b 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" @@ -58,8 +59,8 @@ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, 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 +107,41 @@ 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(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) { wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " "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); } +#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 +188,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 +300,10 @@ 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; +#ifdef CONFIG_OCV + const u8 *oci_ie = NULL; + u8 oci_ie_len = 0; +#endif /* CONFIG_OCV */ size_t left; 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) tfsresp_ie_start = 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 wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos); pos += ie_len + 2; @@ -299,6 +353,26 @@ 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, "WNM: %s", ocv_errorstr); + return; + } + } +#endif /* CONFIG_OCV */ + wpa_s->wnmsleep_used = 0; if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT ||