P2P: Fix association with an AP/P2P GO that is not a P2P manager
[mech_eap.git] / src / p2p / p2p.c
index 82def55..c02044b 100644 (file)
@@ -13,6 +13,8 @@
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "common/wpa_ctrl.h"
+#include "crypto/sha256.h"
+#include "crypto/crypto.h"
 #include "wps/wps_i.h"
 #include "p2p_i.h"
 #include "p2p.h"
@@ -151,6 +153,19 @@ const char * p2p_get_state_txt(struct p2p_data *p2p)
 }
 
 
+struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p)
+{
+       return p2p ? p2p->p2ps_adv_list : NULL;
+}
+
+
+void p2p_set_intended_addr(struct p2p_data *p2p, const u8 *intended_addr)
+{
+       if (p2p && intended_addr)
+               os_memcpy(p2p->intended_addr, intended_addr, ETH_ALEN);
+}
+
+
 u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr)
 {
        struct p2p_device *dev = NULL;
@@ -219,9 +234,15 @@ void p2p_go_neg_failed(struct p2p_data *p2p, int status)
        if (!peer)
                return;
 
-       p2p_clear_timeout(p2p);
        eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
-       p2p_set_state(p2p, P2P_IDLE);
+       if (p2p->state != P2P_SEARCH) {
+               /*
+                * Clear timeouts related to GO Negotiation if no new p2p_find
+                * has been started.
+                */
+               p2p_clear_timeout(p2p);
+               p2p_set_state(p2p, P2P_IDLE);
+       }
 
        peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE;
        peer->wps_method = WPS_NOT_READY;
@@ -757,6 +778,7 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
        if (os_memcmp(addr, p2p_dev_addr, ETH_ALEN) != 0)
                os_memcpy(dev->interface_addr, addr, ETH_ALEN);
        if (msg.ssid &&
+           msg.ssid[1] <= sizeof(dev->oper_ssid) &&
            (msg.ssid[1] != P2P_WILDCARD_SSID_LEN ||
             os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN)
             != 0)) {
@@ -764,6 +786,12 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
                dev->oper_ssid_len = msg.ssid[1];
        }
 
+       if (msg.adv_service_instance && msg.adv_service_instance_len) {
+               wpabuf_free(dev->info.p2ps_instance);
+               dev->info.p2ps_instance = wpabuf_alloc_copy(
+                       msg.adv_service_instance, msg.adv_service_instance_len);
+       }
+
        if (freq >= 2412 && freq <= 2484 && msg.ds_params &&
            *msg.ds_params >= 1 && *msg.ds_params <= 14) {
                int ds_freq;
@@ -823,7 +851,9 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
 
        p2p_update_peer_vendor_elems(dev, ies, ies_len);
 
-       if (dev->flags & P2P_DEV_REPORTED && !wfd_changed)
+       if (dev->flags & P2P_DEV_REPORTED && !wfd_changed &&
+           (!msg.adv_service_instance ||
+            (dev->flags & P2P_DEV_P2PS_REPORTED)))
                return 0;
 
        p2p_dbg(p2p, "Peer found with Listen frequency %d MHz (rx_time=%u.%06u)",
@@ -859,6 +889,9 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
                            !(dev->flags & P2P_DEV_REPORTED_ONCE));
        dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
 
+       if (msg.adv_service_instance)
+               dev->flags |= P2P_DEV_P2PS_REPORTED;
+
        return 0;
 }
 
@@ -893,6 +926,7 @@ static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev)
        wpabuf_free(dev->info.wfd_subelems);
        wpabuf_free(dev->info.vendor_elems);
        wpabuf_free(dev->go_neg_conf);
+       wpabuf_free(dev->info.p2ps_instance);
 
        os_free(dev);
 }
@@ -978,14 +1012,8 @@ static void p2p_search(struct p2p_data *p2p)
                                 p2p->num_req_dev_types, p2p->req_dev_types,
                                 p2p->find_dev_id, pw_id);
        if (res < 0) {
-               p2p_dbg(p2p, "Scan request failed");
+               p2p_dbg(p2p, "Scan request schedule failed");
                p2p_continue_find(p2p);
-       } else {
-               p2p_dbg(p2p, "Running p2p_scan");
-               p2p->p2p_scan_running = 1;
-               eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
-               eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
-                                      p2p, NULL);
        }
 }
 
@@ -998,6 +1026,22 @@ static void p2p_find_timeout(void *eloop_ctx, void *timeout_ctx)
 }
 
 
