P2P: Increase the maximum number of PD Request retries
[mech_eap.git] / src / p2p / p2p_pd.c
index 2a16315..e40f2b7 100644 (file)
@@ -2,14 +2,8 @@
  * Wi-Fi Direct - P2P provision discovery
  * Copyright (c) 2009-2010, Atheros Communications
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
 #include "p2p.h"
 
 
+/*
+ * Number of retries to attempt for provision discovery requests
+ * in case the peer is not listening.
+ */
+#define MAX_PROV_DISC_REQ_RETRIES 120
+
+
 static void p2p_build_wps_ie_config_methods(struct wpabuf *buf,
                                            u16 config_methods)
 {
@@ -45,36 +46,72 @@ static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p,
 {
        struct wpabuf *buf;
        u8 *len;
+       size_t extra = 0;
 
-       buf = wpabuf_alloc(1000);
+#ifdef CONFIG_WIFI_DISPLAY
+       if (p2p->wfd_ie_prov_disc_req)
+               extra = wpabuf_len(p2p->wfd_ie_prov_disc_req);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+       buf = wpabuf_alloc(1000 + extra);
        if (buf == NULL)
                return NULL;
 
        p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token);
 
        len = p2p_buf_add_ie_hdr(buf);
-       p2p_buf_add_capability(buf, p2p->dev_capab, 0);
+       p2p_buf_add_capability(buf, p2p->dev_capab &
+                              ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
        p2p_buf_add_device_info(buf, p2p, NULL);
        if (go) {
-               p2p_buf_add_group_id(buf, go->p2p_device_addr, go->oper_ssid,
-                                    go->oper_ssid_len);
+               p2p_buf_add_group_id(buf, go->info.p2p_device_addr,
+                                    go->oper_ssid, go->oper_ssid_len);
        }
        p2p_buf_update_ie_hdr(buf, len);
 
        /* WPS IE with Config Methods attribute */
        p2p_build_wps_ie_config_methods(buf, config_methods);
 
+#ifdef CONFIG_WIFI_DISPLAY
+       if (p2p->wfd_ie_prov_disc_req)
+               wpabuf_put_buf(buf, p2p->wfd_ie_prov_disc_req);
+#endif /* CONFIG_WIFI_DISPLAY */
+
        return buf;
 }
 
 
 static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
                                                u8 dialog_token,
-                                               u16 config_methods)
+                                               u16 config_methods,
+                                               const u8 *group_id,
+                                               size_t group_id_len)
 {
        struct wpabuf *buf;
+       size_t extra = 0;
+
+#ifdef CONFIG_WIFI_DISPLAY
+       struct wpabuf *wfd_ie = p2p->wfd_ie_prov_disc_resp;
+       if (wfd_ie && group_id) {
+               size_t i;
+               for (i = 0; i < p2p->num_groups; i++) {
+                       struct p2p_group *g = p2p->groups[i];
+                       struct wpabuf *ie;
+                       if (!p2p_group_is_group_id_match(g, group_id,
+                                                        group_id_len))
+                               continue;
+                       ie = p2p_group_get_wfd_ie(g);
+                       if (ie) {
+                               wfd_ie = ie;
+                               break;
+                       }
+               }
+       }
+       if (wfd_ie)
+               extra = wpabuf_len(wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
 
-       buf = wpabuf_alloc(100);
+       buf = wpabuf_alloc(100 + extra);
        if (buf == NULL)
                return NULL;
 
@@ -83,6 +120,11 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
        /* WPS IE with Config Methods attribute */
        p2p_build_wps_ie_config_methods(buf, config_methods);
 
+#ifdef CONFIG_WIFI_DISPLAY
+       if (wfd_ie)
+               wpabuf_put_buf(buf, wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
        return buf;
 }
 
@@ -105,10 +147,19 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
                MAC2STR(sa), msg.wps_config_methods, rx_freq);
 
        dev = p2p_get_device(p2p, sa);
-       if (dev == NULL) {
+       if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
                wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
                        "P2P: Provision Discovery Request from "
                        "unknown peer " MACSTR, MAC2STR(sa));
+               if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1, 0))
+               {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: Provision Discovery Request add device "
+                               "failed " MACSTR, MAC2STR(sa));
+               }
+       } else if (msg.wfd_subelems) {
+               wpabuf_free(dev->info.wfd_subelems);
+               dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
        }
 
        if (!(msg.wps_config_methods &
@@ -119,6 +170,21 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
                goto out;
        }
 
+       if (msg.group_id) {
+               size_t i;
+               for (i = 0; i < p2p->num_groups; i++) {
+                       if (p2p_group_is_group_id_match(p2p->groups[i],
+                                                       msg.group_id,
+                                                       msg.group_id_len))
+                               break;
+               }
+               if (i == p2p->num_groups) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: PD "
+                               "request for unknown P2P Group ID - reject");
+                       goto out;
+               }
+       }
+
        if (dev)
                dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
                                P2P_DEV_PD_PEER_KEYPAD);
