P2P: Fix association with an AP/P2P GO that is not a P2P manager
[mech_eap.git] / src / p2p / p2p.c
index f339e22..c02044b 100644 (file)
@@ -153,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;
@@ -765,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)) {
@@ -772,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;
@@ -831,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)",
@@ -867,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;
 }
 
@@ -901,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);
 }
@@ -1122,7 +1148,7 @@ 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,
-            u8 seek_count, const char **seek)
+            u8 seek_count, const char **seek, int freq)
 {
        int res;
 
@@ -1205,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,
@@ -1240,7 +1279,7 @@ 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;
@@ -1254,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;
 }
 
 
@@ -1717,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;
 }
@@ -2438,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)
@@ -2684,6 +2742,25 @@ inserted:
 }
 
 
+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;
@@ -2832,8 +2909,6 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg)
 
 void p2p_deinit(struct p2p_data *p2p)
 {
-       struct p2ps_advertisement *adv, *prev;
-
 #ifdef CONFIG_WIFI_DISPLAY
        wpabuf_free(p2p->wfd_ie_beacon);
        wpabuf_free(p2p->wfd_ie_probe_req);
@@ -2862,17 +2937,12 @@ 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);
-
-       adv = p2p->p2ps_adv_list;
-       while (adv) {
-               prev = adv;
-               adv = adv->next;
-               os_free(prev);
-       }
+       p2p_service_flush_asp(p2p);
 
        os_free(p2p);
 }
@@ -3138,13 +3208,15 @@ 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;
        }
 
@@ -3175,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.
@@ -3251,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)
@@ -3430,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);
 }
 
 
@@ -3525,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);
@@ -3554,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;
@@ -3760,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;
 
        /*
@@ -3788,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);
        }
 }
@@ -4020,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),
@@ -4046,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 ?
@@ -4477,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;