Cumulative patch from commit 0b08f25445caacb502c394e917a292d7fa595447

0b08f25 wpa_gui: Documentation update
6128a90 hostapd: Add channel 140 to allowed HT40 channel pairs
8ffaafa wpa_cli: Add missing parameters for "set" command completion routine
0fd9d95 wpa_cli: Add completion routine for "get" command
5c6c315 Add IPv4 support function for "get" control interface command
74fa78b Add AVG_BEACON_RSSI to SIGNAL_POLL output
4acdc48 nl80211: Handle NL80211_ATTR_EXT_FEATURES attribute
a0563ac Sync with mac80211-next.git include/uapi/linux/nl80211.h
0f89328 P2P: Move upper layer SD interaction into a separate file
8b94980 FT: Avoid unnecessary allocation for MIC calculation
18da814 The master branch is now used for v2.5 development
bc04db9 Change version information for the 2.4 release
bc1d23a Add ChangeLog entries for v2.4
319d9da Fix bitfield_get_first_zero() to not read beyond buffer
39c3bfc Indicate AP-DISABLED on main AP mode deinit path
f10487e Send CTRL-EVENT-DISCONNECTED on wpa_supplicant AP deinit

Change-Id: I0b8459261265a25fe0edd7b435ede32be3cc6616
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 579582b..6d39613 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -271,6 +271,7 @@
 
 ifdef CONFIG_P2P
 OBJS += p2p_supplicant.c
+OBJS += p2p_supplicant_sd.c
 OBJS += src/p2p/p2p.c
 OBJS += src/p2p/p2p_utils.c
 OBJS += src/p2p/p2p_parse.c
diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog
index f09e7a0..1ac79b4 100644
--- a/wpa_supplicant/ChangeLog
+++ b/wpa_supplicant/ChangeLog
@@ -1,5 +1,83 @@
 ChangeLog for wpa_supplicant
 
+2015-03-15 - v2.4
+	* allow OpenSSL cipher configuration to be set for internal EAP server
+	  (openssl_ciphers parameter)
+	* fixed number of small issues based on hwsim test case failures and
+	  static analyzer reports
+	* P2P:
+	  - add new=<0/1> flag to P2P-DEVICE-FOUND events
+	  - add passive channels in invitation response from P2P Client
+	  - enable nl80211 P2P_DEVICE support by default
+	  - fix regresssion in disallow_freq preventing search on social
+	    channels
+	  - fix regressions in P2P SD query processing
+	  - try to re-invite with social operating channel if no common channels
+	    in invitation
+	  - allow cross connection on parent interface (this fixes number of
+	    use cases with nl80211)
+	  - add support for P2P services (P2PS)
+	  - add p2p_go_ctwindow configuration parameter to allow GO CTWindow to
+	    be configured
+	* increase postponing of EAPOL-Start by one second with AP/GO that
+	  supports WPS 2.0 (this makes it less likely to trigger extra roundtrip
+	  of identity frames)
+	* add support for PMKSA caching with SAE
+	* add support for control mesh BSS (IEEE 802.11s) operations
+	* fixed number of issues with D-Bus P2P commands
+	* fixed regression in ap_scan=2 special case for WPS
+	* fixed macsec_validate configuration
+	* add a workaround for incorrectly behaving APs that try to use
+	  EAPOL-Key descriptor version 3 when the station supports PMF even if
+	  PMF is not enabled on the AP
+	* allow TLS v1.1 and v1.2 to be negotiated by default; previous behavior
+	  of disabling these can be configured to work around issues with broken
+	  servers with phase1="tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1"
+	* add support for Suite B (128-bit and 192-bit level) key management and
+	  cipher suites
+	* add WMM-AC support (WMM_AC_ADDTS/WMM_AC_DELTS)
+	* improved BSS Transition Management processing
+	* add support for neighbor report
+	* add support for link measurement
+	* fixed expiration of BSS entry with all-zeros BSSID
+	* add optional LAST_ID=x argument to LIST_NETWORK to allow all
+	  configured networks to be listed even with huge number of network
+	  profiles
+	* add support for EAP Re-Authentication Protocol (ERP)
+	* fixed EAP-IKEv2 fragmentation reassembly
+	* improved PKCS#11 configuration for OpenSSL
+	* set stdout to be line-buffered
+	* add TDLS channel switch configuration
+	* add support for MAC address randomization in scans with nl80211
+	* enable HT for IBSS if supported by the driver
+	* add BSSID black and white lists (bssid_blacklist, bssid_whitelist)
+	* add support for domain_suffix_match with GnuTLS
+	* add OCSP stapling client support with GnuTLS
+	* include peer certificate in EAP events even without a separate probe
+	  operation; old behavior can be restored with cert_in_cb=0
+	* add peer ceritficate alt subject name to EAP events
+	  (CTRL-EVENT-EAP-PEER-ALT)
+	* add domain_match network profile parameter (similar to
+	  domain_suffix_match, but full match is required)
+	* enable AP/GO mode HT Tx STBC automatically based on driver support
+	* add ANQP-QUERY-DONE event to provide information on ANQP parsing
+	  status
+	* allow passive scanning to be forced with passive_scan=1
+	* add a workaround for Linux packet socket behavior when interface is in
+	  bridge
+	* increase 5 GHz band preference in BSS selection (estimate SNR, if info
+	  not available from driver; estimate maximum throughput based on common
+	  HT/VHT/specific TX rate support)
+	* add INTERWORKING_ADD_NETWORK ctrl_iface command; this can be used to
+	  implement Interworking network selection behavior in upper layers
+	  software components
+	* add optional reassoc_same_bss_optim=1 (disabled by default)
+	  optimization to avoid unnecessary Authentication frame exchange
+	* extend TDLS frame padding workaround to cover all packets
+	* allow wpa_supplicant to recover nl80211 functionality if the cfg80211
+	  module gets removed and reloaded without restarting wpa_supplicant
+	* allow hostapd DFS implementation to be used in wpa_supplicant AP mode
+
 2014-10-09 - v2.3
 	* fixed number of minor issues identified in static analyzer warnings
 	* fixed wfd_dev_info to be more careful and not read beyond the buffer
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 0f82af9..976b984 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -276,6 +276,7 @@
 
 ifdef CONFIG_P2P
 OBJS += p2p_supplicant.o
+OBJS += p2p_supplicant_sd.o
 OBJS += ../src/p2p/p2p.o
 OBJS += ../src/p2p/p2p_utils.o
 OBJS += ../src/p2p/p2p_parse.o
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index ea5b2b4..7ecf7a8 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -707,6 +707,9 @@
 	hostapd_interface_free(wpa_s->ap_iface);
 	wpa_s->ap_iface = NULL;
 	wpa_drv_deinit_ap(wpa_s);
+	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR
+		" reason=%d locally_generated=1",
+		MAC2STR(wpa_s->own_addr), WLAN_REASON_DEAUTH_LEAVING);
 }
 
 
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 8e6cd20..a01a910 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -4052,6 +4052,31 @@
 }
 
 
+#ifdef CONFIG_P2P
+static int wpa_config_get_ipv4(const char *name, struct wpa_config *config,
+			       long offset, char *buf, size_t buflen,
+			       int pretty_print)
+{
+	void *val = ((u8 *) config) + (long) offset;
+	int res;
+	char addr[INET_ADDRSTRLEN];
+
+	if (!val || !inet_ntop(AF_INET, val, addr, sizeof(addr)))
+		return -1;
+
+	if (pretty_print)
+		res = os_snprintf(buf, buflen, "%s=%s\n", name, addr);
+	else
+		res = os_snprintf(buf, buflen, "%s", addr);
+
+	if (os_snprintf_error(buflen, res))
+		res = -1;
+
+	return res;
+}
+#endif /* CONFIG_P2P */
+
+
 #ifdef OFFSET
 #undef OFFSET
 #endif /* OFFSET */
@@ -4067,7 +4092,8 @@
 #define STR(f) _STR(f), NULL, NULL
 #define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max
 #define BIN(f) #f, wpa_global_config_parse_bin, NULL, OFFSET(f), NULL, NULL
-#define IPV4(f) #f, wpa_global_config_parse_ipv4, NULL, OFFSET(f), NULL, NULL
+#define IPV4(f) #f, wpa_global_config_parse_ipv4, wpa_config_get_ipv4,  \
+	OFFSET(f), NULL, NULL
 
 static const struct global_parse_data global_fields[] = {
 #ifdef CONFIG_CTRL_IFACE
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index b4aefb6..4ebc3a1 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -6476,6 +6476,14 @@
 		pos += ret;
 	}
 
+	if (si.avg_beacon_signal) {
+		ret = os_snprintf(pos, end - pos,
+				  "AVG_BEACON_RSSI=%d\n", si.avg_beacon_signal);
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+
 	return pos - buf;
 }
 
diff --git a/wpa_supplicant/doc/docbook/wpa_gui.sgml b/wpa_supplicant/doc/docbook/wpa_gui.sgml
index 84766db..fe91236 100644
--- a/wpa_supplicant/doc/docbook/wpa_gui.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_gui.sgml
@@ -17,6 +17,7 @@
       <arg>-p <replaceable>path to ctrl sockets</replaceable></arg>
       <arg>-i <replaceable>ifname</replaceable></arg>
       <arg>-t</arg>
+      <arg>-q</arg>
     </cmdsynopsis>
   </refsynopsisdiv>
 
@@ -57,6 +58,13 @@
 	manager supports it). By default the main status window is
 	shown.</para></listitem>
       </varlistentry>
+
+      <varlistentry>
+	<term>-q</term>
+
+        <listitem><para>Run program in the quiet mode - do not display tray
+	icon pop-up messages.</para></listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
   <refsect1>
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index b200ca0..bb61808 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -2388,1227 +2388,6 @@
 }
 
 