+void p2p_notify_scan_trigger_status(struct p2p_data *p2p, int status)
+{
+       if (status != 0) {
+               p2p_dbg(p2p, "Scan request failed");
+               /* Do continue find even for the first p2p_find_scan */
+               p2p_continue_find(p2p);
+       } else {
+               p2p_dbg(p2p, "Running p2p_scan");
+               p2p->p2p_scan_running = 1;
+               eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
+               eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
+                                      p2p, NULL);
+       }
+}
+
+
 static int p2p_run_after_scan(struct p2p_data *p2p)
 {
        struct p2p_device *dev;
@@ -1067,10 +1111,44 @@ static void p2p_free_req_dev_types(struct p2p_data *p2p)
 }
 
 
+static int p2ps_gen_hash(struct p2p_data *p2p, const char *str, u8 *hash)
+{
+       u8 buf[SHA256_MAC_LEN];
+       char str_buf[256];
+       const u8 *adv_array;
+       size_t i, adv_len;
+
+       if (!str || !hash)
+               return 0;
+
+       if (!str[0]) {
+               os_memcpy(hash, p2p->wild_card_hash, P2PS_HASH_LEN);
+               return 1;
+       }
+
+       adv_array = (u8 *) str_buf;
+       adv_len = os_strlen(str);
+
+       for (i = 0; str[i] && i < adv_len; i++) {
+               if (str[i] >= 'A' && str[i] <= 'Z')
+                       str_buf[i] = str[i] - 'A' + 'a';
+               else
+                       str_buf[i] = str[i];
+       }
+
+       if (sha256_vector(1, &adv_array, &adv_len, buf))
+               return 0;
+
+       os_memcpy(hash, buf, P2PS_HASH_LEN);
+       return 1;
+}
+
+
 int p2p_find(struct p2p_data *p2p, unsigned int timeout,
             enum p2p_discovery_type type,
             unsigned int num_req_dev_types, const u8 *req_dev_types,
-            const u8 *dev_id, unsigned int search_delay)
+            const u8 *dev_id, unsigned int search_delay,
+            u8 seek_count, const char **seek, int freq)
 {
        int res;
 
@@ -1097,6 +1175,47 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
        } else
                p2p->find_dev_id = NULL;
 
+       if (seek_count == 0 || !seek) {
+               /* Not an ASP search */
+               p2p->p2ps_seek = 0;
+       } else if (seek_count == 1 && seek && (!seek[0] || !seek[0][0])) {
+               /*
+                * An empty seek string means no hash values, but still an ASP
+                * search.
+                */
+               p2p->p2ps_seek_count = 0;
+               p2p->p2ps_seek = 1;
+       } else if (seek && seek_count <= P2P_MAX_QUERY_HASH) {
+               u8 buf[P2PS_HASH_LEN];
+               int i;
+
+               p2p->p2ps_seek_count = seek_count;
+               for (i = 0; i < seek_count; i++) {
+                       if (!p2ps_gen_hash(p2p, seek[i], buf))
+                               continue;
+
+                       /* If asking for wildcard, don't do others */
+                       if (os_memcmp(buf, p2p->wild_card_hash,
+                                     P2PS_HASH_LEN) == 0) {
+                               p2p->p2ps_seek_count = 0;
+                               break;
+                       }
+
+                       os_memcpy(&p2p->query_hash[i * P2PS_HASH_LEN], buf,
+                                 P2PS_HASH_LEN);
+               }
+               p2p->p2ps_seek = 1;
+       } else {
+               p2p->p2ps_seek_count = 0;
+               p2p->p2ps_seek = 1;
+       }
+
+       /* Special case to perform wildcard search */
+       if (p2p->p2ps_seek_count == 0 && p2p->p2ps_seek) {
+               p2p->p2ps_seek_count = 1;
+               os_memcpy(&p2p->query_hash, p2p->wild_card_hash, P2PS_HASH_LEN);
+       }
+
        p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
        p2p_clear_timeout(p2p);
        p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
@@ -1112,6 +1231,19 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
                                       p2p, NULL);
        switch (type) {
        case P2P_FIND_START_WITH_FULL:
+               if (freq > 0) {
+                       /*
+                        * Start with the specified channel and then move to
+                        * social channels only scans.
+                        */
+                       res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx,
+                                                P2P_SCAN_SPECIFIC, freq,
+                                                p2p->num_req_dev_types,
+                                                p2p->req_dev_types, dev_id,
+                                                DEV_PW_DEFAULT);
+                       break;
+               }
+               /* fall through */
        case P2P_FIND_PROGRESSIVE:
                res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_FULL, 0,
                                         p2p->num_req_dev_types,
