Prepare for ICE renomination.
Add an ICE nomination attribute. If a connection switched on the controlling side, increase the nomination value set in the attribute. The controlled side will also be ready for re-nomination option; it will switch if a nomination comes with a higher nomination value even though it may be at a lower priority. Plus, don't nominate or re-nominate if the nomination value at the current connection has been acknowledged. BUG= R=deadbeef@webrtc.org, pthatcher@webrtc.org Review URL: https://codereview.webrtc.org/2163403002 . Cr-Commit-Position: refs/heads/master@{#13631}
This commit is contained in:
parent
6ce738da31
commit
8cd8f81748
@ -403,6 +403,12 @@ void P2PTransportChannel::SetIceConfig(const IceConfig& config) {
|
||||
LOG(LS_INFO) << "Set receiving_switching_delay to"
|
||||
<< *config_.receiving_switching_delay;
|
||||
}
|
||||
|
||||
if (config_.default_nomination_mode != config.default_nomination_mode) {
|
||||
config_.default_nomination_mode = config.default_nomination_mode;
|
||||
LOG(LS_INFO) << "Set default nomination mode to "
|
||||
<< static_cast<int>(config_.default_nomination_mode);
|
||||
}
|
||||
}
|
||||
|
||||
const IceConfig& P2PTransportChannel::config() const {
|
||||
@ -1140,10 +1146,10 @@ int P2PTransportChannel::CompareConnections(
|
||||
if (ice_role_ == ICEROLE_CONTROLLED) {
|
||||
// Compare the connections based on the nomination states and the last data
|
||||
// received time if this is on the controlled side.
|
||||
if (a->nominated() && !b->nominated()) {
|
||||
if (a->remote_nomination() > b->remote_nomination()) {
|
||||
return a_is_better;
|
||||
}
|
||||
if (!a->nominated() && b->nominated()) {
|
||||
if (a->remote_nomination() < b->remote_nomination()) {
|
||||
return b_is_better;
|
||||
}
|
||||
|
||||
@ -1285,6 +1291,7 @@ void P2PTransportChannel::SwitchSelectedConnection(Connection* conn) {
|
||||
Connection* old_selected_connection = selected_connection_;
|
||||
selected_connection_ = conn;
|
||||
if (selected_connection_) {
|
||||
++nomination_;
|
||||
if (old_selected_connection) {
|
||||
LOG_J(LS_INFO, this) << "Previous selected connection: "
|
||||
<< old_selected_connection->ToString();
|
||||
@ -1585,32 +1592,66 @@ void P2PTransportChannel::MarkConnectionPinged(Connection* conn) {
|
||||
}
|
||||
|
||||
// Apart from sending ping from |conn| this method also updates
|
||||
// |use_candidate_attr| flag. The criteria to update this flag is
|
||||
// explained below.
|
||||
// Set USE-CANDIDATE if doing ICE AND this channel is in CONTROLLING AND
|
||||
// a) Channel is in FULL ICE AND
|
||||
// a.1) |conn| is the selected connection OR
|
||||
// a.2) there is no selected connection OR
|
||||
// a.3) the selected connection is unwritable OR
|
||||
// a.4) |conn| has higher priority than selected_connection.
|
||||
// b) we're doing LITE ICE AND
|
||||
// b.1) |conn| is the selected_connection AND
|
||||
// b.2) |conn| is writable.
|
||||
// |use_candidate_attr| and |nomination| flags. One of the flags is set to
|
||||
// nominate |conn| if this channel is in CONTROLLING.
|
||||
void P2PTransportChannel::PingConnection(Connection* conn) {
|
||||
bool use_candidate = false;
|
||||
if (remote_ice_mode_ == ICEMODE_FULL && ice_role_ == ICEROLE_CONTROLLING) {
|
||||
use_candidate =
|
||||
(conn == selected_connection_) || (selected_connection_ == NULL) ||
|
||||
(!selected_connection_->writable()) ||
|
||||
(CompareConnectionCandidates(selected_connection_, conn) < 0);
|
||||
} else if (remote_ice_mode_ == ICEMODE_LITE && conn == selected_connection_) {
|
||||
use_candidate = selected_connection_->writable();
|
||||
bool use_candidate_attr = false;
|
||||
uint32_t nomination = 0;
|
||||
if (ice_role_ == ICEROLE_CONTROLLING) {
|
||||
if (remote_supports_renomination_) {
|
||||
nomination = GetNominationAttr(conn);
|
||||
} else {
|
||||
use_candidate_attr =
|
||||
GetUseCandidateAttr(conn, config_.default_nomination_mode);
|
||||
}
|
||||
}
|
||||
conn->set_use_candidate_attr(use_candidate);
|
||||
conn->set_nomination(nomination);
|
||||
conn->set_use_candidate_attr(use_candidate_attr);
|
||||
last_ping_sent_ms_ = rtc::TimeMillis();
|
||||
conn->Ping(last_ping_sent_ms_);
|
||||
}
|
||||
|
||||
uint32_t P2PTransportChannel::GetNominationAttr(Connection* conn) const {
|
||||
return (conn == selected_connection_) ? nomination_ : 0;
|
||||
}
|
||||
|
||||
// Nominate a connection based on the NominationMode.
|
||||
bool P2PTransportChannel::GetUseCandidateAttr(Connection* conn,
|
||||
NominationMode mode) const {
|
||||
switch (mode) {
|
||||
case NominationMode::REGULAR:
|
||||
// TODO(honghaiz): Implement regular nomination.
|
||||
return false;
|
||||
case NominationMode::AGGRESSIVE:
|
||||
if (remote_ice_mode_ == ICEMODE_LITE) {
|
||||
return GetUseCandidateAttr(conn, NominationMode::REGULAR);
|
||||
}
|
||||
return true;
|
||||
case NominationMode::SEMI_AGGRESSIVE: {
|
||||
// Nominate if
|
||||
// a) Remote is in FULL ICE AND
|
||||
// a.1) |conn| is the selected connection OR
|
||||
// a.2) there is no selected connection OR
|
||||
// a.3) the selected connection is unwritable OR
|
||||
// a.4) |conn| has higher priority than selected_connection.
|
||||
// b) Remote is in LITE ICE AND
|
||||
// b.1) |conn| is the selected_connection AND
|
||||
// b.2) |conn| is writable.
|
||||
bool selected = conn == selected_connection_;
|
||||
if (remote_ice_mode_ == ICEMODE_LITE) {
|
||||
return selected && conn->writable();
|
||||
}
|
||||
bool better_than_selected =
|
||||
!selected_connection_ || !selected_connection_->writable() ||
|
||||
CompareConnectionCandidates(selected_connection_, conn) < 0;
|
||||
return selected || better_than_selected;
|
||||
}
|
||||
default:
|
||||
RTC_DCHECK(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// When a connection's state changes, we need to figure out who to use as
|
||||
// the selected connection again. It could have become usable, or become
|
||||
// unusable.
|
||||
|
||||
@ -209,6 +209,11 @@ class P2PTransportChannel : public TransportChannelImpl,
|
||||
return remote_candidates_;
|
||||
}
|
||||
|
||||
// Public for unit tests.
|
||||
void set_remote_supports_renomination(bool remote_supports_renomination) {
|
||||
remote_supports_renomination_ = remote_supports_renomination;
|
||||
}
|
||||
|
||||
private:
|
||||
rtc::Thread* thread() const { return worker_thread_; }
|
||||
bool IsGettingPorts() { return allocator_session()->IsGettingPorts(); }
|
||||
@ -313,6 +318,9 @@ class P2PTransportChannel : public TransportChannelImpl,
|
||||
void OnCheckAndPing();
|
||||
void OnRegatherOnFailedNetworks();
|
||||
|
||||
uint32_t GetNominationAttr(Connection* conn) const;
|
||||
bool GetUseCandidateAttr(Connection* conn, NominationMode mode) const;
|
||||
|
||||
// Returns true if we should switch to the new connection.
|
||||
// sets |missed_receiving_unchanged_threshold| to true if either
|
||||
// the selected connection or the new connection missed its
|
||||
@ -402,6 +410,13 @@ class P2PTransportChannel : public TransportChannelImpl,
|
||||
IceConfig config_;
|
||||
int last_sent_packet_id_ = -1; // -1 indicates no packet was sent before.
|
||||
bool started_pinging_ = false;
|
||||
// TODO(honghaiz): Put this and ICE role inside ICEParameters and rename this
|
||||
// as renomination. Set its value in subsequent CLs based on signaling
|
||||
// exchange.
|
||||
bool remote_supports_renomination_ = false;
|
||||
// The value put in the "nomination" attribute for the next nominated
|
||||
// connection. A zero-value indicates the connection will not be nominated.
|
||||
uint32_t nomination_ = 0;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(P2PTransportChannel);
|
||||
};
|
||||
|
||||
@ -849,6 +849,14 @@ class P2PTransportChannelTestBase : public testing::Test,
|
||||
force_relay_ = relay;
|
||||
}
|
||||
|
||||
void ConnectSignalNominated(Connection* conn) {
|
||||
conn->SignalNominated.connect(this,
|
||||
&P2PTransportChannelTestBase::OnNominated);
|
||||
}
|
||||
|
||||
void OnNominated(Connection* conn) { nominated_ = true; }
|
||||
bool nominated() { return nominated_; }
|
||||
|
||||
private:
|
||||
rtc::Thread* main_;
|
||||
std::unique_ptr<rtc::PhysicalSocketServer> pss_;
|
||||
@ -866,6 +874,8 @@ class P2PTransportChannelTestBase : public testing::Test,
|
||||
RemoteIceCredentialSource remote_ice_credential_source_ = FROM_CANDIDATE;
|
||||
bool force_relay_;
|
||||
int selected_candidate_pair_switches_ = 0;
|
||||
|
||||
bool nominated_ = false;
|
||||
};
|
||||
|
||||
// The tests have only a few outcomes, which we predefine.
|
||||
@ -1862,9 +1872,10 @@ class P2PTransportChannelMultihomedTest : public P2PTransportChannelTestBase {
|
||||
}
|
||||
|
||||
void DestroyAllButBestConnection(cricket::P2PTransportChannel* channel) {
|
||||
const cricket::Connection* best_connection = channel->best_connection();
|
||||
const cricket::Connection* selected_connection =
|
||||
channel->selected_connection();
|
||||
for (cricket::Connection* conn : channel->connections()) {
|
||||
if (conn != best_connection) {
|
||||
if (conn != selected_connection) {
|
||||
conn->Destroy();
|
||||
}
|
||||
}
|
||||
@ -1991,6 +2002,64 @@ TEST_F(P2PTransportChannelMultihomedTest, TestFailoverControllingSide) {
|
||||
DestroyChannels();
|
||||
}
|
||||
|
||||
// Test that when the controlling side switches the selected connection,
|
||||
// the nomination of the selected connection on the controlled side will
|
||||
// increase.
|
||||
TEST_F(P2PTransportChannelMultihomedTest, TestIceRenomination) {
|
||||
rtc::ScopedFakeClock clock;
|
||||
// Adding alternate address will make sure |kPublicAddrs| has the higher
|
||||
// priority than others. This is due to FakeNetwork::AddInterface method.
|
||||
AddAddress(0, kAlternateAddrs[0]);
|
||||
AddAddress(0, kPublicAddrs[0]);
|
||||
AddAddress(1, kPublicAddrs[1]);
|
||||
|
||||
// Use only local ports for simplicity.
|
||||
SetAllocatorFlags(0, kOnlyLocalPorts);
|
||||
SetAllocatorFlags(1, kOnlyLocalPorts);
|
||||
|
||||
// Create channels and let them go writable, as usual.
|
||||
CreateChannels(1);
|
||||
ep1_ch1()->set_remote_supports_renomination(true);
|
||||
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
|
||||
ep2_ch1()->receiving() &&
|
||||
ep2_ch1()->writable(),
|
||||
3000, clock);
|
||||
EXPECT_TRUE_SIMULATED_WAIT(
|
||||
ep2_ch1()->selected_connection()->remote_nomination() > 0 &&
|
||||
ep1_ch1()->selected_connection()->acked_nomination() > 0,
|
||||
kDefaultTimeout, clock);
|
||||
const Connection* selected_connection1 = ep1_ch1()->selected_connection();
|
||||
Connection* selected_connection2 =
|
||||
const_cast<Connection*>(ep2_ch1()->selected_connection());
|
||||
uint32_t remote_nomination2 = selected_connection2->remote_nomination();
|
||||
// |selected_connection2| should not be nominated any more since the previous
|
||||
// nomination has been acknowledged.
|
||||
ConnectSignalNominated(selected_connection2);
|
||||
SIMULATED_WAIT(nominated(), 3000, clock);
|
||||
EXPECT_FALSE(nominated());
|
||||
|
||||
// Make the receiving timeout shorter for testing.
|
||||
IceConfig config = CreateIceConfig(1000, GATHER_ONCE);
|
||||
ep1_ch1()->SetIceConfig(config);
|
||||
ep2_ch1()->SetIceConfig(config);
|
||||
|
||||
// Blackhole any traffic to or from the public addrs.
|
||||
LOG(LS_INFO) << "Failing over...";
|
||||
fw()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, kPublicAddrs[0]);
|
||||
|
||||
// The selected connection on the controlling side should switch.
|
||||
EXPECT_TRUE_SIMULATED_WAIT(
|
||||
ep1_ch1()->selected_connection() != selected_connection1, 3000, clock);
|
||||
// The connection on the controlled side should be nominated again
|
||||
// and have an increased nomination.
|
||||
EXPECT_TRUE_SIMULATED_WAIT(
|
||||
ep2_ch1()->selected_connection()->remote_nomination() >
|
||||
remote_nomination2,
|
||||
kDefaultTimeout, clock);
|
||||
|
||||
DestroyChannels();
|
||||
}
|
||||
|
||||
// Test that if an interface fails temporarily and then recovers quickly,
|
||||
// the selected connection will not switch.
|
||||
// The case that it will switch over to the backup connection if the selected
|
||||
@ -2295,26 +2364,27 @@ TEST_F(P2PTransportChannelMultihomedTest,
|
||||
// to be created and the new one will be the best connection.
|
||||
AddAddress(1, wifi[1], "test_wifi1", rtc::ADAPTER_TYPE_WIFI);
|
||||
const cricket::Connection* conn;
|
||||
EXPECT_TRUE_WAIT((conn = ep1_ch1()->best_connection()) != nullptr &&
|
||||
EXPECT_TRUE_WAIT((conn = ep1_ch1()->selected_connection()) != nullptr &&
|
||||
conn->remote_candidate().address().EqualIPs(wifi[1]),
|
||||
kDefaultTimeout);
|
||||
EXPECT_TRUE_WAIT((conn = ep2_ch1()->best_connection()) != nullptr &&
|
||||
EXPECT_TRUE_WAIT((conn = ep2_ch1()->selected_connection()) != nullptr &&
|
||||
conn->local_candidate().address().EqualIPs(wifi[1]),
|
||||
kDefaultTimeout);
|
||||
|
||||
// Add a new cellular interface on end point 1, we should expect a new
|
||||
// backup connection created using this new interface.
|
||||
AddAddress(0, cellular[0], "test_cellular0", rtc::ADAPTER_TYPE_CELLULAR);
|
||||
EXPECT_TRUE_WAIT(ep1_ch1()->GetState() == cricket::STATE_COMPLETED &&
|
||||
(conn = GetConnectionWithLocalAddress(
|
||||
ep1_ch1(), cellular[0])) != nullptr &&
|
||||
conn != ep1_ch1()->best_connection() && conn->writable(),
|
||||
kDefaultTimeout);
|
||||
EXPECT_TRUE_WAIT(
|
||||
ep1_ch1()->GetState() == cricket::STATE_COMPLETED &&
|
||||
(conn = GetConnectionWithLocalAddress(ep1_ch1(), cellular[0])) !=
|
||||
nullptr &&
|
||||
conn != ep1_ch1()->selected_connection() && conn->writable(),
|
||||
kDefaultTimeout);
|
||||
EXPECT_TRUE_WAIT(
|
||||
ep2_ch1()->GetState() == cricket::STATE_COMPLETED &&
|
||||
(conn = GetConnectionWithRemoteAddress(ep2_ch1(), cellular[0])) !=
|
||||
nullptr &&
|
||||
conn != ep2_ch1()->best_connection() && conn->receiving(),
|
||||
conn != ep2_ch1()->selected_connection() && conn->receiving(),
|
||||
kDefaultTimeout);
|
||||
|
||||
DestroyChannels();
|
||||
@ -2360,7 +2430,7 @@ TEST_F(P2PTransportChannelMultihomedTest,
|
||||
RemoveAddress(1, kAlternateAddrs[1]);
|
||||
AddAddress(1, kAlternateAddrs[0]);
|
||||
EXPECT_TRUE_SIMULATED_WAIT(
|
||||
ep1_ch1()->best_connection() && ep2_ch1()->best_connection() &&
|
||||
ep1_ch1()->selected_connection() && ep2_ch1()->selected_connection() &&
|
||||
LocalCandidate(ep1_ch1())->address().EqualIPs(kPublicAddrs[0]) &&
|
||||
RemoteCandidate(ep1_ch1())->address().EqualIPs(kAlternateAddrs[0]),
|
||||
3000, clock);
|
||||
@ -2397,7 +2467,8 @@ TEST_F(P2PTransportChannelMultihomedTest,
|
||||
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
|
||||
ep2_ch1()->receiving() && ep2_ch1()->writable(),
|
||||
3000, clock);
|
||||
EXPECT_TRUE(ep1_ch1()->best_connection() && ep2_ch1()->best_connection() &&
|
||||
EXPECT_TRUE(ep1_ch1()->selected_connection() &&
|
||||
ep2_ch1()->selected_connection() &&
|
||||
LocalCandidate(ep1_ch1())->address().EqualIPs(wifi[0]) &&
|
||||
RemoteCandidate(ep1_ch1())->address().EqualIPs(wifi[1]));
|
||||
|
||||
@ -2408,7 +2479,7 @@ TEST_F(P2PTransportChannelMultihomedTest,
|
||||
// Then the interface of the best connection goes away.
|
||||
RemoveAddress(0, wifi[0]);
|
||||
EXPECT_TRUE_SIMULATED_WAIT(
|
||||
ep1_ch1()->best_connection() && ep2_ch1()->best_connection() &&
|
||||
ep1_ch1()->selected_connection() && ep2_ch1()->selected_connection() &&
|
||||
LocalCandidate(ep1_ch1())->address().EqualIPs(cellular[0]) &&
|
||||
RemoteCandidate(ep1_ch1())->address().EqualIPs(wifi[1]),
|
||||
3000, clock);
|
||||
@ -2440,7 +2511,8 @@ TEST_F(P2PTransportChannelMultihomedTest, TestRestoreBackupConnection) {
|
||||
ep2_ch1()->receiving() &&
|
||||
ep2_ch1()->writable(),
|
||||
3000, clock);
|
||||
EXPECT_TRUE(ep1_ch1()->best_connection() && ep2_ch1()->best_connection() &&
|
||||
EXPECT_TRUE(ep1_ch1()->selected_connection() &&
|
||||
ep2_ch1()->selected_connection() &&
|
||||
LocalCandidate(ep1_ch1())->address().EqualIPs(wifi[0]) &&
|
||||
RemoteCandidate(ep1_ch1())->address().EqualIPs(wifi[1]));
|
||||
|
||||
@ -2454,7 +2526,7 @@ TEST_F(P2PTransportChannelMultihomedTest, TestRestoreBackupConnection) {
|
||||
EXPECT_TRUE_SIMULATED_WAIT(
|
||||
(conn = GetConnectionWithLocalAddress(ep1_ch1(), cellular[0])) !=
|
||||
nullptr &&
|
||||
conn != ep1_ch1()->best_connection() && conn->writable(),
|
||||
conn != ep1_ch1()->selected_connection() && conn->writable(),
|
||||
5000, clock);
|
||||
|
||||
DestroyChannels();
|
||||
@ -2544,13 +2616,13 @@ class P2PTransportChannelPingTest : public testing::Test,
|
||||
Connection* conn = GetConnectionTo(&channel, ip_addr, port);
|
||||
|
||||
if (conn && writable) {
|
||||
conn->ReceivedPingResponse(LOW_RTT); // make it writable
|
||||
conn->ReceivedPingResponse(LOW_RTT, "id"); // make it writable
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
|
||||
void NominateConnection(Connection* conn) {
|
||||
conn->set_nominated(true);
|
||||
void NominateConnection(Connection* conn, uint32_t remote_nomination = 1U) {
|
||||
conn->set_remote_nomination(remote_nomination);
|
||||
conn->SignalNominated(conn);
|
||||
}
|
||||
|
||||
@ -2566,13 +2638,18 @@ class P2PTransportChannelPingTest : public testing::Test,
|
||||
|
||||
void ReceivePingOnConnection(Connection* conn,
|
||||
const std::string& remote_ufrag,
|
||||
int priority) {
|
||||
int priority,
|
||||
uint32_t nomination = 0) {
|
||||
IceMessage msg;
|
||||
msg.SetType(STUN_BINDING_REQUEST);
|
||||
msg.AddAttribute(new StunByteStringAttribute(
|
||||
STUN_ATTR_USERNAME,
|
||||
conn->local_candidate().username() + ":" + remote_ufrag));
|
||||
msg.AddAttribute(new StunUInt32Attribute(STUN_ATTR_PRIORITY, priority));
|
||||
if (nomination != 0) {
|
||||
msg.AddAttribute(
|
||||
new StunUInt32Attribute(STUN_ATTR_NOMINATION, nomination));
|
||||
}
|
||||
msg.SetTransactionID(rtc::CreateRandomString(kStunTransactionIdLength));
|
||||
msg.AddMessageIntegrity(conn->local_candidate().password());
|
||||
msg.AddFingerprint();
|
||||
@ -2651,7 +2728,7 @@ TEST_F(P2PTransportChannelPingTest, TestAllConnectionsPingedSufficiently) {
|
||||
|
||||
// Low-priority connection becomes writable so that the other connection
|
||||
// is not pruned.
|
||||
conn1->ReceivedPingResponse(LOW_RTT);
|
||||
conn1->ReceivedPingResponse(LOW_RTT, "id");
|
||||
EXPECT_TRUE_WAIT(
|
||||
conn1->num_pings_sent() >= MIN_PINGS_AT_WEAK_PING_INTERVAL &&
|
||||
conn2->num_pings_sent() >= MIN_PINGS_AT_WEAK_PING_INTERVAL,
|
||||
@ -2687,7 +2764,7 @@ TEST_F(P2PTransportChannelPingTest, TestStunPingIntervals) {
|
||||
|
||||
// Stabilizing.
|
||||
|
||||
conn->ReceivedPingResponse(LOW_RTT);
|
||||
conn->ReceivedPingResponse(LOW_RTT, "id");
|
||||
int ping_sent_before = conn->num_pings_sent();
|
||||
start = clock.TimeNanos();
|
||||
// The connection becomes strong but not stable because we haven't been able
|
||||
@ -2703,7 +2780,7 @@ TEST_F(P2PTransportChannelPingTest, TestStunPingIntervals) {
|
||||
// The connection becomes stable after receiving more than RTT_RATIO rtt
|
||||
// samples.
|
||||
for (int i = 0; i < RTT_RATIO; i++) {
|
||||
conn->ReceivedPingResponse(LOW_RTT);
|
||||
conn->ReceivedPingResponse(LOW_RTT, "id");
|
||||
}
|
||||
ping_sent_before = conn->num_pings_sent();
|
||||
start = clock.TimeNanos();
|
||||
@ -2715,7 +2792,7 @@ TEST_F(P2PTransportChannelPingTest, TestStunPingIntervals) {
|
||||
|
||||
// Destabilized.
|
||||
|
||||
conn->ReceivedPingResponse(LOW_RTT);
|
||||
conn->ReceivedPingResponse(LOW_RTT, "id");
|
||||
// Create a in-flight ping.
|
||||
conn->Ping(clock.TimeNanos() / rtc::kNumNanosecsPerMillisec);
|
||||
start = clock.TimeNanos();
|
||||
@ -2797,7 +2874,7 @@ TEST_F(P2PTransportChannelPingTest, TestNoTriggeredChecksWhenWritable) {
|
||||
|
||||
EXPECT_EQ(conn2, FindNextPingableConnectionAndPingIt(&ch));
|
||||
EXPECT_EQ(conn1, FindNextPingableConnectionAndPingIt(&ch));
|
||||
conn1->ReceivedPingResponse(LOW_RTT);
|
||||
conn1->ReceivedPingResponse(LOW_RTT, "id");
|
||||
ASSERT_TRUE(conn1->writable());
|
||||
conn1->ReceivedPing();
|
||||
|
||||
@ -2914,7 +2991,7 @@ TEST_F(P2PTransportChannelPingTest, ConnectionResurrection) {
|
||||
Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
|
||||
ASSERT_TRUE(conn2 != nullptr);
|
||||
conn2->ReceivedPing();
|
||||
conn2->ReceivedPingResponse(LOW_RTT);
|
||||
conn2->ReceivedPingResponse(LOW_RTT, "id");
|
||||
|
||||
// Wait for conn1 to be pruned.
|
||||
EXPECT_TRUE_WAIT(conn1->pruned(), 3000);
|
||||
@ -3015,11 +3092,10 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBeforeNomination) {
|
||||
ASSERT_TRUE(conn3 != nullptr);
|
||||
// Because it has a lower priority, the selected connection is still conn2.
|
||||
EXPECT_EQ(conn2, ch.selected_connection());
|
||||
conn3->ReceivedPingResponse(LOW_RTT); // Become writable.
|
||||
conn3->ReceivedPingResponse(LOW_RTT, "id"); // Become writable.
|
||||
// But if it is nominated via use_candidate, it is chosen as the selected
|
||||
// connection.
|
||||
conn3->set_nominated(true);
|
||||
conn3->SignalNominated(conn3);
|
||||
NominateConnection(conn3);
|
||||
EXPECT_EQ(conn3, ch.selected_connection());
|
||||
EXPECT_EQ(conn3, last_selected_candidate_pair());
|
||||
EXPECT_EQ(-1, last_sent_packet_id());
|
||||
@ -3035,13 +3111,12 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBeforeNomination) {
|
||||
EXPECT_EQ(conn3, ch.selected_connection());
|
||||
// But if it is nominated via use_candidate and writable, it will be set as
|
||||
// the selected connection.
|
||||
conn4->set_nominated(true);
|
||||
conn4->SignalNominated(conn4);
|
||||
NominateConnection(conn4);
|
||||
// Not switched yet because conn4 is not writable.
|
||||
EXPECT_EQ(conn3, ch.selected_connection());
|
||||
reset_channel_ready_to_send();
|
||||
// The selected connection switches after conn4 becomes writable.
|
||||
conn4->ReceivedPingResponse(LOW_RTT);
|
||||
conn4->ReceivedPingResponse(LOW_RTT, "id");
|
||||
EXPECT_EQ_WAIT(conn4, ch.selected_connection(), kDefaultTimeout);
|
||||
EXPECT_EQ(conn4, last_selected_candidate_pair());
|
||||
EXPECT_EQ(last_packet_id, last_sent_packet_id());
|
||||
@ -3076,7 +3151,7 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionFromUnknownAddress) {
|
||||
ASSERT_TRUE(conn1 != nullptr);
|
||||
EXPECT_TRUE(port->sent_binding_response());
|
||||
EXPECT_EQ(conn1, ch.selected_connection());
|
||||
conn1->ReceivedPingResponse(LOW_RTT);
|
||||
conn1->ReceivedPingResponse(LOW_RTT, "id");
|
||||
EXPECT_EQ(conn1, ch.selected_connection());
|
||||
port->set_sent_binding_response(false);
|
||||
|
||||
@ -3088,9 +3163,8 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionFromUnknownAddress) {
|
||||
EXPECT_EQ(conn1, ch.selected_connection());
|
||||
// When it is nominated via use_candidate and writable, it is chosen as the
|
||||
// selected connection.
|
||||
conn2->ReceivedPingResponse(LOW_RTT); // Become writable.
|
||||
conn2->set_nominated(true);
|
||||
conn2->SignalNominated(conn2);
|
||||
conn2->ReceivedPingResponse(LOW_RTT, "id"); // Become writable.
|
||||
NominateConnection(conn2);
|
||||
EXPECT_EQ(conn2, ch.selected_connection());
|
||||
|
||||
// Another request with unknown address, it will not be set as the selected
|
||||
@ -3101,7 +3175,7 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionFromUnknownAddress) {
|
||||
Connection* conn3 = WaitForConnectionTo(&ch, "3.3.3.3", 3);
|
||||
ASSERT_TRUE(conn3 != nullptr);
|
||||
EXPECT_TRUE(port->sent_binding_response());
|
||||
conn3->ReceivedPingResponse(LOW_RTT); // Become writable.
|
||||
conn3->ReceivedPingResponse(LOW_RTT, "id"); // Become writable.
|
||||
EXPECT_EQ(conn2, ch.selected_connection());
|
||||
port->set_sent_binding_response(false);
|
||||
|
||||
@ -3115,7 +3189,7 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionFromUnknownAddress) {
|
||||
EXPECT_TRUE(port->sent_binding_response());
|
||||
// conn4 is not the selected connection yet because it is not writable.
|
||||
EXPECT_EQ(conn2, ch.selected_connection());
|
||||
conn4->ReceivedPingResponse(LOW_RTT); // Become writable.
|
||||
conn4->ReceivedPingResponse(LOW_RTT, "id"); // Become writable.
|
||||
EXPECT_EQ_WAIT(conn4, ch.selected_connection(), kDefaultTimeout);
|
||||
|
||||
// Test that the request from an unknown address contains a ufrag from an old
|
||||
@ -3155,7 +3229,7 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBasedOnMediaReceived) {
|
||||
conn2->ReceivedPing(); // Start receiving.
|
||||
conn2->OnReadPacket("ABC", 3, rtc::CreatePacketTime(0));
|
||||
EXPECT_EQ(conn2, ch.selected_connection());
|
||||
conn2->ReceivedPingResponse(LOW_RTT); // Become writable.
|
||||
conn2->ReceivedPingResponse(LOW_RTT, "id"); // Become writable.
|
||||
|
||||
// Now another STUN message with an unknown address and use_candidate will
|
||||
// nominate the selected connection.
|
||||
@ -3173,13 +3247,13 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBasedOnMediaReceived) {
|
||||
Connection* conn3 = WaitForConnectionTo(&ch, "3.3.3.3", 3);
|
||||
ASSERT_TRUE(conn3 != nullptr);
|
||||
EXPECT_EQ(conn2, ch.selected_connection()); // Not writable yet.
|
||||
conn3->ReceivedPingResponse(LOW_RTT); // Become writable.
|
||||
conn3->ReceivedPingResponse(LOW_RTT, "id"); // Become writable.
|
||||
EXPECT_EQ_WAIT(conn3, ch.selected_connection(), kDefaultTimeout);
|
||||
|
||||
// Now another data packet will not switch the selected connection because the
|
||||
// selected connection was nominated by the controlling side.
|
||||
conn2->ReceivedPing();
|
||||
conn2->ReceivedPingResponse(LOW_RTT);
|
||||
conn2->ReceivedPingResponse(LOW_RTT, "id");
|
||||
conn2->OnReadPacket("XYZ", 3, rtc::CreatePacketTime(0));
|
||||
EXPECT_EQ_WAIT(conn3, ch.selected_connection(), kDefaultTimeout);
|
||||
}
|
||||
@ -3268,6 +3342,65 @@ TEST_F(P2PTransportChannelPingTest,
|
||||
EXPECT_EQ(0, reset_selected_candidate_pair_switches());
|
||||
}
|
||||
|
||||
TEST_F(P2PTransportChannelPingTest,
|
||||
TestControlledAgentSelectsConnectionWithHigherNomination) {
|
||||
rtc::ScopedFakeClock clock;
|
||||
|
||||
FakePortAllocator pa(rtc::Thread::Current(), nullptr);
|
||||
P2PTransportChannel ch("test", 1, &pa);
|
||||
PrepareChannel(&ch);
|
||||
ch.SetIceRole(ICEROLE_CONTROLLED);
|
||||
ch.MaybeStartGathering();
|
||||
// The connections have decreasing priority.
|
||||
Connection* conn1 =
|
||||
CreateConnectionWithCandidate(ch, clock, "1.1.1.1", 1, 10, false);
|
||||
ASSERT_TRUE(conn1 != nullptr);
|
||||
Connection* conn2 =
|
||||
CreateConnectionWithCandidate(ch, clock, "2.2.2.2", 2, 9, false);
|
||||
ASSERT_TRUE(conn2 != nullptr);
|
||||
|
||||
// conn1 is the selected connection because it has a higher priority,
|
||||
EXPECT_EQ_SIMULATED_WAIT(conn1, last_selected_candidate_pair(),
|
||||
kDefaultTimeout, clock);
|
||||
reset_selected_candidate_pair_switches();
|
||||
|
||||
// conn2 is nominated; it becomes selected.
|
||||
NominateConnection(conn2);
|
||||
EXPECT_EQ(1, reset_selected_candidate_pair_switches());
|
||||
EXPECT_EQ(conn2, last_selected_candidate_pair());
|
||||
|
||||
// conn1 is selected because of its priority.
|
||||
NominateConnection(conn1);
|
||||
EXPECT_EQ(1, reset_selected_candidate_pair_switches());
|
||||
EXPECT_EQ(conn1, last_selected_candidate_pair());
|
||||
|
||||
// conn2 gets higher remote nomination; it is selected again.
|
||||
NominateConnection(conn2, 2U);
|
||||
EXPECT_EQ(1, reset_selected_candidate_pair_switches());
|
||||
EXPECT_EQ(conn2, last_selected_candidate_pair());
|
||||
|
||||
// Make sure sorting won't reselect candidate pair.
|
||||
SIMULATED_WAIT(false, 100, clock);
|
||||
EXPECT_EQ(0, reset_selected_candidate_pair_switches());
|
||||
}
|
||||
|
||||
TEST_F(P2PTransportChannelPingTest,
|
||||
TestControlledAgentIgnoresSmallerNomination) {
|
||||
rtc::ScopedFakeClock clock;
|
||||
FakePortAllocator pa(rtc::Thread::Current(), nullptr);
|
||||
P2PTransportChannel ch("test", 1, &pa);
|
||||
PrepareChannel(&ch);
|
||||
ch.SetIceRole(ICEROLE_CONTROLLED);
|
||||
ch.MaybeStartGathering();
|
||||
Connection* conn =
|
||||
CreateConnectionWithCandidate(ch, clock, "1.1.1.1", 1, 10, false);
|
||||
ReceivePingOnConnection(conn, kIceUfrag[1], 1, 2U);
|
||||
EXPECT_EQ(2U, conn->remote_nomination());
|
||||
// Smaller nomination is ignored.
|
||||
ReceivePingOnConnection(conn, kIceUfrag[1], 1, 1U);
|
||||
EXPECT_EQ(2U, conn->remote_nomination());
|
||||
}
|
||||
|
||||
TEST_F(P2PTransportChannelPingTest,
|
||||
TestControlledAgentWriteStateTakesHigherPrecedenceThanNomination) {
|
||||
rtc::ScopedFakeClock clock;
|
||||
@ -3289,7 +3422,7 @@ TEST_F(P2PTransportChannelPingTest,
|
||||
EXPECT_EQ(1, reset_selected_candidate_pair_switches());
|
||||
|
||||
// conn2 becomes writable; it is selected even though it is not nominated.
|
||||
conn2->ReceivedPingResponse(LOW_RTT);
|
||||
conn2->ReceivedPingResponse(LOW_RTT, "id");
|
||||
|
||||
EXPECT_EQ_SIMULATED_WAIT(1, reset_selected_candidate_pair_switches(),
|
||||
kDefaultTimeout, clock);
|
||||
@ -3297,7 +3430,7 @@ TEST_F(P2PTransportChannelPingTest,
|
||||
kDefaultTimeout, clock);
|
||||
|
||||
// If conn1 is also writable, it will become selected.
|
||||
conn1->ReceivedPingResponse(LOW_RTT);
|
||||
conn1->ReceivedPingResponse(LOW_RTT, "id");
|
||||
EXPECT_EQ_SIMULATED_WAIT(1, reset_selected_candidate_pair_switches(),
|
||||
kDefaultTimeout, clock);
|
||||
EXPECT_EQ_SIMULATED_WAIT(conn1, last_selected_candidate_pair(),
|
||||
@ -3358,16 +3491,15 @@ TEST_F(P2PTransportChannelPingTest, TestDontPruneWhenWeak) {
|
||||
Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
|
||||
ASSERT_TRUE(conn1 != nullptr);
|
||||
EXPECT_EQ(conn1, ch.selected_connection());
|
||||
conn1->ReceivedPingResponse(LOW_RTT); // Becomes writable and receiving
|
||||
conn1->ReceivedPingResponse(LOW_RTT, "id"); // Becomes writable and receiving
|
||||
|
||||
// When a higher-priority, nominated candidate comes in, the connections with
|
||||
// lower-priority are pruned.
|
||||
ch.AddRemoteCandidate(CreateUdpCandidate(LOCAL_PORT_TYPE, "2.2.2.2", 2, 10));
|
||||
Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
|
||||
ASSERT_TRUE(conn2 != nullptr);
|
||||
conn2->ReceivedPingResponse(LOW_RTT); // Becomes writable and receiving
|
||||
conn2->set_nominated(true);
|
||||
conn2->SignalNominated(conn2);
|
||||
conn2->ReceivedPingResponse(LOW_RTT, "id"); // Becomes writable and receiving
|
||||
NominateConnection(conn2);
|
||||
EXPECT_TRUE_WAIT(conn1->pruned(), 3000);
|
||||
|
||||
ch.SetIceConfig(CreateIceConfig(500, GATHER_ONCE));
|
||||
@ -3422,7 +3554,7 @@ TEST_F(P2PTransportChannelPingTest, TestGetState) {
|
||||
// Now there are two connections, so the transport channel is connecting.
|
||||
EXPECT_EQ(TransportChannelState::STATE_CONNECTING, ch.GetState());
|
||||
// |conn1| becomes writable and receiving; it then should prune |conn2|.
|
||||
conn1->ReceivedPingResponse(LOW_RTT);
|
||||
conn1->ReceivedPingResponse(LOW_RTT, "id");
|
||||
EXPECT_TRUE_WAIT(conn2->pruned(), 1000);
|
||||
EXPECT_EQ(TransportChannelState::STATE_COMPLETED, ch.GetState());
|
||||
conn1->Prune(); // All connections are pruned.
|
||||
@ -3444,7 +3576,7 @@ TEST_F(P2PTransportChannelPingTest, TestConnectionPrunedAgain) {
|
||||
Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
|
||||
ASSERT_TRUE(conn1 != nullptr);
|
||||
EXPECT_EQ(conn1, ch.selected_connection());
|
||||
conn1->ReceivedPingResponse(LOW_RTT); // Becomes writable and receiving
|
||||
conn1->ReceivedPingResponse(LOW_RTT, "id"); // Becomes writable and receiving
|
||||
|
||||
// Add a low-priority connection |conn2|, which will be pruned, but it will
|
||||
// not be deleted right away. Once the current selected connection becomes not
|
||||
@ -3463,12 +3595,12 @@ TEST_F(P2PTransportChannelPingTest, TestConnectionPrunedAgain) {
|
||||
conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
|
||||
ASSERT_TRUE(conn2 != nullptr);
|
||||
EXPECT_EQ_WAIT(Connection::STATE_INPROGRESS, conn2->state(), 1000);
|
||||
conn2->ReceivedPingResponse(LOW_RTT);
|
||||
conn2->ReceivedPingResponse(LOW_RTT, "id");
|
||||
EXPECT_EQ_WAIT(conn2, ch.selected_connection(), 1000);
|
||||
EXPECT_EQ(TransportChannelState::STATE_CONNECTING, ch.GetState());
|
||||
|
||||
// When |conn1| comes back again, |conn2| will be pruned again.
|
||||
conn1->ReceivedPingResponse(LOW_RTT);
|
||||
conn1->ReceivedPingResponse(LOW_RTT, "id");
|
||||
EXPECT_EQ_WAIT(conn1, ch.selected_connection(), 1000);
|
||||
EXPECT_TRUE_WAIT(!conn2->active(), 1000);
|
||||
EXPECT_EQ(TransportChannelState::STATE_COMPLETED, ch.GetState());
|
||||
@ -3517,7 +3649,7 @@ TEST_F(P2PTransportChannelPingTest, TestStopPortAllocatorSessions) {
|
||||
ch.AddRemoteCandidate(CreateUdpCandidate(LOCAL_PORT_TYPE, "1.1.1.1", 1, 100));
|
||||
Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
|
||||
ASSERT_TRUE(conn1 != nullptr);
|
||||
conn1->ReceivedPingResponse(LOW_RTT); // Becomes writable and receiving
|
||||
conn1->ReceivedPingResponse(LOW_RTT, "id"); // Becomes writable and receiving
|
||||
EXPECT_TRUE(!ch.allocator_session()->IsGettingPorts());
|
||||
|
||||
// Start a new session. Even though conn1, which belongs to an older
|
||||
@ -3526,7 +3658,7 @@ TEST_F(P2PTransportChannelPingTest, TestStopPortAllocatorSessions) {
|
||||
ch.SetIceCredentials(kIceUfrag[1], kIcePwd[1]);
|
||||
ch.MaybeStartGathering();
|
||||
conn1->Prune();
|
||||
conn1->ReceivedPingResponse(LOW_RTT);
|
||||
conn1->ReceivedPingResponse(LOW_RTT, "id");
|
||||
EXPECT_TRUE(ch.allocator_session()->IsGettingPorts());
|
||||
|
||||
// But if a new connection created from the new session becomes writable,
|
||||
@ -3534,7 +3666,7 @@ TEST_F(P2PTransportChannelPingTest, TestStopPortAllocatorSessions) {
|
||||
ch.AddRemoteCandidate(CreateUdpCandidate(LOCAL_PORT_TYPE, "2.2.2.2", 2, 100));
|
||||
Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
|
||||
ASSERT_TRUE(conn2 != nullptr);
|
||||
conn2->ReceivedPingResponse(LOW_RTT); // Becomes writable and receiving
|
||||
conn2->ReceivedPingResponse(LOW_RTT, "id"); // Becomes writable and receiving
|
||||
EXPECT_TRUE(!ch.allocator_session()->IsGettingPorts());
|
||||
}
|
||||
|
||||
@ -3707,7 +3839,7 @@ TEST_F(P2PTransportChannelMostLikelyToWorkFirstTest,
|
||||
Connection* conn3 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
|
||||
EXPECT_EQ(conn3->local_candidate().type(), LOCAL_PORT_TYPE);
|
||||
EXPECT_EQ(conn3->remote_candidate().type(), RELAY_PORT_TYPE);
|
||||
conn3->ReceivedPingResponse(LOW_RTT);
|
||||
conn3->ReceivedPingResponse(LOW_RTT, "id");
|
||||
ASSERT_TRUE(conn3->writable());
|
||||
conn3->ReceivedPing();
|
||||
|
||||
@ -3725,7 +3857,7 @@ TEST_F(P2PTransportChannelMostLikelyToWorkFirstTest,
|
||||
// pingable connection.
|
||||
EXPECT_TRUE_WAIT(conn3 == ch.selected_connection(), 5000);
|
||||
WAIT(false, max_strong_interval + 100);
|
||||
conn3->ReceivedPingResponse(LOW_RTT);
|
||||
conn3->ReceivedPingResponse(LOW_RTT, "id");
|
||||
ASSERT_TRUE(conn3->writable());
|
||||
EXPECT_EQ(conn3, FindNextPingableConnectionAndPingIt(&ch));
|
||||
|
||||
|
||||
@ -775,15 +775,17 @@ class ConnectionRequest : public StunRequest {
|
||||
if (connection_->port()->GetIceRole() == ICEROLE_CONTROLLING) {
|
||||
request->AddAttribute(new StunUInt64Attribute(
|
||||
STUN_ATTR_ICE_CONTROLLING, connection_->port()->IceTiebreaker()));
|
||||
// Since we are trying aggressive nomination, sending USE-CANDIDATE
|
||||
// attribute in every ping.
|
||||
// If we are dealing with a ice-lite end point, nomination flag
|
||||
// in Connection will be set to false by default. Once the connection
|
||||
// becomes "best connection", nomination flag will be turned on.
|
||||
// We should have either USE_CANDIDATE attribute or ICE_NOMINATION
|
||||
// attribute but not both. That was enforced in p2ptransportchannel.
|
||||
if (connection_->use_candidate_attr()) {
|
||||
request->AddAttribute(new StunByteStringAttribute(
|
||||
STUN_ATTR_USE_CANDIDATE));
|
||||
}
|
||||
if (connection_->nomination() &&
|
||||
connection_->nomination() != connection_->acked_nomination()) {
|
||||
request->AddAttribute(new StunUInt32Attribute(
|
||||
STUN_ATTR_NOMINATION, connection_->nomination()));
|
||||
}
|
||||
} else if (connection_->port()->GetIceRole() == ICEROLE_CONTROLLED) {
|
||||
request->AddAttribute(new StunUInt64Attribute(
|
||||
STUN_ATTR_ICE_CONTROLLED, connection_->port()->IceTiebreaker()));
|
||||
@ -846,12 +848,13 @@ Connection::Connection(Port* port,
|
||||
: port_(port),
|
||||
local_candidate_index_(index),
|
||||
remote_candidate_(remote_candidate),
|
||||
recv_rate_tracker_(100, 10u),
|
||||
send_rate_tracker_(100, 10u),
|
||||
write_state_(STATE_WRITE_INIT),
|
||||
receiving_(false),
|
||||
connected_(true),
|
||||
pruned_(false),
|
||||
use_candidate_attr_(false),
|
||||
nominated_(false),
|
||||
remote_ice_mode_(ICEMODE_FULL),
|
||||
requests_(port->thread()),
|
||||
rtt_(DEFAULT_RTT),
|
||||
@ -859,8 +862,6 @@ Connection::Connection(Port* port,
|
||||
last_ping_received_(0),
|
||||
last_data_received_(0),
|
||||
last_ping_response_received_(0),
|
||||
recv_rate_tracker_(100, 10u),
|
||||
send_rate_tracker_(100, 10u),
|
||||
reported_(false),
|
||||
state_(STATE_WAITING),
|
||||
receiving_timeout_(WEAK_CONNECTION_RECEIVE_TIMEOUT),
|
||||
@ -1062,10 +1063,24 @@ void Connection::HandleBindingRequest(IceMessage* msg) {
|
||||
}
|
||||
|
||||
if (port_->GetIceRole() == ICEROLE_CONTROLLED) {
|
||||
const StunByteStringAttribute* use_candidate_attr =
|
||||
msg->GetByteString(STUN_ATTR_USE_CANDIDATE);
|
||||
if (use_candidate_attr) {
|
||||
set_nominated(true);
|
||||
const StunUInt32Attribute* nomination_attr =
|
||||
msg->GetUInt32(STUN_ATTR_NOMINATION);
|
||||
uint32_t nomination = 0;
|
||||
if (nomination_attr) {
|
||||
nomination = nomination_attr->value();
|
||||
if (nomination == 0) {
|
||||
LOG(LS_ERROR) << "Invalid nomination: " << nomination;
|
||||
}
|
||||
} else {
|
||||
const StunByteStringAttribute* use_candidate_attr =
|
||||
msg->GetByteString(STUN_ATTR_USE_CANDIDATE);
|
||||
if (use_candidate_attr) {
|
||||
nomination = 1;
|
||||
}
|
||||
}
|
||||
// We don't un-nominate a connection, so we only keep a larger nomination.
|
||||
if (nomination > remote_nomination_) {
|
||||
set_remote_nomination(nomination);
|
||||
SignalNominated(this);
|
||||
}
|
||||
}
|
||||
@ -1199,9 +1214,10 @@ void Connection::UpdateState(int64_t now) {
|
||||
void Connection::Ping(int64_t now) {
|
||||
last_ping_sent_ = now;
|
||||
ConnectionRequest *req = new ConnectionRequest(this);
|
||||
pings_since_last_response_.push_back(SentPing(req->id(), now));
|
||||
pings_since_last_response_.push_back(SentPing(req->id(), now, nomination_));
|
||||
LOG_J(LS_VERBOSE, this) << "Sending STUN ping "
|
||||
<< ", id=" << rtc::hex_encode(req->id());
|
||||
<< ", id=" << rtc::hex_encode(req->id())
|
||||
<< ", nomination=" << nomination_;
|
||||
requests_.Send(req);
|
||||
state_ = STATE_INPROGRESS;
|
||||
num_pings_sent_++;
|
||||
@ -1212,17 +1228,25 @@ void Connection::ReceivedPing() {
|
||||
UpdateReceiving(last_ping_received_);
|
||||
}
|
||||
|
||||
void Connection::ReceivedPingResponse(int rtt) {
|
||||
void Connection::ReceivedPingResponse(int rtt, const std::string& request_id) {
|
||||
// We've already validated that this is a STUN binding response with
|
||||
// the correct local and remote username for this connection.
|
||||
// So if we're not already, become writable. We may be bringing a pruned
|
||||
// connection back to life, but if we don't really want it, we can always
|
||||
// prune it again.
|
||||
auto iter = std::find_if(
|
||||
pings_since_last_response_.begin(), pings_since_last_response_.end(),
|
||||
[request_id](const SentPing& ping) { return ping.id == request_id; });
|
||||
if (iter != pings_since_last_response_.end() &&
|
||||
iter->nomination > acked_nomination_) {
|
||||
acked_nomination_ = iter->nomination;
|
||||
}
|
||||
|
||||
pings_since_last_response_.clear();
|
||||
last_ping_response_received_ = rtc::TimeMillis();
|
||||
UpdateReceiving(last_ping_response_received_);
|
||||
set_write_state(STATE_WRITABLE);
|
||||
set_state(STATE_SUCCEEDED);
|
||||
pings_since_last_response_.clear();
|
||||
rtt_samples_++;
|
||||
rtt_ = (RTT_RATIO * rtt_ + rtt) / (RTT_RATIO + 1);
|
||||
}
|
||||
@ -1295,21 +1319,16 @@ std::string Connection::ToString() const {
|
||||
const Candidate& local = local_candidate();
|
||||
const Candidate& remote = remote_candidate();
|
||||
std::stringstream ss;
|
||||
ss << "Conn[" << ToDebugId()
|
||||
<< ":" << port_->content_name()
|
||||
<< ":" << local.id() << ":" << local.component()
|
||||
<< ":" << local.generation()
|
||||
<< ":" << local.type() << ":" << local.protocol()
|
||||
<< ":" << local.address().ToSensitiveString()
|
||||
<< "->" << remote.id() << ":" << remote.component()
|
||||
<< ":" << remote.priority()
|
||||
<< ":" << remote.type() << ":"
|
||||
<< remote.protocol() << ":" << remote.address().ToSensitiveString() << "|"
|
||||
<< CONNECT_STATE_ABBREV[connected()]
|
||||
<< RECEIVE_STATE_ABBREV[receiving()]
|
||||
<< WRITE_STATE_ABBREV[write_state()]
|
||||
<< ICESTATE[state()] << "|"
|
||||
<< priority() << "|";
|
||||
ss << "Conn[" << ToDebugId() << ":" << port_->content_name() << ":"
|
||||
<< local.id() << ":" << local.component() << ":" << local.generation()
|
||||
<< ":" << local.type() << ":" << local.protocol() << ":"
|
||||
<< local.address().ToSensitiveString() << "->" << remote.id() << ":"
|
||||
<< remote.component() << ":" << remote.priority() << ":" << remote.type()
|
||||
<< ":" << remote.protocol() << ":" << remote.address().ToSensitiveString()
|
||||
<< "|" << CONNECT_STATE_ABBREV[connected()]
|
||||
<< RECEIVE_STATE_ABBREV[receiving()] << WRITE_STATE_ABBREV[write_state()]
|
||||
<< ICESTATE[state()] << "|" << remote_nomination() << "|" << nomination()
|
||||
<< "|" << priority() << "|";
|
||||
if (rtt_ < DEFAULT_RTT) {
|
||||
ss << rtt_ << "]";
|
||||
} else {
|
||||
@ -1330,20 +1349,16 @@ void Connection::OnConnectionRequestResponse(ConnectionRequest* request,
|
||||
|
||||
int rtt = request->Elapsed();
|
||||
|
||||
ReceivedPingResponse(rtt);
|
||||
|
||||
if (LOG_CHECK_LEVEL_V(sev)) {
|
||||
bool use_candidate = (
|
||||
response->GetByteString(STUN_ATTR_USE_CANDIDATE) != nullptr);
|
||||
std::string pings;
|
||||
PrintPingsSinceLastResponse(&pings, 5);
|
||||
LOG_JV(sev, this) << "Received STUN ping response"
|
||||
<< ", id=" << rtc::hex_encode(request->id())
|
||||
<< ", code=0" // Makes logging easier to parse.
|
||||
<< ", rtt=" << rtt
|
||||
<< ", use_candidate=" << use_candidate
|
||||
<< ", pings_since_last_response=" << pings;
|
||||
}
|
||||
ReceivedPingResponse(rtt, request->id());
|
||||
|
||||
stats_.recv_ping_responses++;
|
||||
|
||||
@ -1390,10 +1405,10 @@ void Connection::OnConnectionRequestTimeout(ConnectionRequest* request) {
|
||||
void Connection::OnConnectionRequestSent(ConnectionRequest* request) {
|
||||
// Log at LS_INFO if we send a ping on an unwritable connection.
|
||||
rtc::LoggingSeverity sev = !writable() ? rtc::LS_INFO : rtc::LS_VERBOSE;
|
||||
bool use_candidate = use_candidate_attr();
|
||||
LOG_JV(sev, this) << "Sent STUN ping"
|
||||
<< ", id=" << rtc::hex_encode(request->id())
|
||||
<< ", use_candidate=" << use_candidate;
|
||||
<< ", use_candidate=" << use_candidate_attr()
|
||||
<< ", nomination=" << nomination();
|
||||
stats_.sent_ping_requests_total++;
|
||||
if (stats_.recv_ping_responses == 0) {
|
||||
stats_.sent_ping_requests_before_first_response++;
|
||||
|
||||
@ -417,11 +417,12 @@ class Connection : public CandidatePairInterface,
|
||||
public sigslot::has_slots<> {
|
||||
public:
|
||||
struct SentPing {
|
||||
SentPing(const std::string id, int64_t sent_time)
|
||||
: id(id), sent_time(sent_time) {}
|
||||
SentPing(const std::string id, int64_t sent_time, uint32_t nomination)
|
||||
: id(id), sent_time(sent_time), nomination(nomination) {}
|
||||
|
||||
std::string id;
|
||||
int64_t sent_time;
|
||||
uint32_t nomination;
|
||||
};
|
||||
|
||||
// States are from RFC 5245. http://tools.ietf.org/html/rfc5245#section-5.7.4
|
||||
@ -511,8 +512,16 @@ class Connection : public CandidatePairInterface,
|
||||
bool use_candidate_attr() const { return use_candidate_attr_; }
|
||||
void set_use_candidate_attr(bool enable);
|
||||
|
||||
bool nominated() const { return nominated_; }
|
||||
void set_nominated(bool nominated) { nominated_ = nominated; }
|
||||
void set_nomination(uint32_t value) { nomination_ = value; }
|
||||
|
||||
uint32_t remote_nomination() const { return remote_nomination_; }
|
||||
bool nominated() const { return remote_nomination_ > 0; }
|
||||
// Public for unit tests.
|
||||
void set_remote_nomination(uint32_t remote_nomination) {
|
||||
remote_nomination_ = remote_nomination;
|
||||
}
|
||||
// Public for unit tests.
|
||||
uint32_t acked_nomination() const { return acked_nomination_; }
|
||||
|
||||
void set_remote_ice_mode(IceMode mode) {
|
||||
remote_ice_mode_ = mode;
|
||||
@ -539,7 +548,7 @@ class Connection : public CandidatePairInterface,
|
||||
// Called when this connection should try checking writability again.
|
||||
int64_t last_ping_sent() const { return last_ping_sent_; }
|
||||
void Ping(int64_t now);
|
||||
void ReceivedPingResponse(int rtt);
|
||||
void ReceivedPingResponse(int rtt, const std::string& request_id);
|
||||
int64_t last_ping_response_received() const {
|
||||
return last_ping_response_received_;
|
||||
}
|
||||
@ -631,11 +640,19 @@ class Connection : public CandidatePairInterface,
|
||||
void set_state(State state);
|
||||
void set_connected(bool value);
|
||||
|
||||
uint32_t nomination() const { return nomination_; }
|
||||
|
||||
void OnMessage(rtc::Message *pmsg);
|
||||
|
||||
Port* port_;
|
||||
size_t local_candidate_index_;
|
||||
Candidate remote_candidate_;
|
||||
|
||||
ConnectionInfo stats_;
|
||||
rtc::RateTracker recv_rate_tracker_;
|
||||
rtc::RateTracker send_rate_tracker_;
|
||||
|
||||
private:
|
||||
WriteState write_state_;
|
||||
bool receiving_;
|
||||
bool connected_;
|
||||
@ -645,9 +662,19 @@ class Connection : public CandidatePairInterface,
|
||||
// But when peer is ice-lite, this flag "must" be initialized to false and
|
||||
// turn on when connection becomes "best connection".
|
||||
bool use_candidate_attr_;
|
||||
// Whether this connection has been nominated by the controlling side via
|
||||
// the use_candidate attribute.
|
||||
bool nominated_;
|
||||
// Used by the controlling side to indicate that this connection will be
|
||||
// selected for transmission if the peer supports ICE-renomination when this
|
||||
// value is positive. A larger-value indicates that a connection is nominated
|
||||
// later and should be selected by the controlled side with higher precedence.
|
||||
// A zero-value indicates not nominating this connection.
|
||||
uint32_t nomination_ = 0;
|
||||
// The last nomination that has been acknowledged.
|
||||
uint32_t acked_nomination_ = 0;
|
||||
// Used by the controlled side to remember the nomination value received from
|
||||
// the controlling side. When the peer does not support ICE re-nomination,
|
||||
// its value will be 1 if the connection has been nominated.
|
||||
uint32_t remote_nomination_ = 0;
|
||||
|
||||
IceMode remote_ice_mode_;
|
||||
StunRequestManager requests_;
|
||||
int rtt_;
|
||||
@ -660,12 +687,6 @@ class Connection : public CandidatePairInterface,
|
||||
int64_t receiving_unchanged_since_ = 0;
|
||||
std::vector<SentPing> pings_since_last_response_;
|
||||
|
||||
rtc::RateTracker recv_rate_tracker_;
|
||||
rtc::RateTracker send_rate_tracker_;
|
||||
|
||||
ConnectionInfo stats_;
|
||||
|
||||
private:
|
||||
void MaybeAddPrflxCandidate(ConnectionRequest* request,
|
||||
StunMessage* response);
|
||||
|
||||
|
||||
@ -606,6 +606,7 @@ enum IceAttributeType {
|
||||
STUN_ATTR_USE_CANDIDATE = 0x0025, // No content, Length = 0
|
||||
STUN_ATTR_ICE_CONTROLLED = 0x8029, // UInt64
|
||||
STUN_ATTR_ICE_CONTROLLING = 0x802A, // UInt64
|
||||
STUN_ATTR_NOMINATION = 0xC001, // UInt32
|
||||
// UInt32. The higher 16 bits are the network ID. The lower 16 bits are the
|
||||
// network cost.
|
||||
STUN_ATTR_NETWORK_INFO = 0xC057
|
||||
@ -624,6 +625,7 @@ class IceMessage : public StunMessage {
|
||||
switch (type) {
|
||||
case STUN_ATTR_PRIORITY:
|
||||
case STUN_ATTR_NETWORK_INFO:
|
||||
case STUN_ATTR_NOMINATION:
|
||||
return STUN_VALUE_UINT32;
|
||||
case STUN_ATTR_USE_CANDIDATE: return STUN_VALUE_BYTE_STRING;
|
||||
case STUN_ATTR_ICE_CONTROLLED: return STUN_VALUE_UINT64;
|
||||
|
||||
@ -162,6 +162,15 @@ struct TransportStats {
|
||||
TransportChannelStatsList channel_stats;
|
||||
};
|
||||
|
||||
// ICE Nomination mode.
|
||||
enum class NominationMode {
|
||||
REGULAR, // Nominate once per ICE restart (Not implemented yet).
|
||||
AGGRESSIVE, // Nominate every connection except that it will behave as if
|
||||
// REGULAR when the remote is an ICE-LITE endpoint.
|
||||
SEMI_AGGRESSIVE // Our current implementation of the nomination algorithm.
|
||||
// The details are described in P2PTransportChannel.
|
||||
};
|
||||
|
||||
// Information about ICE configuration.
|
||||
// TODO(deadbeef): Use rtc::Optional to represent unset values, instead of
|
||||
// -1.
|
||||
@ -199,6 +208,10 @@ struct IceConfig {
|
||||
// in case that the selected connection may become receiving soon.
|
||||
rtc::Optional<int> receiving_switching_delay;
|
||||
|
||||
// TODO(honghaiz): Change the default to regular nomination.
|
||||
// Default nomination mode if the remote does not support renomination.
|
||||
NominationMode default_nomination_mode = NominationMode::SEMI_AGGRESSIVE;
|
||||
|
||||
IceConfig() {}
|
||||
IceConfig(int receiving_timeout_ms,
|
||||
int backup_connection_ping_interval,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user