When a remote candidate is added, update all prflx candidates.
Previously they were only being updated for connections using the
most current "generation" of ports. This results in the older-
generation prflx candidate pair being prioritized above newer-
generation candidate pairs.
Review-Url: https://codereview.webrtc.org/2087713002
Cr-Commit-Position: refs/heads/master@{#13245}
diff --git a/webrtc/p2p/base/p2ptransportchannel.cc b/webrtc/p2p/base/p2ptransportchannel.cc
index c55b310..1e565ec 100644
--- a/webrtc/p2p/base/p2ptransportchannel.cc
+++ b/webrtc/p2p/base/p2ptransportchannel.cc
@@ -783,6 +783,12 @@
}
}
+ // If this candidate matches what was thought to be a peer reflexive
+ // candidate, we need to update the candidate priority/etc.
+ for (Connection* conn : connections_) {
+ conn->MaybeUpdatePeerReflexiveCandidate(new_remote_candidate);
+ }
+
// Create connections to this remote candidate.
CreateConnections(new_remote_candidate, NULL);
@@ -881,9 +887,6 @@
}
// No new connection was created.
- // Check if this is a peer reflexive candidate.
- connection->MaybeUpdatePeerReflexiveCandidate(remote_candidate);
-
// It is not legal to try to change any of the parameters of an existing
// connection; however, the other side can send a duplicate candidate.
if (!remote_candidate.IsEquivalent(connection->remote_candidate())) {
diff --git a/webrtc/p2p/base/p2ptransportchannel_unittest.cc b/webrtc/p2p/base/p2ptransportchannel_unittest.cc
index 227e913..b13fac2 100644
--- a/webrtc/p2p/base/p2ptransportchannel_unittest.cc
+++ b/webrtc/p2p/base/p2ptransportchannel_unittest.cc
@@ -150,7 +150,6 @@
ss_.get(), kSocksProxyAddrs[0]),
socks_server2_(ss_.get(), kSocksProxyAddrs[1],
ss_.get(), kSocksProxyAddrs[1]),
- clear_remote_candidates_ufrag_pwd_(false),
force_relay_(false) {
ep1_.role_ = cricket::ICEROLE_CONTROLLING;
ep2_.role_ = cricket::ICEROLE_CONTROLLED;
@@ -327,9 +326,7 @@
channel->SignalRoleConflict.connect(
this, &P2PTransportChannelTestBase::OnRoleConflict);
channel->SetIceCredentials(local_ice_ufrag, local_ice_pwd);
- if (clear_remote_candidates_ufrag_pwd_) {
- // This only needs to be set if we're clearing them from the
- // candidates. Some unit tests rely on this not being set.
+ if (remote_ice_credential_source_ == FROM_SETICECREDENTIALS) {
channel->SetRemoteIceCredentials(remote_ice_ufrag, remote_ice_pwd);
}
channel->SetIceRole(GetEndpoint(endpoint)->ice_role());
@@ -706,7 +703,7 @@
static_cast<CandidatesData*>(msg->pdata));
cricket::P2PTransportChannel* rch = GetRemoteChannel(data->channel);
for (auto& c : data->candidates) {
- if (clear_remote_candidates_ufrag_pwd_) {
+ if (remote_ice_credential_source_ != FROM_CANDIDATE) {
c.set_username("");
c.set_password("");
}
@@ -787,8 +784,13 @@
return GetChannelData(ch)->ch_packets_;
}
- void set_clear_remote_candidates_ufrag_pwd(bool clear) {
- clear_remote_candidates_ufrag_pwd_ = clear;
+ enum RemoteIceCredentialSource { FROM_CANDIDATE, FROM_SETICECREDENTIALS };
+
+ // How does the test pass ICE credentials to the P2PTransportChannel?
+ // On the candidate itself, or through SetIceCredentials?
+ // Goes through the candidate itself by default.
+ void set_remote_ice_credential_source(RemoteIceCredentialSource source) {
+ remote_ice_credential_source_ = source;
}
void set_force_relay(bool relay) {
@@ -809,7 +811,7 @@
rtc::SocksProxyServer socks_server2_;
Endpoint ep1_;
Endpoint ep2_;
- bool clear_remote_candidates_ufrag_pwd_;
+ RemoteIceCredentialSource remote_ice_credential_source_ = FROM_CANDIDATE;
bool force_relay_;
};
@@ -888,7 +890,7 @@
SetAllocatorFlags(1, allocator_flags2);
SetAllocationStepDelay(1, delay);
- set_clear_remote_candidates_ufrag_pwd(true);
+ set_remote_ice_credential_source(FROM_SETICECREDENTIALS);
}
void ConfigureEndpoint(int endpoint, Config config) {
switch (config) {
@@ -1106,7 +1108,7 @@
ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags,
kDefaultPortAllocatorFlags);
// Emulate no remote credentials coming in.
- set_clear_remote_candidates_ufrag_pwd(false);
+ set_remote_ice_credential_source(FROM_CANDIDATE);
CreateChannels(1);
// Only have remote credentials come in for ep2, not ep1.
ep2_ch1()->SetRemoteIceCredentials(kIceUfrag[0], kIcePwd[0]);
@@ -1158,7 +1160,7 @@
ConfigureEndpoints(OPEN, NAT_SYMMETRIC, kDefaultPortAllocatorFlags,
kDefaultPortAllocatorFlags);
// Emulate no remote credentials coming in.
- set_clear_remote_candidates_ufrag_pwd(false);
+ set_remote_ice_credential_source(FROM_CANDIDATE);
CreateChannels(1);
// Only have remote credentials come in for ep2, not ep1.
ep2_ch1()->SetRemoteIceCredentials(kIceUfrag[0], kIcePwd[0]);
@@ -1201,9 +1203,67 @@
DestroyChannels();
}
+// Test that we properly create a connection on a STUN ping from unknown address
+// when the signaling is slow, even if the new candidate is created due to the
+// remote peer doing an ICE restart, pairing this candidate across generations.
+//
+// Previously this wasn't working due to a bug where the peer reflexive
+// candidate was only updated for the newest generation candidate pairs, and
+// not older-generation candidate pairs created by pairing candidates across
+// generations. This resulted in the old-generation prflx candidate being
+// prioritized above new-generation candidate pairs.
+TEST_F(P2PTransportChannelTest,
+ PeerReflexiveCandidateBeforeSignalingWithIceRestart) {
+ ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags,
+ kDefaultPortAllocatorFlags);
+ // Only gather relay candidates, so that when the prflx candidate arrives
+ // it's prioritized above the current candidate pair.
+ GetEndpoint(0)->allocator_->set_candidate_filter(cricket::CF_RELAY);
+ GetEndpoint(1)->allocator_->set_candidate_filter(cricket::CF_RELAY);
+ // Setting this allows us to control when SetRemoteIceCredentials is called.
+ set_remote_ice_credential_source(FROM_CANDIDATE);
+ CreateChannels(1);
+ // Wait for the initial connection to be made.
+ ep1_ch1()->SetRemoteIceCredentials(kIceUfrag[1], kIcePwd[1]);
+ ep2_ch1()->SetRemoteIceCredentials(kIceUfrag[0], kIcePwd[0]);
+ EXPECT_TRUE_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
+ ep2_ch1()->receiving() && ep2_ch1()->writable(),
+ kDefaultTimeout);
+
+ // Simulate an ICE restart on ep2, but don't signal the candidate or new
+ // ICE credentials until after a prflx connection has been made.
+ PauseCandidates(1);
+ ep2_ch1()->SetIceCredentials(kIceUfrag[3], kIcePwd[3]);
+ ep1_ch1()->SetRemoteIceCredentials(kIceUfrag[3], kIcePwd[3]);
+ ep2_ch1()->MaybeStartGathering();
+
+ // The caller should have the best connection connected to the peer reflexive
+ // candidate.
+ EXPECT_EQ_WAIT("prflx",
+ ep1_ch1()->best_connection()->remote_candidate().type(),
+ kDefaultTimeout);
+ const cricket::Connection* prflx_best_connection =
+ ep1_ch1()->best_connection();
+
+ // Now simulate the ICE restart on ep1.
+ ep1_ch1()->SetIceCredentials(kIceUfrag[2], kIcePwd[2]);
+ ep2_ch1()->SetRemoteIceCredentials(kIceUfrag[2], kIcePwd[2]);
+ ep1_ch1()->MaybeStartGathering();
+
+ // Finally send the candidates from ep2's ICE restart and verify that ep1 uses
+ // their information to update the peer reflexive candidate.
+ ResumeCandidates(1);
+
+ EXPECT_EQ_WAIT("relay",
+ ep1_ch1()->best_connection()->remote_candidate().type(),
+ kDefaultTimeout);
+ EXPECT_EQ(prflx_best_connection, ep1_ch1()->best_connection());
+ DestroyChannels();
+}
+
// Test that if remote candidates don't have ufrag and pwd, we still work.
TEST_F(P2PTransportChannelTest, RemoteCandidatesWithoutUfragPwd) {
- set_clear_remote_candidates_ufrag_pwd(true);
+ set_remote_ice_credential_source(FROM_SETICECREDENTIALS);
ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags,
kDefaultPortAllocatorFlags);
CreateChannels(1);