@@ -1128,17 +1260,11 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
                return -1;
        }
 
-       if (res == 0) {
-               p2p_dbg(p2p, "Running p2p_scan");
-               p2p->p2p_scan_running = 1;
-               eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
-               eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
-                                      p2p, NULL);
-       } else if (p2p->p2p_scan_running) {
+       if (res != 0 && p2p->p2p_scan_running) {
                p2p_dbg(p2p, "Failed to start p2p_scan - another p2p_scan was already running");
                /* wait for the previous p2p_scan to complete */
                res = 0; /* do not report failure */
-       } else {
+       } else if (res != 0) {
                p2p_dbg(p2p, "Failed to start p2p_scan");
                p2p_set_state(p2p, P2P_IDLE);
                eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
@@ -1153,8 +1279,11 @@ void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq)
        p2p_dbg(p2p, "Stopping find");
        eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
        p2p_clear_timeout(p2p);
-       if (p2p->state == P2P_SEARCH)
+       if (p2p->state == P2P_SEARCH || p2p->state == P2P_SD_DURING_FIND)
                p2p->cfg->find_stopped(p2p->cfg->cb_ctx);
+
+       p2p->p2ps_seek_count = 0;
+
        p2p_set_state(p2p, P2P_IDLE);
        p2p_free_req_dev_types(p2p);
        p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
@@ -1164,6 +1293,7 @@ void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq)
        p2p->sd_peer = NULL;
        p2p->invite_peer = NULL;
        p2p_stop_listen_for_freq(p2p, freq);
+       p2p->send_action_in_progress = 0;
 }
 
 
@@ -1356,8 +1486,8 @@ int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev,
        if (go)
                p2p_channels_remove_freqs(&p2p->channels, &p2p->no_go_freq);
        else if (!force_freq)
-               p2p_channels_union(&p2p->channels, &p2p->cfg->cli_channels,
-                                  &p2p->channels);
+               p2p_channels_union_inplace(&p2p->channels,
+                                          &p2p->cfg->cli_channels);
        p2p_channels_dump(p2p, "after go/cli filter/add", &p2p->channels);
 
        p2p_dbg(p2p, "Own preference for operation channel: Operating Class %u Channel %u%s",
@@ -1627,7 +1757,14 @@ void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len)
 
 int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params)
 {
-       p2p_build_ssid(p2p, params->ssid, &params->ssid_len);
+       if (p2p->ssid_set) {
+               os_memcpy(params->ssid, p2p->ssid, p2p->ssid_len);
+               params->ssid_len = p2p->ssid_len;
+       } else {
+               p2p_build_ssid(p2p, params->ssid, &params->ssid_len);
+       }
+       p2p->ssid_set = 0;
+
        p2p_random(params->passphrase, p2p->cfg->passphrase_len);
        return 0;
 }
@@ -1721,7 +1858,6 @@ static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa,
                                           rx_freq);
                break;
        case P2P_INVITATION_RESP:
-               p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
                p2p_process_invitation_resp(p2p, sa, data + 1, len - 1);
                break;
        case P2P_PROV_DISC_REQ:
@@ -2027,6 +2163,9 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p)
        if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P])
                extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]);
 
+       if (p2p->query_count)
+               extra += MAX_SVC_ADV_IE_LEN;
+
        buf = wpabuf_alloc(1000 + extra);
        if (buf == NULL)
                return NULL;
@@ -2061,10 +2200,38 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p)
        p2p_buf_add_device_info(buf, p2p, NULL);
        p2p_buf_update_ie_hdr(buf, len);
 
+       if (p2p->query_count) {
+               p2p_buf_add_service_instance(buf, p2p, p2p->query_count,
+                                            p2p->query_hash,
+                                            p2p->p2ps_adv_list);
+       }
+
        return buf;
 }
 
 
