Patchwork [v3,2/3] hostap: set driver probe-response template

login
register
mail settings
Submitter Arik Nemtsov
Date Nov. 8, 2011, 2:11 p.m.
Message ID <1320761478-29133-2-git-send-email-arik@wizery.com>
Download mbox | patch
Permalink /patch/124368/
State Superseded
Headers show

Comments

Arik Nemtsov - Nov. 8, 2011, 2:11 p.m.
Configure a probe response template for drivers that support it. The
template is updated when the beacon is updated.

The probe response template is propagated to kernel via the set_ap()
callback.

Signed-off-by: Guy Eilam <guy@wizery.com>
Signed-off-by: Arik Nemtsov <arik@wizery.com>
---
v1->3:
- save offload-supported protocols bitmap in AP/P2P GO modes
- use set_ap() to propagate probe-response (and re-factor beacon
  generating code so both happen in a common function)
- add warnings when unsupported protocols are offloaded


hostapd/main.c                    |    5 +-
 src/ap/ap_list.c                  |    4 +-
 src/ap/beacon.c                   |  364 ++++++++++++++++++++++++-------------
 src/ap/beacon.h                   |    4 +-
 src/ap/hostapd.c                  |    4 +-
 src/ap/hostapd.h                  |    7 +
 src/ap/ieee802_11.c               |    6 +-
 src/ap/ieee802_11_ht.c            |    2 +-
 src/ap/sta_info.c                 |    2 +-
 src/ap/wps_hostapd.c              |    2 +-
 src/drivers/driver.h              |   27 +++
 wpa_supplicant/ap.c               |    4 +-
 wpa_supplicant/wpa_supplicant.c   |    2 +
 wpa_supplicant/wpa_supplicant_i.h |    7 +
 14 files changed, 298 insertions(+), 142 deletions(-)

Patch

diff --git a/hostapd/main.c b/hostapd/main.c
index 99c137d..a781e44 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -303,8 +303,11 @@  static int hostapd_driver_init(struct hostapd_iface *iface)
 	}
 
 	if (hapd->driver->get_capa &&
-	    hapd->driver->get_capa(hapd->drv_priv, &capa) == 0)
+	    hapd->driver->get_capa(hapd->drv_priv, &capa) == 0) {
 		iface->drv_flags = capa.flags;
+		iface->probe_resp_offload_protocols =
+			capa.probe_resp_offload_protocols;
+	}
 
 	return 0;
 }
diff --git a/src/ap/ap_list.c b/src/ap/ap_list.c
index 9b9fc9e..40ded63 100644
--- a/src/ap/ap_list.c
+++ b/src/ap/ap_list.c
@@ -326,7 +326,7 @@  void ap_list_process_beacon(struct hostapd_iface *iface,
 #endif /* CONFIG_IEEE80211N */
 
 	if (set_beacon)
-		ieee802_11_set_beacons(iface);
+		ieee802_11_set_beacons_and_probes(iface);
 }
 
 
@@ -381,7 +381,7 @@  static void ap_list_timer(void *eloop_ctx, void *timeout_ctx)
 	}
 
 	if (set_beacon)
-		ieee802_11_set_beacons(iface);
+		ieee802_11_set_beacons_and_probes(iface);
 }
 
 
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 4ba223c..f3b306a 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -186,18 +186,133 @@  static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len)
 }
 
 
