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

login
register
mail settings
Submitter Arik Nemtsov
Date Nov. 10, 2011, 9:33 a.m.
Message ID <1320917605-2806-2-git-send-email-arik@wizery.com>
Download mbox | patch
Permalink /patch/124832/
State Accepted
Headers show

Comments

Arik Nemtsov - Nov. 10, 2011, 9:33 a.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-hostap: Arik Nemtsov <arik@wizery.com>
Signed-off-by: Arik Nemtsov <arik@wizery.com>
---
 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(-)
Jouni Malinen - Dec. 10, 2011, 7:18 p.m.
On Thu, Nov 10, 2011 at 11:33:24AM +0200, Arik Nemtsov wrote:
> 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.

This was a bit difficult patch to review - it would have helped if it
had been split in multiple parts. I ended up dropping parts that did not
seem to be related and/or needed (splitting of ieee802_11_set_beacon()
into pieces and renaming of some of the functions. In addition, I
committed this in smaller parts to make it easier to review and address
conflicts (this area is quite difficult between number of trees). Please
let me know if I missed something from the patch that should really have
went in.

> diff --git 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,
> -		ieee802_11_set_beacons(iface);
> +		ieee802_11_set_beacons_and_probes(iface);

I dropped all renames like this as mentioned above..

> diff --git 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)

> +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;

> +		host_to_le16(hostapd_own_capab_info(hapd, sta, 1));

That's a bug... sta was hardcoded to NULL after this change. I changed
the caller to pass in the sta pointer instead.

> -void ieee802_11_set_beacon(struct hostapd_data *hapd)
> +static int hostapd_alloc_beacon(struct hostapd_data *hapd,
> +				u8 **head, u8 **tail)

These changes of splitting ieee802_11_set_beacon() did not seem to
really do anything, so I dropped them. This would have just added
likelihood of conflicts between different trees.

> diff --git a/src/drivers/driver.h b/src/drivers/driver.h
> @@ -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);

I assume this was just forgotten here - I dropped it.
Arik Nemtsov - Dec. 11, 2011, 9:50 a.m.
On Sat, Dec 10, 2011 at 21:18, Jouni Malinen <j@w1.fi> wrote:
> On Thu, Nov 10, 2011 at 11:33:24AM +0200, Arik Nemtsov wrote:
>> 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.
>
> This was a bit difficult patch to review - it would have helped if it
> had been split in multiple parts. I ended up dropping parts that did not
> seem to be related and/or needed (splitting of ieee802_11_set_beacon()
> into pieces and renaming of some of the functions. In addition, I
> committed this in smaller parts to make it easier to review and address
> conflicts (this area is quite difficult between number of trees). Please

That's a good idea.

> let me know if I missed something from the patch that should really have
> went in.

The small refactor of the the gen_probe_resp (to remove the alloc
function) and the addition of hostapd_probe_resp_offloads() look ok.

>
>> diff --git 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,
>> -             ieee802_11_set_beacons(iface);
>> +             ieee802_11_set_beacons_and_probes(iface);
>
> I dropped all renames like this as mentioned above..

Ok

>
>> diff --git 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)
>
>> +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;
>
>> +             host_to_le16(hostapd_own_capab_info(hapd, sta, 1));
>
> That's a bug... sta was hardcoded to NULL after this change. I changed
> the caller to pass in the sta pointer instead.

Right. Thanks for caching that.

>
>> -void ieee802_11_set_beacon(struct hostapd_data *hapd)
>> +static int hostapd_alloc_beacon(struct hostapd_data *hapd,
>> +                             u8 **head, u8 **tail)
>
> These changes of splitting ieee802_11_set_beacon() did not seem to
> really do anything, so I dropped them. This would have just added
> likelihood of conflicts between different trees.

The rename was just for clarity.

>
>> diff --git a/src/drivers/driver.h b/src/drivers/driver.h
>> @@ -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);
>
> I assume this was just forgotten here - I dropped it.

