P2P: Check Invitation Response dialog token match for resend case
Commit ac330cfd87
('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>
This commit is contained in:
parent
1f90dfd2cd
commit
36b5c3335a
3 changed files with 31 additions and 3 deletions
|
@ -1731,7 +1731,6 @@ static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa,
|
||||||
rx_freq);
|
rx_freq);
|
||||||
break;
|
break;
|
||||||
case P2P_INVITATION_RESP:
|
case P2P_INVITATION_RESP:
|
||||||
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
|
|
||||||
p2p_process_invitation_resp(p2p, sa, data + 1, len - 1);
|
p2p_process_invitation_resp(p2p, sa, data + 1, len - 1);
|
||||||
break;
|
break;
|
||||||
case P2P_PROV_DISC_REQ:
|
case P2P_PROV_DISC_REQ:
|
||||||
|
|
|
@ -269,7 +269,8 @@ struct p2p_data {
|
||||||
u8 invite_go_dev_addr_buf[ETH_ALEN];
|
u8 invite_go_dev_addr_buf[ETH_ALEN];
|
||||||
int invite_dev_pw_id;
|
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
|
* sd_peer - Pointer to Service Discovery peer
|
||||||
|
|
|
@ -427,22 +427,46 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
|
||||||
if (dev == NULL) {
|
if (dev == NULL) {
|
||||||
p2p_dbg(p2p, "Ignore Invitation Response from unknown peer "
|
p2p_dbg(p2p, "Ignore Invitation Response from unknown peer "
|
||||||
MACSTR, MAC2STR(sa));
|
MACSTR, MAC2STR(sa));
|
||||||
|
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dev != p2p->invite_peer) {
|
if (dev != p2p->invite_peer) {
|
||||||
p2p_dbg(p2p, "Ignore unexpected Invitation Response from peer "
|
p2p_dbg(p2p, "Ignore unexpected Invitation Response from peer "
|
||||||
MACSTR, MAC2STR(sa));
|
MACSTR, MAC2STR(sa));
|
||||||
|
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p2p_parse(data, len, &msg))
|
if (p2p_parse(data, len, &msg)) {
|
||||||
|
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!msg.status) {
|
if (!msg.status) {
|
||||||
p2p_dbg(p2p, "Mandatory Status attribute missing in Invitation Response from "
|
p2p_dbg(p2p, "Mandatory Status attribute missing in Invitation Response from "
|
||||||
MACSTR, MAC2STR(sa));
|
MACSTR, MAC2STR(sa));
|
||||||
p2p_parse_free(&msg);
|
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;
|
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_channel_random_social(&p2p->cfg->channels, &p2p->op_reg_class,
|
||||||
&p2p->op_channel) == 0) {
|
&p2p->op_channel) == 0) {
|
||||||
p2p->retry_invite_req = 0;
|
p2p->retry_invite_req = 0;
|
||||||
|
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
|
||||||
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
|
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
|
||||||
p2p_set_state(p2p, P2P_INVITE);
|
p2p_set_state(p2p, P2P_INVITE);
|
||||||
p2p_dbg(p2p, "Resend Invitation Request setting op_class %u channel %u as operating channel",
|
p2p_dbg(p2p, "Resend Invitation Request setting op_class %u channel %u as operating channel",
|
||||||
p2p->op_reg_class, p2p->op_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_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr,
|
||||||
p2p->invite_dev_pw_id);
|
p2p->invite_dev_pw_id);
|
||||||
p2p_parse_free(&msg);
|
p2p_parse_free(&msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
|
||||||
p2p->retry_invite_req = 0;
|
p2p->retry_invite_req = 0;
|
||||||
|
|
||||||
if (!msg.channel_list && *msg.status == P2P_SC_SUCCESS) {
|
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->invite_dev_pw_id = dev_pw_id;
|
||||||
p2p->retry_invite_req = role == P2P_INVITE_ROLE_GO &&
|
p2p->retry_invite_req = role == P2P_INVITE_ROLE_GO &&
|
||||||
persistent_group && !force_freq;
|
persistent_group && !force_freq;
|
||||||
|
p2p->retry_invite_req_sent = 0;
|
||||||
|
|
||||||
dev = p2p_get_device(p2p, peer);
|
dev = p2p_get_device(p2p, peer);
|
||||||
if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0 &&
|
if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0 &&
|
||||||
|
|
Loading…
Reference in a new issue