+#define MAX_PROBERESP_LEN 768
+
+int hostapd_alloc_probe_resp(struct hostapd_data *hapd, size_t resp_basic_len,
+			       u8 **proberesp)
+{
+	size_t buflen = resp_basic_len;
+#ifdef CONFIG_WPS
+	if (hapd->wps_probe_resp_ie)
+		buflen += wpabuf_len(hapd->wps_probe_resp_ie);
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+	if (hapd->p2p_probe_resp_ie)
+		buflen += wpabuf_len(hapd->p2p_probe_resp_ie);
+#endif /* CONFIG_P2P */
+	*proberesp = os_zalloc(buflen);
+	if (*proberesp == NULL)
+		return -1;
+
+	return 0;
+}
+
+
+static size_t hostapd_gen_probe_resp(struct hostapd_data *hapd,
+				     const struct ieee80211_mgmt *req,
+				     struct ieee80211_mgmt *resp,
+				     size_t resp_basic_len,
+				     int is_p2p)
+{
+	u8 *pos, *epos;
+	struct sta_info *sta = NULL;
+
+	/* TODO: verify that supp_rates contains at least one matching rate
+	 * with AP configuration */
+	epos = ((u8 *) resp) + resp_basic_len;
+
+	resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_PROBE_RESP);
+	if (req)
+		os_memcpy(resp->da, req->sa, ETH_ALEN);
+	else
+		os_memset(resp->da, 0, sizeof(resp->da));
+	os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
+
+	os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
+	resp->u.probe_resp.beacon_int =
+		host_to_le16(hapd->iconf->beacon_int);
+
+	/* 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));
+
+	pos = resp->u.probe_resp.variable;
+	*pos++ = WLAN_EID_SSID;
+	*pos++ = hapd->conf->ssid.ssid_len;
+	os_memcpy(pos, hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len);
+	pos += hapd->conf->ssid.ssid_len;
+
+	/* Supported rates */
+	pos = hostapd_eid_supp_rates(hapd, pos);
+
+	/* DS Params */
+	pos = hostapd_eid_ds_params(hapd, pos);
+
+	pos = hostapd_eid_country(hapd, pos, epos - pos);
+
+	/* ERP Information element */
+	pos = hostapd_eid_erp_info(hapd, pos);
+
+	/* Extended supported rates */
+	pos = hostapd_eid_ext_supp_rates(hapd, pos);
+
+	/* RSN, MDIE, WPA */
+	pos = hostapd_eid_wpa(hapd, pos, epos - pos);
+
+#ifdef CONFIG_IEEE80211N
+	pos = hostapd_eid_ht_capabilities(hapd, pos);
+	pos = hostapd_eid_ht_operation(hapd, pos);
+#endif /* CONFIG_IEEE80211N */
+
+	pos = hostapd_eid_ext_capab(hapd, pos);
+
+	pos = hostapd_eid_time_adv(hapd, pos);
+	pos = hostapd_eid_time_zone(hapd, pos);
+
+	pos = hostapd_eid_interworking(hapd, pos);
+	pos = hostapd_eid_adv_proto(hapd, pos);
+	pos = hostapd_eid_roaming_consortium(hapd, pos);
+
+	/* Wi-Fi Alliance WMM */
+	pos = hostapd_eid_wmm(hapd, pos);
+
+#ifdef CONFIG_WPS
+	if (hapd->conf->wps_state && hapd->wps_probe_resp_ie) {
+		os_memcpy(pos, wpabuf_head(hapd->wps_probe_resp_ie),
+			  wpabuf_len(hapd->wps_probe_resp_ie));
+		pos += wpabuf_len(hapd->wps_probe_resp_ie);
+	}
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P
+	if ((hapd->conf->p2p & P2P_ENABLED) && is_p2p &&
+	    hapd->p2p_probe_resp_ie) {
+		os_memcpy(pos, wpabuf_head(hapd->p2p_probe_resp_ie),
+			  wpabuf_len(hapd->p2p_probe_resp_ie));
+		pos += wpabuf_len(hapd->p2p_probe_resp_ie);
+	}
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_P2P_MANAGER
+	if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) ==
+	    P2P_MANAGE)
+		pos = hostapd_eid_p2p_manage(hapd, pos);
+#endif /* CONFIG_P2P_MANAGER */
+
+	return pos - (u8 *) resp;
+}
+
+
 void handle_probe_req(struct hostapd_data *hapd,
 		      const struct ieee80211_mgmt *mgmt, size_t len)
 {
 	struct ieee80211_mgmt *resp;
 	struct ieee802_11_elems elems;
 	char *ssid;
-	u8 *pos, *epos;
 	const u8 *ie;
-	size_t ssid_len, ie_len;
+	size_t ie_len;
 	struct sta_info *sta = NULL;
-	size_t buflen;
-	size_t i;
+	size_t i, resp_len;
 
 	ie = mgmt->u.probe_req.variable;
 	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
@@ -220,7 +335,6 @@  void handle_probe_req(struct hostapd_data *hapd,
 	}
 
 	ssid = NULL;
-	ssid_len = 0;
 
 	if ((!elems.ssid || !elems.supp_rates)) {
 		wpa_printf(MSG_DEBUG, "STA " MACSTR " sent probe request "
@@ -267,7 +381,6 @@  void handle_probe_req(struct hostapd_data *hapd,
 	     os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) ==
 	     0)) {
 		ssid = hapd->conf->ssid.ssid;
-		ssid_len = hapd->conf->ssid.ssid_len;
 		if (sta)
 			sta->ssid_probe = &hapd->conf->ssid;
 	}
@@ -315,99 +428,13 @@  void handle_probe_req(struct hostapd_data *hapd,
 	}
 #endif /* CONFIG_INTERWORKING */
 
-	/* TODO: verify that supp_rates contains at least one matching rate
-	 * with AP configuration */
-#define MAX_PROBERESP_LEN 768
-	buflen = MAX_PROBERESP_LEN;
-#ifdef CONFIG_WPS
-	if (hapd->wps_probe_resp_ie)
-		buflen += wpabuf_len(hapd->wps_probe_resp_ie);
-#endif /* CONFIG_WPS */
-#ifdef CONFIG_P2P
-	if (hapd->p2p_probe_resp_ie)
-		buflen += wpabuf_len(hapd->p2p_probe_resp_ie);
-#endif /* CONFIG_P2P */
-	resp = os_zalloc(buflen);
-	if (resp == NULL)
+	if (hostapd_alloc_probe_resp(hapd, MAX_PROBERESP_LEN, (u8 **)&resp) < 0)
 		return;
-	epos = ((u8 *) resp) + MAX_PROBERESP_LEN;
-
-	resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-					   WLAN_FC_STYPE_PROBE_RESP);
-	os_memcpy(resp->da, mgmt->sa, ETH_ALEN);
-	os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
-
-	os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
-	resp->u.probe_resp.beacon_int =
-		host_to_le16(hapd->iconf->beacon_int);
-
-	/* 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));
 
-	pos = resp->u.probe_resp.variable;
-	*pos++ = WLAN_EID_SSID;
-	*pos++ = ssid_len;
-	os_memcpy(pos, ssid, ssid_len);
-	pos += ssid_len;
-
-	/* Supported rates */
-	pos = hostapd_eid_supp_rates(hapd, pos);
-
-	/* DS Params */
-	pos = hostapd_eid_ds_params(hapd, pos);
-
-	pos = hostapd_eid_country(hapd, pos, epos - pos);
-
-	/* ERP Information element */
-	pos = hostapd_eid_erp_info(hapd, pos);
-
-	/* Extended supported rates */
-	pos = hostapd_eid_ext_supp_rates(hapd, pos);
-
-	/* RSN, MDIE, WPA */
-	pos = hostapd_eid_wpa(hapd, pos, epos - pos);
-
-#ifdef CONFIG_IEEE80211N
-	pos = hostapd_eid_ht_capabilities(hapd, pos);
-	pos = hostapd_eid_ht_operation(hapd, pos);
-#endif /* CONFIG_IEEE80211N */
-
-	pos = hostapd_eid_ext_capab(hapd, pos);
-
-	pos = hostapd_eid_time_adv(hapd, pos);
-	pos = hostapd_eid_time_zone(hapd, pos);
-
-	pos = hostapd_eid_interworking(hapd, pos);
-	pos = hostapd_eid_adv_proto(hapd, pos);
-	pos = hostapd_eid_roaming_consortium(hapd, pos);
-
-	/* Wi-Fi Alliance WMM */
-	pos = hostapd_eid_wmm(hapd, pos);
-
-#ifdef CONFIG_WPS
-	if (hapd->conf->wps_state && hapd->wps_probe_resp_ie) {
-		os_memcpy(pos, wpabuf_head(hapd->wps_probe_resp_ie),
-			  wpabuf_len(hapd->wps_probe_resp_ie));
-		pos += wpabuf_len(hapd->wps_probe_resp_ie);
-	}
-#endif /* CONFIG_WPS */
-
-#ifdef CONFIG_P2P
-	if ((hapd->conf->p2p & P2P_ENABLED) && elems.p2p &&
-	    hapd->p2p_probe_resp_ie) {
-		os_memcpy(pos, wpabuf_head(hapd->p2p_probe_resp_ie),
-			  wpabuf_len(hapd->p2p_probe_resp_ie));
-		pos += wpabuf_len(hapd->p2p_probe_resp_ie);
-	}
-#endif /* CONFIG_P2P */
-#ifdef CONFIG_P2P_MANAGER
-	if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) ==
-	    P2P_MANAGE)
-		pos = hostapd_eid_p2p_manage(hapd, pos);
-#endif /* CONFIG_P2P_MANAGER */
+	resp_len = hostapd_gen_probe_resp(hapd, mgmt, resp, MAX_PROBERESP_LEN,
+					  (int)elems.p2p);
 
