diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index ea1676172..bcc7e6446 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -218,6 +218,8 @@ void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer, os_memset(&res, 0, sizeof(res)); res.status = status; if (peer) { + wpabuf_free(peer->go_neg_conf); + peer->go_neg_conf = NULL; os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN); os_memcpy(res.peer_interface_addr, peer->intended_addr, @@ -802,6 +804,7 @@ static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev) } wpabuf_free(dev->info.wfd_subelems); + wpabuf_free(dev->go_neg_conf); os_free(dev); } @@ -1611,6 +1614,8 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer) peer->go_neg_req_sent = 0; peer->wps_method = WPS_NOT_READY; peer->oob_pw_id = 0; + wpabuf_free(peer->go_neg_conf); + peer->go_neg_conf = NULL; p2p_set_state(p2p, P2P_PROVISIONING); p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res); @@ -2952,12 +2957,43 @@ static void p2p_go_neg_conf_cb(struct p2p_data *p2p, struct p2p_device *dev; p2p_dbg(p2p, "GO Negotiation Confirm TX callback: result=%d", result); - p2p->cfg->send_action_done(p2p->cfg->cb_ctx); if (result == P2P_SEND_ACTION_FAILED) { + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); return; } + + dev = p2p->go_neg_peer; + if (result == P2P_SEND_ACTION_NO_ACK) { + /* + * Retry GO Negotiation Confirmation + * P2P_GO_NEG_CNF_MAX_RETRY_COUNT times if we did not receive + * ACK for confirmation. + */ + if (dev && dev->go_neg_conf && + dev->go_neg_conf_sent <= P2P_GO_NEG_CNF_MAX_RETRY_COUNT) { + p2p_dbg(p2p, "GO Negotiation Confirm retry %d", + dev->go_neg_conf_sent); + p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM; + if (p2p_send_action(p2p, dev->go_neg_conf_freq, + dev->info.p2p_device_addr, + p2p->cfg->dev_addr, + dev->info.p2p_device_addr, + wpabuf_head(dev->go_neg_conf), + wpabuf_len(dev->go_neg_conf), 0) >= + 0) { + dev->go_neg_conf_sent++; + return; + } + p2p_dbg(p2p, "Failed to re-send Action frame"); + + /* + * Continue with the assumption that the first attempt + * went through and just the ACK frame was lost. + */ + } + /* * It looks like the TX status for GO Negotiation Confirm is * often showing failure even when the peer has actually @@ -2971,7 +3007,8 @@ static void p2p_go_neg_conf_cb(struct p2p_data *p2p, p2p_dbg(p2p, "Assume GO Negotiation Confirm TX was actually received by the peer even though Ack was not reported"); } - dev = p2p->go_neg_peer; + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + if (dev == NULL) return; diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c index a32cfac65..ac9390263 100644 --- a/src/p2p/p2p_go_neg.c +++ b/src/p2p/p2p_go_neg.c @@ -896,7 +896,6 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { struct p2p_device *dev; - struct wpabuf *conf; int go = -1; struct p2p_message msg; u8 status = P2P_SC_SUCCESS; @@ -1101,10 +1100,13 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN); fail: - conf = p2p_build_go_neg_conf(p2p, dev, msg.dialog_token, status, - msg.operating_channel, go); + /* Store GO Negotiation Confirmation to allow retransmission */ + wpabuf_free(dev->go_neg_conf); + dev->go_neg_conf = p2p_build_go_neg_conf(p2p, dev, msg.dialog_token, + status, msg.operating_channel, + go); p2p_parse_free(&msg); - if (conf == NULL) + if (dev->go_neg_conf == NULL) return; p2p_dbg(p2p, "Sending GO Negotiation Confirm"); if (status == P2P_SC_SUCCESS) { @@ -1116,13 +1118,18 @@ fail: freq = rx_freq; else freq = dev->listen_freq; + + dev->go_neg_conf_freq = freq; + dev->go_neg_conf_sent = 0; + if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa, - wpabuf_head(conf), wpabuf_len(conf), 200) < 0) { + wpabuf_head(dev->go_neg_conf), + wpabuf_len(dev->go_neg_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); - } - wpabuf_free(conf); + } else + dev->go_neg_conf_sent++; if (status != P2P_SC_SUCCESS) { p2p_dbg(p2p, "GO Negotiation failed"); p2p_go_neg_failed(p2p, dev, status); diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index 8c225ec54..44b66c4a1 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -12,6 +12,8 @@ #include "utils/list.h" #include "p2p.h" +#define P2P_GO_NEG_CNF_MAX_RETRY_COUNT 1 + enum p2p_role_indication; enum p2p_go_state { @@ -108,6 +110,22 @@ struct p2p_device { u8 go_timeout; u8 client_timeout; + + /** + * go_neg_conf_sent - Number of GO Negotiation Confirmation retries + */ + u8 go_neg_conf_sent; + + /** + * freq - Frquency on which the GO Negotiation Confirmation is sent + */ + int go_neg_conf_freq; + + /** + * go_neg_conf - GO Negotiation Confirmation frame + */ + struct wpabuf *go_neg_conf; + int sd_pending_bcast_queries; };