+static int p2p_service_find_asp(struct p2p_data *p2p, const u8 *hash)
+{
+       struct p2ps_advertisement *adv_data;
+
+       p2p_dbg(p2p, "ASP find - ASP list: %p", p2p->p2ps_adv_list);
+
+       /* Wildcard always matches if we have actual services */
+       if (os_memcmp(hash, p2p->wild_card_hash, P2PS_HASH_LEN) == 0)
+               return p2p->p2ps_adv_list != NULL;
+
+       adv_data = p2p->p2ps_adv_list;
+       while (adv_data) {
+               p2p_dbg(p2p, "ASP hash: %x =? %x", hash[0], adv_data->hash[0]);
+               if (os_memcmp(hash, adv_data->hash, P2PS_HASH_LEN) == 0)
+                       return 1;
+               adv_data = adv_data->next;
+       }
+
+       return 0;
+}
+
+
 static enum p2p_probe_req_status
 p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
                const u8 *bssid, const u8 *ie, size_t ie_len)
@@ -2075,13 +2242,6 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
        struct p2p_message msg;
        struct wpabuf *ies;
 
-       if (!p2p->in_listen || !p2p->drv_in_listen) {
-               /* not in Listen state - ignore Probe Request */
-               p2p_dbg(p2p, "Not in Listen state (in_listen=%d drv_in_listen=%d) - ignore Probe Request",
-                       p2p->in_listen, p2p->drv_in_listen);
-               return P2P_PREQ_NOT_LISTEN;
-       }
-
        if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) ==
            ParseFailed) {
                /* Ignore invalid Probe Request frames */
@@ -2132,6 +2292,64 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
                return P2P_PREQ_NOT_P2P;
        }
 
+       p2p->p2ps_svc_found = 0;
+
+       if (msg.service_hash && msg.service_hash_count) {
+               const u8 *hash = msg.service_hash;
+               u8 *dest = p2p->query_hash;
+               u8 i;
+
+               p2p->query_count = 0;
+               for (i = 0; i < msg.service_hash_count; i++) {
+                       if (p2p_service_find_asp(p2p, hash)) {
+                               p2p->p2ps_svc_found = 1;
+
+                               if (!os_memcmp(hash, p2p->wild_card_hash,
+                                              P2PS_HASH_LEN)) {
+                                       /* We found match(es) but wildcard
+                                        * will return all */
+                                       p2p->query_count = 1;
+                                       os_memcpy(p2p->query_hash, hash,
+                                                 P2PS_HASH_LEN);
+                                       break;
+                               }
+
+                               /* Save each matching hash */
+                               if (p2p->query_count < P2P_MAX_QUERY_HASH) {
+                                       os_memcpy(dest, hash, P2PS_HASH_LEN);
+                                       dest += P2PS_HASH_LEN;
+                                       p2p->query_count++;
+                               } else {
+                                       /* We found match(es) but too many to
+                                        * return all */
+                                       p2p->query_count = 0;
+                                       break;
+                               }
+                       }
+                       hash += P2PS_HASH_LEN;
+               }
+
+               p2p_dbg(p2p, "ASP adv found: %d", p2p->p2ps_svc_found);
+
+               /* Probed hash unknown */
+               if (!p2p->p2ps_svc_found) {
+                       p2p_parse_free(&msg);
+                       return P2P_PREQ_NOT_PROCESSED;
+               }
+       } else {
+               /* This is not a P2PS Probe Request */
+               p2p->query_count = 0;
+               p2p_dbg(p2p, "No P2PS Hash in Probe Request");
+
+               if (!p2p->in_listen || !p2p->drv_in_listen) {
+                       /* not in Listen state - ignore Probe Request */
+                       p2p_dbg(p2p, "Not in Listen state (in_listen=%d drv_in_listen=%d) - ignore Probe Request",
+                               p2p->in_listen, p2p->drv_in_listen);
+                       p2p_parse_free(&msg);
+                       return P2P_PREQ_NOT_LISTEN;
+               }
+       }
+
        if (msg.device_id &&
            os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
                /* Device ID did not match */
@@ -2229,6 +2447,7 @@ p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
        p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len);
 
        res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len);
+       p2p->query_count = 0;
 
        if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) &&
            p2p->go_neg_peer &&
