diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c index bb80b0304..fefae8c37 100644 --- a/src/drivers/driver_atheros.c +++ b/src/drivers/driver_atheros.c @@ -875,7 +875,56 @@ static void atheros_raw_recv_hs20(void *ctx, const u8 *src_addr, const u8 *buf, } #endif /* CONFIG_HS20 */ -#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) +#if defined(CONFIG_IEEE80211V) && !defined(CONFIG_IEEE80211R) +static void atheros_raw_recv_11v(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct atheros_driver_data *drv = ctx; + union wpa_event_data event; + const struct ieee80211_mgmt *mgmt; + u16 fc; + u16 stype; + + /* Do 11R processing for WNM ACTION frames */ + if (len < IEEE80211_HDRLEN) + return; + mgmt = (const struct ieee80211_mgmt *) buf; + + fc = le_to_host16(mgmt->frame_control); + + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT) + return; + stype = WLAN_FC_GET_STYPE(fc); + + wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype, + (int) len); + + if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore", + __func__); + return; + } + + switch (stype) { + case WLAN_FC_STYPE_ACTION: + if (&mgmt->u.action.category > buf + len) + break; + os_memset(&event, 0, sizeof(event)); + event.rx_action.da = mgmt->da; + event.rx_action.sa = mgmt->sa; + event.rx_action.bssid = mgmt->bssid; + event.rx_action.category = mgmt->u.action.category; + event.rx_action.data = &mgmt->u.action.category; + event.rx_action.len = buf + len - event.rx_action.data; + wpa_supplicant_event(drv->hapd, EVENT_RX_ACTION, &event); + break; + default: + break; + } +} +#endif /* CONFIG_IEEE80211V */ + +#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211V) static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { @@ -885,6 +934,9 @@ static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, #ifdef CONFIG_IEEE80211R atheros_raw_recv_11r(ctx, src_addr, buf, len); #endif /* CONFIG_IEEE80211R */ +#if defined(CONFIG_IEEE80211V) && !defined(CONFIG_IEEE80211R) + atheros_raw_recv_11v(ctx, src_addr, buf, len); +#endif /* CONFIG_IEEE80211V */ #ifdef CONFIG_HS20 atheros_raw_recv_hs20(ctx, src_addr, buf, len); #endif /* CONFIG_HS20 */ @@ -906,6 +958,9 @@ static int atheros_receive_pkt(struct atheros_driver_data *drv) IEEE80211_FILTER_TYPE_AUTH | IEEE80211_FILTER_TYPE_ACTION); #endif +#ifdef CONFIG_IEEE80211V + filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION; +#endif /* CONFIG_IEEE80211V */ #ifdef CONFIG_HS20 filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION; #endif /* CONFIG_HS20 */ @@ -1906,6 +1961,162 @@ static int atheros_send_action(void *priv, unsigned int freq, } +#ifdef CONFIG_IEEE80211V +static int athr_wnm_tfs(struct atheros_driver_data *drv, const u8* peer, + u8 *ie, u16 *len, enum wnm_oper oper) +{ +#define IEEE80211_APPIE_MAX 1024 /* max appie buffer size */ + u8 buf[IEEE80211_APPIE_MAX]; + struct ieee80211req_getset_appiebuf *tfs_ie; + u16 val; + + wpa_printf(MSG_DEBUG, "atheros: ifname=%s, WNM TFS IE oper=%d " MACSTR, + drv->iface, oper, MAC2STR(peer)); + + switch (oper) { + case WNM_SLEEP_TFS_REQ_IE_SET: + if (*len > IEEE80211_APPIE_MAX - + sizeof(struct ieee80211req_getset_appiebuf)) { + wpa_printf(MSG_DEBUG, "TFS Req IE(s) too large"); + return -1; + } + tfs_ie = (struct ieee80211req_getset_appiebuf *) buf; + tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM; + tfs_ie->app_buflen = ETH_ALEN + 2 + 2 + *len; + + /* Command header for driver */ + os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN); + val = oper; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2); + val = *len; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2); + + /* copy the ie */ + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2 + 2, ie, *len); + + if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie, + IEEE80211_APPIE_MAX)) { + wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: " + "%s", __func__, strerror(errno)); + return -1; + } + break; + case WNM_SLEEP_TFS_RESP_IE_ADD: + tfs_ie = (struct ieee80211req_getset_appiebuf *) buf; + tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM; + tfs_ie->app_buflen = IEEE80211_APPIE_MAX - + sizeof(struct ieee80211req_getset_appiebuf); + /* Command header for driver */ + os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN); + val = oper; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2); + val = 0; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2); + + if (set80211priv(drv, IEEE80211_IOCTL_GET_APPIEBUF, tfs_ie, + IEEE80211_APPIE_MAX)) { + wpa_printf(MSG_DEBUG, "%s: Failed to get WNM TFS IE: " + "%s", __func__, strerror(errno)); + return -1; + } + + *len = tfs_ie->app_buflen; + os_memcpy(ie, &(tfs_ie->app_buf[0]), *len); + wpa_printf(MSG_DEBUG, "atheros: %c len=%d", tfs_ie->app_buf[0], + *len); + break; + case WNM_SLEEP_TFS_RESP_IE_NONE: + *len = 0; + break; + case WNM_SLEEP_TFS_IE_DEL: + tfs_ie = (struct ieee80211req_getset_appiebuf *) buf; + tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM; + tfs_ie->app_buflen = IEEE80211_APPIE_MAX - + sizeof(struct ieee80211req_getset_appiebuf); + /* Command header for driver */ + os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN); + val = oper; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2); + val = 0; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2); + + if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie, + IEEE80211_APPIE_MAX)) { + wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: " + "%s", __func__, strerror(errno)); + return -1; + } + break; + default: + wpa_printf(MSG_DEBUG, "Unsupported TFS oper %d", oper); + break; + } + + return 0; +} + + +static int atheros_wnm_sleep(struct atheros_driver_data *drv, + const u8 *peer, enum wnm_oper oper) +{ + u8 *data, *pos; + size_t dlen; + int ret; + u16 val; + + wpa_printf(MSG_DEBUG, "atheros: WNM-Sleep Oper %d, " MACSTR, + oper, MAC2STR(peer)); + + dlen = ETH_ALEN + 2 + 2; + data = os_malloc(dlen); + if (data == NULL) + return -1; + + /* Command header for driver */ + pos = data; + os_memcpy(pos, peer, ETH_ALEN); + pos += ETH_ALEN; + + val = oper; + os_memcpy(pos, &val, 2); + pos += 2; + + val = 0; + os_memcpy(pos, &val, 2); + + ret = atheros_set_wps_ie(drv, data, dlen, IEEE80211_APPIE_FRAME_WNM); + + os_free(data); + + return ret; +} + + +static int atheros_wnm_oper(void *priv, enum wnm_oper oper, const u8 *peer, + u8 *buf, u16 *buf_len) +{ + struct atheros_driver_data *drv = priv; + + switch (oper) { + case WNM_SLEEP_ENTER_CONFIRM: + case WNM_SLEEP_ENTER_FAIL: + case WNM_SLEEP_EXIT_CONFIRM: + case WNM_SLEEP_EXIT_FAIL: + return atheros_wnm_sleep(drv, peer, oper); + case WNM_SLEEP_TFS_REQ_IE_SET: + case WNM_SLEEP_TFS_RESP_IE_ADD: + case WNM_SLEEP_TFS_RESP_IE_NONE: + case WNM_SLEEP_TFS_IE_DEL: + return athr_wnm_tfs(drv, peer, buf, buf_len, oper); + default: + wpa_printf(MSG_DEBUG, "atheros: Unsupported WNM operation %d", + oper); + return -1; + } +} +#endif /* CONFIG_IEEE80211V */ + + const struct wpa_driver_ops wpa_driver_atheros_ops = { .name = "atheros", .hapd_init = atheros_init, @@ -1937,4 +2148,7 @@ const struct wpa_driver_ops wpa_driver_atheros_ops = { .add_sta_node = atheros_add_sta_node, #endif /* CONFIG_IEEE80211R */ .send_action = atheros_send_action, +#ifdef CONFIG_IEEE80211V + .wnm_oper = atheros_wnm_oper, +#endif /* CONFIG_IEEE80211V */ };