P2P: Check Invitation Response dialog token match for resend case
authorSunil Dutt <usdutt@qti.qualcomm.com>
Mon, 8 Dec 2014 09:41:16 +0000 (15:11 +0530)
committerJouni Malinen <j@w1.fi>
Tue, 9 Dec 2014 14:26:47 +0000 (16:26 +0200)
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 <jouni@qca.qualcomm.com>
src/p2p/p2p.c
src/p2p/p2p_i.h
src/p2p/p2p_invitation.c

index 6c6f1be..77bd7fc 100644 (file)
@@ -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:
index 5c7c182..62711e7 100644 (file)
@@ -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
index 784bfa5..558c6dd 100644 (file)
@@ -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 &&