@@ -2266,10 +2485,21 @@ static int p2p_assoc_req_ie_wlan_ap(struct p2p_data *p2p, const u8 *bssid,
        size_t tmplen;
        int res;
        u8 group_capab;
+       struct p2p_message msg;
 
        if (p2p_ie == NULL)
                return 0; /* WLAN AP is not a P2P manager */
 
+       os_memset(&msg, 0, sizeof(msg));
+       if (p2p_parse_p2p_ie(p2p_ie, &msg) < 0)
+               return 0;
+
+       p2p_dbg(p2p, "BSS P2P manageability %s",
+               msg.manageability ? "enabled" : "disabled");
+
+       if (!msg.manageability)
+               return 0;
+
        /*
         * (Re)Association Request - P2P IE
         * P2P Capability attribute (shall be present)
@@ -2386,6 +2616,151 @@ int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end)
 }
 
 
+struct p2ps_advertisement *
+p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id)
+{
+       struct p2ps_advertisement *adv_data;
+
+       if (!p2p)
+               return NULL;
+
+       adv_data = p2p->p2ps_adv_list;
+       while (adv_data) {
+               if (adv_data->id == adv_id)
+                       return adv_data;
+               adv_data = adv_data->next;
+       }
+
+       return NULL;
+}
+
+
+int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id)
+{
+       struct p2ps_advertisement *adv_data;
+       struct p2ps_advertisement **prior;
+
+       if (!p2p)
+               return -1;
+
+       adv_data = p2p->p2ps_adv_list;
+       prior = &p2p->p2ps_adv_list;
+       while (adv_data) {
+               if (adv_data->id == adv_id) {
+                       p2p_dbg(p2p, "Delete ASP adv_id=0x%x", adv_id);
+                       *prior = adv_data->next;
+                       os_free(adv_data);
+                       return 0;
+               }
+               prior = &adv_data->next;
+               adv_data = adv_data->next;
+       }
+
+       return -1;
+}
+
+
+int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id,
+                       const char *adv_str, u8 svc_state, u16 config_methods,
+                       const char *svc_info)
+{
+       struct p2ps_advertisement *adv_data, *tmp, **prev;
+       u8 buf[P2PS_HASH_LEN];
+       size_t adv_data_len, adv_len, info_len = 0;
+
+       if (!p2p || !adv_str || !adv_str[0])
+               return -1;
+
+       if (!(config_methods & p2p->cfg->config_methods)) {
+               p2p_dbg(p2p, "Config methods not supported svc: 0x%x dev: 0x%x",
+                       config_methods, p2p->cfg->config_methods);
+               return -1;
+       }
+
+       if (!p2ps_gen_hash(p2p, adv_str, buf))
+               return -1;
+
+       if (svc_info)
+               info_len = os_strlen(svc_info);
+       adv_len = os_strlen(adv_str);
+       adv_data_len = sizeof(struct p2ps_advertisement) + adv_len + 1 +
+               info_len + 1;
+
+       adv_data = os_zalloc(adv_data_len);
+       if (!adv_data)
+               return -1;
+
+       os_memcpy(adv_data->hash, buf, P2PS_HASH_LEN);
+       adv_data->id = adv_id;
+       adv_data->state = svc_state;
+       adv_data->config_methods = config_methods & p2p->cfg->config_methods;
+       adv_data->auto_accept = (u8) auto_accept;
+       os_memcpy(adv_data->svc_name, adv_str, adv_len);
+
+       if (svc_info && info_len) {
+               adv_data->svc_info = &adv_data->svc_name[adv_len + 1];
+               os_memcpy(adv_data->svc_info, svc_info, info_len);
+       }
+
+       /*
+        * Group Advertisements by service string. They do not need to be
+        * sorted, but groups allow easier Probe Response instance grouping
+        */
+       tmp = p2p->p2ps_adv_list;
+       prev = &p2p->p2ps_adv_list;
+       while (tmp) {
+               if (tmp->id == adv_data->id) {
+                       if (os_strcmp(tmp->svc_name, adv_data->svc_name) != 0) {
+                               os_free(adv_data);
+                               return -1;
+                       }
+                       adv_data->next = tmp->next;
+                       *prev = adv_data;
+                       os_free(tmp);
+                       goto inserted;
+               } else {
+                       if (os_strcmp(tmp->svc_name, adv_data->svc_name) == 0) {
+                               adv_data->next = tmp->next;
+                               tmp->next = adv_data;
+                               goto inserted;
+                       }
+               }
+               prev = &tmp->next;
+               tmp = tmp->next;
+       }
+
+       /* No svc_name match found */
+       adv_data->next = p2p->p2ps_adv_list;
+       p2p->p2ps_adv_list = adv_data;
+
+inserted:
+       p2p_dbg(p2p,
+               "Added ASP advertisement adv_id=0x%x config_methods=0x%x svc_state=0x%x adv_str='%s'",
+               adv_id, adv_data->config_methods, svc_state, adv_str);
+
+       return 0;
+}
+
+
+void p2p_service_flush_asp(struct p2p_data *p2p)
+{
+       struct p2ps_advertisement *adv, *prev;
+
+       if (!p2p)
+               return;
+
+       adv = p2p->p2ps_adv_list;
+       while (adv) {
+               prev = adv;
+               adv = adv->next;
+               os_free(prev);
+       }
+
+       p2p->p2ps_adv_list = NULL;
+       p2p_dbg(p2p, "All ASP advertisements flushed");
+}
+
+
 int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr)
 {
        struct p2p_message msg;
@@ -2499,6 +2874,8 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg)
                        p2p->cfg->num_pref_chan = 0;
        }
 