-/*
- * DNS Header section is used only to calculate compression pointers, so the
- * contents of this data does not matter, but the length needs to be reserved
- * in the virtual packet.
- */
-#define DNS_HEADER_LEN 12
-
-/*
- * 27-octet in-memory packet from P2P specification containing two implied
- * queries for _tcp.lcoal. PTR IN and _udp.local. PTR IN
- */
-#define P2P_SD_IN_MEMORY_LEN 27
-
-static int p2p_sd_dns_uncompress_label(char **upos, char *uend, u8 *start,
-				       u8 **spos, const u8 *end)
-{
-	while (*spos < end) {
-		u8 val = ((*spos)[0] & 0xc0) >> 6;
-		int len;
-
-		if (val == 1 || val == 2) {
-			/* These are reserved values in RFC 1035 */
-			wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
-				   "sequence starting with 0x%x", val);
-			return -1;
-		}
-
-		if (val == 3) {
-			u16 offset;
-			u8 *spos_tmp;
-
-			/* Offset */
-			if (*spos + 2 > end) {
-				wpa_printf(MSG_DEBUG, "P2P: No room for full "
-					   "DNS offset field");
-				return -1;
-			}
-
-			offset = (((*spos)[0] & 0x3f) << 8) | (*spos)[1];
-			if (offset >= *spos - start) {
-				wpa_printf(MSG_DEBUG, "P2P: Invalid DNS "
-					   "pointer offset %u", offset);
-				return -1;
-			}
-
-			(*spos) += 2;
-			spos_tmp = start + offset;
-			return p2p_sd_dns_uncompress_label(upos, uend, start,
-							   &spos_tmp,
-							   *spos - 2);
-		}
-
-		/* Label */
-		len = (*spos)[0] & 0x3f;
-		if (len == 0)
-			return 0;
-
-		(*spos)++;
-		if (*spos + len > end) {
-			wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
-				   "sequence - no room for label with length "
-				   "%u", len);
-			return -1;
-		}
-
-		if (*upos + len + 2 > uend)
-			return -2;
-
-		os_memcpy(*upos, *spos, len);
-		*spos += len;
-		*upos += len;
-		(*upos)[0] = '.';
-		(*upos)++;
-		(*upos)[0] = '\0';
-	}
-
-	return 0;
-}
-
-
-/* Uncompress domain names per RFC 1035 using the P2P SD in-memory packet.
- * Returns -1 on parsing error (invalid input sequence), -2 if output buffer is
- * not large enough */
-static int p2p_sd_dns_uncompress(char *buf, size_t buf_len, const u8 *msg,
-				 size_t msg_len, size_t offset)
-{
-	/* 27-octet in-memory packet from P2P specification */
-	const char *prefix = "\x04_tcp\x05local\x00\x00\x0C\x00\x01"
-		"\x04_udp\xC0\x11\x00\x0C\x00\x01";
-	u8 *tmp, *end, *spos;
-	char *upos, *uend;
-	int ret = 0;
-
-	if (buf_len < 2)
-		return -1;
-	if (offset > msg_len)
-		return -1;
-
-	tmp = os_malloc(DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN + msg_len);
-	if (tmp == NULL)
-		return -1;
-	spos = tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN;
-	end = spos + msg_len;
-	spos += offset;
-
-	os_memset(tmp, 0, DNS_HEADER_LEN);
-	os_memcpy(tmp + DNS_HEADER_LEN, prefix, P2P_SD_IN_MEMORY_LEN);
-	os_memcpy(tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN, msg, msg_len);
-
-	upos = buf;
-	uend = buf + buf_len;
-
-	ret = p2p_sd_dns_uncompress_label(&upos, uend, tmp, &spos, end);
-	if (ret) {
-		os_free(tmp);
-		return ret;
-	}
-
-	if (upos == buf) {
-		upos[0] = '.';
-		upos[1] = '\0';
-	} else if (upos[-1] == '.')
-		upos[-1] = '\0';
-
-	os_free(tmp);
-	return 0;
-}
-
-
-static struct p2p_srv_bonjour *
-wpas_p2p_service_get_bonjour(struct wpa_supplicant *wpa_s,
-			     const struct wpabuf *query)
-{
-	struct p2p_srv_bonjour *bsrv;
-	size_t len;
-
-	len = wpabuf_len(query);
-	dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
-			 struct p2p_srv_bonjour, list) {
-		if (len == wpabuf_len(bsrv->query) &&
-		    os_memcmp(wpabuf_head(query), wpabuf_head(bsrv->query),
-			      len) == 0)
-			return bsrv;
-	}
-	return NULL;
-}
-
-
-static struct p2p_srv_upnp *
-wpas_p2p_service_get_upnp(struct wpa_supplicant *wpa_s, u8 version,
-			  const char *service)
-{
-	struct p2p_srv_upnp *usrv;
-
-	dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
-			 struct p2p_srv_upnp, list) {
-		if (version == usrv->version &&
-		    os_strcmp(service, usrv->service) == 0)
-			return usrv;
-	}
-	return NULL;
-}
-
-
-static void wpas_sd_add_empty(struct wpabuf *resp, u8 srv_proto,
-			      u8 srv_trans_id, u8 status)
-{
-	u8 *len_pos;
-
-	if (wpabuf_tailroom(resp) < 5)
-		return;
-
-	/* Length (to be filled) */
-	len_pos = wpabuf_put(resp, 2);
-	wpabuf_put_u8(resp, srv_proto);
-	wpabuf_put_u8(resp, srv_trans_id);
-	/* Status Code */
-	wpabuf_put_u8(resp, status);
-	/* Response Data: empty */
-	WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
-}
-
-
-static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto,
-					u8 srv_trans_id)
-{
-	wpas_sd_add_empty(resp, srv_proto, srv_trans_id,
-			  P2P_SD_PROTO_NOT_AVAILABLE);
-}
-
-
-static void wpas_sd_add_bad_request(struct wpabuf *resp, u8 srv_proto,
-				    u8 srv_trans_id)
-{
-	wpas_sd_add_empty(resp, srv_proto, srv_trans_id, P2P_SD_BAD_REQUEST);
-}
-
-
-static void wpas_sd_add_not_found(struct wpabuf *resp, u8 srv_proto,
-				  u8 srv_trans_id)
-{
-	wpas_sd_add_empty(resp, srv_proto, srv_trans_id,
-			  P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
-}
-
-
-static void wpas_sd_all_bonjour(struct wpa_supplicant *wpa_s,
-				struct wpabuf *resp, u8 srv_trans_id)
-{
-	struct p2p_srv_bonjour *bsrv;
-	u8 *len_pos;
-
-	wpa_printf(MSG_DEBUG, "P2P: SD Request for all Bonjour services");
-
-	if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
-		wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
-		return;
-	}
-
-	dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
-			 struct p2p_srv_bonjour, list) {
-		if (wpabuf_tailroom(resp) <
-		    5 + wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp))
-			return;
-		/* Length (to be filled) */
-		len_pos = wpabuf_put(resp, 2);
-		wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
-		wpabuf_put_u8(resp, srv_trans_id);
-		/* Status Code */
-		wpabuf_put_u8(resp, P2P_SD_SUCCESS);
-		wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
-				  wpabuf_head(bsrv->resp),
-				  wpabuf_len(bsrv->resp));
-		/* Response Data */
-		wpabuf_put_buf(resp, bsrv->query); /* Key */
-		wpabuf_put_buf(resp, bsrv->resp); /* Value */
-		WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
-			     2);
-	}
-}
-
-
-static int match_bonjour_query(struct p2p_srv_bonjour *bsrv, const u8 *query,
-			       size_t query_len)
-{
-	char str_rx[256], str_srv[256];
-
-	if (query_len < 3 || wpabuf_len(bsrv->query) < 3)
-		return 0; /* Too short to include DNS Type and Version */
-	if (os_memcmp(query + query_len - 3,
-		      wpabuf_head_u8(bsrv->query) + wpabuf_len(bsrv->query) - 3,
-		      3) != 0)
-		return 0; /* Mismatch in DNS Type or Version */
-	if (query_len == wpabuf_len(bsrv->query) &&
-	    os_memcmp(query, wpabuf_head(bsrv->query), query_len - 3) == 0)
-		return 1; /* Binary match */
-
-	if (p2p_sd_dns_uncompress(str_rx, sizeof(str_rx), query, query_len - 3,
-				  0))
-		return 0; /* Failed to uncompress query */
-	if (p2p_sd_dns_uncompress(str_srv, sizeof(str_srv),
-				  wpabuf_head(bsrv->query),
-				  wpabuf_len(bsrv->query) - 3, 0))
-		return 0; /* Failed to uncompress service */
-
-	return os_strcmp(str_rx, str_srv) == 0;
-}
-
-
-static void wpas_sd_req_bonjour(struct wpa_supplicant *wpa_s,
-				struct wpabuf *resp, u8 srv_trans_id,
-				const u8 *query, size_t query_len)
-{
-	struct p2p_srv_bonjour *bsrv;
-	u8 *len_pos;
-	int matches = 0;
-
-	wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for Bonjour",
-			  query, query_len);
-	if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
-		wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
-		wpas_sd_add_proto_not_avail(resp, P2P_SERV_BONJOUR,
-					    srv_trans_id);
-		return;
-	}
-
-	if (query_len == 0) {
-		wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
-		return;
-	}
-
-	dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
-			 struct p2p_srv_bonjour, list) {
-		if (!match_bonjour_query(bsrv, query, query_len))
-			continue;
-
-		if (wpabuf_tailroom(resp) <
-		    5 + query_len + wpabuf_len(bsrv->resp))
-			return;
-
-		matches++;
-
-		/* Length (to be filled) */
-		len_pos = wpabuf_put(resp, 2);
-		wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
-		wpabuf_put_u8(resp, srv_trans_id);
-
-		/* Status Code */
-		wpabuf_put_u8(resp, P2P_SD_SUCCESS);
-		wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
-				  wpabuf_head(bsrv->resp),
-				  wpabuf_len(bsrv->resp));
-
-		/* Response Data */
-		wpabuf_put_data(resp, query, query_len); /* Key */
-		wpabuf_put_buf(resp, bsrv->resp); /* Value */
-
-		WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
-	}
-
-	if (matches == 0) {
-		wpa_printf(MSG_DEBUG, "P2P: Requested Bonjour service not "
-			   "available");
-		if (wpabuf_tailroom(resp) < 5)
-			return;
-
-		/* Length (to be filled) */
-		len_pos = wpabuf_put(resp, 2);
-		wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
-		wpabuf_put_u8(resp, srv_trans_id);
-
-		/* Status Code */
-		wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
-		/* Response Data: empty */
-		WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
-			     2);
-	}
-}
-
-
-static void wpas_sd_all_upnp(struct wpa_supplicant *wpa_s,
-			     struct wpabuf *resp, u8 srv_trans_id)
-{
-	struct p2p_srv_upnp *usrv;
-	u8 *len_pos;
-
-	wpa_printf(MSG_DEBUG, "P2P: SD Request for all UPnP services");
-
-	if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
-		wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
-		return;
-	}
-
-	dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
-			 struct p2p_srv_upnp, list) {
-		if (wpabuf_tailroom(resp) < 5 + 1 + os_strlen(usrv->service))
-			return;
-
-		/* Length (to be filled) */
-		len_pos = wpabuf_put(resp, 2);
-		wpabuf_put_u8(resp, P2P_SERV_UPNP);
-		wpabuf_put_u8(resp, srv_trans_id);
-
-		/* Status Code */
-		wpabuf_put_u8(resp, P2P_SD_SUCCESS);
-		/* Response Data */
-		wpabuf_put_u8(resp, usrv->version);
-		wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
-			   usrv->service);
-		wpabuf_put_str(resp, usrv->service);
-		WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
-			     2);
-	}
-}
-
-
-static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s,
-			     struct wpabuf *resp, u8 srv_trans_id,
-			     const u8 *query, size_t query_len)
-{
-	struct p2p_srv_upnp *usrv;
-	u8 *len_pos;
-	u8 version;
-	char *str;
-	int count = 0;
-
-	wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for UPnP",
-			  query, query_len);
-
-	if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
-		wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
-		wpas_sd_add_proto_not_avail(resp, P2P_SERV_UPNP,
-					    srv_trans_id);
-		return;
-	}
-
-	if (query_len == 0) {
-		wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
-		return;
-	}
-
-	if (wpabuf_tailroom(resp) < 5)
-		return;
-
-	/* Length (to be filled) */
-	len_pos = wpabuf_put(resp, 2);
-	wpabuf_put_u8(resp, P2P_SERV_UPNP);
-	wpabuf_put_u8(resp, srv_trans_id);
-
-	version = query[0];
-	str = os_malloc(query_len);
-	if (str == NULL)
-		return;
-	os_memcpy(str, query + 1, query_len - 1);
-	str[query_len - 1] = '\0';
-
-	dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
-			 struct p2p_srv_upnp, list) {
-		if (version != usrv->version)
-			continue;
-
-		if (os_strcmp(str, "ssdp:all") != 0 &&
-		    os_strstr(usrv->service, str) == NULL)
-			continue;
-
-		if (wpabuf_tailroom(resp) < 2)
-			break;
-		if (count == 0) {
-			/* Status Code */
-			wpabuf_put_u8(resp, P2P_SD_SUCCESS);
-			/* Response Data */
-			wpabuf_put_u8(resp, version);
-		} else
-			wpabuf_put_u8(resp, ',');
-
-		count++;
-
-		wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
-			   usrv->service);
-		if (wpabuf_tailroom(resp) < os_strlen(usrv->service))
-			break;
-		wpabuf_put_str(resp, usrv->service);
-	}
-	os_free(str);
-
-	if (count == 0) {
-		wpa_printf(MSG_DEBUG, "P2P: Requested UPnP service not "
-			   "available");
-		/* Status Code */
-		wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
-		/* Response Data: empty */
-	}
-
-	WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
-}
-
-
-#ifdef CONFIG_WIFI_DISPLAY
-static void wpas_sd_req_wfd(struct wpa_supplicant *wpa_s,
-			    struct wpabuf *resp, u8 srv_trans_id,
-			    const u8 *query, size_t query_len)
-{
-	const u8 *pos;
-	u8 role;
-	u8 *len_pos;
-
-	wpa_hexdump(MSG_DEBUG, "P2P: SD Request for WFD", query, query_len);
-
-	if (!wpa_s->global->wifi_display) {
-		wpa_printf(MSG_DEBUG, "P2P: WFD protocol not available");
-		wpas_sd_add_proto_not_avail(resp, P2P_SERV_WIFI_DISPLAY,
-					    srv_trans_id);
-		return;
-	}
-
-	if (query_len < 1) {
-		wpa_printf(MSG_DEBUG, "P2P: Missing WFD Requested Device "
-			   "Role");
-		return;
-	}
-
-	if (wpabuf_tailroom(resp) < 5)
-		return;
-
-	pos = query;
-	role = *pos++;
-	wpa_printf(MSG_DEBUG, "P2P: WSD for device role 0x%x", role);
-
-	/* TODO: role specific handling */
-
-	/* Length (to be filled) */
-	len_pos = wpabuf_put(resp, 2);
-	wpabuf_put_u8(resp, P2P_SERV_WIFI_DISPLAY);
-	wpabuf_put_u8(resp, srv_trans_id);
-	wpabuf_put_u8(resp, P2P_SD_SUCCESS); /* Status Code */
-
-	while (pos < query + query_len) {
-		if (*pos < MAX_WFD_SUBELEMS &&
-		    wpa_s->global->wfd_subelem[*pos] &&
-		    wpabuf_tailroom(resp) >=
-		    wpabuf_len(wpa_s->global->wfd_subelem[*pos])) {
-			wpa_printf(MSG_DEBUG, "P2P: Add WSD response "
-				   "subelement %u", *pos);
-			wpabuf_put_buf(resp, wpa_s->global->wfd_subelem[*pos]);
-		}
-		pos++;
-	}
-
-	WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
-}
-#endif /* CONFIG_WIFI_DISPLAY */
-
-
-static int find_p2ps_substr(struct p2ps_advertisement *adv_data,
-			    const u8 *needle, size_t needle_len)
-{
-	const u8 *haystack = (const u8 *) adv_data->svc_info;
-	size_t haystack_len, i;
-
-	/* Allow search term to be empty */
-	if (!needle || !needle_len)
-		return 1;
-
-	if (!haystack)
-		return 0;
-
-	haystack_len = os_strlen(adv_data->svc_info);
-	for (i = 0; i < haystack_len; i++) {
-		if (haystack_len - i < needle_len)
-			break;
-		if (os_memcmp(haystack + i, needle, needle_len) == 0)
-			return 1;
-	}
-
-	return 0;
-}
-
-
-static void wpas_sd_req_asp(struct wpa_supplicant *wpa_s,
-			    struct wpabuf *resp, u8 srv_trans_id,
-			    const u8 *query, size_t query_len)
-{
-	struct p2ps_advertisement *adv_data;
-	const u8 *svc = &query[1];
-	const u8 *info = NULL;
-	size_t svc_len = query[0];
-	size_t info_len = 0;
-	int prefix = 0;
-	u8 *count_pos = NULL;
-	u8 *len_pos = NULL;
-
-	wpa_hexdump(MSG_DEBUG, "P2P: SD Request for ASP", query, query_len);
-
-	if (!wpa_s->global->p2p) {
-		wpa_printf(MSG_DEBUG, "P2P: ASP protocol not available");
-		wpas_sd_add_proto_not_avail(resp, P2P_SERV_P2PS, srv_trans_id);
-		return;
-	}
-
-	/* Info block is optional */
-	if (svc_len + 1 < query_len) {
-		info = &svc[svc_len];
-		info_len = *info++;
-	}
-
-	/* Range check length of svc string and info block */
-	if (svc_len + (info_len ? info_len + 2 : 1) > query_len) {
-		wpa_printf(MSG_DEBUG, "P2P: ASP bad request");
-		wpas_sd_add_bad_request(resp, P2P_SERV_P2PS, srv_trans_id);
-		return;
-	}
-
-	/* Detect and correct for prefix search */
-	if (svc_len && svc[svc_len - 1] == '*') {
-		prefix = 1;
-		svc_len--;
-	}
-
-	for (adv_data = p2p_get_p2ps_adv_list(wpa_s->global->p2p);
-	     adv_data; adv_data = adv_data->next) {
-		/* If not a prefix match, reject length mismatches */
-		if (!prefix && svc_len != os_strlen(adv_data->svc_name))
-			continue;
-
-		/* Search each service for request */
-		if (os_memcmp(adv_data->svc_name, svc, svc_len) == 0 &&
-		    find_p2ps_substr(adv_data, info, info_len)) {
-			size_t len = os_strlen(adv_data->svc_name);
-			size_t svc_info_len = 0;
-
-			if (adv_data->svc_info)
-				svc_info_len = os_strlen(adv_data->svc_info);
-
-			if (len > 0xff || svc_info_len > 0xffff)
-				return;
-
-			/* Length & Count to be filled as we go */
-			if (!len_pos && !count_pos) {
-				if (wpabuf_tailroom(resp) <
-				    len + svc_info_len + 16)
-					return;
-
-				len_pos = wpabuf_put(resp, 2);
-				wpabuf_put_u8(resp, P2P_SERV_P2PS);
-				wpabuf_put_u8(resp, srv_trans_id);
-				/* Status Code */
-				wpabuf_put_u8(resp, P2P_SD_SUCCESS);
-				count_pos = wpabuf_put(resp, 1);
-				*count_pos = 0;
-			} else if (wpabuf_tailroom(resp) <
-				   len + svc_info_len + 10)
-				return;
-
-			if (svc_info_len) {
-				wpa_printf(MSG_DEBUG,
-					   "P2P: Add Svc: %s info: %s",
-					   adv_data->svc_name,
-					   adv_data->svc_info);
-			} else {
-				wpa_printf(MSG_DEBUG, "P2P: Add Svc: %s",
-					   adv_data->svc_name);
-			}
-
-			/* Advertisement ID */
-			wpabuf_put_le32(resp, adv_data->id);
-
-			/* Config Methods */
-			wpabuf_put_be16(resp, adv_data->config_methods);
-
-			/* Service Name */
-			wpabuf_put_u8(resp, (u8) len);
-			wpabuf_put_data(resp, adv_data->svc_name, len);
-
-			/* Service State */
-			wpabuf_put_u8(resp, adv_data->state);
-
-			/* Service Information */
-			wpabuf_put_le16(resp, (u16) svc_info_len);
-			wpabuf_put_data(resp, adv_data->svc_info, svc_info_len);
-
-			/* Update length and count */
-			(*count_pos)++;
-			WPA_PUT_LE16(len_pos,
-				     (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
-		}
-	}
-
-	/* Return error if no matching svc found */
-	if (count_pos == NULL) {
-		wpa_printf(MSG_DEBUG, "P2P: ASP service not found");
-		wpas_sd_add_not_found(resp, P2P_SERV_P2PS, srv_trans_id);
-	}
-}
-
-
-static void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
-			    u16 update_indic, const u8 *tlvs, size_t tlvs_len)
-{
-	struct wpa_supplicant *wpa_s = ctx;
-	const u8 *pos = tlvs;
-	const u8 *end = tlvs + tlvs_len;
-	const u8 *tlv_end;
-	u16 slen;
-	struct wpabuf *resp;
-	u8 srv_proto, srv_trans_id;
-	size_t buf_len;
-	char *buf;
-
-	wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Request TLVs",
-		    tlvs, tlvs_len);
-	buf_len = 2 * tlvs_len + 1;
-	buf = os_malloc(buf_len);
-	if (buf) {
-		wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
-		wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_REQ "%d "
-			     MACSTR " %u %u %s",
-			     freq, MAC2STR(sa), dialog_token, update_indic,
-			     buf);
-		os_free(buf);
-	}
-
-	if (wpa_s->p2p_sd_over_ctrl_iface) {
-		wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
-					   update_indic, tlvs, tlvs_len);
-		return; /* to be processed by an external program */
-	}
-
-	resp = wpabuf_alloc(10000);
-	if (resp == NULL)
-		return;
-
-	while (pos + 1 < end) {
-		wpa_printf(MSG_DEBUG, "P2P: Service Request TLV");
-		slen = WPA_GET_LE16(pos);
-		pos += 2;
-		if (pos + slen > end || slen < 2) {
-			wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data "
-				   "length");
-			wpabuf_free(resp);
-			return;
-		}
-		tlv_end = pos + slen;
-
-		srv_proto = *pos++;
-		wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
-			   srv_proto);
-		srv_trans_id = *pos++;
-		wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
-			   srv_trans_id);
-
-		wpa_hexdump(MSG_MSGDUMP, "P2P: Query Data",
-			    pos, tlv_end - pos);
-
-
-		if (wpa_s->force_long_sd) {
-			wpa_printf(MSG_DEBUG, "P2P: SD test - force long "
-				   "response");
-			wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
-			wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
-			goto done;
-		}
-
-		switch (srv_proto) {
-		case P2P_SERV_ALL_SERVICES:
-			wpa_printf(MSG_DEBUG, "P2P: Service Discovery Request "
-				   "for all services");
-			if (dl_list_empty(&wpa_s->global->p2p_srv_upnp) &&
-			    dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
-				wpa_printf(MSG_DEBUG, "P2P: No service "
-					   "discovery protocols available");
-				wpas_sd_add_proto_not_avail(
-					resp, P2P_SERV_ALL_SERVICES,
-					srv_trans_id);
-				break;
-			}
-			wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
-			wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
-			break;
-		case P2P_SERV_BONJOUR:
-			wpas_sd_req_bonjour(wpa_s, resp, srv_trans_id,
-					    pos, tlv_end - pos);
-			break;
-		case P2P_SERV_UPNP:
-			wpas_sd_req_upnp(wpa_s, resp, srv_trans_id,
-					 pos, tlv_end - pos);
-			break;
-#ifdef CONFIG_WIFI_DISPLAY
-		case P2P_SERV_WIFI_DISPLAY:
-			wpas_sd_req_wfd(wpa_s, resp, srv_trans_id,
-					pos, tlv_end - pos);
-			break;
-#endif /* CONFIG_WIFI_DISPLAY */
-		case P2P_SERV_P2PS:
-			wpas_sd_req_asp(wpa_s, resp, srv_trans_id,
-					pos, tlv_end - pos);
-			break;
-		default:
-			wpa_printf(MSG_DEBUG, "P2P: Unavailable service "
-				   "protocol %u", srv_proto);
-			wpas_sd_add_proto_not_avail(resp, srv_proto,
-						    srv_trans_id);
-			break;
-		}
-
-		pos = tlv_end;
-	}
-
-done:
-	wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
-				   update_indic, tlvs, tlvs_len);
-
-	wpas_p2p_sd_response(wpa_s, freq, sa, dialog_token, resp);
-
-	wpabuf_free(resp);
-}
-
-
-static void wpas_sd_p2ps_serv_response(struct wpa_supplicant *wpa_s,
-				       const u8 *sa, u8 srv_trans_id,
-				       const u8 *pos, const u8 *tlv_end)
-{
-	u8 left = *pos++;
-	u32 adv_id;
-	u8 svc_status;
-	u16 config_methods;
-	char svc_str[256];
-
-	while (left-- && pos < tlv_end) {
-		char *buf = NULL;
-		size_t buf_len;
-		u8 svc_len;
-
-		/* Sanity check fixed length+svc_str */
-		if (pos + 6 >= tlv_end)
-			break;
-		svc_len = pos[6];
-		if (pos + svc_len + 10 > tlv_end)
-			break;
-
-		/* Advertisement ID */
-		adv_id = WPA_GET_LE32(pos);
-		pos += sizeof(u32);
-
-		/* Config Methods */
-		config_methods = WPA_GET_BE16(pos);
-		pos += sizeof(u16);
-
-		/* Service Name */
-		pos++; /* svc_len */
-		os_memcpy(svc_str, pos, svc_len);
-		svc_str[svc_len] = '\0';
-		pos += svc_len;
-
-		/* Service Status */
-		svc_status = *pos++;
-
-		/* Service Information Length */
-		buf_len = WPA_GET_LE16(pos);
-		pos += sizeof(u16);
-
-		/* Sanity check buffer length */
-		if (buf_len > (unsigned int) (tlv_end - pos))
-			break;
-
-		if (buf_len) {
-			buf = os_zalloc(2 * buf_len + 1);
-			if (buf) {
-				utf8_escape((const char *) pos, buf_len, buf,
-					    2 * buf_len + 1);
-			}
-		}
-
-		pos += buf_len;
-
-		if (buf) {
-			wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP
-				       MACSTR " %x %x %x %x %s '%s'",
-				       MAC2STR(sa), srv_trans_id, adv_id,
-				       svc_status, config_methods, svc_str,
-				       buf);
-			os_free(buf);
-		} else {
-			wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP
-				       MACSTR " %x %x %x %x %s",
-				       MAC2STR(sa), srv_trans_id, adv_id,
-				       svc_status, config_methods, svc_str);
-		}
-	}
-}
-
-
-static void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
-			     const u8 *tlvs, size_t tlvs_len)
-{
-	struct wpa_supplicant *wpa_s = ctx;
-	const u8 *pos = tlvs;
-	const u8 *end = tlvs + tlvs_len;
-	const u8 *tlv_end;
-	u16 slen;
-	size_t buf_len;
-	char *buf;
-
-	wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Response TLVs",
-		    tlvs, tlvs_len);
-	if (tlvs_len > 1500) {
-		/* TODO: better way for handling this */
-		wpa_msg_ctrl(wpa_s, MSG_INFO,
-			     P2P_EVENT_SERV_DISC_RESP MACSTR
-			     " %u <long response: %u bytes>",
-			     MAC2STR(sa), update_indic,
-			     (unsigned int) tlvs_len);
-	} else {
-		buf_len = 2 * tlvs_len + 1;
-		buf = os_malloc(buf_len);
-		if (buf) {
-			wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
-			wpa_msg_ctrl(wpa_s, MSG_INFO,
-				     P2P_EVENT_SERV_DISC_RESP MACSTR " %u %s",
-				     MAC2STR(sa), update_indic, buf);
-			os_free(buf);
-		}
-	}
-
-	while (pos < end) {
-		u8 srv_proto, srv_trans_id, status;
-
-		wpa_printf(MSG_DEBUG, "P2P: Service Response TLV");
-		slen = WPA_GET_LE16(pos);
-		pos += 2;
-		if (pos + slen > end || slen < 3) {
-			wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data "
-				   "length");
-			return;
-		}
-		tlv_end = pos + slen;
-
-		srv_proto = *pos++;
-		wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
-			   srv_proto);
-		srv_trans_id = *pos++;
-		wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
-			   srv_trans_id);
-		status = *pos++;
-		wpa_printf(MSG_DEBUG, "P2P: Status Code ID %u",
-			   status);
-
-		wpa_hexdump(MSG_MSGDUMP, "P2P: Response Data",
-			    pos, tlv_end - pos);
-
-		if (srv_proto == P2P_SERV_P2PS && pos < tlv_end) {
-			wpas_sd_p2ps_serv_response(wpa_s, sa, srv_trans_id,
-						   pos, tlv_end);
-		}
-
-		pos = tlv_end;
-	}
-
-	wpas_notify_p2p_sd_response(wpa_s, sa, update_indic, tlvs, tlvs_len);
-}
-
-
-u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
-			const struct wpabuf *tlvs)
-{
-	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
-		return 0;
-	return (uintptr_t) p2p_sd_request(wpa_s->global->p2p, dst, tlvs);
-}
-
-
-u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
-			     u8 version, const char *query)
-{
-	struct wpabuf *tlvs;
-	u64 ret;
-
-	tlvs = wpabuf_alloc(2 + 1 + 1 + 1 + os_strlen(query));
-	if (tlvs == NULL)
-		return 0;
-	wpabuf_put_le16(tlvs, 1 + 1 + 1 + os_strlen(query));
-	wpabuf_put_u8(tlvs, P2P_SERV_UPNP); /* Service Protocol Type */
-	wpabuf_put_u8(tlvs, 1); /* Service Transaction ID */
-	wpabuf_put_u8(tlvs, version);
-	wpabuf_put_str(tlvs, query);
-	ret = wpas_p2p_sd_request(wpa_s, dst, tlvs);
-	wpabuf_free(tlvs);
-	return ret;
-}
-
-
-u64 wpas_p2p_sd_request_asp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 id,
-			    const char *svc_str, const char *info_substr)
-{
-	struct wpabuf *tlvs;
-	size_t plen, svc_len, substr_len = 0;
-	u64 ret;
-
-	svc_len = os_strlen(svc_str);
-	if (info_substr)
-		substr_len = os_strlen(info_substr);
-
-	if (svc_len > 0xff || substr_len > 0xff)
-		return 0;
-
-	plen = 1 + 1 + 1 + svc_len + 1 + substr_len;
-	tlvs = wpabuf_alloc(2 + plen);
-	if (tlvs == NULL)
-		return 0;
-
-	wpabuf_put_le16(tlvs, plen);
-	wpabuf_put_u8(tlvs, P2P_SERV_P2PS);
-	wpabuf_put_u8(tlvs, id); /* Service Transaction ID */
-	wpabuf_put_u8(tlvs, (u8) svc_len); /* Service String Length */
-	wpabuf_put_data(tlvs, svc_str, svc_len);
-	wpabuf_put_u8(tlvs, (u8) substr_len); /* Info Substring Length */
-	wpabuf_put_data(tlvs, info_substr, substr_len);
-	ret = wpas_p2p_sd_request(wpa_s, dst, tlvs);
-	wpabuf_free(tlvs);
-
-	return ret;
-}
-
-
-#ifdef CONFIG_WIFI_DISPLAY
-
-static u64 wpas_p2p_sd_request_wfd(struct wpa_supplicant *wpa_s, const u8 *dst,
-				   const struct wpabuf *tlvs)
-{
-	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
-		return 0;
-	return (uintptr_t) p2p_sd_request_wfd(wpa_s->global->p2p, dst, tlvs);
-}
-
-
-#define MAX_WFD_SD_SUBELEMS 20
-
-static void wfd_add_sd_req_role(struct wpabuf *tlvs, u8 id, u8 role,
-				const char *subelems)
-{
-	u8 *len;
-	const char *pos;
-	int val;
-	int count = 0;
-
-	len = wpabuf_put(tlvs, 2);
-	wpabuf_put_u8(tlvs, P2P_SERV_WIFI_DISPLAY); /* Service Protocol Type */
-	wpabuf_put_u8(tlvs, id); /* Service Transaction ID */
-
-	wpabuf_put_u8(tlvs, role);
-
-	pos = subelems;
-	while (*pos) {
-		val = atoi(pos);
-		if (val >= 0 && val < 256) {
-			wpabuf_put_u8(tlvs, val);
-			count++;
-			if (count == MAX_WFD_SD_SUBELEMS)
-				break;
-		}
-		pos = os_strchr(pos + 1, ',');
-		if (pos == NULL)
-			break;
-		pos++;
-	}
-
-	WPA_PUT_LE16(len, (u8 *) wpabuf_put(tlvs, 0) - len - 2);
-}
-
-
-u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s,
-				     const u8 *dst, const char *role)
-{
-	struct wpabuf *tlvs;
-	u64 ret;
-	const char *subelems;
-	u8 id = 1;
-
-	subelems = os_strchr(role, ' ');
-	if (subelems == NULL)
-		return 0;
-	subelems++;
-
-	tlvs = wpabuf_alloc(4 * (2 + 1 + 1 + 1 + MAX_WFD_SD_SUBELEMS));
-	if (tlvs == NULL)
-		return 0;
-
-	if (os_strstr(role, "[source]"))
-		wfd_add_sd_req_role(tlvs, id++, 0x00, subelems);
-	if (os_strstr(role, "[pri-sink]"))
-		wfd_add_sd_req_role(tlvs, id++, 0x01, subelems);
-	if (os_strstr(role, "[sec-sink]"))
-		wfd_add_sd_req_role(tlvs, id++, 0x02, subelems);
-	if (os_strstr(role, "[source+sink]"))
-		wfd_add_sd_req_role(tlvs, id++, 0x03, subelems);
-
-	ret = wpas_p2p_sd_request_wfd(wpa_s, dst, tlvs);
-	wpabuf_free(tlvs);
-	return ret;
-}
-
-#endif /* CONFIG_WIFI_DISPLAY */
-
-
-int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req)
-{
-	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
-		return -1;
-	return p2p_sd_cancel_request(wpa_s->global->p2p,
-				     (void *) (uintptr_t) req);
-}
-
-
-void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq,
-			  const u8 *dst, u8 dialog_token,
-			  const struct wpabuf *resp_tlvs)
-{
-	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
-		return;
-	p2p_sd_response(wpa_s->global->p2p, freq, dst, dialog_token,
-			resp_tlvs);
-}
-
-
-void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s)
-{
-	if (wpa_s->global->p2p)
-		p2p_sd_service_update(wpa_s->global->p2p);
-}
-
-
-static void wpas_p2p_srv_bonjour_free(struct p2p_srv_bonjour *bsrv)
-{
-	dl_list_del(&bsrv->list);
-	wpabuf_free(bsrv->query);
-	wpabuf_free(bsrv->resp);
-	os_free(bsrv);
-}
-
-
-static void wpas_p2p_srv_upnp_free(struct p2p_srv_upnp *usrv)
-{
-	dl_list_del(&usrv->list);
-	os_free(usrv->service);
-	os_free(usrv);
-}
-
-
-void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s)
-{
-	struct p2p_srv_bonjour *bsrv, *bn;
-	struct p2p_srv_upnp *usrv, *un;
-
-	dl_list_for_each_safe(bsrv, bn, &wpa_s->global->p2p_srv_bonjour,
-			      struct p2p_srv_bonjour, list)
-		wpas_p2p_srv_bonjour_free(bsrv);
-
-	dl_list_for_each_safe(usrv, un, &wpa_s->global->p2p_srv_upnp,
-			      struct p2p_srv_upnp, list)
-		wpas_p2p_srv_upnp_free(usrv);
-
-	wpas_p2p_sd_service_update(wpa_s);
-}
-
-
-int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id)
-{
-	if (adv_id == 0)
-		return 1;
-
-	if (p2p_service_p2ps_id(wpa_s->global->p2p, adv_id))
-		return 1;
-
-	return 0;
-}
-
-
-int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id)
-{
-	return p2p_service_del_asp(wpa_s->global->p2p, adv_id);
-}
-
-
-int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s,
-			     int auto_accept, u32 adv_id,
-			     const char *adv_str, u8 svc_state,
-			     u16 config_methods, const char *svc_info)
-{
-	return p2p_service_add_asp(wpa_s->global->p2p, auto_accept, adv_id,
-				   adv_str, svc_state, config_methods,
-				   svc_info);
-}
-
-
-int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s,
-				 struct wpabuf *query, struct wpabuf *resp)
-{
-	struct p2p_srv_bonjour *bsrv;
-
-	bsrv = os_zalloc(sizeof(*bsrv));
-	if (bsrv == NULL)
-		return -1;
-	bsrv->query = query;
-	bsrv->resp = resp;
-	dl_list_add(&wpa_s->global->p2p_srv_bonjour, &bsrv->list);
-
-	wpas_p2p_sd_service_update(wpa_s);
-	return 0;
-}
-
-
-int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s,
-				 const struct wpabuf *query)
-{
-	struct p2p_srv_bonjour *bsrv;
-
-	bsrv = wpas_p2p_service_get_bonjour(wpa_s, query);
-	if (bsrv == NULL)
-		return -1;
-	wpas_p2p_srv_bonjour_free(bsrv);
-	wpas_p2p_sd_service_update(wpa_s);
-	return 0;
-}
-
-
-int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version,
-			      const char *service)
-{
-	struct p2p_srv_upnp *usrv;
-
-	if (wpas_p2p_service_get_upnp(wpa_s, version, service))
-		return 0; /* Already listed */
-	usrv = os_zalloc(sizeof(*usrv));
-	if (usrv == NULL)
-		return -1;
-	usrv->version = version;
-	usrv->service = os_strdup(service);
-	if (usrv->service == NULL) {
-		os_free(usrv);
-		return -1;
-	}
-	dl_list_add(&wpa_s->global->p2p_srv_upnp, &usrv->list);
-
-	wpas_p2p_sd_service_update(wpa_s);
-	return 0;
-}
-
-
-int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version,
-			      const char *service)
-{
-	struct p2p_srv_upnp *usrv;
-
-	usrv = wpas_p2p_service_get_upnp(wpa_s, version, service);
-	if (usrv == NULL)
-		return -1;
-	wpas_p2p_srv_upnp_free(usrv);
-	wpas_p2p_sd_service_update(wpa_s);
-	return 0;
-}
-
-
 static void wpas_prov_disc_local_display(struct wpa_supplicant *wpa_s,
 					 const u8 *peer, const char *params,
 					 unsigned int generated_pin)
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
index b786178..1d3c67b 100644
--- a/wpa_supplicant/p2p_supplicant.h
+++ b/wpa_supplicant/p2p_supplicant.h
@@ -94,6 +94,10 @@
 			     u16 config_methods, const char *svc_info);
 int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id);
 int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id);