-	if (hostapd_drv_send_mlme(hapd, resp, pos - (u8 *) resp) < 0)
+	if (hostapd_drv_send_mlme(hapd, resp, resp_len) < 0)
 		perror("handle_probe_req: send");
 
 	os_free(resp);
@@ -417,28 +444,15 @@  void handle_probe_req(struct hostapd_data *hapd,
 		   elems.ssid_len == 0 ? "broadcast" : "our");
 }
 
-#endif /* NEED_AP_MLME */
 
-
-void ieee802_11_set_beacon(struct hostapd_data *hapd)
+static int hostapd_alloc_beacon(struct hostapd_data *hapd,
+				u8 **head, u8 **tail)
 {
-	struct ieee80211_mgmt *head = NULL;
-	u8 *tail = NULL;
-	size_t head_len = 0, tail_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
+	size_t tail_len;
 
 #define BEACON_HEAD_BUF_SIZE 256
 #define BEACON_TAIL_BUF_SIZE 512
-	head = os_zalloc(BEACON_HEAD_BUF_SIZE);
+	*head = os_zalloc(BEACON_HEAD_BUF_SIZE);
 	tail_len = BEACON_TAIL_BUF_SIZE;
 #ifdef CONFIG_WPS
 	if (hapd->conf->wps_state && hapd->wps_beacon_ie)
@@ -448,14 +462,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 */
-	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;
+	*tail = os_malloc(tail_len);
+	if (*head == NULL || *tail == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to alloc beacon data");
+		os_free(*head);
+		os_free(*tail);
+		return -1;
 	}
 
+	return 0;
+}
+
+
+static void hostapd_gen_beacon(struct hostapd_data *hapd,
+			       struct ieee80211_mgmt *head,
+			       u8 *tail, size_t *head_len,
+			       size_t *tail_len)
+{
+	u16 capab_info;
+	u8 *pos, *tailpos;
+
+	tailpos = tail;
+
 	head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
 					   WLAN_FC_STYPE_BEACON);
 	head->duration = host_to_le16(0);