+       p2ps_gen_hash(p2p, P2PS_WILD_HASH_STR, p2p->wild_card_hash);
+
        p2p->min_disc_int = 1;
        p2p->max_disc_int = 3;
        p2p->max_disc_tu = -1;
@@ -2560,10 +2937,13 @@ void p2p_deinit(struct p2p_data *p2p)
        os_free(p2p->cfg->serial_number);
        os_free(p2p->cfg->pref_chan);
        os_free(p2p->groups);
+       os_free(p2p->p2ps_prov);
        wpabuf_free(p2p->sd_resp);
        os_free(p2p->after_scan_tx);
        p2p_remove_wps_vendor_extensions(p2p);
        os_free(p2p->no_go_freq.range);
+       p2p_service_flush_asp(p2p);
+
        os_free(p2p);
 }
 
@@ -2828,17 +3208,19 @@ static void p2p_sd_cb(struct p2p_data *p2p, int success)
                if (p2p->sd_peer)
                        p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
                p2p->sd_peer = NULL;
-               p2p_continue_find(p2p);
+               if (p2p->state != P2P_IDLE)
+                       p2p_continue_find(p2p);
                return;
        }
 
        if (p2p->sd_peer == NULL) {
                p2p_dbg(p2p, "No SD peer entry known");
-               p2p_continue_find(p2p);
+               if (p2p->state != P2P_IDLE)
+                       p2p_continue_find(p2p);
                return;
        }
 
