X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fap%2Fbeacon.c;h=38182aebebcc5962e1238a7d57cff0314bc23f38;hb=04059ab84458f43dda9130e4fff745b268424b99;hp=b71106381aab24793f3fbdab86c06d109397f17e;hpb=baf513d6952c67b94925c5e82291be19f858ad1e;p=mech_eap.git diff --git a/src/ap/beacon.c b/src/ap/beacon.c index b711063..38182ae 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -2,16 +2,10 @@ * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response * Copyright (c) 2002-2004, Instant802 Networks, Inc. * Copyright (c) 2005-2006, Devicescape Software, Inc. - * Copyright (c) 2008-2009, Jouni Malinen + * Copyright (c) 2008-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -21,7 +15,7 @@ #include "utils/common.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" -#include "drivers/driver.h" +#include "common/hw_features_common.h" #include "wps/wps_defs.h" #include "p2p/p2p.h" #include "hostapd.h" @@ -33,10 +27,61 @@ #include "p2p_hostapd.h" #include "ap_drv_ops.h" #include "beacon.h" +#include "hs20.h" +#include "dfs.h" +#include "taxonomy.h" #ifdef NEED_AP_MLME +static u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid, + size_t len) +{ + size_t i; + + for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) { + if (hapd->conf->radio_measurements[i]) + break; + } + + if (i == RRM_CAPABILITIES_IE_LEN || len < 2 + RRM_CAPABILITIES_IE_LEN) + return eid; + + *eid++ = WLAN_EID_RRM_ENABLED_CAPABILITIES; + *eid++ = RRM_CAPABILITIES_IE_LEN; + os_memcpy(eid, hapd->conf->radio_measurements, RRM_CAPABILITIES_IE_LEN); + + return eid + RRM_CAPABILITIES_IE_LEN; +} + + +static u8 * hostapd_eid_bss_load(struct hostapd_data *hapd, u8 *eid, size_t len) +{ + if (len < 2 + 5) + return eid; + +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->conf->bss_load_test_set) { + *eid++ = WLAN_EID_BSS_LOAD; + *eid++ = 5; + os_memcpy(eid, hapd->conf->bss_load_test, 5); + eid += 5; + return eid; + } +#endif /* CONFIG_TESTING_OPTIONS */ + if (hapd->conf->bss_load_update_period) { + *eid++ = WLAN_EID_BSS_LOAD; + *eid++ = 5; + WPA_PUT_LE16(eid, hapd->num_sta); + eid += 2; + *eid++ = hapd->iface->channel_utilization; + WPA_PUT_LE16(eid, 0); /* no available admission capabity */ + eid += 2; + } + return eid; +} + + static u8 ieee802_11_erp_info(struct hostapd_data *hapd) { u8 erp = 0; @@ -92,6 +137,74 @@ static u8 * hostapd_eid_erp_info(struct hostapd_data *hapd, u8 *eid) } +static u8 * hostapd_eid_pwr_constraint(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + u8 local_pwr_constraint = 0; + int dfs; + + if (hapd->iface->current_mode == NULL || + hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A) + return eid; + + /* Let host drivers add this IE if DFS support is offloaded */ + if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) + return eid; + + /* + * There is no DFS support and power constraint was not directly + * requested by config option. + */ + if (!hapd->iconf->ieee80211h && + hapd->iconf->local_pwr_constraint == -1) + return eid; + + /* Check if DFS is required by regulatory. */ + dfs = hostapd_is_dfs_required(hapd->iface); + if (dfs < 0) { + wpa_printf(MSG_WARNING, "Failed to check if DFS is required; ret=%d", + dfs); + dfs = 0; + } + + if (dfs == 0 && hapd->iconf->local_pwr_constraint == -1) + return eid; + + /* + * ieee80211h (DFS) is enabled so Power Constraint element shall + * be added when running on DFS channel whenever local_pwr_constraint + * is configured or not. In order to meet regulations when TPC is not + * implemented using a transmit power that is below the legal maximum + * (including any mitigation factor) should help. In this case, + * indicate 3 dB below maximum allowed transmit power. + */ + if (hapd->iconf->local_pwr_constraint == -1) + local_pwr_constraint = 3; + + /* + * A STA that is not an AP shall use a transmit power less than or + * equal to the local maximum transmit power level for the channel. + * The local maximum transmit power can be calculated from the formula: + * local max TX pwr = max TX pwr - local pwr constraint + * Where max TX pwr is maximum transmit power level specified for + * channel in Country element and local pwr constraint is specified + * for channel in this Power Constraint element. + */ + + /* Element ID */ + *pos++ = WLAN_EID_PWR_CONSTRAINT; + /* Length */ + *pos++ = 1; + /* Local Power Constraint */ + if (local_pwr_constraint) + *pos++ = local_pwr_constraint; + else + *pos++ = hapd->iconf->local_pwr_constraint; + + return pos; +} + + static u8 * hostapd_eid_country_add(u8 *pos, u8 *end, int chan_spacing, struct hostapd_channel_data *start, struct hostapd_channel_data *prev) @@ -145,7 +258,7 @@ static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid, continue; /* can use same entry */ } - if (start) { + if (start && prev) { pos = hostapd_eid_country_add(pos, end, chan_spacing, start, prev); start = NULL; @@ -186,13 +299,76 @@ static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len) } +static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid) +{ +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->iface->cs_oper_class && hapd->iconf->ecsa_ie_only) + return eid; +#endif /* CONFIG_TESTING_OPTIONS */ + + if (!hapd->cs_freq_params.channel) + return eid; + + *eid++ = WLAN_EID_CHANNEL_SWITCH; + *eid++ = 3; + *eid++ = hapd->cs_block_tx; + *eid++ = hapd->cs_freq_params.channel; + *eid++ = hapd->cs_count; + + return eid; +} + + +static u8 * hostapd_eid_ecsa(struct hostapd_data *hapd, u8 *eid) +{ + if (!hapd->cs_freq_params.channel || !hapd->iface->cs_oper_class) + return eid; + + *eid++ = WLAN_EID_EXT_CHANSWITCH_ANN; + *eid++ = 4; + *eid++ = hapd->cs_block_tx; + *eid++ = hapd->iface->cs_oper_class; + *eid++ = hapd->cs_freq_params.channel; + *eid++ = hapd->cs_count; + + return eid; +} + + +static u8 * hostapd_eid_supported_op_classes(struct hostapd_data *hapd, u8 *eid) +{ + u8 op_class, channel; + + if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) || + !hapd->iface->freq) + return eid; + + if (ieee80211_freq_to_channel_ext(hapd->iface->freq, + hapd->iconf->secondary_channel, + hapd->iconf->vht_oper_chwidth, + &op_class, &channel) == + NUM_HOSTAPD_MODES) + return eid; + + *eid++ = WLAN_EID_SUPPORTED_OPERATING_CLASSES; + *eid++ = 2; + + /* Current Operating Class */ + *eid++ = op_class; + + /* TODO: Advertise all the supported operating classes */ + *eid++ = 0; + + return eid; +} + + static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, - struct sta_info *sta, const struct ieee80211_mgmt *req, int is_p2p, size_t *resp_len) { struct ieee80211_mgmt *resp; - u8 *pos, *epos; + u8 *pos, *epos, *csa_pos; size_t buflen; #define MAX_PROBERESP_LEN 768 @@ -205,6 +381,19 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, if (hapd->p2p_probe_resp_ie) buflen += wpabuf_len(hapd->p2p_probe_resp_ie); #endif /* CONFIG_P2P */ +#ifdef CONFIG_FST + if (hapd->iface->fst_ies) + buflen += wpabuf_len(hapd->iface->fst_ies); +#endif /* CONFIG_FST */ + if (hapd->conf->vendor_elements) + buflen += wpabuf_len(hapd->conf->vendor_elements); + if (hapd->conf->vendor_vht) { + buflen += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) + + 2 + sizeof(struct ieee80211_vht_operation); + } + + buflen += hostapd_mbo_ie_len(hapd); + resp = os_zalloc(buflen); if (resp == NULL) return NULL; @@ -223,7 +412,7 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, /* hardware or low-level driver will setup seq_ctrl and timestamp */ resp->u.probe_resp.capab_info = - host_to_le16(hostapd_own_capab_info(hapd, sta, 1)); + host_to_le16(hostapd_own_capab_info(hapd)); pos = resp->u.probe_resp.variable; *pos++ = WLAN_EID_SSID; @@ -239,6 +428,15 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, pos = hostapd_eid_country(hapd, pos, epos - pos); + /* Power Constraint element */ + pos = hostapd_eid_pwr_constraint(hapd, pos); + + /* CSA IE */ + csa_pos = hostapd_eid_csa(hapd, pos); + if (csa_pos != pos) + hapd->cs_c_off_proberesp = csa_pos - (u8 *) resp - 1; + pos = csa_pos; + /* ERP Information element */ pos = hostapd_eid_erp_info(hapd, pos); @@ -248,7 +446,23 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, /* RSN, MDIE, WPA */ pos = hostapd_eid_wpa(hapd, pos, epos - pos); + pos = hostapd_eid_bss_load(hapd, pos, epos - pos); + + pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos); + + /* eCSA IE */ + csa_pos = hostapd_eid_ecsa(hapd, pos); + if (csa_pos != pos) + hapd->cs_c_off_ecsa_proberesp = csa_pos - (u8 *) resp - 1; + pos = csa_pos; + + pos = hostapd_eid_supported_op_classes(hapd, pos); + #ifdef CONFIG_IEEE80211N + /* Secondary Channel Offset element */ + /* TODO: The standard doesn't specify a position for this element. */ + pos = hostapd_eid_secondary_channel(hapd, pos); + pos = hostapd_eid_ht_capabilities(hapd, pos); pos = hostapd_eid_ht_operation(hapd, pos); #endif /* CONFIG_IEEE80211N */ @@ -262,6 +476,25 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, pos = hostapd_eid_adv_proto(hapd, pos); pos = hostapd_eid_roaming_consortium(hapd, pos); +#ifdef CONFIG_FST + if (hapd->iface->fst_ies) { + os_memcpy(pos, wpabuf_head(hapd->iface->fst_ies), + wpabuf_len(hapd->iface->fst_ies)); + pos += wpabuf_len(hapd->iface->fst_ies); + } +#endif /* CONFIG_FST */ + +#ifdef CONFIG_IEEE80211AC + if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { + pos = hostapd_eid_vht_capabilities(hapd, pos, 0); + pos = hostapd_eid_vht_operation(hapd, pos); + pos = hostapd_eid_txpower_envelope(hapd, pos); + pos = hostapd_eid_wb_chsw_wrapper(hapd, pos); + } + if (hapd->conf->vendor_vht) + pos = hostapd_eid_vendor_vht(hapd, pos); +#endif /* CONFIG_IEEE80211AC */ + /* Wi-Fi Alliance WMM */ pos = hostapd_eid_wmm(hapd, pos); @@ -287,11 +520,162 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, pos = hostapd_eid_p2p_manage(hapd, pos); #endif /* CONFIG_P2P_MANAGER */ +#ifdef CONFIG_HS20 + pos = hostapd_eid_hs20_indication(hapd, pos); + pos = hostapd_eid_osen(hapd, pos); +#endif /* CONFIG_HS20 */ + + pos = hostapd_eid_mbo(hapd, pos, (u8 *) resp + buflen - pos); + + if (hapd->conf->vendor_elements) { + os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements), + wpabuf_len(hapd->conf->vendor_elements)); + pos += wpabuf_len(hapd->conf->vendor_elements); + } + *resp_len = pos - (u8 *) resp; return (u8 *) resp; } +enum ssid_match_result { + NO_SSID_MATCH, + EXACT_SSID_MATCH, + WILDCARD_SSID_MATCH +}; + +static enum ssid_match_result ssid_match(struct hostapd_data *hapd, + const u8 *ssid, size_t ssid_len, + const u8 *ssid_list, + size_t ssid_list_len) +{ + const u8 *pos, *end; + int wildcard = 0; + + if (ssid_len == 0) + wildcard = 1; + if (ssid_len == hapd->conf->ssid.ssid_len && + os_memcmp(ssid, hapd->conf->ssid.ssid, ssid_len) == 0) + return EXACT_SSID_MATCH; + + if (ssid_list == NULL) + return wildcard ? WILDCARD_SSID_MATCH : NO_SSID_MATCH; + + pos = ssid_list; + end = ssid_list + ssid_list_len; + while (end - pos >= 1) { + if (2 + pos[1] > end - pos) + break; + if (pos[1] == 0) + wildcard = 1; + if (pos[1] == hapd->conf->ssid.ssid_len && + os_memcmp(pos + 2, hapd->conf->ssid.ssid, pos[1]) == 0) + return EXACT_SSID_MATCH; + pos += 2 + pos[1]; + } + + return wildcard ? WILDCARD_SSID_MATCH : NO_SSID_MATCH; +} + + +void sta_track_expire(struct hostapd_iface *iface, int force) +{ + struct os_reltime now; + struct hostapd_sta_info *info; + + if (!iface->num_sta_seen) + return; + + os_get_reltime(&now); + while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info, + list))) { + if (!force && + !os_reltime_expired(&now, &info->last_seen, + iface->conf->track_sta_max_age)) + break; + force = 0; + + wpa_printf(MSG_MSGDUMP, "%s: Expire STA tracking entry for " + MACSTR, iface->bss[0]->conf->iface, + MAC2STR(info->addr)); + dl_list_del(&info->list); + iface->num_sta_seen--; + os_free(info); + } +} + + +static struct hostapd_sta_info * sta_track_get(struct hostapd_iface *iface, + const u8 *addr) +{ + struct hostapd_sta_info *info; + + dl_list_for_each(info, &iface->sta_seen, struct hostapd_sta_info, list) + if (os_memcmp(addr, info->addr, ETH_ALEN) == 0) + return info; + + return NULL; +} + + +void sta_track_add(struct hostapd_iface *iface, const u8 *addr) +{ + struct hostapd_sta_info *info; + + info = sta_track_get(iface, addr); + if (info) { + /* Move the most recent entry to the end of the list */ + dl_list_del(&info->list); + dl_list_add_tail(&iface->sta_seen, &info->list); + os_get_reltime(&info->last_seen); + return; + } + + /* Add a new entry */ + info = os_zalloc(sizeof(*info)); + if (info == NULL) + return; + os_memcpy(info->addr, addr, ETH_ALEN); + os_get_reltime(&info->last_seen); + + if (iface->num_sta_seen >= iface->conf->track_sta_max_num) { + /* Expire oldest entry to make room for a new one */ + sta_track_expire(iface, 1); + } + + wpa_printf(MSG_MSGDUMP, "%s: Add STA tracking entry for " + MACSTR, iface->bss[0]->conf->iface, MAC2STR(addr)); + dl_list_add_tail(&iface->sta_seen, &info->list); + iface->num_sta_seen++; +} + + +struct hostapd_data * +sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr, + const char *ifname) +{ + struct hapd_interfaces *interfaces = iface->interfaces; + size_t i, j; + + for (i = 0; i < interfaces->count; i++) { + struct hostapd_data *hapd = NULL; + + iface = interfaces->iface[i]; + for (j = 0; j < iface->num_bss; j++) { + hapd = iface->bss[j]; + if (os_strcmp(ifname, hapd->conf->iface) == 0) + break; + hapd = NULL; + } + + if (hapd && sta_track_get(iface, addr)) + return hapd; + } + + return NULL; +} + + void handle_probe_req(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int ssi_signal) @@ -300,14 +684,19 @@ void handle_probe_req(struct hostapd_data *hapd, struct ieee802_11_elems elems; const u8 *ie; size_t ie_len; - struct sta_info *sta = NULL; size_t i, resp_len; int noack; + enum ssid_match_result res; + int ret; + u16 csa_offs[2]; + size_t csa_offs_len; - ie = mgmt->u.probe_req.variable; - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) + if (len < IEEE80211_HDRLEN) return; - ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); + ie = ((const u8 *) mgmt) + IEEE80211_HDRLEN; + if (hapd->iconf->track_sta_max_num) + sta_track_add(hapd->iface, mgmt->sa); + ie_len = len - IEEE80211_HDRLEN; for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx, @@ -331,8 +720,29 @@ void handle_probe_req(struct hostapd_data *hapd, return; } + /* + * No need to reply if the Probe Request frame was sent on an adjacent + * channel. IEEE Std 802.11-2012 describes this as a requirement for an + * AP with dot11RadioMeasurementActivated set to true, but strictly + * speaking does not allow such ignoring of Probe Request frames if + * dot11RadioMeasurementActivated is false. Anyway, this can help reduce + * number of unnecessary Probe Response frames for cases where the STA + * is less likely to see them (Probe Request frame sent on a + * neighboring, but partially overlapping, channel). + */ + if (elems.ds_params && + hapd->iface->current_mode && + (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G || + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B) && + hapd->iconf->channel != elems.ds_params[0]) { + wpa_printf(MSG_DEBUG, + "Ignore Probe Request due to DS Params mismatch: chan=%u != ds.chan=%u", + hapd->iconf->channel, elems.ds_params[0]); + return; + } + #ifdef CONFIG_P2P - if (hapd->p2p && elems.wps_ie) { + if (hapd->p2p && hapd->p2p_group && elems.wps_ie) { struct wpabuf *wps; wps = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA); if (wps && !p2p_group_match_dev_type(hapd->p2p_group, wps)) { @@ -345,7 +755,7 @@ void handle_probe_req(struct hostapd_data *hapd, wpabuf_free(wps); } - if (hapd->p2p && elems.p2p) { + if (hapd->p2p && hapd->p2p_group && elems.p2p) { struct wpabuf *p2p; p2p = ieee802_11_vendor_ie_concat(ie, ie_len, P2P_IE_VENDOR_TYPE); if (p2p && !p2p_group_match_dev_id(hapd->p2p_group, p2p)) { @@ -358,14 +768,13 @@ void handle_probe_req(struct hostapd_data *hapd, } #endif /* CONFIG_P2P */ - if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0) { + if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0 && + elems.ssid_list_len == 0) { wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for " "broadcast SSID ignored", MAC2STR(mgmt->sa)); return; } - sta = ap_get_sta(hapd, mgmt->sa); - #ifdef CONFIG_P2P if ((hapd->conf->p2p & P2P_GROUP_OWNER) && elems.ssid_len == P2P_WILDCARD_SSID_LEN && @@ -376,27 +785,31 @@ void handle_probe_req(struct hostapd_data *hapd, } #endif /* CONFIG_P2P */ - if (elems.ssid_len == 0 || - (elems.ssid_len == hapd->conf->ssid.ssid_len && - os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) == - 0)) { +#ifdef CONFIG_TAXONOMY + { + struct sta_info *sta = ap_get_sta(hapd, mgmt->sa); if (sta) - sta->ssid_probe = &hapd->conf->ssid; - } else { + taxonomy_sta_info_probe_req(hapd, sta, ie, ie_len); + } +#endif /* CONFIG_TAXONOMY */ + + res = ssid_match(hapd, elems.ssid, elems.ssid_len, + elems.ssid_list, elems.ssid_list_len); + if (res == NO_SSID_MATCH) { if (!(mgmt->da[0] & 0x01)) { - char ssid_txt[33]; - ieee802_11_print_ssid(ssid_txt, elems.ssid, - elems.ssid_len); wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR - " for foreign SSID '%s' (DA " MACSTR ")", - MAC2STR(mgmt->sa), ssid_txt, - MAC2STR(mgmt->da)); + " for foreign SSID '%s' (DA " MACSTR ")%s", + MAC2STR(mgmt->sa), + wpa_ssid_txt(elems.ssid, elems.ssid_len), + MAC2STR(mgmt->da), + elems.ssid_list ? " (SSID list)" : ""); } return; } #ifdef CONFIG_INTERWORKING - if (elems.interworking && elems.interworking_len >= 1) { + if (hapd->conf->interworking && + elems.interworking && elems.interworking_len >= 1) { u8 ant = elems.interworking[0] & 0x0f; if (ant != INTERWORKING_ANT_WILDCARD && ant != hapd->conf->access_network_type) { @@ -407,7 +820,7 @@ void handle_probe_req(struct hostapd_data *hapd, } } - if (elems.interworking && + if (hapd->conf->interworking && elems.interworking && (elems.interworking_len == 7 || elems.interworking_len == 9)) { const u8 *hessid; if (elems.interworking_len == 7) @@ -425,10 +838,54 @@ void handle_probe_req(struct hostapd_data *hapd, } #endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_P2P + if ((hapd->conf->p2p & P2P_GROUP_OWNER) && + supp_rates_11b_only(&elems)) { + /* Indicates support for 11b rates only */ + wpa_printf(MSG_EXCESSIVE, "P2P: Ignore Probe Request from " + MACSTR " with only 802.11b rates", + MAC2STR(mgmt->sa)); + return; + } +#endif /* CONFIG_P2P */ + /* TODO: verify that supp_rates contains at least one matching rate * with AP configuration */ - resp = hostapd_gen_probe_resp(hapd, sta, mgmt, elems.p2p != NULL, + if (hapd->conf->no_probe_resp_if_seen_on && + is_multicast_ether_addr(mgmt->da) && + is_multicast_ether_addr(mgmt->bssid) && + sta_track_seen_on(hapd->iface, mgmt->sa, + hapd->conf->no_probe_resp_if_seen_on)) { + wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR + " since STA has been seen on %s", + hapd->conf->iface, MAC2STR(mgmt->sa), + hapd->conf->no_probe_resp_if_seen_on); + return; + } + + if (hapd->conf->no_probe_resp_if_max_sta && + is_multicast_ether_addr(mgmt->da) && + is_multicast_ether_addr(mgmt->bssid) && + hapd->num_sta >= hapd->conf->max_num_sta && + !ap_get_sta(hapd, mgmt->sa)) { + wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR + " since no room for additional STA", + hapd->conf->iface, MAC2STR(mgmt->sa)); + return; + } + +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->iconf->ignore_probe_probability > 0.0 && + drand48() < hapd->iconf->ignore_probe_probability) { + wpa_printf(MSG_INFO, + "TESTING: ignoring probe request from " MACSTR, + MAC2STR(mgmt->sa)); + return; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + resp = hostapd_gen_probe_resp(hapd, mgmt, elems.p2p != NULL, &resp_len); if (resp == NULL) return; @@ -437,10 +894,26 @@ void handle_probe_req(struct hostapd_data *hapd, * If this is a broadcast probe request, apply no ack policy to avoid * excessive retries. */ - noack = !!(elems.ssid_len == 0 && is_broadcast_ether_addr(mgmt->da)); + noack = !!(res == WILDCARD_SSID_MATCH && + is_broadcast_ether_addr(mgmt->da)); + + csa_offs_len = 0; + if (hapd->csa_in_progress) { + if (hapd->cs_c_off_proberesp) + csa_offs[csa_offs_len++] = + hapd->cs_c_off_proberesp; + + if (hapd->cs_c_off_ecsa_proberesp) + csa_offs[csa_offs_len++] = + hapd->cs_c_off_ecsa_proberesp; + } + + ret = hostapd_drv_send_mlme_csa(hapd, resp, resp_len, noack, + csa_offs_len ? csa_offs : NULL, + csa_offs_len); - if (hostapd_drv_send_mlme(hapd, resp, resp_len, noack) < 0) - perror("handle_probe_req: send"); + if (ret < 0) + wpa_printf(MSG_INFO, "handle_probe_req: send failed"); os_free(resp); @@ -482,29 +955,23 @@ static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd, "this"); /* Generate a Probe Response template for the non-P2P case */ - return hostapd_gen_probe_resp(hapd, NULL, NULL, 0, resp_len); + return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len); } #endif /* NEED_AP_MLME */ -void ieee802_11_set_beacon(struct hostapd_data *hapd) +int ieee802_11_build_ap_params(struct hostapd_data *hapd, + struct wpa_driver_ap_params *params) { struct ieee80211_mgmt *head = NULL; u8 *tail = NULL; size_t head_len = 0, tail_len = 0; u8 *resp = NULL; size_t resp_len = 0; - struct wpa_driver_ap_params params; - struct wpabuf *beacon, *proberesp, *assocresp; #ifdef NEED_AP_MLME u16 capab_info; - u8 *pos, *tailpos; -#endif /* NEED_AP_MLME */ - - hapd->beacon_set_done = 1; - -#ifdef NEED_AP_MLME + u8 *pos, *tailpos, *csa_pos; #define BEACON_HEAD_BUF_SIZE 256 #define BEACON_TAIL_BUF_SIZE 512 @@ -518,12 +985,28 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) if (hapd->p2p_beacon_ie) tail_len += wpabuf_len(hapd->p2p_beacon_ie); #endif /* CONFIG_P2P */ +#ifdef CONFIG_FST + if (hapd->iface->fst_ies) + tail_len += wpabuf_len(hapd->iface->fst_ies); +#endif /* CONFIG_FST */ + if (hapd->conf->vendor_elements) + tail_len += wpabuf_len(hapd->conf->vendor_elements); + +#ifdef CONFIG_IEEE80211AC + if (hapd->conf->vendor_vht) { + tail_len += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) + + 2 + sizeof(struct ieee80211_vht_operation); + } +#endif /* CONFIG_IEEE80211AC */ + + tail_len += hostapd_mbo_ie_len(hapd); + tailpos = tail = os_malloc(tail_len); if (head == NULL || tail == NULL) { wpa_printf(MSG_ERROR, "Failed to set beacon data"); os_free(head); os_free(tail); - return; + return -1; } head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, @@ -537,7 +1020,7 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) host_to_le16(hapd->iconf->beacon_int); /* hardware or low-level driver will setup seq_ctrl and timestamp */ - capab_info = hostapd_own_capab_info(hapd, NULL, 0); + capab_info = hostapd_own_capab_info(hapd); head->u.beacon.capab_info = host_to_le16(capab_info); pos = &head->u.beacon.variable[0]; @@ -568,6 +1051,15 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) tailpos = hostapd_eid_country(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - tailpos); + /* Power Constraint element */ + tailpos = hostapd_eid_pwr_constraint(hapd, tailpos); + + /* CSA IE */ + csa_pos = hostapd_eid_csa(hapd, tailpos); + if (csa_pos != tailpos) + hapd->cs_c_off_beacon = csa_pos - tail - 1; + tailpos = csa_pos; + /* ERP Information element */ tailpos = hostapd_eid_erp_info(hapd, tailpos); @@ -578,7 +1070,26 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - tailpos); + tailpos = hostapd_eid_rm_enabled_capab(hapd, tailpos, + tail + BEACON_TAIL_BUF_SIZE - + tailpos); + + tailpos = hostapd_eid_bss_load(hapd, tailpos, + tail + BEACON_TAIL_BUF_SIZE - tailpos); + + /* eCSA IE */ + csa_pos = hostapd_eid_ecsa(hapd, tailpos); + if (csa_pos != tailpos) + hapd->cs_c_off_ecsa_beacon = csa_pos - tail - 1; + tailpos = csa_pos; + + tailpos = hostapd_eid_supported_op_classes(hapd, tailpos); + #ifdef CONFIG_IEEE80211N + /* Secondary Channel Offset element */ + /* TODO: The standard doesn't specify a position for this element. */ + tailpos = hostapd_eid_secondary_channel(hapd, tailpos); + tailpos = hostapd_eid_ht_capabilities(hapd, tailpos); tailpos = hostapd_eid_ht_operation(hapd, tailpos); #endif /* CONFIG_IEEE80211N */ @@ -595,6 +1106,25 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) tailpos = hostapd_eid_adv_proto(hapd, tailpos); tailpos = hostapd_eid_roaming_consortium(hapd, tailpos); +#ifdef CONFIG_FST + if (hapd->iface->fst_ies) { + os_memcpy(tailpos, wpabuf_head(hapd->iface->fst_ies), + wpabuf_len(hapd->iface->fst_ies)); + tailpos += wpabuf_len(hapd->iface->fst_ies); + } +#endif /* CONFIG_FST */ + +#ifdef CONFIG_IEEE80211AC + if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { + tailpos = hostapd_eid_vht_capabilities(hapd, tailpos, 0); + tailpos = hostapd_eid_vht_operation(hapd, tailpos); + tailpos = hostapd_eid_txpower_envelope(hapd, tailpos); + tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos); + } + if (hapd->conf->vendor_vht) + tailpos = hostapd_eid_vendor_vht(hapd, tailpos); +#endif /* CONFIG_IEEE80211AC */ + /* Wi-Fi Alliance WMM */ tailpos = hostapd_eid_wmm(hapd, tailpos); @@ -619,96 +1149,193 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) tailpos = hostapd_eid_p2p_manage(hapd, tailpos); #endif /* CONFIG_P2P_MANAGER */ +#ifdef CONFIG_HS20 + tailpos = hostapd_eid_hs20_indication(hapd, tailpos); + tailpos = hostapd_eid_osen(hapd, tailpos); +#endif /* CONFIG_HS20 */ + + tailpos = hostapd_eid_mbo(hapd, tailpos, tail + tail_len - tailpos); + + if (hapd->conf->vendor_elements) { + os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements), + wpabuf_len(hapd->conf->vendor_elements)); + tailpos += wpabuf_len(hapd->conf->vendor_elements); + } + tail_len = tailpos > tail ? tailpos - tail : 0; resp = hostapd_probe_resp_offloads(hapd, &resp_len); #endif /* NEED_AP_MLME */ - os_memset(¶ms, 0, sizeof(params)); - params.head = (u8 *) head; - params.head_len = head_len; - params.tail = tail; - params.tail_len = tail_len; - params.proberesp = resp; - params.proberesp_len = resp_len; - params.dtim_period = hapd->conf->dtim_period; - params.beacon_int = hapd->iconf->beacon_int; - params.basic_rates = hapd->iconf->basic_rates; - params.ssid = (u8 *) hapd->conf->ssid.ssid; - params.ssid_len = hapd->conf->ssid.ssid_len; - params.pairwise_ciphers = hapd->conf->rsn_pairwise ? - hapd->conf->rsn_pairwise : hapd->conf->wpa_pairwise; - params.group_cipher = hapd->conf->wpa_group; - params.key_mgmt_suites = hapd->conf->wpa_key_mgmt; - params.auth_algs = hapd->conf->auth_algs; - params.wpa_version = hapd->conf->wpa; - params.privacy = hapd->conf->ssid.wep.keys_set || hapd->conf->wpa || + os_memset(params, 0, sizeof(*params)); + params->head = (u8 *) head; + params->head_len = head_len; + params->tail = tail; + params->tail_len = tail_len; + params->proberesp = resp; + params->proberesp_len = resp_len; + params->dtim_period = hapd->conf->dtim_period; + params->beacon_int = hapd->iconf->beacon_int; + params->basic_rates = hapd->iface->basic_rates; + params->ssid = hapd->conf->ssid.ssid; + params->ssid_len = hapd->conf->ssid.ssid_len; + if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) == + (WPA_PROTO_WPA | WPA_PROTO_RSN)) + params->pairwise_ciphers = hapd->conf->wpa_pairwise | + hapd->conf->rsn_pairwise; + else if (hapd->conf->wpa & WPA_PROTO_RSN) + params->pairwise_ciphers = hapd->conf->rsn_pairwise; + else if (hapd->conf->wpa & WPA_PROTO_WPA) + params->pairwise_ciphers = hapd->conf->wpa_pairwise; + params->group_cipher = hapd->conf->wpa_group; + params->key_mgmt_suites = hapd->conf->wpa_key_mgmt; + params->auth_algs = hapd->conf->auth_algs; + params->wpa_version = hapd->conf->wpa; + params->privacy = hapd->conf->ssid.wep.keys_set || hapd->conf->wpa || (hapd->conf->ieee802_1x && (hapd->conf->default_wep_key_len || hapd->conf->individual_wep_key_len)); switch (hapd->conf->ignore_broadcast_ssid) { case 0: - params.hide_ssid = NO_SSID_HIDING; + params->hide_ssid = NO_SSID_HIDING; break; case 1: - params.hide_ssid = HIDDEN_SSID_ZERO_LEN; + params->hide_ssid = HIDDEN_SSID_ZERO_LEN; break; case 2: - params.hide_ssid = HIDDEN_SSID_ZERO_CONTENTS; + params->hide_ssid = HIDDEN_SSID_ZERO_CONTENTS; break; } - hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp); - params.beacon_ies = beacon; - params.proberesp_ies = proberesp; - params.assocresp_ies = assocresp; - params.isolate = hapd->conf->isolate; + params->isolate = hapd->conf->isolate; + params->smps_mode = hapd->iconf->ht_capab & HT_CAP_INFO_SMPS_MASK; #ifdef NEED_AP_MLME - params.cts_protect = !!(ieee802_11_erp_info(hapd) & + params->cts_protect = !!(ieee802_11_erp_info(hapd) & ERP_INFO_USE_PROTECTION); - params.preamble = hapd->iface->num_sta_no_short_preamble == 0 && + params->preamble = hapd->iface->num_sta_no_short_preamble == 0 && hapd->iconf->preamble == SHORT_PREAMBLE; if (hapd->iface->current_mode && hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) - params.short_slot_time = + params->short_slot_time = hapd->iface->num_sta_no_short_slot_time > 0 ? 0 : 1; else - params.short_slot_time = -1; + params->short_slot_time = -1; if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n) - params.ht_opmode = -1; + params->ht_opmode = -1; else - params.ht_opmode = hapd->iface->ht_op_mode; + params->ht_opmode = hapd->iface->ht_op_mode; #endif /* NEED_AP_MLME */ - params.interworking = hapd->conf->interworking; + params->interworking = hapd->conf->interworking; if (hapd->conf->interworking && !is_zero_ether_addr(hapd->conf->hessid)) - params.hessid = hapd->conf->hessid; - params.access_network_type = hapd->conf->access_network_type; - params.ap_max_inactivity = hapd->conf->ap_max_inactivity; - if (hostapd_drv_set_ap(hapd, ¶ms)) - wpa_printf(MSG_ERROR, "Failed to set beacon parameters"); - hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp); + params->hessid = hapd->conf->hessid; + params->access_network_type = hapd->conf->access_network_type; + params->ap_max_inactivity = hapd->conf->ap_max_inactivity; +#ifdef CONFIG_P2P + params->p2p_go_ctwindow = hapd->iconf->p2p_go_ctwindow; +#endif /* CONFIG_P2P */ +#ifdef CONFIG_HS20 + params->disable_dgaf = hapd->conf->disable_dgaf; + if (hapd->conf->osen) { + params->privacy = 1; + params->osen = 1; + } +#endif /* CONFIG_HS20 */ + params->pbss = hapd->conf->pbss; + return 0; +} - os_free(tail); - os_free(head); - os_free(resp); + +void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params) +{ + os_free(params->tail); + params->tail = NULL; + os_free(params->head); + params->head = NULL; + os_free(params->proberesp); + params->proberesp = NULL; +} + + +int ieee802_11_set_beacon(struct hostapd_data *hapd) +{ + struct wpa_driver_ap_params params; + struct hostapd_freq_params freq; + struct hostapd_iface *iface = hapd->iface; + struct hostapd_config *iconf = iface->conf; + struct wpabuf *beacon, *proberesp, *assocresp; + int res, ret = -1; + + if (hapd->csa_in_progress) { + wpa_printf(MSG_ERROR, "Cannot set beacons during CSA period"); + return -1; + } + + hapd->beacon_set_done = 1; + + if (ieee802_11_build_ap_params(hapd, ¶ms) < 0) + return -1; + + if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) < + 0) + goto fail; + + params.beacon_ies = beacon; + params.proberesp_ies = proberesp; + params.assocresp_ies = assocresp; + params.reenable = hapd->reenable_beacon; + hapd->reenable_beacon = 0; + + if (iface->current_mode && + hostapd_set_freq_params(&freq, iconf->hw_mode, iface->freq, + iconf->channel, iconf->ieee80211n, + iconf->ieee80211ac, + iconf->secondary_channel, + iconf->vht_oper_chwidth, + iconf->vht_oper_centr_freq_seg0_idx, + iconf->vht_oper_centr_freq_seg1_idx, + iface->current_mode->vht_capab) == 0) + params.freq = &freq; + + res = hostapd_drv_set_ap(hapd, ¶ms); + hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp); + if (res) + wpa_printf(MSG_ERROR, "Failed to set beacon parameters"); + else + ret = 0; +fail: + ieee802_11_free_ap_params(¶ms); + return ret; } -void ieee802_11_set_beacons(struct hostapd_iface *iface) +int ieee802_11_set_beacons(struct hostapd_iface *iface) { size_t i; - for (i = 0; i < iface->num_bss; i++) - ieee802_11_set_beacon(iface->bss[i]); + int ret = 0; + + for (i = 0; i < iface->num_bss; i++) { + if (iface->bss[i]->started && + ieee802_11_set_beacon(iface->bss[i]) < 0) + ret = -1; + } + + return ret; } /* only update beacons if started */ -void ieee802_11_update_beacons(struct hostapd_iface *iface) +int ieee802_11_update_beacons(struct hostapd_iface *iface) { size_t i; - for (i = 0; i < iface->num_bss; i++) - if (iface->bss[i]->beacon_set_done) - ieee802_11_set_beacon(iface->bss[i]); + int ret = 0; + + for (i = 0; i < iface->num_bss; i++) { + if (iface->bss[i]->beacon_set_done && iface->bss[i]->started && + ieee802_11_set_beacon(iface->bss[i]) < 0) + ret = -1; + } + + return ret; } #endif /* CONFIG_NATIVE_WINDOWS */