From 36b5c3335ad512061d2b39af03bb7e3508209951 Mon Sep 17 00:00:00 2001 From: Sunil Dutt Date: Mon, 8 Dec 2014 15:11:16 +0530 Subject: [PATCH] P2P: Check Invitation Response dialog token match for resend case Commit ac330cfd87397a1a01e697984f3944f427e88dad ('P2P: Reinvite with social operation channel if no common channels') introduced a mechamisn to reinvite a peer during a persistent group reinvocation from a GO with a different operating channel proposal. This mechanism can fail if the inviting device (GO) ends up getting a retransmitted, duplicated Invitation Response frame processed second time while waiting for the response to the retried Invitation Request (using one of the social channels as the operating channel). IEEE 802.11 duplicate frame detection mechanisms are supposed to prevent this type of sequence, but not all drivers support those rules properly for pre-association frames, including P2P Public Action frames. Work around this issue by checking that the dialog token in the Invitation Response frame matches the one from the last Invitation Request if the special invitation retry mechanism is used. This is safer to do now than to enable dialog token matching for all invitation cases. Signed-off-by: Jouni Malinen --- src/p2p/p2p.c | 1 - src/p2p/p2p_i.h | 3 ++- src/p2p/p2p_invitation.c | 30 +++++++++++++++++++++++++++++- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index 6c6f1be67..77bd7fc7f 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -1731,7 +1731,6 @@ static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa, rx_freq); break; case P2P_INVITATION_RESP: - p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_process_invitation_resp(p2p, sa, data + 1, len - 1); break; case P2P_PROV_DISC_REQ: diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index 5c7c1828b..62711e7c8 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -269,7 +269,8 @@ struct p2p_data { u8 invite_go_dev_addr_buf[ETH_ALEN]; int invite_dev_pw_id; - unsigned int retry_invite_req; + unsigned int retry_invite_req:1; + unsigned int retry_invite_req_sent:1; /** * sd_peer - Pointer to Service Discovery peer diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c index 784bfa50f..558c6dd0c 100644 --- a/src/p2p/p2p_invitation.c +++ b/src/p2p/p2p_invitation.c @@ -427,22 +427,46 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, if (dev == NULL) { p2p_dbg(p2p, "Ignore Invitation Response from unknown peer " MACSTR, MAC2STR(sa)); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); return; } if (dev != p2p->invite_peer) { p2p_dbg(p2p, "Ignore unexpected Invitation Response from peer " MACSTR, MAC2STR(sa)); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); return; } - if (p2p_parse(data, len, &msg)) + if (p2p_parse(data, len, &msg)) { + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); return; + } if (!msg.status) { p2p_dbg(p2p, "Mandatory Status attribute missing in Invitation Response from " MACSTR, MAC2STR(sa)); p2p_parse_free(&msg); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + return; + } + + /* + * We should not really receive a replayed response twice since + * duplicate frames are supposed to be dropped. However, not all drivers + * do that for pre-association frames. We did not use to verify dialog + * token matches for invitation response frames, but that check can be + * safely used to drop a replayed response to the previous Invitation + * Request in case the suggested operating channel was changed. This + * allows a duplicated reject frame to be dropped with the assumption + * that the real response follows after it. + */ + if (*msg.status == P2P_SC_FAIL_NO_COMMON_CHANNELS && + p2p->retry_invite_req_sent && + msg.dialog_token != dev->dialog_token) { + p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)", + msg.dialog_token, dev->dialog_token); + p2p_parse_free(&msg); return; } @@ -451,15 +475,18 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, p2p_channel_random_social(&p2p->cfg->channels, &p2p->op_reg_class, &p2p->op_channel) == 0) { p2p->retry_invite_req = 0; + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p->cfg->stop_listen(p2p->cfg->cb_ctx); p2p_set_state(p2p, P2P_INVITE); p2p_dbg(p2p, "Resend Invitation Request setting op_class %u channel %u as operating channel", p2p->op_reg_class, p2p->op_channel); + p2p->retry_invite_req_sent = 1; p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr, p2p->invite_dev_pw_id); p2p_parse_free(&msg); return; } + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p->retry_invite_req = 0; if (!msg.channel_list && *msg.status == P2P_SC_SUCCESS) { @@ -625,6 +652,7 @@ int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role, p2p->invite_dev_pw_id = dev_pw_id; p2p->retry_invite_req = role == P2P_INVITE_ROLE_GO && persistent_group && !force_freq; + p2p->retry_invite_req_sent = 0; dev = p2p_get_device(p2p, peer); if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0 &&