-       if (p2p->sd_query->for_all_peers) {
+       if (p2p->sd_query && p2p->sd_query->for_all_peers) {
                /* Update the pending broadcast SD query count for this device
                 */
                p2p->sd_peer->sd_pending_bcast_queries--;
@@ -2865,9 +3247,6 @@ static void p2p_retry_pd(struct p2p_data *p2p)
 {
        struct p2p_device *dev;
 
-       if (p2p->state != P2P_IDLE)
-               return;
-
        /*
         * Retry the prov disc req attempt only for the peer that the user had
         * requested.
@@ -2941,6 +3320,51 @@ static void p2p_prov_disc_cb(struct p2p_data *p2p, int success)
 }
 
 
+static int p2p_check_after_scan_tx_continuation(struct p2p_data *p2p)
+{
+       if (p2p->after_scan_tx_in_progress) {
+               p2p->after_scan_tx_in_progress = 0;
+               if (p2p->start_after_scan != P2P_AFTER_SCAN_NOTHING &&
+                   p2p_run_after_scan(p2p))
+                       return 1;
+               if (p2p->state == P2P_SEARCH) {
+                       p2p_dbg(p2p, "Continue find after after_scan_tx completion");
+                       p2p_continue_find(p2p);
+               }
+       }
+
+       return 0;
+}
+
+
+static void p2p_prov_disc_resp_cb(struct p2p_data *p2p, int success)
+{
+       p2p_dbg(p2p, "Provision Discovery Response TX callback: success=%d",
+               success);
+
+       if (p2p->send_action_in_progress) {
+               p2p->send_action_in_progress = 0;
+               p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+       }
+
+       p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
+       if (!success)
+               goto continue_search;
+
+       if (!p2p->cfg->prov_disc_resp_cb ||
+           p2p->cfg->prov_disc_resp_cb(p2p->cfg->cb_ctx) < 1)
+               goto continue_search;
+
+       p2p_dbg(p2p,
+               "Post-Provision Discovery operations started - do not try to continue other P2P operations");
+       return;
+
+continue_search:
+       p2p_check_after_scan_tx_continuation(p2p);
+}
+
+
 int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
                         struct os_reltime *rx_time, int level, const u8 *ies,
                         size_t ies_len)
@@ -2983,6 +3407,7 @@ void p2p_scan_res_handled(struct p2p_data *p2p)
 
 void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id)
 {
+       u8 dev_capab;
        u8 *len;
 
 #ifdef CONFIG_WIFI_DISPLAY
@@ -2995,8 +3420,15 @@ void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id)
                               p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P]);
 
        len = p2p_buf_add_ie_hdr(ies);
-       p2p_buf_add_capability(ies, p2p->dev_capab &
-                              ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
+
+       dev_capab = p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+
+       /* P2PS requires Probe Request frames to include SD bit */
+       if (p2p->p2ps_seek && p2p->p2ps_seek_count)
+               dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY;
+
+       p2p_buf_add_capability(ies, dev_capab, 0);
+
        if (dev_id)
                p2p_buf_add_device_id(ies, dev_id);
        if (p2p->cfg->reg_class && p2p->cfg->channel)
@@ -3006,6 +3438,10 @@ void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id)
        if (p2p->ext_listen_interval)
                p2p_buf_add_ext_listen_timing(ies, p2p->ext_listen_period,
                                              p2p->ext_listen_interval);
+
+       if (p2p->p2ps_seek && p2p->p2ps_seek_count)
+               p2p_buf_add_service_hash(ies, p2p);
+
        /* TODO: p2p_buf_add_operating_channel() if GO */
        p2p_buf_update_ie_hdr(ies, len);
 }
@@ -3108,13 +3544,19 @@ static void p2p_go_neg_resp_failure_cb(struct p2p_data *p2p, int success,
        p2p_dbg(p2p, "GO Negotiation Response (failure) TX callback: success=%d", success);
        if (p2p->go_neg_peer && p2p->go_neg_peer->status != P2P_SC_SUCCESS) {
                p2p_go_neg_failed(p2p, p2p->go_neg_peer->status);
-       } else if (success) {
+               return;
+       }
+
+       if (success) {
                struct p2p_device *dev;
                dev = p2p_get_device(p2p, addr);
                if (dev &&
                    dev->status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
                        dev->flags |= P2P_DEV_PEER_WAITING_RESPONSE;
        }
+
+       if (p2p->state == P2P_SEARCH || p2p->state == P2P_SD_DURING_FIND)
+               p2p_continue_find(p2p);
 }
 
 
@@ -3191,9 +3633,9 @@ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
        int success;
 
        p2p_dbg(p2p, "Action frame TX callback (state=%d freq=%u dst=" MACSTR
-               " src=" MACSTR " bssid=" MACSTR " result=%d",
+               " src=" MACSTR " bssid=" MACSTR " result=%d p2p_state=%s)",
                p2p->pending_action_state, freq, MAC2STR(dst), MAC2STR(src),
-               MAC2STR(bssid), result);
+               MAC2STR(bssid), result, p2p_state_txt(p2p->state));
        success = result == P2P_SEND_ACTION_SUCCESS;
        state = p2p->pending_action_state;
        p2p->pending_action_state = P2P_NO_PENDING_ACTION;
@@ -3203,16 +3645,7 @@ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
                        p2p->send_action_in_progress = 0;
                        p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
                }
-               if (p2p->after_scan_tx_in_progress) {
-                       p2p->after_scan_tx_in_progress = 0;
-                       if (p2p->start_after_scan != P2P_AFTER_SCAN_NOTHING &&
-                           p2p_run_after_scan(p2p))
-                               break;
-                       if (p2p->state == P2P_SEARCH) {
-                               p2p_dbg(p2p, "Continue find after after_scan_tx completion");
-                               p2p_continue_find(p2p);
-                       }
-               }
+               p2p_check_after_scan_tx_continuation(p2p);
                break;
        case P2P_PENDING_GO_NEG_REQUEST:
                p2p_go_neg_req_cb(p2p, success);
@@ -3232,6 +3665,9 @@ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
        case P2P_PENDING_PD:
                p2p_prov_disc_cb(p2p, success);
                break;
+       case P2P_PENDING_PD_RESPONSE:
+               p2p_prov_disc_resp_cb(p2p, success);
+               break;
        case P2P_PENDING_INVITATION_REQUEST:
                p2p_invitation_req_cb(p2p, success);
                break;