+void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+		     u16 update_indic, const u8 *tlvs, size_t tlvs_len);
+void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
+		      const u8 *tlvs, size_t tlvs_len);
 int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr);
 int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 		    struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
diff --git a/wpa_supplicant/p2p_supplicant_sd.c b/wpa_supplicant/p2p_supplicant_sd.c
new file mode 100644
index 0000000..cb68c03
--- /dev/null
+++ b/wpa_supplicant/p2p_supplicant_sd.c
@@ -0,0 +1,1237 @@
+/*
+ * wpa_supplicant - P2P service discovery
+ * Copyright (c) 2009-2010, Atheros Communications
+ * Copyright (c) 2010-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "p2p/p2p.h"
+#include "wpa_supplicant_i.h"
+#include "notify.h"
+#include "p2p_supplicant.h"
+
+
+/*
+ * DNS Header section is used only to calculate compression pointers, so the
+ * contents of this data does not matter, but the length needs to be reserved
+ * in the virtual packet.
+ */
+#define DNS_HEADER_LEN 12
+
+/*
+ * 27-octet in-memory packet from P2P specification containing two implied
+ * queries for _tcp.lcoal. PTR IN and _udp.local. PTR IN
+ */
+#define P2P_SD_IN_MEMORY_LEN 27
+
+static int p2p_sd_dns_uncompress_label(char **upos, char *uend, u8 *start,
+				       u8 **spos, const u8 *end)
+{
+	while (*spos < end) {
+		u8 val = ((*spos)[0] & 0xc0) >> 6;
+		int len;
+
+		if (val == 1 || val == 2) {
+			/* These are reserved values in RFC 1035 */
+			wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
+				   "sequence starting with 0x%x", val);
+			return -1;
+		}
+
+		if (val == 3) {
+			u16 offset;
+			u8 *spos_tmp;
+
+			/* Offset */
+			if (*spos + 2 > end) {
+				wpa_printf(MSG_DEBUG, "P2P: No room for full "
+					   "DNS offset field");
+				return -1;
+			}
+
+			offset = (((*spos)[0] & 0x3f) << 8) | (*spos)[1];
+			if (offset >= *spos - start) {
+				wpa_printf(MSG_DEBUG, "P2P: Invalid DNS "
+					   "pointer offset %u", offset);
+				return -1;
+			}
+
+			(*spos) += 2;
+			spos_tmp = start + offset;
+			return p2p_sd_dns_uncompress_label(upos, uend, start,
+							   &spos_tmp,
+							   *spos - 2);
+		}
+
+		/* Label */
+		len = (*spos)[0] & 0x3f;
+		if (len == 0)
+			return 0;
+
+		(*spos)++;
+		if (*spos + len > end) {
+			wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
+				   "sequence - no room for label with length "
+				   "%u", len);
+			return -1;
+		}
+
+		if (*upos + len + 2 > uend)
+			return -2;
+
+		os_memcpy(*upos, *spos, len);
+		*spos += len;
+		*upos += len;
+		(*upos)[0] = '.';
+		(*upos)++;
+		(*upos)[0] = '\0';
+	}
+
+	return 0;
+}
+
+
+/* Uncompress domain names per RFC 1035 using the P2P SD in-memory packet.
+ * Returns -1 on parsing error (invalid input sequence), -2 if output buffer is
+ * not large enough */
+static int p2p_sd_dns_uncompress(char *buf, size_t buf_len, const u8 *msg,
+				 size_t msg_len, size_t offset)
+{
+	/* 27-octet in-memory packet from P2P specification */
+	const char *prefix = "\x04_tcp\x05local\x00\x00\x0C\x00\x01"
+		"\x04_udp\xC0\x11\x00\x0C\x00\x01";
+	u8 *tmp, *end, *spos;
+	char *upos, *uend;
+	int ret = 0;
+
+	if (buf_len < 2)
+		return -1;
+	if (offset > msg_len)
+		return -1;
+
+	tmp = os_malloc(DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN + msg_len);
+	if (tmp == NULL)
+		return -1;
+	spos = tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN;
+	end = spos + msg_len;
+	spos += offset;
+
+	os_memset(tmp, 0, DNS_HEADER_LEN);
+	os_memcpy(tmp + DNS_HEADER_LEN, prefix, P2P_SD_IN_MEMORY_LEN);
+	os_memcpy(tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN, msg, msg_len);
+
+	upos = buf;
+	uend = buf + buf_len;
+
+	ret = p2p_sd_dns_uncompress_label(&upos, uend, tmp, &spos, end);
+	if (ret) {
+		os_free(tmp);
+		return ret;
+	}
+
+	if (upos == buf) {
+		upos[0] = '.';
+		upos[1] = '\0';
+	} else if (upos[-1] == '.')
+		upos[-1] = '\0';
+
+	os_free(tmp);
+	return 0;
+}
+
+
+static struct p2p_srv_bonjour *
+wpas_p2p_service_get_bonjour(struct wpa_supplicant *wpa_s,
+			     const struct wpabuf *query)
+{
+	struct p2p_srv_bonjour *bsrv;
+	size_t len;
+
+	len = wpabuf_len(query);
+	dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
+			 struct p2p_srv_bonjour, list) {
+		if (len == wpabuf_len(bsrv->query) &&
+		    os_memcmp(wpabuf_head(query), wpabuf_head(bsrv->query),
+			      len) == 0)
+			return bsrv;
+	}
+	return NULL;
+}
+
+
+static struct p2p_srv_upnp *
+wpas_p2p_service_get_upnp(struct wpa_supplicant *wpa_s, u8 version,
+			  const char *service)
+{
+	struct p2p_srv_upnp *usrv;
+
+	dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
+			 struct p2p_srv_upnp, list) {
+		if (version == usrv->version &&
+		    os_strcmp(service, usrv->service) == 0)
+			return usrv;
+	}
+	return NULL;
+}
+
+
+static void wpas_sd_add_empty(struct wpabuf *resp, u8 srv_proto,
+			      u8 srv_trans_id, u8 status)
+{
+	u8 *len_pos;
+
+	if (wpabuf_tailroom(resp) < 5)
+		return;
+
+	/* Length (to be filled) */
+	len_pos = wpabuf_put(resp, 2);
+	wpabuf_put_u8(resp, srv_proto);
+	wpabuf_put_u8(resp, srv_trans_id);
+	/* Status Code */
+	wpabuf_put_u8(resp, status);
+	/* Response Data: empty */
+	WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+}
+
+
+static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto,
+					u8 srv_trans_id)
+{
+	wpas_sd_add_empty(resp, srv_proto, srv_trans_id,
+			  P2P_SD_PROTO_NOT_AVAILABLE);
+}
+
+
+static void wpas_sd_add_bad_request(struct wpabuf *resp, u8 srv_proto,
+				    u8 srv_trans_id)
+{
+	wpas_sd_add_empty(resp, srv_proto, srv_trans_id, P2P_SD_BAD_REQUEST);
+}
+
+
+static void wpas_sd_add_not_found(struct wpabuf *resp, u8 srv_proto,
+				  u8 srv_trans_id)
+{
+	wpas_sd_add_empty(resp, srv_proto, srv_trans_id,
+			  P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
+}
+
+
+static void wpas_sd_all_bonjour(struct wpa_supplicant *wpa_s,
+				struct wpabuf *resp, u8 srv_trans_id)
+{
+	struct p2p_srv_bonjour *bsrv;
+	u8 *len_pos;
+
+	wpa_printf(MSG_DEBUG, "P2P: SD Request for all Bonjour services");
+
+	if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
+		wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
+		return;
+	}
+
+	dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
+			 struct p2p_srv_bonjour, list) {
+		if (wpabuf_tailroom(resp) <
+		    5 + wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp))
+			return;
+		/* Length (to be filled) */
+		len_pos = wpabuf_put(resp, 2);
+		wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
+		wpabuf_put_u8(resp, srv_trans_id);
+		/* Status Code */
+		wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+		wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
+				  wpabuf_head(bsrv->resp),
+				  wpabuf_len(bsrv->resp));
+		/* Response Data */
+		wpabuf_put_buf(resp, bsrv->query); /* Key */
+		wpabuf_put_buf(resp, bsrv->resp); /* Value */
+		WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
+			     2);
+	}
+}
+
+
+static int match_bonjour_query(struct p2p_srv_bonjour *bsrv, const u8 *query,
+			       size_t query_len)
+{
+	char str_rx[256], str_srv[256];
+
+	if (query_len < 3 || wpabuf_len(bsrv->query) < 3)
+		return 0; /* Too short to include DNS Type and Version */
+	if (os_memcmp(query + query_len - 3,
+		      wpabuf_head_u8(bsrv->query) + wpabuf_len(bsrv->query) - 3,
+		      3) != 0)
+		return 0; /* Mismatch in DNS Type or Version */
+	if (query_len == wpabuf_len(bsrv->query) &&
+	    os_memcmp(query, wpabuf_head(bsrv->query), query_len - 3) == 0)
+		return 1; /* Binary match */
+
+	if (p2p_sd_dns_uncompress(str_rx, sizeof(str_rx), query, query_len - 3,
+				  0))
+		return 0; /* Failed to uncompress query */
+	if (p2p_sd_dns_uncompress(str_srv, sizeof(str_srv),
+				  wpabuf_head(bsrv->query),
+				  wpabuf_len(bsrv->query) - 3, 0))
+		return 0; /* Failed to uncompress service */
+
+	return os_strcmp(str_rx, str_srv) == 0;
+}
+
+
+static void wpas_sd_req_bonjour(struct wpa_supplicant *wpa_s,
+				struct wpabuf *resp, u8 srv_trans_id,
+				const u8 *query, size_t query_len)
+{
+	struct p2p_srv_bonjour *bsrv;
+	u8 *len_pos;
+	int matches = 0;
+
+	wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for Bonjour",
+			  query, query_len);
+	if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
+		wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
+		wpas_sd_add_proto_not_avail(resp, P2P_SERV_BONJOUR,
+					    srv_trans_id);
+		return;
+	}
+
+	if (query_len == 0) {
+		wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
+		return;
+	}
+
+	dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
+			 struct p2p_srv_bonjour, list) {
+		if (!match_bonjour_query(bsrv, query, query_len))
+			continue;
+
+		if (wpabuf_tailroom(resp) <
+		    5 + query_len + wpabuf_len(bsrv->resp))
+			return;
+
+		matches++;
+
+		/* Length (to be filled) */
+		len_pos = wpabuf_put(resp, 2);
+		wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
+		wpabuf_put_u8(resp, srv_trans_id);
+
+		/* Status Code */
+		wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+		wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
+				  wpabuf_head(bsrv->resp),
+				  wpabuf_len(bsrv->resp));
+
+		/* Response Data */
+		wpabuf_put_data(resp, query, query_len); /* Key */
+		wpabuf_put_buf(resp, bsrv->resp); /* Value */
+
+		WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+	}
+
+	if (matches == 0) {
+		wpa_printf(MSG_DEBUG, "P2P: Requested Bonjour service not "
+			   "available");
+		if (wpabuf_tailroom(resp) < 5)
+			return;
+
+		/* Length (to be filled) */
+		len_pos = wpabuf_put(resp, 2);
+		wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
+		wpabuf_put_u8(resp, srv_trans_id);
+
+		/* Status Code */
+		wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
+		/* Response Data: empty */
+		WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
+			     2);
+	}
+}
+
+
+static void wpas_sd_all_upnp(struct wpa_supplicant *wpa_s,
+			     struct wpabuf *resp, u8 srv_trans_id)
+{
+	struct p2p_srv_upnp *usrv;
+	u8 *len_pos;
+
+	wpa_printf(MSG_DEBUG, "P2P: SD Request for all UPnP services");
+
+	if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
+		wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
+		return;
+	}
+
+	dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
+			 struct p2p_srv_upnp, list) {
+		if (wpabuf_tailroom(resp) < 5 + 1 + os_strlen(usrv->service))
+			return;
+
+		/* Length (to be filled) */
+		len_pos = wpabuf_put(resp, 2);
+		wpabuf_put_u8(resp, P2P_SERV_UPNP);
+		wpabuf_put_u8(resp, srv_trans_id);
+
+		/* Status Code */
+		wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+		/* Response Data */
+		wpabuf_put_u8(resp, usrv->version);
+		wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
+			   usrv->service);
+		wpabuf_put_str(resp, usrv->service);
+		WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
+			     2);
+	}
+}
+
+
+static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s,
+			     struct wpabuf *resp, u8 srv_trans_id,
+			     const u8 *query, size_t query_len)
+{
+	struct p2p_srv_upnp *usrv;
+	u8 *len_pos;
+	u8 version;
+	char *str;
+	int count = 0;
+
+	wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for UPnP",
+			  query, query_len);
+
+	if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
+		wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
+		wpas_sd_add_proto_not_avail(resp, P2P_SERV_UPNP,
+					    srv_trans_id);
+		return;
+	}
+
+	if (query_len == 0) {
+		wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
+		return;
+	}
+
+	if (wpabuf_tailroom(resp) < 5)
+		return;
+
+	/* Length (to be filled) */
+	len_pos = wpabuf_put(resp, 2);
+	wpabuf_put_u8(resp, P2P_SERV_UPNP);
+	wpabuf_put_u8(resp, srv_trans_id);
+
+	version = query[0];
+	str = os_malloc(query_len);
+	if (str == NULL)
+		return;
+	os_memcpy(str, query + 1, query_len - 1);
+	str[query_len - 1] = '\0';
+
+	dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
+			 struct p2p_srv_upnp, list) {
+		if (version != usrv->version)
+			continue;
+
+		if (os_strcmp(str, "ssdp:all") != 0 &&
+		    os_strstr(usrv->service, str) == NULL)
+			continue;
+
+		if (wpabuf_tailroom(resp) < 2)
+			break;
+		if (count == 0) {
+			/* Status Code */
+			wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+			/* Response Data */
+			wpabuf_put_u8(resp, version);
+		} else
+			wpabuf_put_u8(resp, ',');
+
+		count++;
+
+		wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
+			   usrv->service);
+		if (wpabuf_tailroom(resp) < os_strlen(usrv->service))
+			break;
+		wpabuf_put_str(resp, usrv->service);
+	}
+	os_free(str);
+
+	if (count == 0) {
+		wpa_printf(MSG_DEBUG, "P2P: Requested UPnP service not "
+			   "available");
+		/* Status Code */
+		wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
+		/* Response Data: empty */
+	}
+
+	WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+}
+
+
+#ifdef CONFIG_WIFI_DISPLAY
+static void wpas_sd_req_wfd(struct wpa_supplicant *wpa_s,
+			    struct wpabuf *resp, u8 srv_trans_id,
+			    const u8 *query, size_t query_len)
+{
+	const u8 *pos;
+	u8 role;
+	u8 *len_pos;
+
+	wpa_hexdump(MSG_DEBUG, "P2P: SD Request for WFD", query, query_len);
+
+	if (!wpa_s->global->wifi_display) {
+		wpa_printf(MSG_DEBUG, "P2P: WFD protocol not available");
+		wpas_sd_add_proto_not_avail(resp, P2P_SERV_WIFI_DISPLAY,
+					    srv_trans_id);
+		return;
+	}
+
+	if (query_len < 1) {
+		wpa_printf(MSG_DEBUG, "P2P: Missing WFD Requested Device "
+			   "Role");
+		return;
+	}
+
+	if (wpabuf_tailroom(resp) < 5)
+		return;
+
+	pos = query;
+	role = *pos++;
+	wpa_printf(MSG_DEBUG, "P2P: WSD for device role 0x%x", role);
+
+	/* TODO: role specific handling */
+
+	/* Length (to be filled) */
+	len_pos = wpabuf_put(resp, 2);
+	wpabuf_put_u8(resp, P2P_SERV_WIFI_DISPLAY);
+	wpabuf_put_u8(resp, srv_trans_id);
+	wpabuf_put_u8(resp, P2P_SD_SUCCESS); /* Status Code */
+
+	while (pos < query + query_len) {
+		if (*pos < MAX_WFD_SUBELEMS &&
+		    wpa_s->global->wfd_subelem[*pos] &&
+		    wpabuf_tailroom(resp) >=
+		    wpabuf_len(wpa_s->global->wfd_subelem[*pos])) {
+			wpa_printf(MSG_DEBUG, "P2P: Add WSD response "
+				   "subelement %u", *pos);
+			wpabuf_put_buf(resp, wpa_s->global->wfd_subelem[*pos]);
+		}
+		pos++;
+	}
+
+	WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+}
+#endif /* CONFIG_WIFI_DISPLAY */
+
+
+static int find_p2ps_substr(struct p2ps_advertisement *adv_data,
+			    const u8 *needle, size_t needle_len)
+{
+	const u8 *haystack = (const u8 *) adv_data->svc_info;
+	size_t haystack_len, i;
+
+	/* Allow search term to be empty */
+	if (!needle || !needle_len)
+		return 1;
+
+	if (!haystack)
+		return 0;
+
+	haystack_len = os_strlen(adv_data->svc_info);
+	for (i = 0; i < haystack_len; i++) {
+		if (haystack_len - i < needle_len)
+			break;
+		if (os_memcmp(haystack + i, needle, needle_len) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static void wpas_sd_req_asp(struct wpa_supplicant *wpa_s,
+			    struct wpabuf *resp, u8 srv_trans_id,
+			    const u8 *query, size_t query_len)
+{
+	struct p2ps_advertisement *adv_data;
+	const u8 *svc = &query[1];
+	const u8 *info = NULL;
+	size_t svc_len = query[0];
+	size_t info_len = 0;
+	int prefix = 0;
+	u8 *count_pos = NULL;
+	u8 *len_pos = NULL;
+
+	wpa_hexdump(MSG_DEBUG, "P2P: SD Request for ASP", query, query_len);
+
+	if (!wpa_s->global->p2p) {
+		wpa_printf(MSG_DEBUG, "P2P: ASP protocol not available");
+		wpas_sd_add_proto_not_avail(resp, P2P_SERV_P2PS, srv_trans_id);
+		return;
+	}
+
+	/* Info block is optional */
+	if (svc_len + 1 < query_len) {
+		info = &svc[svc_len];
+		info_len = *info++;
+	}
+
+	/* Range check length of svc string and info block */
+	if (svc_len + (info_len ? info_len + 2 : 1) > query_len) {
+		wpa_printf(MSG_DEBUG, "P2P: ASP bad request");
+		wpas_sd_add_bad_request(resp, P2P_SERV_P2PS, srv_trans_id);
+		return;
+	}
+
+	/* Detect and correct for prefix search */
+	if (svc_len && svc[svc_len - 1] == '*') {
+		prefix = 1;
+		svc_len--;
+	}
+
+	for (adv_data = p2p_get_p2ps_adv_list(wpa_s->global->p2p);
+	     adv_data; adv_data = adv_data->next) {
+		/* If not a prefix match, reject length mismatches */
+		if (!prefix && svc_len != os_strlen(adv_data->svc_name))
+			continue;
+
+		/* Search each service for request */
+		if (os_memcmp(adv_data->svc_name, svc, svc_len) == 0 &&
+		    find_p2ps_substr(adv_data, info, info_len)) {
+			size_t len = os_strlen(adv_data->svc_name);
+			size_t svc_info_len = 0;
+
+			if (adv_data->svc_info)
+				svc_info_len = os_strlen(adv_data->svc_info);
+
+			if (len > 0xff || svc_info_len > 0xffff)
+				return;
+
+			/* Length & Count to be filled as we go */
+			if (!len_pos && !count_pos) {
+				if (wpabuf_tailroom(resp) <
+				    len + svc_info_len + 16)
+					return;
+
+				len_pos = wpabuf_put(resp, 2);
+				wpabuf_put_u8(resp, P2P_SERV_P2PS);
+				wpabuf_put_u8(resp, srv_trans_id);
+				/* Status Code */
+				wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+				count_pos = wpabuf_put(resp, 1);
+				*count_pos = 0;
+			} else if (wpabuf_tailroom(resp) <
+				   len + svc_info_len + 10)
+				return;
+
+			if (svc_info_len) {
+				wpa_printf(MSG_DEBUG,
+					   "P2P: Add Svc: %s info: %s",
+					   adv_data->svc_name,
+					   adv_data->svc_info);
+			} else {
+				wpa_printf(MSG_DEBUG, "P2P: Add Svc: %s",
+					   adv_data->svc_name);
+			}
+
+			/* Advertisement ID */
+			wpabuf_put_le32(resp, adv_data->id);
+
+			/* Config Methods */
+			wpabuf_put_be16(resp, adv_data->config_methods);
+
+			/* Service Name */
+			wpabuf_put_u8(resp, (u8) len);
+			wpabuf_put_data(resp, adv_data->svc_name, len);
+
+			/* Service State */
+			wpabuf_put_u8(resp, adv_data->state);
+
+			/* Service Information */
+			wpabuf_put_le16(resp, (u16) svc_info_len);
+			wpabuf_put_data(resp, adv_data->svc_info, svc_info_len);
+
+			/* Update length and count */
+			(*count_pos)++;
+			WPA_PUT_LE16(len_pos,
+				     (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+		}
+	}
+
+	/* Return error if no matching svc found */
+	if (count_pos == NULL) {
+		wpa_printf(MSG_DEBUG, "P2P: ASP service not found");
+		wpas_sd_add_not_found(resp, P2P_SERV_P2PS, srv_trans_id);
+	}
+}
+
+
+void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+		     u16 update_indic, const u8 *tlvs, size_t tlvs_len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	const u8 *pos = tlvs;
+	const u8 *end = tlvs + tlvs_len;
+	const u8 *tlv_end;
+	u16 slen;
+	struct wpabuf *resp;
+	u8 srv_proto, srv_trans_id;
+	size_t buf_len;
+	char *buf;
+
+	wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Request TLVs",
+		    tlvs, tlvs_len);
+	buf_len = 2 * tlvs_len + 1;
+	buf = os_malloc(buf_len);
+	if (buf) {
+		wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
+		wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_REQ "%d "
+			     MACSTR " %u %u %s",
+			     freq, MAC2STR(sa), dialog_token, update_indic,
+			     buf);
+		os_free(buf);
+	}
+
+	if (wpa_s->p2p_sd_over_ctrl_iface) {
+		wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
+					   update_indic, tlvs, tlvs_len);
+		return; /* to be processed by an external program */
+	}
+
+	resp = wpabuf_alloc(10000);
+	if (resp == NULL)
+		return;
+
+	while (pos + 1 < end) {
+		wpa_printf(MSG_DEBUG, "P2P: Service Request TLV");
+		slen = WPA_GET_LE16(pos);
+		pos += 2;
+		if (pos + slen > end || slen < 2) {
+			wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data "
+				   "length");
+			wpabuf_free(resp);
+			return;
+		}
+		tlv_end = pos + slen;
+
+		srv_proto = *pos++;
+		wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
+			   srv_proto);
+		srv_trans_id = *pos++;
+		wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
+			   srv_trans_id);
+
+		wpa_hexdump(MSG_MSGDUMP, "P2P: Query Data",
+			    pos, tlv_end - pos);
+
+
+		if (wpa_s->force_long_sd) {
+			wpa_printf(MSG_DEBUG, "P2P: SD test - force long "
+				   "response");
+			wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
+			wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
+			goto done;
+		}
+
+		switch (srv_proto) {
+		case P2P_SERV_ALL_SERVICES:
+			wpa_printf(MSG_DEBUG, "P2P: Service Discovery Request "
+				   "for all services");
+			if (dl_list_empty(&wpa_s->global->p2p_srv_upnp) &&
+			    dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
+				wpa_printf(MSG_DEBUG, "P2P: No service "
+					   "discovery protocols available");
+				wpas_sd_add_proto_not_avail(
+					resp, P2P_SERV_ALL_SERVICES,
+					srv_trans_id);
+				break;
+			}
+			wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
+			wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
+			break;
+		case P2P_SERV_BONJOUR:
+			wpas_sd_req_bonjour(wpa_s, resp, srv_trans_id,
+					    pos, tlv_end - pos);
+			break;
+		case P2P_SERV_UPNP:
+			wpas_sd_req_upnp(wpa_s, resp, srv_trans_id,
+					 pos, tlv_end - pos);
+			break;
+#ifdef CONFIG_WIFI_DISPLAY
+		case P2P_SERV_WIFI_DISPLAY:
+			wpas_sd_req_wfd(wpa_s, resp, srv_trans_id,
+					pos, tlv_end - pos);
+			break;
+#endif /* CONFIG_WIFI_DISPLAY */
+		case P2P_SERV_P2PS:
+			wpas_sd_req_asp(wpa_s, resp, srv_trans_id,
+					pos, tlv_end - pos);
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "P2P: Unavailable service "
+				   "protocol %u", srv_proto);
+			wpas_sd_add_proto_not_avail(resp, srv_proto,
+						    srv_trans_id);
+			break;
+		}
+
+		pos = tlv_end;
+	}
+
+done:
+	wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
+				   update_indic, tlvs, tlvs_len);
+
+	wpas_p2p_sd_response(wpa_s, freq, sa, dialog_token, resp);
+
+	wpabuf_free(resp);
+}
+
+
+static void wpas_sd_p2ps_serv_response(struct wpa_supplicant *wpa_s,
+				       const u8 *sa, u8 srv_trans_id,
+				       const u8 *pos, const u8 *tlv_end)
+{
+	u8 left = *pos++;
+	u32 adv_id;
+	u8 svc_status;
+	u16 config_methods;
+	char svc_str[256];
+
+	while (left-- && pos < tlv_end) {
+		char *buf = NULL;
+		size_t buf_len;
+		u8 svc_len;
+
+		/* Sanity check fixed length+svc_str */
+		if (pos + 6 >= tlv_end)
+			break;
+		svc_len = pos[6];
+		if (pos + svc_len + 10 > tlv_end)
+			break;
+
+		/* Advertisement ID */
+		adv_id = WPA_GET_LE32(pos);
+		pos += sizeof(u32);
+
+		/* Config Methods */
+		config_methods = WPA_GET_BE16(pos);
+		pos += sizeof(u16);
+
+		/* Service Name */
+		pos++; /* svc_len */
+		os_memcpy(svc_str, pos, svc_len);
+		svc_str[svc_len] = '\0';
+		pos += svc_len;
+
+		/* Service Status */
+		svc_status = *pos++;
+
+		/* Service Information Length */
+		buf_len = WPA_GET_LE16(pos);
+		pos += sizeof(u16);
+
+		/* Sanity check buffer length */
+		if (buf_len > (unsigned int) (tlv_end - pos))
+			break;
+
+		if (buf_len) {
+			buf = os_zalloc(2 * buf_len + 1);
+			if (buf) {
+				utf8_escape((const char *) pos, buf_len, buf,
+					    2 * buf_len + 1);
+			}
+		}
+
+		pos += buf_len;
+
+		if (buf) {
+			wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP
+				       MACSTR " %x %x %x %x %s '%s'",
+				       MAC2STR(sa), srv_trans_id, adv_id,
+				       svc_status, config_methods, svc_str,
+				       buf);
+			os_free(buf);
+		} else {
+			wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP
+				       MACSTR " %x %x %x %x %s",
+				       MAC2STR(sa), srv_trans_id, adv_id,
+				       svc_status, config_methods, svc_str);
+		}
+	}
+}
+
+
+void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
+		      const u8 *tlvs, size_t tlvs_len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	const u8 *pos = tlvs;
+	const u8 *end = tlvs + tlvs_len;
+	const u8 *tlv_end;
+	u16 slen;
+	size_t buf_len;
+	char *buf;
+
+	wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Response TLVs",
+		    tlvs, tlvs_len);
+	if (tlvs_len > 1500) {
+		/* TODO: better way for handling this */
+		wpa_msg_ctrl(wpa_s, MSG_INFO,
+			     P2P_EVENT_SERV_DISC_RESP MACSTR
+			     " %u <long response: %u bytes>",
+			     MAC2STR(sa), update_indic,
+			     (unsigned int) tlvs_len);
+	} else {
+		buf_len = 2 * tlvs_len + 1;
+		buf = os_malloc(buf_len);
+		if (buf) {
+			wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
+			wpa_msg_ctrl(wpa_s, MSG_INFO,
+				     P2P_EVENT_SERV_DISC_RESP MACSTR " %u %s",
+				     MAC2STR(sa), update_indic, buf);
+			os_free(buf);
+		}
+	}
+
+	while (pos < end) {
+		u8 srv_proto, srv_trans_id, status;
+
+		wpa_printf(MSG_DEBUG, "P2P: Service Response TLV");
+		slen = WPA_GET_LE16(pos);
+		pos += 2;
+		if (pos + slen > end || slen < 3) {
+			wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data "
+				   "length");
+			return;
+		}
+		tlv_end = pos + slen;
+
+		srv_proto = *pos++;
+		wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
+			   srv_proto);
+		srv_trans_id = *pos++;
+		wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
+			   srv_trans_id);
+		status = *pos++;
+		wpa_printf(MSG_DEBUG, "P2P: Status Code ID %u",
+			   status);
+
+		wpa_hexdump(MSG_MSGDUMP, "P2P: Response Data",
+			    pos, tlv_end - pos);
+
+		if (srv_proto == P2P_SERV_P2PS && pos < tlv_end) {
+			wpas_sd_p2ps_serv_response(wpa_s, sa, srv_trans_id,
+						   pos, tlv_end);
+		}
+
+		pos = tlv_end;
+	}
+
+	wpas_notify_p2p_sd_response(wpa_s, sa, update_indic, tlvs, tlvs_len);
+}
+
+
+u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
+			const struct wpabuf *tlvs)
+{
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return 0;
+	return (uintptr_t) p2p_sd_request(wpa_s->global->p2p, dst, tlvs);
+}
+
+
+u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
+			     u8 version, const char *query)
+{
+	struct wpabuf *tlvs;
+	u64 ret;
+
+	tlvs = wpabuf_alloc(2 + 1 + 1 + 1 + os_strlen(query));
+	if (tlvs == NULL)
+		return 0;
+	wpabuf_put_le16(tlvs, 1 + 1 + 1 + os_strlen(query));
+	wpabuf_put_u8(tlvs, P2P_SERV_UPNP); /* Service Protocol Type */
+	wpabuf_put_u8(tlvs, 1); /* Service Transaction ID */
+	wpabuf_put_u8(tlvs, version);
+	wpabuf_put_str(tlvs, query);
+	ret = wpas_p2p_sd_request(wpa_s, dst, tlvs);
+	wpabuf_free(tlvs);
+	return ret;
+}
+
+
+u64 wpas_p2p_sd_request_asp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 id,
+			    const char *svc_str, const char *info_substr)
+{
+	struct wpabuf *tlvs;
+	size_t plen, svc_len, substr_len = 0;
+	u64 ret;
+
+	svc_len = os_strlen(svc_str);
+	if (info_substr)
+		substr_len = os_strlen(info_substr);
+
+	if (svc_len > 0xff || substr_len > 0xff)
+		return 0;
+
+	plen = 1 + 1 + 1 + svc_len + 1 + substr_len;
+	tlvs = wpabuf_alloc(2 + plen);
+	if (tlvs == NULL)
+		return 0;
+
+	wpabuf_put_le16(tlvs, plen);
+	wpabuf_put_u8(tlvs, P2P_SERV_P2PS);
+	wpabuf_put_u8(tlvs, id); /* Service Transaction ID */
+	wpabuf_put_u8(tlvs, (u8) svc_len); /* Service String Length */
+	wpabuf_put_data(tlvs, svc_str, svc_len);
+	wpabuf_put_u8(tlvs, (u8) substr_len); /* Info Substring Length */
+	wpabuf_put_data(tlvs, info_substr, substr_len);
+	ret = wpas_p2p_sd_request(wpa_s, dst, tlvs);
+	wpabuf_free(tlvs);
+
+	return ret;
+}
+
+
+#ifdef CONFIG_WIFI_DISPLAY
+
+static u64 wpas_p2p_sd_request_wfd(struct wpa_supplicant *wpa_s, const u8 *dst,
+				   const struct wpabuf *tlvs)
+{
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return 0;
+	return (uintptr_t) p2p_sd_request_wfd(wpa_s->global->p2p, dst, tlvs);
+}
+
+
+#define MAX_WFD_SD_SUBELEMS 20
+
+static void wfd_add_sd_req_role(struct wpabuf *tlvs, u8 id, u8 role,
+				const char *subelems)
+{
+	u8 *len;
+	const char *pos;
+	int val;
+	int count = 0;
+
+	len = wpabuf_put(tlvs, 2);
+	wpabuf_put_u8(tlvs, P2P_SERV_WIFI_DISPLAY); /* Service Protocol Type */
+	wpabuf_put_u8(tlvs, id); /* Service Transaction ID */
+
+	wpabuf_put_u8(tlvs, role);
+
+	pos = subelems;
+	while (*pos) {
+		val = atoi(pos);
+		if (val >= 0 && val < 256) {
+			wpabuf_put_u8(tlvs, val);
+			count++;
+			if (count == MAX_WFD_SD_SUBELEMS)
+				break;
+		}
+		pos = os_strchr(pos + 1, ',');
+		if (pos == NULL)
+			break;
+		pos++;
+	}
+
+	WPA_PUT_LE16(len, (u8 *) wpabuf_put(tlvs, 0) - len - 2);
+}
+
+
+u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s,
+				     const u8 *dst, const char *role)
+{
+	struct wpabuf *tlvs;
+	u64 ret;
+	const char *subelems;
+	u8 id = 1;
+
+	subelems = os_strchr(role, ' ');
+	if (subelems == NULL)
+		return 0;
+	subelems++;
+
+	tlvs = wpabuf_alloc(4 * (2 + 1 + 1 + 1 + MAX_WFD_SD_SUBELEMS));
+	if (tlvs == NULL)
+		return 0;
+
+	if (os_strstr(role, "[source]"))
+		wfd_add_sd_req_role(tlvs, id++, 0x00, subelems);
+	if (os_strstr(role, "[pri-sink]"))
+		wfd_add_sd_req_role(tlvs, id++, 0x01, subelems);
+	if (os_strstr(role, "[sec-sink]"))
+		wfd_add_sd_req_role(tlvs, id++, 0x02, subelems);
+	if (os_strstr(role, "[source+sink]"))
+		wfd_add_sd_req_role(tlvs, id++, 0x03, subelems);
+
+	ret = wpas_p2p_sd_request_wfd(wpa_s, dst, tlvs);
+	wpabuf_free(tlvs);
+	return ret;
+}
+
+#endif /* CONFIG_WIFI_DISPLAY */
+
+
+int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req)
+{
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return -1;
+	return p2p_sd_cancel_request(wpa_s->global->p2p,
+				     (void *) (uintptr_t) req);
+}
+
+
+void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq,
+			  const u8 *dst, u8 dialog_token,
+			  const struct wpabuf *resp_tlvs)
+{
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return;
+	p2p_sd_response(wpa_s->global->p2p, freq, dst, dialog_token,
+			resp_tlvs);
+}
+
+
+void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->global->p2p)
+		p2p_sd_service_update(wpa_s->global->p2p);
+}
+
+
+static void wpas_p2p_srv_bonjour_free(struct p2p_srv_bonjour *bsrv)
+{
+	dl_list_del(&bsrv->list);
+	wpabuf_free(bsrv->query);
+	wpabuf_free(bsrv->resp);
+	os_free(bsrv);
+}
+
+
+static void wpas_p2p_srv_upnp_free(struct p2p_srv_upnp *usrv)
+{
+	dl_list_del(&usrv->list);
+	os_free(usrv->service);
+	os_free(usrv);
+}
+
+
+void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s)
+{
+	struct p2p_srv_bonjour *bsrv, *bn;
+	struct p2p_srv_upnp *usrv, *un;
+
+	dl_list_for_each_safe(bsrv, bn, &wpa_s->global->p2p_srv_bonjour,
+			      struct p2p_srv_bonjour, list)
+		wpas_p2p_srv_bonjour_free(bsrv);
+
+	dl_list_for_each_safe(usrv, un, &wpa_s->global->p2p_srv_upnp,
+			      struct p2p_srv_upnp, list)
+		wpas_p2p_srv_upnp_free(usrv);
+
+	wpas_p2p_sd_service_update(wpa_s);
+}
+
+
+int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id)
+{
+	if (adv_id == 0)
+		return 1;
+
+	if (p2p_service_p2ps_id(wpa_s->global->p2p, adv_id))
+		return 1;
+
+	return 0;
+}
+
+
+int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id)
+{
+	return p2p_service_del_asp(wpa_s->global->p2p, adv_id);
+}
+
+
+int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s,
+			     int auto_accept, u32 adv_id,
+			     const char *adv_str, u8 svc_state,
+			     u16 config_methods, const char *svc_info)
+{
+	return p2p_service_add_asp(wpa_s->global->p2p, auto_accept, adv_id,
+				   adv_str, svc_state, config_methods,
+				   svc_info);
+}
+
+
+int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s,
+				 struct wpabuf *query, struct wpabuf *resp)
+{
+	struct p2p_srv_bonjour *bsrv;
+
+	bsrv = os_zalloc(sizeof(*bsrv));
+	if (bsrv == NULL)
+		return -1;
+	bsrv->query = query;
+	bsrv->resp = resp;
+	dl_list_add(&wpa_s->global->p2p_srv_bonjour, &bsrv->list);
+
+	wpas_p2p_sd_service_update(wpa_s);
+	return 0;
+}
+
+
+int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s,
+				 const struct wpabuf *query)
+{
+	struct p2p_srv_bonjour *bsrv;
+
+	bsrv = wpas_p2p_service_get_bonjour(wpa_s, query);
+	if (bsrv == NULL)
+		return -1;
+	wpas_p2p_srv_bonjour_free(bsrv);
+	wpas_p2p_sd_service_update(wpa_s);
+	return 0;
+}
+
+
+int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version,
+			      const char *service)
+{
+	struct p2p_srv_upnp *usrv;
+
+	if (wpas_p2p_service_get_upnp(wpa_s, version, service))
+		return 0; /* Already listed */
+	usrv = os_zalloc(sizeof(*usrv));
+	if (usrv == NULL)
+		return -1;
+	usrv->version = version;
+	usrv->service = os_strdup(service);
+	if (usrv->service == NULL) {
+		os_free(usrv);
+		return -1;
+	}
+	dl_list_add(&wpa_s->global->p2p_srv_upnp, &usrv->list);
+
+	wpas_p2p_sd_service_update(wpa_s);
+	return 0;
+}
+
+
+int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version,
+			      const char *service)
+{
+	struct p2p_srv_upnp *usrv;
+
+	usrv = wpas_p2p_service_get_upnp(wpa_s, version, service);
+	if (usrv == NULL)
+		return -1;
+	wpas_p2p_srv_upnp_free(usrv);
+	wpas_p2p_sd_service_update(wpa_s);
+	return 0;
+}
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 5a0af0d..2b40bbf 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -608,35 +608,57 @@
 		"uapsd", "ps", "wifi_display", "bssid_filter", "disallow_aps",
 		"no_keep_alive",
 		/* global configuration parameters */