@@ -139,7 +205,8 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
 
 out:
        resp = p2p_build_prov_disc_resp(p2p, msg.dialog_token,
-                                       reject ? 0 : msg.wps_config_methods);
+                                       reject ? 0 : msg.wps_config_methods,
+                                       msg.group_id, msg.group_id_len);
        if (resp == NULL) {
                p2p_parse_free(&msg);
                return;
@@ -160,10 +227,9 @@ out:
                return;
        }
        p2p->pending_action_state = P2P_NO_PENDING_ACTION;
-       if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, sa,
-                                 p2p->cfg->dev_addr, p2p->cfg->dev_addr,
-                                 wpabuf_head(resp), wpabuf_len(resp), 200) <
-           0) {
+       if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
+                           p2p->cfg->dev_addr,
+                           wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
                wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
                        "P2P: Failed to send Action frame");
        }
@@ -180,8 +246,8 @@ out:
                                        msg.device_name, msg.config_methods,
                                        msg.capability ? msg.capability[0] : 0,
                                        msg.capability ? msg.capability[1] :
-                                       0);
-
+                                       0,
+                                       msg.group_id, msg.group_id_len);
        }
        p2p_parse_free(&msg);
 }
@@ -193,19 +259,20 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
        struct p2p_message msg;
        struct p2p_device *dev;
        u16 report_config_methods = 0;
+       int success = 0;
 
        if (p2p_parse(data, len, &msg))
                return;
 
        wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
-               "P2P: Received Provisioning Discovery Response from " MACSTR
+               "P2P: Received Provision Discovery Response from " MACSTR
                " with config methods 0x%x",
                MAC2STR(sa), msg.wps_config_methods);
 
        dev = p2p_get_device(p2p, sa);
        if (dev == NULL || !dev->req_config_methods) {
                wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
-                       "P2P: Ignore Provisioning Discovery Response from "
+                       "P2P: Ignore Provision Discovery Response from "
                        MACSTR " with no pending request", MAC2STR(sa));
                p2p_parse_free(&msg);
                return;
@@ -213,16 +280,32 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
 
        if (dev->dialog_token != msg.dialog_token) {
                wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
-                       "P2P: Ignore Provisioning Discovery Response with "
+                       "P2P: Ignore Provision Discovery Response with "
                        "unexpected Dialog Token %u (expected %u)",
                        msg.dialog_token, dev->dialog_token);
                p2p_parse_free(&msg);
                return;
        }
 
+       if (p2p->pending_action_state == P2P_PENDING_PD) {
+               os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
+               p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+       }
+
+       /*
+        * If the response is from the peer to whom a user initiated request
+        * was sent earlier, we reset that state info here.
+        */
+       if (p2p->user_initiated_pd &&
+           os_memcmp(p2p->pending_pd_devaddr, sa, ETH_ALEN) == 0)
+               p2p_reset_pending_pd(p2p);
+
        if (msg.wps_config_methods != dev->req_config_methods) {
                wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer rejected "
-                       "our Provisioning Discovery Request");
+                       "our Provision Discovery Request");
+               if (p2p->cfg->prov_disc_fail)
+                       p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
+                                                P2P_PROV_DISC_REJECTED);
                p2p_parse_free(&msg);
                goto out;
        }
@@ -240,38 +323,57 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
                        MAC2STR(sa));
                dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
        }
+
+       /* Store the provisioning info */
+       dev->wps_prov_info = msg.wps_config_methods;
+
        p2p_parse_free(&msg);
+       success = 1;
 
 out:
        dev->req_config_methods = 0;
        p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