@@ -493,7 +521,7 @@  void ieee802_11_set_beacon(struct hostapd_data *hapd)
 	/* DS Params */
 	pos = hostapd_eid_ds_params(hapd, pos);
 
-	head_len = pos - (u8 *) head;
+	*head_len = pos - (u8 *) head;
 
 	tailpos = hostapd_eid_country(hapd, tailpos,
 				      tail + BEACON_TAIL_BUF_SIZE - tailpos);
@@ -549,15 +577,30 @@  void ieee802_11_set_beacon(struct hostapd_data *hapd)
 		tailpos = hostapd_eid_p2p_manage(hapd, tailpos);
 #endif /* CONFIG_P2P_MANAGER */
 
-	tail_len = tailpos > tail ? tailpos - tail : 0;
+	*tail_len = tailpos > tail ? tailpos - tail : 0;
+
+}
 
 #endif /* NEED_AP_MLME */
 
+
+static void ieee802_11_set_ap(struct hostapd_data *hapd, const u8 *head,
+			      size_t head_len, const u8 *tail, size_t tail_len,
+			      const u8 *proberesp, size_t proberesp_len)
+{
+	struct wpa_driver_ap_params params;
+	struct wpabuf *beacon_ies = NULL, *proberesp_ies = NULL,
+		      *assocresp_ies = NULL;
+
+	hapd->beacon_set_done = 1;
+
 	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 = proberesp;
+	params.proberesp_len = proberesp_len;
 	params.dtim_period = hapd->conf->dtim_period;
 	params.beacon_int = hapd->iconf->beacon_int;
 	params.ssid = (u8 *) hapd->conf->ssid.ssid;
@@ -583,10 +626,12 @@  void ieee802_11_set_beacon(struct hostapd_data *hapd)
 		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;
+
+	hostapd_build_ap_extra_ies(hapd, &beacon_ies, &proberesp_ies,
+				   &assocresp_ies);
+	params.beacon_ies = beacon_ies;
+	params.proberesp_ies = proberesp_ies;
+	params.assocresp_ies = assocresp_ies;
 	params.isolate = hapd->conf->isolate;
 #ifdef NEED_AP_MLME
 	params.cts_protect = !!(ieee802_11_erp_info(hapd) &
@@ -611,18 +656,81 @@  void ieee802_11_set_beacon(struct hostapd_data *hapd)
 	params.access_network_type = hapd->conf->access_network_type;
 	if (hostapd_drv_set_ap(hapd, &params))
 		wpa_printf(MSG_ERROR, "Failed to set beacon parameters");
-	hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
 
+	hostapd_free_ap_extra_ies(hapd, beacon_ies, proberesp_ies,
+				  assocresp_ies);
+
+}
+
+
+void ieee802_11_set_beacon_and_probe(struct hostapd_data *hapd)
+{
+	u8 *resp = NULL, *head = NULL, *tail = NULL;
+	size_t resp_len = 0, head_len = 0, tail_len = 0;
+
+	if (hostapd_alloc_beacon(hapd, &head, &tail) < 0)
+		goto fail;
+
+	hostapd_gen_beacon(hapd, (struct ieee80211_mgmt *)head, tail,
+			   &head_len, &tail_len);
+
+#ifdef NEED_AP_MLME
+
+	/* check probe response offloading caps and print warnings */
+	if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD))
+		goto set;
+
+#ifdef CONFIG_WPS
+	if (hapd->conf->wps_state && hapd->wps_probe_resp_ie &&
+	    (!(hapd->iface->probe_resp_offload_protocols &
+			WPA_DRIVER_PROBE_RESP_OFFLOAD_SUPPORT_WPS) ||
+	    !(hapd->iface->probe_resp_offload_protocols &
+			WPA_DRIVER_PROBE_RESP_OFFLOAD_SUPPORT_WPS2)))
+		wpa_printf(MSG_WARNING, "Device is trying to offload WPS "
+			   "probe-responses while not supporting WPS/WPS2 "
+			   "probe-response offloading.");
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P
+	if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_probe_resp_ie &&
+	    !(hapd->iface->probe_resp_offload_protocols &
+			WPA_DRIVER_PROBE_RESP_OFFLOAD_SUPPORT_P2P))
+		wpa_printf(MSG_WARNING, "Device is trying to offload P2P "
+			   "probe-responses while not supporting P2P "
+			   "probe-response offloading.");
+#endif  /* CONFIG_P2P */
+
+	if (hapd->conf->interworking &&
+		!(hapd->iface->probe_resp_offload_protocols &
+			WPA_DRIVER_PROBE_RESP_OFFLOAD_SUPPORT_80211U))
+		wpa_printf(MSG_WARNING, "Device is trying to offload 802.11u "
+			   "(interworking) probe-responses while not "
+			   "supporting 802.11u probe-response offloading.");
+
+	if (hostapd_alloc_probe_resp(hapd, MAX_PROBERESP_LEN, &resp) < 0)
+		goto set;
+
+	/* we generate a probe-resp for the non-p2p case */
+	resp_len = hostapd_gen_probe_resp(hapd, NULL,
+					  (struct ieee80211_mgmt *)resp,
+					  MAX_PROBERESP_LEN, 0);
+#endif /* NEED_AP_MLME */
+
+set:
+	ieee802_11_set_ap(hapd, head, head_len, tail, tail_len, resp, resp_len);
+
+fail:
 	os_free(tail);
 	os_free(head);