-		"eapol_version", "ap_scan", "disable_scan_offload",
-		"fast_reauth", "opensc_engine_path", "pkcs11_engine_path",
-		"pkcs11_module_path", "openssl_ciphers",
-		"pcsc_reader", "pcsc_pin",
-		"driver_param", "dot11RSNAConfigPMKLifetime",
+#ifdef CONFIG_CTRL_IFACE
+		"ctrl_interface", "no_ctrl_interface", "ctrl_interface_group",
+#endif /* CONFIG_CTRL_IFACE */
+		"eapol_version", "ap_scan", "bgscan",
+#ifdef CONFIG_MESH
+		"user_mpm", "max_peer_links", "mesh_max_inactivity",
+#endif /* CONFIG_MESH */
+		"disable_scan_offload", "fast_reauth", "opensc_engine_path",
+		"pkcs11_engine_path", "pkcs11_module_path", "openssl_ciphers",
+		"pcsc_reader", "pcsc_pin", "external_sim", "driver_param",
+		"dot11RSNAConfigPMKLifetime",
 		"dot11RSNAConfigPMKReauthThreshold",
 		"dot11RSNAConfigSATimeout",
-		"update_config", "load_dynamic_eap", "uuid", "device_name",
-		"manufacturer", "model_name", "model_number", "serial_number",
-		"device_type", "os_version", "config_methods",
-		"wps_cred_processing", "wps_vendor_ext_m1", "sec_device_type",
+#ifndef CONFIG_NO_CONFIG_WRITE
+		"update_config",
+#endif /* CONFIG_NO_CONFIG_WRITE */
+		"load_dynamic_eap",
+#ifdef CONFIG_WPS
+		"uuid", "device_name", "manufacturer", "model_name",
+		"model_number", "serial_number", "device_type", "os_version",
+		"config_methods", "wps_cred_processing", "wps_vendor_ext_m1",
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+		"sec_device_type",
 		"p2p_listen_reg_class", "p2p_listen_channel",
