P2P: Report dev_found event (if not yet done) from GO Neg Req RX
[mech_eap.git] / src / p2p / p2p_go_neg.c
index 8dee942..a32cfac 100644 (file)
@@ -103,6 +103,8 @@ u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method)
                return DEV_PW_USER_SPECIFIED;
        case WPS_PBC:
                return DEV_PW_PUSHBUTTON;
+       case WPS_NFC:
+               return DEV_PW_NFC_CONNECTION_HANDOVER;
        default:
                return DEV_PW_DEFAULT;
        }
@@ -118,6 +120,8 @@ static const char * p2p_wps_method_str(enum p2p_wps_method wps_method)
                return "Keypad";
        case WPS_PBC:
                return "PBC";
+       case WPS_NFC:
+               return "NFC";
        default:
                return "??";
        }
@@ -131,6 +135,7 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p,
        u8 *len;
        u8 group_capab;
        size_t extra = 0;
+       u16 pw_id;
 
 #ifdef CONFIG_WIFI_DISPLAY
        if (p2p->wfd_ie_go_neg)
@@ -172,8 +177,10 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p,
        p2p_buf_update_ie_hdr(buf, len);
 
        /* WPS IE with Device Password ID attribute */
-       if (p2p_build_wps_ie(p2p, buf, p2p_wps_method_pw_id(peer->wps_method),
-                            0) < 0) {
+       pw_id = p2p_wps_method_pw_id(peer->wps_method);
+       if (peer->oob_pw_id)
+               pw_id = peer->oob_pw_id;
+       if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) {
                p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Request");
                wpabuf_free(buf);
                return NULL;
@@ -210,6 +217,8 @@ int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev)
        }
 
        freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+       if (dev->oob_go_neg_freq > 0)
+               freq = dev->oob_go_neg_freq;
        if (freq <= 0) {
                p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
                        MACSTR " to send GO Negotiation Request",
@@ -250,6 +259,7 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
        u8 *len;
        u8 group_capab;
        size_t extra = 0;
+       u16 pw_id;
 
        p2p_dbg(p2p, "Building GO Negotiation Response");
 
@@ -312,9 +322,10 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
        p2p_buf_update_ie_hdr(buf, len);
 
        /* WPS IE with Device Password ID attribute */
-       if (p2p_build_wps_ie(p2p, buf,
-                            p2p_wps_method_pw_id(peer ? peer->wps_method :
-                                                 WPS_NOT_READY), 0) < 0) {
+       pw_id = p2p_wps_method_pw_id(peer ? peer->wps_method : WPS_NOT_READY);
+       if (peer && peer->oob_pw_id)
+               pw_id = peer->oob_pw_id;
+       if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) {
                p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Response");
                wpabuf_free(buf);
                return NULL;
@@ -583,17 +594,53 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
        if (msg.status && *msg.status) {
                p2p_dbg(p2p, "Unexpected Status attribute (%d) in GO Negotiation Request",
                        *msg.status);
+               if (dev && p2p->go_neg_peer == dev &&
+                   *msg.status == P2P_SC_FAIL_REJECTED_BY_USER) {
+                       /*
+                        * This mechanism for using Status attribute in GO
+                        * Negotiation Request is not compliant with the P2P
+                        * specification, but some deployed devices use it to
+                        * indicate rejection of GO Negotiation in a case where
+                        * they have sent out GO Negotiation Response with
+                        * status 1. The P2P specification explicitly disallows
+                        * this. To avoid unnecessary interoperability issues
+                        * and extra frames, mark the pending negotiation as
+                        * failed and do not reply to this GO Negotiation
+                        * Request frame.
+                        */
+                       p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+                       p2p_go_neg_failed(p2p, dev, *msg.status);
+                       p2p_parse_free(&msg);
+                       return;
+               }
                goto fail;
        }
 
        if (dev == NULL)
                dev = p2p_add_dev_from_go_neg_req(p2p, sa, &msg);
-       else if (dev->flags & P2P_DEV_PROBE_REQ_ONLY)
+       else if ((dev->flags & P2P_DEV_PROBE_REQ_ONLY) ||
+                 !(dev->flags & P2P_DEV_REPORTED))
                p2p_add_dev_info(p2p, sa, dev, &msg);
+       else if (!dev->listen_freq && !dev->oper_freq) {
+               /*
+                * This may happen if the peer entry was added based on PD
+                * Request and no Probe Request/Response frame has been received
+                * from this peer (or that information has timed out).
+                */
+               p2p_dbg(p2p, "Update peer " MACSTR
+                       " based on GO Neg Req since listen/oper freq not known",
+                       MAC2STR(dev->info.p2p_device_addr));
+               p2p_add_dev_info(p2p, sa, dev, &msg);
+       }
+
        if (dev && dev->flags & P2P_DEV_USER_REJECTED) {
                p2p_dbg(p2p, "User has rejected this peer");
                status = P2P_SC_FAIL_REJECTED_BY_USER;
-       } else if (dev == NULL || dev->wps_method == WPS_NOT_READY) {
+       } else if (dev == NULL ||
+                  (dev->wps_method == WPS_NOT_READY &&
+                   (p2p->authorized_oob_dev_pw_id == 0 ||
+                    p2p->authorized_oob_dev_pw_id !=
+                    msg.dev_password_id))) {
                p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR,
                        MAC2STR(sa));
                status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
@@ -680,6 +727,28 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
                        }
                        break;
                default:
+                       if (msg.dev_password_id &&
+                           msg.dev_password_id == dev->oob_pw_id) {
+                               p2p_dbg(p2p, "Peer using NFC");
+                               if (dev->wps_method != WPS_NFC) {
+                                       p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
+                                               p2p_wps_method_str(
+                                                       dev->wps_method));
+                                       status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+                                       goto fail;
+                               }
+                               break;
+                       }
+#ifdef CONFIG_WPS_NFC
+                       if (p2p->authorized_oob_dev_pw_id &&
+                           msg.dev_password_id ==
+                           p2p->authorized_oob_dev_pw_id) {
+                               p2p_dbg(p2p, "Using static handover with our device password from NFC Tag");
+                               dev->wps_method = WPS_NFC;
+                               dev->oob_pw_id = p2p->authorized_oob_dev_pw_id;
+                               break;
+                       }
+#endif /* CONFIG_WPS_NFC */
                        p2p_dbg(p2p, "Unsupported Device Password ID %d",
                                msg.dev_password_id);
                        status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
@@ -1005,6 +1074,17 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
                }
                break;
        default:
+               if (msg.dev_password_id &&
+                   msg.dev_password_id == dev->oob_pw_id) {
+                       p2p_dbg(p2p, "Peer using NFC");
+                       if (dev->wps_method != WPS_NFC) {
+                               p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
+                                       p2p_wps_method_str(dev->wps_method));
+                               status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+                               goto fail;
+                       }
+                       break;
+               }
                p2p_dbg(p2p, "Unsupported Device Password ID %d",
                        msg.dev_password_id);
                status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
@@ -1037,7 +1117,7 @@ fail:
        else
                freq = dev->listen_freq;
        if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa,
-                           wpabuf_head(conf), wpabuf_len(conf), 0) < 0) {
+                           wpabuf_head(conf), wpabuf_len(conf), 200) < 0) {
                p2p_dbg(p2p, "Failed to send Action frame");
                p2p_go_neg_failed(p2p, dev, -1);
                p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
@@ -1076,6 +1156,7 @@ void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa,
 
        if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) {
                p2p_dbg(p2p, "Was not expecting GO Negotiation Confirm - ignore");
+               p2p_parse_free(&msg);
                return;
        }
        dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;