+	os_free(resp);
 }
 
 
-void ieee802_11_set_beacons(struct hostapd_iface *iface)
+void ieee802_11_set_beacons_and_probes(struct hostapd_iface *iface)
 {
 	size_t i;
 	for (i = 0; i < iface->num_bss; i++)
-		ieee802_11_set_beacon(iface->bss[i]);
+		ieee802_11_set_beacon_and_probe(iface->bss[i]);
 }
 
 #endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/ap/beacon.h b/src/ap/beacon.h
index a944f5f..df687da 100644
--- a/src/ap/beacon.h
+++ b/src/ap/beacon.h
@@ -20,7 +20,7 @@  struct ieee80211_mgmt;
 
 void handle_probe_req(struct hostapd_data *hapd,
 		      const struct ieee80211_mgmt *mgmt, size_t len);
-void ieee802_11_set_beacon(struct hostapd_data *hapd);
-void ieee802_11_set_beacons(struct hostapd_iface *iface);
+void ieee802_11_set_beacon_and_probe(struct hostapd_data *hapd);
+void ieee802_11_set_beacons_and_probes(struct hostapd_iface *iface);
 
 #endif /* BEACON_H */
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index e180a08..70b46da 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -81,7 +81,7 @@  static void hostapd_reload_bss(struct hostapd_data *hapd)
 		hostapd_set_generic_elem(hapd, (u8 *) "", 0);
 	}
 
