int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd, const u8 *addr, const struct wpabuf *payload) { struct wpabuf *buf; int ret; /* TODO: should refuse to send notification if the STA is not associated * or if the STA did not indicate support for WNM-Notification */ buf = wpabuf_alloc(4 + 6 + wpabuf_len(payload)); if (buf == NULL) return -1; wpabuf_put_u8(buf, WLAN_ACTION_WNM); wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ); wpabuf_put_u8(buf, 1); /* Dialog token */ wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */ /* Deauthentication Imminent Notice subelement */ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); wpabuf_put_u8(buf, 4 + wpabuf_len(payload)); wpabuf_put_be24(buf, OUI_WFA); wpabuf_put_u8(buf, HS20_WNM_DEAUTH_IMMINENT_NOTICE); wpabuf_put_buf(buf, payload); ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, wpabuf_head(buf), wpabuf_len(buf)); wpabuf_free(buf); return ret; }
static void gas_serv_req_local_processing(struct hostapd_data *hapd, const u8 *sa, u8 dialog_token, struct anqp_query_info *qi) { struct wpabuf *buf, *tx_buf; buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL, qi->home_realm_query, qi->home_realm_query_len); wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses", buf); if (!buf) return; if (wpabuf_len(buf) > hapd->gas_frag_limit || hapd->conf->gas_comeback_delay) { struct gas_dialog_info *di; u16 comeback_delay = 1; if (hapd->conf->gas_comeback_delay) { /* Testing - allow overriding of the delay value */ comeback_delay = hapd->conf->gas_comeback_delay; } wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in " "initial response - use GAS comeback"); di = gas_dialog_create(hapd, sa, dialog_token); if (!di) { wpa_printf(MSG_INFO, "ANQP: Could not create dialog " "for " MACSTR " (dialog token %u)", MAC2STR(sa), dialog_token); wpabuf_free(buf); return; } di->sd_resp = buf; di->sd_resp_pos = 0; tx_buf = gas_anqp_build_initial_resp_buf( dialog_token, WLAN_STATUS_SUCCESS, comeback_delay, NULL); } else { wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)"); tx_buf = gas_anqp_build_initial_resp_buf( dialog_token, WLAN_STATUS_SUCCESS, 0, buf); wpabuf_free(buf); } if (!tx_buf) return; hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, wpabuf_head(tx_buf), wpabuf_len(tx_buf)); wpabuf_free(tx_buf); }
static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, const char *url) { struct ieee80211_mgmt *mgmt; size_t url_len, len; u8 *pos; int res; if (url) url_len = os_strlen(url); else url_len = 0; mgmt = os_zalloc(sizeof(*mgmt) + (url_len ? 1 + url_len : 0)); if (mgmt == NULL) return -1; os_memcpy(mgmt->da, addr, ETH_ALEN); os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); mgmt->u.action.category = WLAN_ACTION_WNM; mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ; mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token; mgmt->u.action.u.bss_tm_req.req_mode = 0; mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0); mgmt->u.action.u.bss_tm_req.validity_interval = 1; pos = mgmt->u.action.u.bss_tm_req.variable; if (url) { *pos++ += url_len; os_memcpy(pos, url, url_len); pos += url_len; } wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to " MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u " "validity_interval=%u", MAC2STR(addr), dialog_token, mgmt->u.action.u.bss_tm_req.req_mode, le_to_host16(mgmt->u.action.u.bss_tm_req.disassoc_timer), mgmt->u.action.u.bss_tm_req.validity_interval); len = pos - &mgmt->u.action.category; res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, mgmt->da, &mgmt->u.action.category, len); os_free(mgmt); return res; }
int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr, u8 osu_method, const char *url) { struct wpabuf *buf; size_t len = 0; int ret; /* TODO: should refuse to send notification if the STA is not associated * or if the STA did not indicate support for WNM-Notification */ if (url) { len = 1 + os_strlen(url); if (5 + len > 255) { wpa_printf(MSG_INFO, "HS 2.0: Too long URL for " "WNM-Notification: '%s'", url); return -1; } } buf = wpabuf_alloc(4 + 7 + len); if (buf == NULL) return -1; wpabuf_put_u8(buf, WLAN_ACTION_WNM); wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ); wpabuf_put_u8(buf, 1); /* Dialog token */ wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */ /* Subscription Remediation subelement */ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); wpabuf_put_u8(buf, 5 + len); wpabuf_put_be24(buf, OUI_WFA); wpabuf_put_u8(buf, HS20_WNM_SUB_REM_NEEDED); if (url) { wpabuf_put_u8(buf, len - 1); wpabuf_put_data(buf, url, len - 1); wpabuf_put_u8(buf, osu_method); } else { /* Server URL and Server Method fields not included */ wpabuf_put_u8(buf, 0); } ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, wpabuf_head(buf), wpabuf_len(buf)); wpabuf_free(buf); return ret; }
static void gas_serv_req_remote_processing(struct hostapd_data *hapd, const u8 *sa, u8 dialog_token, struct anqp_query_info *qi) { struct gas_dialog_info *di; struct wpabuf *tx_buf = NULL; di = gas_dialog_create(hapd, sa, dialog_token); if (!di) { wpa_msg(hapd->msg_ctx, MSG_ERROR, "GAS: Could not create dialog for " MACSTR " (dialog token %u) ", MAC2STR(sa), dialog_token); return; } di->requested = qi->remote_request; di->received = 0; di->all_requested = qi->request; /* send the request to external agent */ if (gas_serv_get_info(hapd, qi->remote_request, qi->param, qi->param_arg, sa, dialog_token) < 0) { gas_serv_dialog_clear(di); tx_buf = gas_anqp_build_initial_resp_buf( dialog_token, WLAN_STATUS_ADV_SRV_UNREACHABLE, 0, NULL); } else if (qi->remote_delay >= GAS_SERV_MIN_COMEBACK_DELAY) { di->comeback_delay = qi->remote_delay; wpa_printf(MSG_DEBUG, "GAS: Tx GAS Initial Resp (comeback = " "%d TU)", qi->remote_delay + GAS_SERV_COMEBACK_DELAY_FUDGE); tx_buf = gas_anqp_build_initial_resp_buf( dialog_token, WLAN_STATUS_SUCCESS, qi->remote_delay + GAS_SERV_COMEBACK_DELAY_FUDGE, NULL); } if (!tx_buf) return; hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, wpabuf_head(tx_buf), wpabuf_len(tx_buf)); wpabuf_free(tx_buf); }
int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd, const u8 *addr, const char *url) { struct wpabuf *buf; int ret; size_t url_len; if (!url) { wpa_printf(MSG_INFO, "HS 2.0: No T&C Server URL available"); return -1; } url_len = os_strlen(url); if (5 + url_len > 255) { wpa_printf(MSG_INFO, "HS 2.0: Too long T&C Server URL for WNM-Notification: '%s'", url); return -1; } buf = wpabuf_alloc(4 + 7 + url_len); if (!buf) return -1; wpabuf_put_u8(buf, WLAN_ACTION_WNM); wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ); wpabuf_put_u8(buf, 1); /* Dialog token */ wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */ /* Terms and Conditions Acceptance subelement */ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); wpabuf_put_u8(buf, 4 + 1 + url_len); wpabuf_put_be24(buf, OUI_WFA); wpabuf_put_u8(buf, HS20_WNM_T_C_ACCEPTANCE); wpabuf_put_u8(buf, url_len); wpabuf_put_str(buf, url); ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, wpabuf_head(buf), wpabuf_len(buf)); wpabuf_free(buf); return ret; }
void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst, struct gas_dialog_info *dialog) { struct wpabuf *buf, *tx_buf; u8 dialog_token = dialog->dialog_token; size_t frag_len; if (dialog->sd_resp == NULL) { buf = gas_serv_build_gas_resp_payload(hapd, dialog->all_requested, dialog, NULL, 0); wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses", buf); if (!buf) goto tx_gas_response_done; dialog->sd_resp = buf; dialog->sd_resp_pos = 0; } frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos; if (frag_len > hapd->gas_frag_limit || dialog->comeback_delay || hapd->conf->gas_comeback_delay) { u16 comeback_delay_tus = dialog->comeback_delay + GAS_SERV_COMEBACK_DELAY_FUDGE; u32 comeback_delay_secs, comeback_delay_usecs; if (hapd->conf->gas_comeback_delay) { /* Testing - allow overriding of the delay value */ comeback_delay_tus = hapd->conf->gas_comeback_delay; } wpa_printf(MSG_DEBUG, "GAS: Response frag_len %u (frag limit " "%u) and comeback delay %u, " "requesting comebacks", (unsigned int) frag_len, (unsigned int) hapd->gas_frag_limit, dialog->comeback_delay); tx_buf = gas_anqp_build_initial_resp_buf(dialog_token, WLAN_STATUS_SUCCESS, comeback_delay_tus, NULL); if (tx_buf) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Initial Resp (comeback = 10TU)"); hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst, wpabuf_head(tx_buf), wpabuf_len(tx_buf)); } wpabuf_free(tx_buf); /* start a timer of 1.5 * comeback-delay */ comeback_delay_tus = comeback_delay_tus + (comeback_delay_tus / 2); comeback_delay_secs = (comeback_delay_tus * 1024) / 1000000; comeback_delay_usecs = (comeback_delay_tus * 1024) - (comeback_delay_secs * 1000000); eloop_register_timeout(comeback_delay_secs, comeback_delay_usecs, gas_serv_clear_cached_ies, dialog, NULL); goto tx_gas_response_done; } buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) + dialog->sd_resp_pos, frag_len); if (buf == NULL) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Buffer allocation " "failed"); goto tx_gas_response_done; } tx_buf = gas_anqp_build_initial_resp_buf(dialog_token, WLAN_STATUS_SUCCESS, 0, buf); wpabuf_free(buf); if (tx_buf == NULL) goto tx_gas_response_done; wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Initial " "Response (frag_id %d frag_len %d)", dialog->sd_frag_id, (int) frag_len); dialog->sd_frag_id++; hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst, wpabuf_head(tx_buf), wpabuf_len(tx_buf)); wpabuf_free(tx_buf); tx_gas_response_done: gas_serv_clear_cached_ies(dialog, NULL); }
static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, const u8 *sa, const u8 *data, size_t len) { const u8 *pos = data; const u8 *end = data + len; const u8 *next; u8 dialog_token; u16 slen; struct anqp_query_info qi; const u8 *adv_proto; if (len < 1 + 2) return; os_memset(&qi, 0, sizeof(qi)); dialog_token = *pos++; wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: GAS Initial Request from " MACSTR " (dialog token %u) ", MAC2STR(sa), dialog_token); if (*pos != WLAN_EID_ADV_PROTO) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Unexpected IE in GAS Initial Request: %u", *pos); return; } adv_proto = pos++; slen = *pos++; next = pos + slen; if (next > end || slen < 2) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Invalid IE in GAS Initial Request"); return; } pos++; /* skip QueryRespLenLimit and PAME-BI */ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { struct wpabuf *buf; wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Unsupported GAS advertisement protocol id %u", *pos); if (sa[0] & 0x01) return; /* Invalid source address - drop silently */ buf = gas_build_initial_resp( dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED, 0, 2 + slen + 2); if (buf == NULL) return; wpabuf_put_data(buf, adv_proto, 2 + slen); wpabuf_put_le16(buf, 0); /* Query Response Length */ hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, wpabuf_head(buf), wpabuf_len(buf)); wpabuf_free(buf); return; } pos = next; /* Query Request */ if (pos + 2 > end) return; slen = WPA_GET_LE16(pos); pos += 2; if (pos + slen > end) return; end = pos + slen; /* ANQP Query Request */ while (pos < end) { u16 info_id, elen; if (pos + 4 > end) return; info_id = WPA_GET_LE16(pos); pos += 2; elen = WPA_GET_LE16(pos); pos += 2; if (pos + elen > end) { wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request"); return; } switch (info_id) { case ANQP_QUERY_LIST: rx_anqp_query_list(hapd, pos, pos + elen, &qi); break; #ifdef CONFIG_HS20 case ANQP_VENDOR_SPECIFIC: rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi); break; #endif /* CONFIG_HS20 */ default: wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query " "Request element %u", info_id); break; } pos += elen; } gas_serv_req_local_processing(hapd, sa, dialog_token, &qi); }
static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, const u8 *sa, const u8 *data, size_t len) { struct gas_dialog_info *dialog; struct wpabuf *buf, *tx_buf; u8 dialog_token; size_t frag_len; int more = 0; wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len); if (len < 1) return; dialog_token = *data; wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u", dialog_token); dialog = gas_serv_dialog_find(hapd, sa, dialog_token); if (!dialog) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD " "response fragment for " MACSTR " dialog token %u", MAC2STR(sa), dialog_token); if (sa[0] & 0x01) return; /* Invalid source address - drop silently */ tx_buf = gas_anqp_build_comeback_resp_buf( dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0, 0, NULL); if (tx_buf == NULL) return; goto send_resp; } if (dialog->sd_resp == NULL) { wpa_printf(MSG_DEBUG, "GAS: Remote request 0x%x received 0x%x", dialog->requested, dialog->received); if ((dialog->requested & dialog->received) != dialog->requested) { wpa_printf(MSG_DEBUG, "GAS: Did not receive response " "from remote processing"); gas_serv_dialog_clear(dialog); tx_buf = gas_anqp_build_comeback_resp_buf( dialog_token, WLAN_STATUS_GAS_RESP_NOT_RECEIVED, 0, 0, 0, NULL); if (tx_buf == NULL) return; goto send_resp; } buf = gas_serv_build_gas_resp_payload(hapd, dialog->all_requested, dialog, NULL, 0); wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses", buf); if (!buf) goto rx_gas_comeback_req_done; dialog->sd_resp = buf; dialog->sd_resp_pos = 0; } frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos; if (frag_len > hapd->gas_frag_limit) { frag_len = hapd->gas_frag_limit; more = 1; } wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u", (unsigned int) frag_len); buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) + dialog->sd_resp_pos, frag_len); if (buf == NULL) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate " "buffer"); goto rx_gas_comeback_req_done; } tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token, WLAN_STATUS_SUCCESS, dialog->sd_frag_id, more, 0, buf); wpabuf_free(buf); if (tx_buf == NULL) goto rx_gas_comeback_req_done; wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response " "(frag_id %d more=%d frag_len=%d)", dialog->sd_frag_id, more, (int) frag_len); dialog->sd_frag_id++; dialog->sd_resp_pos += frag_len; if (more) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain " "to be sent", (int) (wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos)); } else { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of " "SD response sent"); gas_serv_dialog_clear(dialog); gas_serv_free_dialogs(hapd, sa); } send_resp: hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, wpabuf_head(tx_buf), wpabuf_len(tx_buf)); wpabuf_free(tx_buf); return; rx_gas_comeback_req_done: gas_serv_clear_cached_ies(dialog, NULL); }
static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, const u8 *sa, const u8 *data, size_t len, int prot, int std_addr3) { struct gas_dialog_info *dialog; struct wpabuf *buf, *tx_buf; u8 dialog_token; size_t frag_len; int more = 0; wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len); if (len < 1) return; dialog_token = *data; wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u", dialog_token); dialog = gas_serv_dialog_find(hapd, sa, dialog_token); if (!dialog) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD " "response fragment for " MACSTR " dialog token %u", MAC2STR(sa), dialog_token); if (sa[0] & 0x01) return; /* Invalid source address - drop silently */ tx_buf = gas_anqp_build_comeback_resp_buf( dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0, 0, NULL); if (tx_buf == NULL) return; goto send_resp; } frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos; if (frag_len > hapd->gas_frag_limit) { frag_len = hapd->gas_frag_limit; more = 1; } wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u", (unsigned int) frag_len); buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) + dialog->sd_resp_pos, frag_len); if (buf == NULL) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate " "buffer"); gas_serv_dialog_clear(dialog); return; } tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token, WLAN_STATUS_SUCCESS, dialog->sd_frag_id, more, 0, buf); wpabuf_free(buf); if (tx_buf == NULL) { gas_serv_dialog_clear(dialog); return; } wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response " "(frag_id %d more=%d frag_len=%d)", dialog->sd_frag_id, more, (int) frag_len); dialog->sd_frag_id++; dialog->sd_resp_pos += frag_len; if (more) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain " "to be sent", (int) (wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos)); } else { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of " "SD response sent"); gas_serv_dialog_clear(dialog); gas_serv_free_dialogs(hapd, sa); } send_resp: if (prot) convert_to_protected_dual(tx_buf); if (std_addr3) hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, wpabuf_head(tx_buf), wpabuf_len(tx_buf)); else hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa, wpabuf_head(tx_buf), wpabuf_len(tx_buf)); wpabuf_free(tx_buf); }
static void gas_serv_req_local_processing(struct hostapd_data *hapd, const u8 *sa, u8 dialog_token, struct anqp_query_info *qi, int prot, int std_addr3) { struct wpabuf *buf, *tx_buf; buf = gas_serv_build_gas_resp_payload(hapd, qi->request, qi->home_realm_query, qi->home_realm_query_len, qi->icon_name, qi->icon_name_len, qi->extra_req, qi->num_extra_req); wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses", buf); if (!buf) return; #ifdef CONFIG_P2P if (wpabuf_len(buf) == 0 && qi->p2p_sd) { wpa_printf(MSG_DEBUG, "ANQP: Do not send response to P2P SD from generic GAS service (P2P SD implementation will process this)"); wpabuf_free(buf); return; } #endif /* CONFIG_P2P */ if (wpabuf_len(buf) > hapd->gas_frag_limit || hapd->conf->gas_comeback_delay) { struct gas_dialog_info *di; u16 comeback_delay = 1; if (hapd->conf->gas_comeback_delay) { /* Testing - allow overriding of the delay value */ comeback_delay = hapd->conf->gas_comeback_delay; } wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in " "initial response - use GAS comeback"); di = gas_dialog_create(hapd, sa, dialog_token); if (!di) { wpa_printf(MSG_INFO, "ANQP: Could not create dialog " "for " MACSTR " (dialog token %u)", MAC2STR(sa), dialog_token); wpabuf_free(buf); tx_buf = gas_anqp_build_initial_resp_buf( dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE, 0, NULL); } else { di->prot = prot; di->sd_resp = buf; di->sd_resp_pos = 0; tx_buf = gas_anqp_build_initial_resp_buf( dialog_token, WLAN_STATUS_SUCCESS, comeback_delay, NULL); } } else { wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)"); tx_buf = gas_anqp_build_initial_resp_buf( dialog_token, WLAN_STATUS_SUCCESS, 0, buf); wpabuf_free(buf); } if (!tx_buf) return; if (prot) convert_to_protected_dual(tx_buf); if (std_addr3) hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, wpabuf_head(tx_buf), wpabuf_len(tx_buf)); else hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa, wpabuf_head(tx_buf), wpabuf_len(tx_buf)); wpabuf_free(tx_buf); }
/* MLME-SLEEPMODE.response */ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 action_type, u16 intval) { struct ieee80211_mgmt *mgmt; int res; size_t len; 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; u16 wnmtfs_ie_len; u8 *pos; struct sta_info *sta; enum wnm_oper tfs_oper = action_type == WNM_SLEEP_MODE_ENTER ? WNM_SLEEP_TFS_RESP_IE_ADD : WNM_SLEEP_TFS_RESP_IE_NONE; sta = ap_get_sta(hapd, addr); if (sta == NULL) { wpa_printf(MSG_DEBUG, "%s: station not found", __func__); return -EINVAL; } /* WNM-Sleep Mode IE */ os_memset(&wnmsleep_ie, 0, sizeof(struct wnm_sleep_element)); wnmsleep_ie_len = sizeof(struct wnm_sleep_element); wnmsleep_ie.eid = WLAN_EID_WNMSLEEP; wnmsleep_ie.len = wnmsleep_ie_len - 2; wnmsleep_ie.action_type = action_type; wnmsleep_ie.status = WNM_STATUS_SLEEP_ACCEPT; wnmsleep_ie.intval = host_to_le16(intval); /* TFS IE(s) */ wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN); if (wnmtfs_ie == NULL) return -1; if (ieee80211_11_get_tfs_ie(hapd, addr, wnmtfs_ie, &wnmtfs_ie_len, tfs_oper)) { wnmtfs_ie_len = 0; os_free(wnmtfs_ie); wnmtfs_ie = NULL; } #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); if (mgmt == NULL) { wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " "WNM-Sleep Response action frame"); return -1; } os_memcpy(mgmt->da, addr, ETH_ALEN); os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); mgmt->u.action.category = WLAN_ACTION_WNM; mgmt->u.action.u.wnm_sleep_resp.action = WNM_SLEEP_MODE_RESP; mgmt->u.action.u.wnm_sleep_resp.dialogtoken = dialog_token; pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable; /* add key data if MFP is enabled */ if (!wpa_auth_uses_mfp(sta->wpa_sm) || action_type != WNM_SLEEP_MODE_EXIT) { mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0; } else { gtk_elem_len = wpa_wnmsleep_gtk_subelem(sta->wpa_sm, pos); pos += gtk_elem_len; wpa_printf(MSG_DEBUG, "Pass 4, gtk_len = %d", (int) gtk_elem_len); #ifdef CONFIG_IEEE80211W res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos); if (res < 0) { os_free(wnmtfs_ie); os_free(mgmt); return -1; } igtk_elem_len = res; pos += igtk_elem_len; wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d", (int) igtk_elem_len); #endif /* CONFIG_IEEE80211W */ WPA_PUT_LE16((u8 *) &mgmt->u.action.u.wnm_sleep_resp.keydata_len, gtk_elem_len + igtk_elem_len); } os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len); /* copy TFS IE here */ pos += wnmsleep_ie_len; if (wnmtfs_ie) os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len); len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len + igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len; /* In driver, response frame should be forced to sent when STA is in * PS mode */ res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, mgmt->da, &mgmt->u.action.category, len); if (!res) { wpa_printf(MSG_DEBUG, "Successfully send WNM-Sleep Response " "frame"); /* when entering wnmsleep * 1. pause the node in driver * 2. mark the node so that AP won't update GTK/IGTK during * WNM Sleep */ if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT && wnmsleep_ie.action_type == WNM_SLEEP_MODE_ENTER) { sta->flags |= WLAN_STA_WNM_SLEEP_MODE; hostapd_drv_wnm_oper(hapd, WNM_SLEEP_ENTER_CONFIRM, addr, NULL, NULL); wpa_set_wnmsleep(sta->wpa_sm, 1); } /* when exiting wnmsleep * 1. unmark the node * 2. start GTK/IGTK update if MFP is not used * 3. unpause the node in driver */ if ((wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT || wnmsleep_ie.status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) && wnmsleep_ie.action_type == WNM_SLEEP_MODE_EXIT) { sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; wpa_set_wnmsleep(sta->wpa_sm, 0); hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM, addr, NULL, NULL); if (!wpa_auth_uses_mfp(sta->wpa_sm)) wpa_wnmsleep_rekey_gtk(sta->wpa_sm); } } else wpa_printf(MSG_DEBUG, "Fail to send WNM-Sleep Response frame"); #undef MAX_GTK_SUBELEM_LEN #undef MAX_IGTK_SUBELEM_LEN os_free(wnmtfs_ie); os_free(mgmt); return res; }