-		"p2p_oper_reg_class", "p2p_oper_channel",
-		"p2p_go_intent", "p2p_ssid_postfix", "persistent_reconnect",
-		"p2p_intra_bss", "p2p_group_idle", "p2p_pref_chan",
-		"p2p_no_go_freq",
-		"p2p_go_ht40", "p2p_disabled", "p2p_no_group_iface",
-		"p2p_go_vht",
-		"p2p_ignore_shared_freq", "country", "bss_max_count",
-		"bss_expiration_age", "bss_expiration_scan_count",
-		"filter_ssids", "filter_rssi", "max_num_sta",
-		"disassoc_low_ack", "hs20", "interworking", "hessid",
-		"access_network_type", "pbc_in_m1", "autoscan",
-		"wps_nfc_dev_pw_id", "wps_nfc_dh_pubkey", "wps_nfc_dh_privkey",
-		"wps_nfc_dev_pw", "ext_password_backend",
+		"p2p_oper_reg_class", "p2p_oper_channel", "p2p_go_intent",
+		"p2p_ssid_postfix", "persistent_reconnect", "p2p_intra_bss",
+		"p2p_group_idle", "p2p_passphrase_len", "p2p_pref_chan",
+		"p2p_no_go_freq", "p2p_add_cli_chan",
+		"p2p_optimize_listen_chan", "p2p_go_ht40", "p2p_go_vht",
+		"p2p_disabled", "p2p_go_ctwindow", "p2p_no_group_iface",
+		"p2p_ignore_shared_freq", "ip_addr_go", "ip_addr_mask",
+		"ip_addr_start", "ip_addr_end",
+#endif /* CONFIG_P2P */
+		"country", "bss_max_count", "bss_expiration_age",
+		"bss_expiration_scan_count", "filter_ssids", "filter_rssi",
+		"max_num_sta", "disassoc_low_ack",
+#ifdef CONFIG_HS20
+		"hs20",
+#endif /* CONFIG_HS20 */
+		"interworking", "hessid", "access_network_type", "pbc_in_m1",
+		"autoscan", "wps_nfc_dev_pw_id", "wps_nfc_dh_pubkey",
+		"wps_nfc_dh_privkey", "wps_nfc_dev_pw", "ext_password_backend",
 		"p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