-	ieee802_11_set_beacon(hapd);
+	ieee802_11_set_beacon_and_probe(hapd);
 	hostapd_update_wps(hapd);
 
 	if (hapd->conf->ssid.ssid_set &&
@@ -629,7 +629,7 @@  static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
 		return -1;
 	}
 
-	ieee802_11_set_beacon(hapd);
+	ieee802_11_set_beacon_and_probe(hapd);
 
 	if (hapd->wpa_auth && wpa_init_keys(hapd->wpa_auth) < 0)
 		return -1;
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 5401e80..916e9f2 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -191,6 +191,13 @@  struct hostapd_iface {
 	struct ap_info *ap_iter_list;
 
 	unsigned int drv_flags;
+
+	/*
+	 * A bitmap of supported protocols for probe response offload. See
+	 * struct wpa_driver_capa in driver.h
+	 */
+	unsigned int probe_resp_offload_protocols;
+
 	struct hostapd_hw_modes *hw_features;
 	int num_hw_features;
 	struct hostapd_hw_modes *current_mode;
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 9fad335..d65f8d1 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -1018,7 +1018,7 @@  static void handle_assoc(struct hostapd_data *hapd,
 		sta->nonerp_set = 1;
 		hapd->iface->num_sta_non_erp++;
 		if (hapd->iface->num_sta_non_erp == 1)
-			ieee802_11_set_beacons(hapd->iface);
+			ieee802_11_set_beacons_and_probes(hapd->iface);
 	}
 
 	if (!(sta->capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) &&
@@ -1028,7 +1028,7 @@  static void handle_assoc(struct hostapd_data *hapd,
 		if (hapd->iface->current_mode->mode ==
 		    HOSTAPD_MODE_IEEE80211G &&
 		    hapd->iface->num_sta_no_short_slot_time == 1)
-			ieee802_11_set_beacons(hapd->iface);
+			ieee802_11_set_beacons_and_probes(hapd->iface);
 	}
 
 	if (sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
@@ -1042,7 +1042,7 @@  static void handle_assoc(struct hostapd_data *hapd,
 		hapd->iface->num_sta_no_short_preamble++;
 		if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
 		    && hapd->iface->num_sta_no_short_preamble == 1)
-			ieee802_11_set_beacons(hapd->iface);
+			ieee802_11_set_beacons_and_probes(hapd->iface);
 	}
 
 #ifdef CONFIG_IEEE80211N
diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
index 6c3696f..32d1e7c 100644
--- a/src/ap/ieee802_11_ht.c
+++ b/src/ap/ieee802_11_ht.c
@@ -236,7 +236,7 @@  void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta)
 		update_sta_no_ht(hapd, sta);
 
 	if (hostapd_ht_operation_update(hapd->iface) > 0)
-		ieee802_11_set_beacons(hapd->iface);
+		ieee802_11_set_beacons_and_probes(hapd->iface);
 }
 
 
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index dc689ba..df0682d 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -198,7 +198,7 @@  void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
 #endif /* NEED_AP_MLME && CONFIG_IEEE80211N */
 
 	if (set_beacon)
