+#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)
+{
+ u8 *resp;
+ struct ieee802_11_elems elems;
+ const u8 *ie;
+ size_t ie_len;
+ size_t i, resp_len;
+ int noack;
+ enum ssid_match_result res;
+ int ret;
+ u16 csa_offs[2];
+ size_t csa_offs_len;
+
+ if (len < IEEE80211_HDRLEN)
+ return;
+ 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,
+ mgmt->sa, mgmt->da, mgmt->bssid,
+ ie, ie_len, ssi_signal) > 0)
+ return;
+
+ if (!hapd->iconf->send_probe_response)
+ return;
+
+ if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
+ wpa_printf(MSG_DEBUG, "Could not parse ProbeReq from " MACSTR,
+ MAC2STR(mgmt->sa));
+ return;
+ }
+
+ if ((!elems.ssid || !elems.supp_rates)) {
+ wpa_printf(MSG_DEBUG, "STA " MACSTR " sent probe request "
+ "without SSID or supported rates element",
+ MAC2STR(mgmt->sa));
+ 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 && 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)) {
+ wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request "
+ "due to mismatch with Requested Device "
+ "Type");
+ wpabuf_free(wps);
+ return;
+ }
+ wpabuf_free(wps);
+ }
+
+ 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)) {
+ wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request "
+ "due to mismatch with Device ID");
+ wpabuf_free(p2p);
+ return;
+ }
+ wpabuf_free(p2p);
+ }
+#endif /* CONFIG_P2P */
+
+ 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;
+ }
+
+#ifdef CONFIG_P2P
+ if ((hapd->conf->p2p & P2P_GROUP_OWNER) &&
+ elems.ssid_len == P2P_WILDCARD_SSID_LEN &&
+ os_memcmp(elems.ssid, P2P_WILDCARD_SSID,
+ P2P_WILDCARD_SSID_LEN) == 0) {
+ /* Process P2P Wildcard SSID like Wildcard SSID */
+ elems.ssid_len = 0;
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_TAXONOMY
+ {
+ struct sta_info *sta = ap_get_sta(hapd, mgmt->sa);
+ if (sta)
+ 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)) {
+ wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
+ " 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 (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) {
+ wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
+ " for mismatching ANT %u ignored",
+ MAC2STR(mgmt->sa), ant);
+ return;
+ }
+ }
+
+ if (hapd->conf->interworking && elems.interworking &&
+ (elems.interworking_len == 7 || elems.interworking_len == 9)) {
+ const u8 *hessid;
+ if (elems.interworking_len == 7)
+ hessid = elems.interworking + 1;
+ else
+ hessid = elems.interworking + 1 + 2;
+ if (!is_broadcast_ether_addr(hessid) &&
+ os_memcmp(hessid, hapd->conf->hessid, ETH_ALEN) != 0) {
+ wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
+ " for mismatching HESSID " MACSTR
+ " ignored",
+ MAC2STR(mgmt->sa), MAC2STR(hessid));
+ return;
+ }
+ }
+#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 */
+
+ 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;
+
+ /*
+ * If this is a broadcast probe request, apply no ack policy to avoid
+ * excessive retries.
+ */
+ 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 (ret < 0)
+ wpa_printf(MSG_INFO, "handle_probe_req: send failed");