-		"sae_groups", "dtim_period", "beacon_int", "ap_vendor_elements",
-		"ignore_old_scan_res", "freq_list", "external_sim",
-		"tdls_external_control", "p2p_search_delay"
+		"sae_groups", "dtim_period", "beacon_int",
+		"ap_vendor_elements", "ignore_old_scan_res", "freq_list",
+		"scan_cur_freq", "sched_scan_interval",
+		"tdls_external_control", "osu_dir", "wowlan_triggers",
+		"p2p_search_delay", "mac_addr", "rand_addr_lifetime",
+		"preassoc_mac_addr", "key_mgmt_offload", "passive_scan",
+		"reassoc_same_bss_optim"
 	};
 	int i, num_fields = ARRAY_SIZE(fields);
 
@@ -670,6 +692,74 @@
 }
 
 
+static char ** wpa_cli_complete_get(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	const char *fields[] = {
+#ifdef CONFIG_CTRL_IFACE
+		"ctrl_interface", "ctrl_interface_group",
+#endif /* CONFIG_CTRL_IFACE */
+		"eapol_version", "ap_scan",
+#ifdef CONFIG_MESH
+		"user_mpm", "max_peer_links", "mesh_max_inactivity",
+#endif /* CONFIG_MESH */
+		"disable_scan_offload", "fast_reauth", "opensc_engine_path",
+		"pkcs11_engine_path", "pkcs11_module_path", "openssl_ciphers",
+		"pcsc_reader", "pcsc_pin", "external_sim", "driver_param",
+		"dot11RSNAConfigPMKLifetime",
+		"dot11RSNAConfigPMKReauthThreshold",
+		"dot11RSNAConfigSATimeout",
+#ifndef CONFIG_NO_CONFIG_WRITE
+		"update_config",
+#endif /* CONFIG_NO_CONFIG_WRITE */
+#ifdef CONFIG_WPS
+		"device_name", "manufacturer", "model_name", "model_number",
+		"serial_number", "config_methods", "wps_cred_processing",
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+		"p2p_listen_reg_class", "p2p_listen_channel",
+		"p2p_oper_reg_class", "p2p_oper_channel", "p2p_go_intent",
+		"p2p_ssid_postfix", "persistent_reconnect", "p2p_intra_bss",
+		"p2p_group_idle", "p2p_passphrase_len", "p2p_add_cli_chan",
+		"p2p_optimize_listen_chan", "p2p_go_ht40", "p2p_go_vht",
+		"p2p_disabled", "p2p_go_ctwindow", "p2p_no_group_iface",
+		"p2p_ignore_shared_freq", "ip_addr_go", "ip_addr_mask",
+		"ip_addr_start", "ip_addr_end",
+#endif /* CONFIG_P2P */
+		"bss_max_count", "bss_expiration_age",
+		"bss_expiration_scan_count", "filter_ssids", "filter_rssi",
+		"max_num_sta", "disassoc_low_ack",
+#ifdef CONFIG_HS20
+		"hs20",
+#endif /* CONFIG_HS20 */
+		"interworking", "access_network_type", "pbc_in_m1", "autoscan",
+		"wps_nfc_dev_pw_id", "ext_password_backend",
+		"p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
+		"dtim_period", "beacon_int", "ignore_old_scan_res",
+		"scan_cur_freq", "sched_scan_interval",
+		"tdls_external_control", "osu_dir", "wowlan_triggers",
+		"p2p_search_delay", "mac_addr", "rand_addr_lifetime",
+		"preassoc_mac_addr", "key_mgmt_offload", "passive_scan",
+		"reassoc_same_bss_optim"
+	};
+	int i, num_fields = ARRAY_SIZE(fields);
+
+	if (arg == 1) {
+		char **res = os_calloc(num_fields + 1, sizeof(char *));
+		if (res == NULL)
+			return NULL;
+		for (i = 0; i < num_fields; i++) {
+			res[i] = os_strdup(fields[i]);
+			if (res[i] == NULL)
+				return res;
+		}
+		return res;
+	}
+
+	return NULL;
+}
+
+
 static int wpa_cli_cmd_logoff(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	return wpa_ctrl_command(ctrl, "LOGOFF");
@@ -2624,7 +2714,7 @@
 	{ "dump", wpa_cli_cmd_dump, NULL,
 	  cli_cmd_flag_none,
 	  "= dump config variables" },
-	{ "get", wpa_cli_cmd_get, NULL,
+	{ "get", wpa_cli_cmd_get, wpa_cli_complete_get,
 	  cli_cmd_flag_none,
 	  "<name> = get information" },
 	{ "logon", wpa_cli_cmd_logon, NULL,