-		ieee802_11_set_beacons(hapd->iface);
+		ieee802_11_set_beacons_and_probes(hapd->iface);
 
 	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
 	eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index 671bc67..500e8a1 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -142,7 +142,7 @@  static int hostapd_wps_set_ie_cb(void *ctx, struct wpabuf *beacon_ie,
 	wpabuf_free(hapd->wps_probe_resp_ie);
 	hapd->wps_probe_resp_ie = probe_resp_ie;
 	if (hapd->beacon_set_done)
-		ieee802_11_set_beacon(hapd);
+		ieee802_11_set_beacon_and_probe(hapd);
 	return hostapd_set_ap_wps_ie(hapd);
 }
 
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 2500c1e..477af6e 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -556,6 +556,19 @@  struct wpa_driver_ap_params {
 	int beacon_int;
 
 	/**
+	 * proberesp - Probe response template
+	 *
+	 * This is used by drivers that reply to probe requests internally in
+	 * AP mode and require the full probe response template.
+	 */
+	const u8 *proberesp;
+
+	/**
+	 * proberesp_len - Length of the proberesp buffer in octets
+	 */
+	size_t proberesp_len;
+
+	/**
 	 * ssid - The SSID to use in Beacon/Probe Response frames
 	 */
 	const u8 *ssid;
@@ -1444,6 +1457,20 @@  struct wpa_driver_ops {
 	int (*set_ap)(void *priv, struct wpa_driver_ap_params *params);
 
 	/**
+	 * set_probe_resp - Set Probe Response frame template
+	 * @priv: Private driver interface data
+	 * @resp: Probe Response template
+	 * @resp_len: Length of the resp buffer in octets
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function is used to configure a Probe Response template for the
+	 * driver in AP mode. The driver is responsible for building the full
+	 * Probe Response frame by setting the destination address to that of
+	 * the desired recipient.
+	 */
+	int (*set_probe_resp)(void *priv, const u8 *resp, size_t resp_len);
+
+	/**
 	 * hapd_init - Initialize driver interface (hostapd only)
 	 * @hapd: Pointer to hostapd context
 	 * @params: Configuration for the driver wrapper
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index e3d5433..1e17175 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -454,6 +454,8 @@  int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
 		return -1;
 	hapd_iface->owner = wpa_s;
 	hapd_iface->drv_flags = wpa_s->drv_flags;
+	hapd_iface->probe_resp_offload_protocols =
+		wpa_s->probe_resp_offload_protocols;
 
 	wpa_s->ap_iface->conf = conf = hostapd_config_defaults();
 	if (conf == NULL) {
@@ -900,7 +902,7 @@  int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s)
 			P2P_GROUP_FORMATION;
 #endif /* CONFIG_P2P */
 
-	ieee802_11_set_beacons(iface);
+	ieee802_11_set_beacons_and_probes(iface);
 	hapd = iface->bss[0];
 	hostapd_set_ap_wps_ie(hapd);
 
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 2b3140e..1bff61c 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -2325,6 +2325,8 @@  next_driver:
 	if (wpa_drv_get_capa(wpa_s, &capa) == 0) {
 		wpa_s->drv_capa_known = 1;
 		wpa_s->drv_flags = capa.flags;
+		wpa_s->probe_resp_offload_protocols =
+			capa.probe_resp_offload_protocols;
 		wpa_s->max_scan_ssids = capa.max_scan_ssids;
 		wpa_s->max_sched_scan_ssids = capa.max_sched_scan_ssids;
 		wpa_s->sched_scan_supported = capa.sched_scan_supported;
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index fd04a7d..f661383 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -339,6 +339,13 @@  struct wpa_supplicant {
 	int scan_interval; /* time in sec between scans to find suitable AP */
 
 	unsigned int drv_flags;
+
+	/*
+	 * A bitmap of supported protocols for probe response offload. See
+	 * struct wpa_driver_capa in driver.h
+	 */
+	unsigned int probe_resp_offload_protocols;
+
 	int max_scan_ssids;
 	int max_sched_scan_ssids;
 	int sched_scan_supported;