-       if (p2p->cfg->prov_disc_resp)
+       if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Start GO Neg after the PD-before-GO-Neg "
+                       "workaround with " MACSTR,
+                       MAC2STR(dev->info.p2p_device_addr));
+               dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG;
+               p2p_connect_send(p2p, dev);
+               return;
+       }
+       if (success && p2p->cfg->prov_disc_resp)
                p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa,
                                         report_config_methods);
 }
 
 
 int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
-                          int join)
+                          int join, int force_freq)
 {
        struct wpabuf *req;
        int freq;
 
-       freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+       if (force_freq > 0)
+               freq = force_freq;
+       else
+               freq = dev->listen_freq > 0 ? dev->listen_freq :
+                       dev->oper_freq;
        if (freq <= 0) {
                wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
                        "P2P: No Listen/Operating frequency known for the "
                        "peer " MACSTR " to send Provision Discovery Request",
-                       MAC2STR(dev->p2p_device_addr));
+                       MAC2STR(dev->info.p2p_device_addr));
                return -1;
        }
 
        if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
-               if (!(dev->dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
+               if (!(dev->info.dev_capab &
+                     P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
                        wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
                                "P2P: Cannot use PD with P2P Device " MACSTR
                                " that is in a group and is not discoverable",
-                               MAC2STR(dev->p2p_device_addr));
+                               MAC2STR(dev->info.p2p_device_addr));
                        return -1;
                }
                /* TODO: use device discoverability request through GO */
@@ -286,25 +388,28 @@ int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
        if (req == NULL)
                return -1;
 
+       if (p2p->state != P2P_IDLE)
+               p2p_stop_listen_for_freq(p2p, freq);
        p2p->pending_action_state = P2P_PENDING_PD;
-       if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq,
-                                 dev->p2p_device_addr, p2p->cfg->dev_addr,
-                                 dev->p2p_device_addr,
-                                 wpabuf_head(req), wpabuf_len(req), 200) < 0)
-       {
+       if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
+                           p2p->cfg->dev_addr, dev->info.p2p_device_addr,
+                           wpabuf_head(req), wpabuf_len(req), 200) < 0) {
                wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
                        "P2P: Failed to send Action frame");
                wpabuf_free(req);
                return -1;
        }
 
+       os_memcpy(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN);
+
        wpabuf_free(req);
        return 0;
 }
 
 
 int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
-                     u16 config_methods, int join)
+                     u16 config_methods, int join, int force_freq,
+                     int user_initiated_pd)
 {
        struct p2p_device *dev;
 
@@ -324,11 +429,17 @@ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
        if (config_methods == 0)
                return -1;
 
+       /* Reset provisioning info */
+       dev->wps_prov_info = 0;
+
        dev->req_config_methods = config_methods;
+       if (join)
+               dev->flags |= P2P_DEV_PD_FOR_JOIN;
+       else
+               dev->flags &= ~P2P_DEV_PD_FOR_JOIN;
 
-       if (p2p->go_neg_peer ||
-           (p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH &&
-            p2p->state != P2P_LISTEN_ONLY)) {
+       if (p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH &&
+           p2p->state != P2P_LISTEN_ONLY) {
                wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Busy with other "
                        "operations; postpone Provision Discovery Request "
                        "with " MACSTR " (config methods 0x%x)",
@@ -336,5 +447,32 @@ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
                return 0;
        }
 
-       return p2p_send_prov_disc_req(p2p, dev, join);
+       p2p->user_initiated_pd = user_initiated_pd;
+
+       if (p2p->user_initiated_pd)
+               p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES;
+
+       return p2p_send_prov_disc_req(p2p, dev, join, force_freq);
+}
+
+
+void p2p_reset_pending_pd(struct p2p_data *p2p)
+{
+       struct p2p_device *dev;
+
+       dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+               if (os_memcmp(p2p->pending_pd_devaddr,
+                             dev->info.p2p_device_addr, ETH_ALEN))
+                       continue;
+               if (!dev->req_config_methods)
+                       continue;
+               if (dev->flags & P2P_DEV_PD_FOR_JOIN)
+                       continue;
+               /* Reset the config methods of the device */
+               dev->req_config_methods = 0;
+       }
+
+       p2p->user_initiated_pd = 0;
+       os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
+       p2p->pd_retries = 0;
 }