P2P: Fix association with an AP/P2P GO that is not a P2P manager
[mech_eap.git] / src / p2p / p2p.c
index 603585a..c02044b 100644 (file)
@@ -778,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)) {
@@ -1147,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;
 
@@ -1230,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,
@@ -1265,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;
@@ -1279,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;
 }
 
 
@@ -2470,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)
@@ -2716,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;
@@ -2864,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);
@@ -2899,13 +2942,7 @@ void p2p_deinit(struct p2p_data *p2p)
        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);
 }
@@ -3171,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;
        }
 
@@ -3208,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.
@@ -3508,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);
 }
 
 
@@ -3832,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;
 
        /*
@@ -3860,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);
        }
 }
@@ -4551,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;