diff --git a/webrtc/p2p/base/p2ptransportchannel.cc b/webrtc/p2p/base/p2ptransportchannel.cc index b32d149a84..ec1dfc733f 100644 --- a/webrtc/p2p/base/p2ptransportchannel.cc +++ b/webrtc/p2p/base/p2ptransportchannel.cc @@ -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(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. diff --git a/webrtc/p2p/base/p2ptransportchannel.h b/webrtc/p2p/base/p2ptransportchannel.h index 6fafaa4c3e..7d5e304bb3 100644 --- a/webrtc/p2p/base/p2ptransportchannel.h +++ b/webrtc/p2p/base/p2ptransportchannel.h @@ -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); }; diff --git a/webrtc/p2p/base/p2ptransportchannel_unittest.cc b/webrtc/p2p/base/p2ptransportchannel_unittest.cc index b105621059..7cd23daa38 100644 --- a/webrtc/p2p/base/p2ptransportchannel_unittest.cc +++ b/webrtc/p2p/base/p2ptransportchannel_unittest.cc @@ -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 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(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)); diff --git a/webrtc/p2p/base/port.cc b/webrtc/p2p/base/port.cc index fca4f027f9..98b62c4222 100644 --- a/webrtc/p2p/base/port.cc +++ b/webrtc/p2p/base/port.cc @@ -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++; diff --git a/webrtc/p2p/base/port.h b/webrtc/p2p/base/port.h index 081e81af1d..d5ad65f081 100644 --- a/webrtc/p2p/base/port.h +++ b/webrtc/p2p/base/port.h @@ -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 pings_since_last_response_; - rtc::RateTracker recv_rate_tracker_; - rtc::RateTracker send_rate_tracker_; - - ConnectionInfo stats_; - - private: void MaybeAddPrflxCandidate(ConnectionRequest* request, StunMessage* response); diff --git a/webrtc/p2p/base/stun.h b/webrtc/p2p/base/stun.h index c0f5cfdfec..153f63dc58 100644 --- a/webrtc/p2p/base/stun.h +++ b/webrtc/p2p/base/stun.h @@ -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; diff --git a/webrtc/p2p/base/transport.h b/webrtc/p2p/base/transport.h index a7ca72ff8e..c2767fb6f0 100644 --- a/webrtc/p2p/base/transport.h +++ b/webrtc/p2p/base/transport.h @@ -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 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,