@@ -3438,6 +3874,9 @@ static void p2p_timeout_prov_disc_during_find(struct p2p_data *p2p)
 
 static void p2p_timeout_prov_disc_req(struct p2p_data *p2p)
 {
+       u32 adv_id = 0;
+       u8 *adv_mac = NULL;
+
        p2p->pending_action_state = P2P_NO_PENDING_ACTION;
 
        /*
@@ -3466,12 +3905,18 @@ static void p2p_timeout_prov_disc_req(struct p2p_data *p2p)
                                for_join = 1;
                }
 
+               if (p2p->p2ps_prov) {
+                       adv_id = p2p->p2ps_prov->adv_id;
+                       adv_mac = p2p->p2ps_prov->adv_mac;
+               }
+
                if (p2p->cfg->prov_disc_fail)
                        p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx,
                                                 p2p->pending_pd_devaddr,
                                                 for_join ?
                                                 P2P_PROV_DISC_TIMEOUT_JOIN :
-                                                P2P_PROV_DISC_TIMEOUT);
+                                                P2P_PROV_DISC_TIMEOUT,
+                                                adv_id, adv_mac, NULL);
                p2p_reset_pending_pd(p2p);
        }
 }
@@ -3620,6 +4065,8 @@ const char * p2p_wps_method_text(enum p2p_wps_method method)
                return "PBC";
        case WPS_NFC:
                return "NFC";
+       case WPS_P2PS:
+               return "P2PS";
        }
 
        return "??";
@@ -3696,7 +4143,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
                          "country=%c%c\n"
                          "oper_freq=%d\n"
                          "req_config_methods=0x%x\n"
-                         "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s\n"
+                         "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n"
                          "status=%d\n"
                          "invitation_reqs=%u\n",
                          (int) (now.sec - dev->last_seen.sec),
@@ -3722,6 +4169,8 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
                          "[PD_PEER_DISPLAY]" : "",
                          dev->flags & P2P_DEV_PD_PEER_KEYPAD ?
                          "[PD_PEER_KEYPAD]" : "",
+                         dev->flags & P2P_DEV_PD_PEER_P2PS ?
+                         "[PD_PEER_P2PS]" : "",
                          dev->flags & P2P_DEV_USER_REJECTED ?
                          "[USER_REJECTED]" : "",
                          dev->flags & P2P_DEV_PEER_WAITING_RESPONSE ?
@@ -3740,7 +4189,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
                          "[PD_FOR_JOIN]" : "",
                          dev->status,
                          dev->invitation_reqs);
-       if (res < 0 || res >= end - pos)
+       if (os_snprintf_error(end - pos, res))
                return pos - buf;
        pos += res;
 
@@ -3750,7 +4199,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
                                  "ext_listen_interval=%u\n",
                                  dev->ext_listen_period,
                                  dev->ext_listen_interval);
-               if (res < 0 || res >= end - pos)
+               if (os_snprintf_error(end - pos, res))
                        return pos - buf;
                pos += res;
        }
@@ -3760,7 +4209,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
                                  "oper_ssid=%s\n",
                                  wpa_ssid_txt(dev->oper_ssid,
                                               dev->oper_ssid_len));
-               if (res < 0 || res >= end - pos)
+               if (os_snprintf_error(end - pos, res))
                        return pos - buf;
                pos += res;
        }
@@ -3768,7 +4217,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
 #ifdef CONFIG_WIFI_DISPLAY
        if (dev->info.wfd_subelems) {
                res = os_snprintf(pos, end - pos, "wfd_subelems=");
-               if (res < 0 || res >= end - pos)
+               if (os_snprintf_error(end - pos, res))
                        return pos - buf;
                pos += res;
 
@@ -3777,7 +4226,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
                                        wpabuf_len(dev->info.wfd_subelems));
 
                res = os_snprintf(pos, end - pos, "\n");
-               if (res < 0 || res >= end - pos)
+               if (os_snprintf_error(end - pos, res))
                        return pos - buf;
                pos += res;
        }
@@ -4153,15 +4602,18 @@ int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel,
        if (p2p_channel_to_freq(reg_class, channel) < 0)
                return -1;
 
-       p2p_dbg(p2p, "Set Listen channel: reg_class %u channel %u",
-               reg_class, channel);
-
        /*
         * Listen channel was set in configuration or set by control interface;
         * cannot override it.
         */
-       if (p2p->cfg->channel_forced && forced == 0)
+       if (p2p->cfg->channel_forced && forced == 0) {
+               p2p_dbg(p2p,
+                       "Listen channel was previously configured - do not override based on optimization");
                return -1;
+       }
+
+       p2p_dbg(p2p, "Set Listen channel: reg_class %u channel %u",
+               reg_class, channel);
 
        if (p2p->state == P2P_IDLE) {
                p2p->cfg->reg_class = reg_class;