Yea it's a remnant from a previous commit.

Arik
Neeraj Garg - Dec. 12, 2011, 10:09 a.m.
When waiting for go_neg frame from the peer in WAIT_PEER_CONNECT state, I have observed that sometimes it takes 20 to 30 secs for successful go negotiation. I also found out that it is because of 1 second idle time, in WAIT_PEER_CONNECT state. While it is good to have 1 second idle time [for doing power-save or doing some other legacy STA Scan or some other useful stuff], this makes go-neg process slow.

We wait for 1 second idle and then listen for a random time between 200(min)-300(max)ms. Assume P1 is in WAIT_PEER_CONNECT state and P2 is the one which is now to send go_neg frame. If P2 sends Go-neg frame just at the boundary of 300ms of P1 and assume that P2 takes close to 600-800ms for one iteration of sending go_neg request (one iteration is go_neg_request frame time + dwell time + listen_time), P2 needs to transmit at least 16-18 action frames for hitting the listen time of P1.

Following patch reduces the idle time to 500ms. Alternatively we can increase the listen time interval to 500ms just for WAIT_PEER_CONNECT state. Please let me know which approach we should take
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
@@ -326,7 +326,7 @@ static void p2p_timeout_wait_peer_connect(struct p2p_data *p2p) 
-	p2p_set_timeout(p2p, 1, 0); 
+ 	p2p_set_timeout(p2p, 0, 500000); 

Regards,
-Neeraj
Jouni Malinen - Dec. 18, 2011, 4:11 p.m.
On Mon, Dec 12, 2011 at 02:09:26AM -0800, Neeraj Kumar Garg wrote:
> When waiting for go_neg frame from the peer in WAIT_PEER_CONNECT state, I have observed that sometimes it takes 20 to 30 secs for successful go negotiation. I also found out that it is because of 1 second idle time, in WAIT_PEER_CONNECT state. While it is good to have 1 second idle time [for doing power-save or doing some other legacy STA Scan or some other useful stuff], this makes go-neg process slow.

Thanks for bringing this up! For some reason, I don't see this in that
significant way in my tests and while the question itself had come up
previously as shown by the TODO comment there, this didn't look like
critical issue in practice. However, if you are indeed hitting times
like 20 to 30 seconds, this does indeed need some fine tuning.

> We wait for 1 second idle and then listen for a random time between 200(min)-300(max)ms. Assume P1 is in WAIT_PEER_CONNECT state and P2 is the one which is now to send go_neg frame. If P2 sends Go-neg frame just at the boundary of 300ms of P1 and assume that P2 takes close to 600-800ms for one iteration of sending go_neg request (one iteration is go_neg_request frame time + dwell time + listen_time), P2 needs to transmit at least 16-18 action frames for hitting the listen time of P1.

I see quite a bit more frequent retries on my test station that 600-800
ms interval which can obviously speed up the GO Negotiation process
quite a bit. I'd guess this can depend quite a bit on the driver and how
much extra latency there is in channel changes and Action frame TX
operations.

> Following patch reduces the idle time to 500ms. Alternatively we can increase the listen time interval to 500ms just for WAIT_PEER_CONNECT state. Please let me know which approach we should take

I applied this because it is a very simple change and sounds very much
justifiable. Ignoring the extra latencies, this moves from 9..23% in
listen to 16..37% which should make it quite a bit more likely for the
other peer to be able to get an Action frame through.

Anyway, the TODO comment in p2p_timeout_wait_peer_connect() is still
relevant and I think we should really take a look at implementing it.
The idle time here could be adjusted automatically based on what else
could be happening with the radio. If there is no other connection
(e.g., legacy STA associated with an AP) in progress, the idle wait
should really be reduced quite a bit more. This can come at a cost of
more battery use, but for most cases, this should be acceptable since
the device is actually trying to connect with the peer in this
particular case.

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;