From c67e0f5753e9055e5fe76ae5eba76fe9efb3d769 Mon Sep 17 00:00:00 2001 From: Honghai Zhang Date: Mon, 19 Sep 2016 16:57:37 -0700 Subject: [PATCH] Signal to remove remote candidates if ports are pruned. Previously when a Turn port is pruned, if its candidate has been sent to the remote side, the remote side will keep the candidate and use that to create connections. We now signal the remote side to remove the candidates so that at least no new connection will be created using the removed candidates. Also updated the virtual socket server to better support our test cases. 1. Allow the virtual socket server to set transit delay for packets sent from a given IP address. 2. Ensure the ordered packet delivery for each socket (Previously the delivery order is enforced on the whole test case, so if a udp packet gets delayed based on its IP address, all TCP packets sent after the UDP packet will be delayed at least until the UDP packet is received). BUG=webrtc:6380 R=deadbeef@webrtc.org, pthatcher@webrtc.org, skvlad@webrtc.org Review URL: https://codereview.webrtc.org/2261523004 . Cr-Commit-Position: refs/heads/master@{#14297} --- webrtc/base/virtualsocketserver.cc | 30 +- webrtc/base/virtualsocketserver.h | 19 +- webrtc/p2p/client/basicportallocator.cc | 78 ++-- webrtc/p2p/client/basicportallocator.h | 17 +- .../p2p/client/basicportallocator_unittest.cc | 338 +++++++++++------- 5 files changed, 306 insertions(+), 176 deletions(-) diff --git a/webrtc/base/virtualsocketserver.cc b/webrtc/base/virtualsocketserver.cc index 87775cd63c..da2cb1d741 100644 --- a/webrtc/base/virtualsocketserver.cc +++ b/webrtc/base/virtualsocketserver.cc @@ -528,7 +528,6 @@ VirtualSocketServer::VirtualSocketServer(SocketServer* ss) server_owned_(false), msg_queue_(NULL), stop_on_idle_(false), - network_delay_(0), next_ipv4_(kInitialNextIPv4), next_ipv6_(kInitialNextIPv6), next_port_(kFirstEphemeralPort), @@ -541,7 +540,6 @@ VirtualSocketServer::VirtualSocketServer(SocketServer* ss) delay_mean_(0), delay_stddev_(0), delay_samples_(NUM_SAMPLES), - delay_dist_(NULL), drop_prob_(0.0) { if (!server_) { server_ = new PhysicalSocketServer(); @@ -553,7 +551,6 @@ VirtualSocketServer::VirtualSocketServer(SocketServer* ss) VirtualSocketServer::~VirtualSocketServer() { delete bindings_; delete connections_; - delete delay_dist_; if (server_owned_) { delete server_; } @@ -781,7 +778,7 @@ static double Random() { int VirtualSocketServer::Connect(VirtualSocket* socket, const SocketAddress& remote_addr, bool use_delay) { - uint32_t delay = use_delay ? GetRandomTransitDelay() : 0; + uint32_t delay = use_delay ? GetTransitDelay(socket) : 0; VirtualSocket* remote = LookupBinding(remote_addr); if (!CanInteractWith(socket, remote)) { LOG(LS_INFO) << "Address family mismatch between " @@ -803,7 +800,7 @@ bool VirtualSocketServer::Disconnect(VirtualSocket* socket) { if (socket) { // If we simulate packets being delayed, we should simulate the // equivalent of a FIN being delayed as well. - uint32_t delay = GetRandomTransitDelay(); + uint32_t delay = GetTransitDelay(socket); // Remove the mapping. msg_queue_->PostDelayed(RTC_FROM_HERE, delay, socket, MSG_ID_DISCONNECT); return true; @@ -947,7 +944,7 @@ void VirtualSocketServer::AddPacketToNetwork(VirtualSocket* sender, sender->network_.push_back(entry); // Find the delay for crossing the many virtual hops of the network. - uint32_t transit_delay = GetRandomTransitDelay(); + uint32_t transit_delay = GetTransitDelay(sender); // When the incoming packet is from a binding of the any address, translate it // to the default route here such that the recipient will see the default @@ -964,12 +961,12 @@ void VirtualSocketServer::AddPacketToNetwork(VirtualSocket* sender, int64_t ts = TimeAfter(send_delay + transit_delay); if (ordered) { // Ensure that new packets arrive after previous ones - // TODO: consider ordering on a per-socket basis, since this - // introduces artificial delay. - ts = std::max(ts, network_delay_); + ts = std::max(ts, sender->last_delivery_time_); + // A socket should not have both ordered and unordered delivery, so its last + // delivery time only needs to be updated when it has ordered delivery. + sender->last_delivery_time_ = ts; } msg_queue_->PostAt(RTC_FROM_HERE, ts, recipient, MSG_ID_PACKET, p); - network_delay_ = std::max(ts, network_delay_); } void VirtualSocketServer::PurgeNetworkPackets(VirtualSocket* socket, @@ -1016,8 +1013,7 @@ void VirtualSocketServer::UpdateDelayDistribution() { // We take a lock just to make sure we don't leak memory. { CritScope cs(&delay_crit_); - delete delay_dist_; - delay_dist_ = dist; + delay_dist_.reset(dist); } } @@ -1060,10 +1056,16 @@ VirtualSocketServer::Function* VirtualSocketServer::CreateDistribution( return Resample(Invert(Accumulate(f)), 0, 1, samples); } -uint32_t VirtualSocketServer::GetRandomTransitDelay() { +uint32_t VirtualSocketServer::GetTransitDelay(Socket* socket) { + // Use the delay based on the address if it is set. + auto iter = delay_by_ip_.find(socket->GetLocalAddress().ipaddr()); + if (iter != delay_by_ip_.end()) { + return static_cast(iter->second); + } + // Otherwise, use the delay from the distribution distribution. size_t index = rand() % delay_dist_->size(); double delay = (*delay_dist_)[index].second; - //LOG_F(LS_INFO) << "random[" << index << "] = " << delay; + // LOG_F(LS_INFO) << "random[" << index << "] = " << delay; return static_cast(delay); } diff --git a/webrtc/base/virtualsocketserver.h b/webrtc/base/virtualsocketserver.h index cce0279d32..6782d800a0 100644 --- a/webrtc/base/virtualsocketserver.h +++ b/webrtc/base/virtualsocketserver.h @@ -112,6 +112,10 @@ class VirtualSocketServer : public SocketServer, public sigslot::has_slots<> { bool Wait(int cms, bool process_io) override; void WakeUp() override; + void SetDelayOnAddress(const rtc::SocketAddress& address, int delay_ms) { + delay_by_ip_[address.ipaddr()] = delay_ms; + } + typedef std::pair Point; typedef std::vector Function; @@ -194,8 +198,10 @@ class VirtualSocketServer : public SocketServer, public sigslot::has_slots<> { // Computes the number of milliseconds required to send a packet of this size. uint32_t SendDelay(uint32_t size); - // Returns a random transit delay chosen from the appropriate distribution. - uint32_t GetRandomTransitDelay(); + // If the delay has been set for the address of the socket, returns the set + // delay. Otherwise, returns a random transit delay chosen from the + // appropriate distribution. + uint32_t GetTransitDelay(Socket* socket); // Basic operations on functions. Those that return a function also take // ownership of the function given (and hence, may modify or delete it). @@ -243,7 +249,6 @@ class VirtualSocketServer : public SocketServer, public sigslot::has_slots<> { bool server_owned_; MessageQueue* msg_queue_; bool stop_on_idle_; - int64_t network_delay_; in_addr next_ipv4_; in6_addr next_ipv6_; uint16_t next_port_; @@ -260,7 +265,10 @@ class VirtualSocketServer : public SocketServer, public sigslot::has_slots<> { uint32_t delay_mean_; uint32_t delay_stddev_; uint32_t delay_samples_; - Function* delay_dist_; + + std::map delay_by_ip_; + std::unique_ptr delay_dist_; + CriticalSection delay_crit_; double drop_prob_; @@ -358,6 +366,9 @@ class VirtualSocket : public AsyncSocket, // Network model that enforces bandwidth and capacity constraints NetworkQueue network_; size_t network_size_; + // The scheduled delivery time of the last packet sent on this socket. + // It is used to ensure ordered delivery of packets sent on this socket. + int64_t last_delivery_time_ = 0; // Data which has been received from the network RecvBuffer recv_buffer_; diff --git a/webrtc/p2p/client/basicportallocator.cc b/webrtc/p2p/client/basicportallocator.cc index e94feb4d15..dba68f177e 100644 --- a/webrtc/p2p/client/basicportallocator.cc +++ b/webrtc/p2p/client/basicportallocator.cc @@ -314,7 +314,12 @@ void BasicPortAllocatorSession::RegatherOnFailedNetworks() { } // Remove ports from being used locally and send signaling to remove // the candidates on the remote side. - RemovePortsAndCandidates(failed_networks); + std::vector ports_to_prune = GetUnprunedPorts(failed_networks); + if (!ports_to_prune.empty()) { + LOG(LS_INFO) << "Prune " << ports_to_prune.size() + << " ports because their networks failed"; + PrunePortsAndRemoveCandidates(ports_to_prune); + } if (allocation_started_ && network_manager_started_) { DoAllocate(); @@ -615,7 +620,12 @@ void BasicPortAllocatorSession::OnNetworksChanged() { failed_networks.push_back(sequence->network()); } } - RemovePortsAndCandidates(failed_networks); + std::vector ports_to_prune = GetUnprunedPorts(failed_networks); + if (!ports_to_prune.empty()) { + LOG(LS_INFO) << "Prune " << ports_to_prune.size() + << " ports because their networks were gone"; + PrunePortsAndRemoveCandidates(ports_to_prune); + } if (!network_manager_started_) { LOG(LS_INFO) << "Network manager is started"; @@ -757,29 +767,32 @@ bool BasicPortAllocatorSession::PruneTurnPorts(Port* newly_pairable_turn_port) { RTC_CHECK(best_turn_port != nullptr); bool pruned = false; - std::vector pruned_ports; + std::vector ports_to_prune; for (PortData& data : ports_) { if (data.port()->Network()->name() == network_name && data.port()->Type() == RELAY_PORT_TYPE && !data.pruned() && ComparePort(data.port(), best_turn_port) < 0) { - data.set_pruned(); pruned = true; - data.port()->Prune(); if (data.port() != newly_pairable_turn_port) { - pruned_ports.push_back(data.port()); + // These ports will be pruned in PrunePortsAndRemoveCandidates. + ports_to_prune.push_back(&data); + } else { + data.Prune(); } } } - if (!pruned_ports.empty()) { - LOG(LS_INFO) << "Pruned " << pruned_ports.size() << " ports"; - SignalPortsPruned(this, pruned_ports); + + if (!ports_to_prune.empty()) { + LOG(LS_INFO) << "Prune " << ports_to_prune.size() + << " low-priority TURN ports"; + PrunePortsAndRemoveCandidates(ports_to_prune); } return pruned; } void BasicPortAllocatorSession::PruneAllPorts() { for (PortData& data : ports_) { - data.port()->Prune(); + data.Prune(); } } @@ -942,32 +955,41 @@ BasicPortAllocatorSession::PortData* BasicPortAllocatorSession::FindPort( return NULL; } -// Removes ports and candidates created on a given list of networks. -void BasicPortAllocatorSession::RemovePortsAndCandidates( +std::vector +BasicPortAllocatorSession::GetUnprunedPorts( const std::vector& networks) { - std::vector ports_to_remove; - std::vector candidates_to_remove; - for (PortData& data : ports_) { - if (std::find(networks.begin(), networks.end(), - data.sequence()->network()) == networks.end()) { - continue; + std::vector unpruned_ports; + for (PortData& port : ports_) { + if (!port.pruned() && + std::find(networks.begin(), networks.end(), + port.sequence()->network()) != networks.end()) { + unpruned_ports.push_back(&port); } + } + return unpruned_ports; +} + +void BasicPortAllocatorSession::PrunePortsAndRemoveCandidates( + const std::vector& port_data_list) { + std::vector pruned_ports; + std::vector removed_candidates; + for (PortData* data : port_data_list) { // Prune the port so that it may be destroyed. - data.port()->Prune(); - ports_to_remove.push_back(data.port()); - if (data.has_pairable_candidate()) { - GetCandidatesFromPort(data, &candidates_to_remove); + data->Prune(); + pruned_ports.push_back(data->port()); + if (data->has_pairable_candidate()) { + GetCandidatesFromPort(*data, &removed_candidates); // Mark the port as having no pairable candidates so that its candidates // won't be removed multiple times. - data.set_has_pairable_candidate(false); + data->set_has_pairable_candidate(false); } } - if (!ports_to_remove.empty()) { - LOG(LS_INFO) << "Removed " << ports_to_remove.size() << " ports"; - SignalPortsPruned(this, ports_to_remove); + if (!pruned_ports.empty()) { + SignalPortsPruned(this, pruned_ports); } - if (!candidates_to_remove.empty()) { - SignalCandidatesRemoved(this, candidates_to_remove); + if (!removed_candidates.empty()) { + LOG(LS_INFO) << "Removed " << removed_candidates.size() << " candidates"; + SignalCandidatesRemoved(this, removed_candidates); } } diff --git a/webrtc/p2p/client/basicportallocator.h b/webrtc/p2p/client/basicportallocator.h index 204ff040a1..3044559a0b 100644 --- a/webrtc/p2p/client/basicportallocator.h +++ b/webrtc/p2p/client/basicportallocator.h @@ -142,8 +142,13 @@ class BasicPortAllocatorSession : public PortAllocatorSession, return has_pairable_candidate_ && state_ != STATE_ERROR && state_ != STATE_PRUNED; } - - void set_pruned() { state_ = STATE_PRUNED; } + // Sets the state to "PRUNED" and prunes the Port. + void Prune() { + state_ = STATE_PRUNED; + if (port()) { + port()->Prune(); + } + } void set_has_pairable_candidate(bool has_pairable_candidate) { if (has_pairable_candidate) { ASSERT(state_ == STATE_INPROGRESS); @@ -201,8 +206,12 @@ class BasicPortAllocatorSession : public PortAllocatorSession, // in order to avoid leaking any information. Candidate SanitizeRelatedAddress(const Candidate& c) const; - // Removes the ports and candidates on given networks. - void RemovePortsAndCandidates(const std::vector& networks); + std::vector GetUnprunedPorts( + const std::vector& networks); + // Prunes ports and signal the remote side to remove the candidates that + // were previously signaled from these ports. + void PrunePortsAndRemoveCandidates( + const std::vector& port_data_list); // Gets filtered and sanitized candidates generated from a port and // append to |candidates|. void GetCandidatesFromPort(const PortData& data, diff --git a/webrtc/p2p/client/basicportallocator_unittest.cc b/webrtc/p2p/client/basicportallocator_unittest.cc index c9831f525b..cf967a8ae1 100644 --- a/webrtc/p2p/client/basicportallocator_unittest.cc +++ b/webrtc/p2p/client/basicportallocator_unittest.cc @@ -79,7 +79,7 @@ static const char kIcePwd0[] = "TESTICEPWD00000000000000"; static const char kContentName[] = "test content"; -static const int kDefaultAllocationTimeout = 1000; +static const int kDefaultAllocationTimeout = 3000; static const char kTurnUsername[] = "test"; static const char kTurnPassword[] = "test"; @@ -243,6 +243,8 @@ class BasicPortAllocatorTest : public testing::Test, &BasicPortAllocatorTest::OnPortsPruned); session->SignalCandidatesReady.connect( this, &BasicPortAllocatorTest::OnCandidatesReady); + session->SignalCandidatesRemoved.connect( + this, &BasicPortAllocatorTest::OnCandidatesRemoved); session->SignalCandidatesAllocationDone.connect( this, &BasicPortAllocatorTest::OnCandidatesAllocationDone); return session; @@ -404,6 +406,8 @@ class BasicPortAllocatorTest : public testing::Test, EXPECT_EQ(total_ports, ports_.size()); } + rtc::VirtualSocketServer* virtual_socket_server() { return vss_.get(); } + protected: BasicPortAllocator& allocator() { return *allocator_; } @@ -446,6 +450,21 @@ class BasicPortAllocatorTest : public testing::Test, } } + void OnCandidatesRemoved(PortAllocatorSession* session, + const std::vector& removed_candidates) { + auto new_end = std::remove_if( + candidates_.begin(), candidates_.end(), + [removed_candidates](Candidate& candidate) { + for (const Candidate& removed_candidate : removed_candidates) { + if (candidate.MatchesForRemoval(removed_candidate)) { + return true; + } + } + return false; + }); + candidates_.erase(new_end, candidates_.end()); + } + bool HasRelayAddress(const ProtocolAddress& proto_addr) { for (size_t i = 0; i < allocator_->turn_servers().size(); ++i) { RelayServerConfig server_config = allocator_->turn_servers()[i]; @@ -479,6 +498,143 @@ class BasicPortAllocatorTest : public testing::Test, allocator().set_step_delay(kMinimumStepDelay); } + void TestUdpTurnPortPrunesTcpTurnPort() { + turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TCP); + AddInterface(kClientAddr); + allocator_.reset(new BasicPortAllocator(&network_manager_)); + allocator_->SetConfiguration(allocator_->stun_servers(), + allocator_->turn_servers(), 0, true); + AddTurnServers(kTurnUdpIntAddr, kTurnTcpIntAddr); + allocator_->set_step_delay(kMinimumStepDelay); + allocator_->set_flags(allocator().flags() | + PORTALLOCATOR_ENABLE_SHARED_SOCKET | + PORTALLOCATOR_DISABLE_TCP); + + EXPECT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); + session_->StartGettingPorts(); + EXPECT_TRUE_WAIT(candidate_allocation_done_, kDefaultAllocationTimeout); + // Only 2 ports (one STUN and one TURN) are actually being used. + EXPECT_EQ(2U, session_->ReadyPorts().size()); + // We have verified that each port, when it is added to |ports_|, it is + // found in |ready_ports|, and when it is pruned, it is not found in + // |ready_ports|, so we only need to verify the content in one of them. + EXPECT_EQ(2U, ports_.size()); + EXPECT_EQ(1, CountPorts(ports_, "local", PROTO_UDP, kClientAddr)); + EXPECT_EQ(1, CountPorts(ports_, "relay", PROTO_UDP, kClientAddr)); + EXPECT_EQ(0, CountPorts(ports_, "relay", PROTO_TCP, kClientAddr)); + + // Now that we remove candidates when a TURN port is pruned, |candidates_| + // should only contains two candidates regardless whether the TCP TURN port + // is created before or after the UDP turn port. + EXPECT_EQ(2U, candidates_.size()); + // There will only be 2 candidates in |ready_candidates| because it only + // includes the candidates in the ready ports. + const std::vector& ready_candidates = + session_->ReadyCandidates(); + EXPECT_EQ(2U, ready_candidates.size()); + EXPECT_PRED4(HasCandidate, ready_candidates, "local", "udp", kClientAddr); + EXPECT_PRED4(HasCandidate, ready_candidates, "relay", "udp", + rtc::SocketAddress(kTurnUdpExtAddr.ipaddr(), 0)); + } + + void TestIPv6TurnPortPrunesIPv4TurnPort() { + turn_server_.AddInternalSocket(kTurnUdpIntIPv6Addr, PROTO_UDP); + // Add two IP addresses on the same interface. + AddInterface(kClientAddr, "net1"); + AddInterface(kClientIPv6Addr, "net1"); + allocator_.reset(new BasicPortAllocator(&network_manager_)); + allocator_->SetConfiguration(allocator_->stun_servers(), + allocator_->turn_servers(), 0, true); + AddTurnServers(kTurnUdpIntIPv6Addr, rtc::SocketAddress()); + AddTurnServers(kTurnUdpIntAddr, rtc::SocketAddress()); + + allocator_->set_step_delay(kMinimumStepDelay); + allocator_->set_flags( + allocator().flags() | PORTALLOCATOR_ENABLE_SHARED_SOCKET | + PORTALLOCATOR_ENABLE_IPV6 | PORTALLOCATOR_DISABLE_TCP); + + EXPECT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); + session_->StartGettingPorts(); + EXPECT_TRUE_WAIT(candidate_allocation_done_, kDefaultAllocationTimeout); + // Three ports (one IPv4 STUN, one IPv6 STUN and one TURN) will be ready. + EXPECT_EQ(3U, session_->ReadyPorts().size()); + EXPECT_EQ(3U, ports_.size()); + EXPECT_EQ(1, CountPorts(ports_, "local", PROTO_UDP, kClientAddr)); + EXPECT_EQ(1, CountPorts(ports_, "local", PROTO_UDP, kClientIPv6Addr)); + EXPECT_EQ(1, CountPorts(ports_, "relay", PROTO_UDP, kClientIPv6Addr)); + EXPECT_EQ(0, CountPorts(ports_, "relay", PROTO_UDP, kClientAddr)); + + // Now that we remove candidates when a TURN port is pruned, there will be + // exactly 3 candidates in both |candidates_| and |ready_candidates|. + EXPECT_EQ(3U, candidates_.size()); + const std::vector& ready_candidates = + session_->ReadyCandidates(); + EXPECT_EQ(3U, ready_candidates.size()); + EXPECT_PRED4(HasCandidate, ready_candidates, "local", "udp", kClientAddr); + EXPECT_PRED4(HasCandidate, ready_candidates, "relay", "udp", + rtc::SocketAddress(kTurnUdpExtAddr.ipaddr(), 0)); + } + + void TestEachInterfaceHasItsOwnTurnPorts() { + turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TCP); + turn_server_.AddInternalSocket(kTurnUdpIntIPv6Addr, PROTO_UDP); + turn_server_.AddInternalSocket(kTurnTcpIntIPv6Addr, PROTO_TCP); + // Add two interfaces both having IPv4 and IPv6 addresses. + AddInterface(kClientAddr, "net1", rtc::ADAPTER_TYPE_WIFI); + AddInterface(kClientIPv6Addr, "net1", rtc::ADAPTER_TYPE_WIFI); + AddInterface(kClientAddr2, "net2", rtc::ADAPTER_TYPE_CELLULAR); + AddInterface(kClientIPv6Addr2, "net2", rtc::ADAPTER_TYPE_CELLULAR); + allocator_.reset(new BasicPortAllocator(&network_manager_)); + allocator_->SetConfiguration(allocator_->stun_servers(), + allocator_->turn_servers(), 0, true); + // Have both UDP/TCP and IPv4/IPv6 TURN ports. + AddTurnServers(kTurnUdpIntAddr, kTurnTcpIntAddr); + AddTurnServers(kTurnUdpIntIPv6Addr, kTurnTcpIntIPv6Addr); + + allocator_->set_step_delay(kMinimumStepDelay); + allocator_->set_flags(allocator().flags() | + PORTALLOCATOR_ENABLE_SHARED_SOCKET | + PORTALLOCATOR_ENABLE_IPV6); + EXPECT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); + session_->StartGettingPorts(); + EXPECT_TRUE_WAIT(candidate_allocation_done_, kDefaultAllocationTimeout); + // 10 ports (4 STUN and 1 TURN ports on each interface) will be ready to + // use. + EXPECT_EQ(10U, session_->ReadyPorts().size()); + EXPECT_EQ(10U, ports_.size()); + EXPECT_EQ(1, CountPorts(ports_, "local", PROTO_UDP, kClientAddr)); + EXPECT_EQ(1, CountPorts(ports_, "local", PROTO_UDP, kClientAddr2)); + EXPECT_EQ(1, CountPorts(ports_, "local", PROTO_UDP, kClientIPv6Addr)); + EXPECT_EQ(1, CountPorts(ports_, "local", PROTO_UDP, kClientIPv6Addr2)); + EXPECT_EQ(1, CountPorts(ports_, "local", PROTO_TCP, kClientAddr)); + EXPECT_EQ(1, CountPorts(ports_, "local", PROTO_TCP, kClientAddr2)); + EXPECT_EQ(1, CountPorts(ports_, "local", PROTO_TCP, kClientIPv6Addr)); + EXPECT_EQ(1, CountPorts(ports_, "local", PROTO_TCP, kClientIPv6Addr2)); + EXPECT_EQ(1, CountPorts(ports_, "relay", PROTO_UDP, kClientIPv6Addr)); + EXPECT_EQ(1, CountPorts(ports_, "relay", PROTO_UDP, kClientIPv6Addr2)); + + // Now that we remove candidates when TURN ports are pruned, there will be + // exactly 10 candidates in |candidates_|. + EXPECT_EQ(10U, candidates_.size()); + const std::vector& ready_candidates = + session_->ReadyCandidates(); + EXPECT_EQ(10U, ready_candidates.size()); + EXPECT_PRED4(HasCandidate, ready_candidates, "local", "udp", kClientAddr); + EXPECT_PRED4(HasCandidate, ready_candidates, "local", "udp", kClientAddr2); + EXPECT_PRED4(HasCandidate, ready_candidates, "local", "udp", + kClientIPv6Addr); + EXPECT_PRED4(HasCandidate, ready_candidates, "local", "udp", + kClientIPv6Addr2); + EXPECT_PRED4(HasCandidate, ready_candidates, "local", "tcp", kClientAddr); + EXPECT_PRED4(HasCandidate, ready_candidates, "local", "tcp", kClientAddr2); + EXPECT_PRED4(HasCandidate, ready_candidates, "local", "tcp", + kClientIPv6Addr); + EXPECT_PRED4(HasCandidate, ready_candidates, "local", "tcp", + kClientIPv6Addr2); + EXPECT_PRED4(HasCandidate, ready_candidates, "relay", "udp", + rtc::SocketAddress(kTurnUdpExtAddr.ipaddr(), 0)); + } + std::unique_ptr pss_; std::unique_ptr vss_; std::unique_ptr fss_; @@ -1211,142 +1367,72 @@ TEST_F(BasicPortAllocatorTest, TestSharedSocketWithoutNatUsingTurn) { EXPECT_EQ(3U, candidates_.size()); } -// Test that if prune_turn_ports is set, TCP TurnPort will not -// be used if UDP TurnPort is used. -TEST_F(BasicPortAllocatorTest, TestUdpTurnPortPrunesTcpTurnPorts) { - turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TCP); - AddInterface(kClientAddr); - allocator_.reset(new BasicPortAllocator(&network_manager_)); - allocator_->SetConfiguration(allocator_->stun_servers(), - allocator_->turn_servers(), 0, true); - AddTurnServers(kTurnUdpIntAddr, kTurnTcpIntAddr); - allocator_->set_step_delay(kMinimumStepDelay); - allocator_->set_flags(allocator().flags() | - PORTALLOCATOR_ENABLE_SHARED_SOCKET | - PORTALLOCATOR_DISABLE_TCP); +// Test that if prune_turn_ports is set, TCP TURN port will not be used +// if UDP TurnPort is used, given that TCP TURN port becomes ready first. +TEST_F(BasicPortAllocatorTest, + TestUdpTurnPortPrunesTcpTurnPortWithTcpPortReadyFirst) { + // UDP has longer delay than TCP so that TCP TURN port becomes ready first. + virtual_socket_server()->SetDelayOnAddress(kTurnUdpIntAddr, 200); + virtual_socket_server()->SetDelayOnAddress(kTurnTcpIntAddr, 100); - EXPECT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); - session_->StartGettingPorts(); - EXPECT_TRUE_WAIT(candidate_allocation_done_, kDefaultAllocationTimeout); - // Only 2 ports (one STUN and one TURN) are actually being used. - EXPECT_EQ(2U, session_->ReadyPorts().size()); - // We have verified that each port, when it is added to |ports_|, it is found - // in |ready_ports|, and when it is pruned, it is not found in |ready_ports|, - // so we only need to verify the content in one of them. - EXPECT_EQ(2U, ports_.size()); - EXPECT_EQ(1, CountPorts(ports_, "local", PROTO_UDP, kClientAddr)); - EXPECT_EQ(1, CountPorts(ports_, "relay", PROTO_UDP, kClientAddr)); - EXPECT_EQ(0, CountPorts(ports_, "relay", PROTO_TCP, kClientAddr)); - - // We don't remove candidates, so the size of |candidates_| will depend on - // when the TCP TURN port becomes ready. If it is ready after the UDP TURN - // port becomes ready, its candidates will be used there will be 3 candidates. - // Otherwise there will be only 2 candidates. - EXPECT_LE(2U, candidates_.size()); - // There will only be 2 candidates in |ready_candidates| because it only - // includes the candidates in the ready ports. - const std::vector& ready_candidates = session_->ReadyCandidates(); - EXPECT_EQ(2U, ready_candidates.size()); - EXPECT_PRED4(HasCandidate, ready_candidates, "local", "udp", kClientAddr); - EXPECT_PRED4(HasCandidate, ready_candidates, "relay", "udp", - rtc::SocketAddress(kTurnUdpExtAddr.ipaddr(), 0)); + TestUdpTurnPortPrunesTcpTurnPort(); } -// Tests that if prune_turn_ports is set, IPv4 TurnPort will not -// be used if IPv6 TurnPort is used. -TEST_F(BasicPortAllocatorTest, TestIPv6TurnPortPrunesIPv4TurnPorts) { - turn_server_.AddInternalSocket(kTurnUdpIntIPv6Addr, PROTO_UDP); - // Add two IP addresses on the same interface. - AddInterface(kClientAddr, "net1"); - AddInterface(kClientIPv6Addr, "net1"); - allocator_.reset(new BasicPortAllocator(&network_manager_)); - allocator_->SetConfiguration(allocator_->stun_servers(), - allocator_->turn_servers(), 0, true); - AddTurnServers(kTurnUdpIntIPv6Addr, rtc::SocketAddress()); +// Test that if prune_turn_ports is set, TCP TURN port will not be used +// if UDP TurnPort is used, given that UDP TURN port becomes ready first. +TEST_F(BasicPortAllocatorTest, + TestUdpTurnPortPrunesTcpTurnPortsWithUdpPortReadyFirst) { + // UDP has shorter delay than TCP so that UDP TURN port becomes ready first. + virtual_socket_server()->SetDelayOnAddress(kTurnUdpIntAddr, 100); + virtual_socket_server()->SetDelayOnAddress(kTurnTcpIntAddr, 200); - allocator_->set_step_delay(kMinimumStepDelay); - allocator_->set_flags(allocator().flags() | - PORTALLOCATOR_ENABLE_SHARED_SOCKET | - PORTALLOCATOR_ENABLE_IPV6 | PORTALLOCATOR_DISABLE_TCP); + TestUdpTurnPortPrunesTcpTurnPort(); +} - EXPECT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); - session_->StartGettingPorts(); - EXPECT_TRUE_WAIT(candidate_allocation_done_, kDefaultAllocationTimeout); - // Three ports (one IPv4 STUN, one IPv6 STUN and one TURN) will be ready. - EXPECT_EQ(3U, session_->ReadyPorts().size()); - EXPECT_EQ(3U, ports_.size()); - EXPECT_EQ(1, CountPorts(ports_, "local", PROTO_UDP, kClientAddr)); - EXPECT_EQ(1, CountPorts(ports_, "local", PROTO_UDP, kClientIPv6Addr)); - EXPECT_EQ(1, CountPorts(ports_, "relay", PROTO_UDP, kClientIPv6Addr)); - EXPECT_EQ(0, CountPorts(ports_, "relay", PROTO_UDP, kClientAddr)); +// Tests that if prune_turn_ports is set, IPv4 TurnPort will not be used +// if IPv6 TurnPort is used, given that IPv4 TURN port becomes ready first. +TEST_F(BasicPortAllocatorTest, + TestIPv6TurnPortPrunesIPv4TurnPortWithIPv4PortReadyFirst) { + // IPv6 has longer delay than IPv4, so that IPv4 TURN port becomes ready + // first. + virtual_socket_server()->SetDelayOnAddress(kTurnUdpIntAddr, 100); + virtual_socket_server()->SetDelayOnAddress(kTurnUdpIntIPv6Addr, 200); - // We don't remove candidates, so there may be more than 3 elemenets in - // |candidates_|, although |ready_candidates| only includes the candidates - // in |ready_ports|. - EXPECT_LE(3U, candidates_.size()); - const std::vector& ready_candidates = session_->ReadyCandidates(); - EXPECT_EQ(3U, ready_candidates.size()); - EXPECT_PRED4(HasCandidate, ready_candidates, "local", "udp", kClientAddr); - EXPECT_PRED4(HasCandidate, ready_candidates, "relay", "udp", - rtc::SocketAddress(kTurnUdpExtAddr.ipaddr(), 0)); + TestIPv6TurnPortPrunesIPv4TurnPort(); +} + +// Tests that if prune_turn_ports is set, IPv4 TurnPort will not be used +// if IPv6 TurnPort is used, given that IPv6 TURN port becomes ready first. +TEST_F(BasicPortAllocatorTest, + TestIPv6TurnPortPrunesIPv4TurnPortWithIPv6PortReadyFirst) { + // IPv6 has longer delay than IPv4, so that IPv6 TURN port becomes ready + // first. + virtual_socket_server()->SetDelayOnAddress(kTurnUdpIntAddr, 200); + virtual_socket_server()->SetDelayOnAddress(kTurnUdpIntIPv6Addr, 100); + + TestIPv6TurnPortPrunesIPv4TurnPort(); } // Tests that if prune_turn_ports is set, each network interface -// will has its own set of TurnPorts based on their priorities. -TEST_F(BasicPortAllocatorTest, TestEachInterfaceHasItsOwnTurnPorts) { - turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TCP); - turn_server_.AddInternalSocket(kTurnUdpIntIPv6Addr, PROTO_UDP); - turn_server_.AddInternalSocket(kTurnTcpIntIPv6Addr, PROTO_TCP); - // Add two interfaces both having IPv4 and IPv6 addresses. - AddInterface(kClientAddr, "net1", rtc::ADAPTER_TYPE_WIFI); - AddInterface(kClientIPv6Addr, "net1", rtc::ADAPTER_TYPE_WIFI); - AddInterface(kClientAddr2, "net2", rtc::ADAPTER_TYPE_CELLULAR); - AddInterface(kClientIPv6Addr2, "net2", rtc::ADAPTER_TYPE_CELLULAR); - allocator_.reset(new BasicPortAllocator(&network_manager_)); - allocator_->SetConfiguration(allocator_->stun_servers(), - allocator_->turn_servers(), 0, true); - // Have both UDP/TCP and IPv4/IPv6 TURN ports. - AddTurnServers(kTurnUdpIntAddr, kTurnTcpIntAddr); - AddTurnServers(kTurnUdpIntIPv6Addr, kTurnTcpIntIPv6Addr); +// will has its own set of TurnPorts based on their priorities, in the default +// case where no transit delay is set. +TEST_F(BasicPortAllocatorTest, TestEachInterfaceHasItsOwnTurnPortsNoDelay) { + TestEachInterfaceHasItsOwnTurnPorts(); +} - allocator_->set_step_delay(kMinimumStepDelay); - allocator_->set_flags(allocator().flags() | - PORTALLOCATOR_ENABLE_SHARED_SOCKET | - PORTALLOCATOR_ENABLE_IPV6); - EXPECT_TRUE(CreateSession(ICE_CANDIDATE_COMPONENT_RTP)); - session_->StartGettingPorts(); - EXPECT_TRUE_WAIT(candidate_allocation_done_, kDefaultAllocationTimeout); - // 10 ports (4 STUN and 1 TURN ports on each interface) will be ready to use. - EXPECT_EQ(10U, session_->ReadyPorts().size()); - EXPECT_EQ(10U, ports_.size()); - EXPECT_EQ(1, CountPorts(ports_, "local", PROTO_UDP, kClientAddr)); - EXPECT_EQ(1, CountPorts(ports_, "local", PROTO_UDP, kClientAddr2)); - EXPECT_EQ(1, CountPorts(ports_, "local", PROTO_UDP, kClientIPv6Addr)); - EXPECT_EQ(1, CountPorts(ports_, "local", PROTO_UDP, kClientIPv6Addr2)); - EXPECT_EQ(1, CountPorts(ports_, "local", PROTO_TCP, kClientAddr)); - EXPECT_EQ(1, CountPorts(ports_, "local", PROTO_TCP, kClientAddr2)); - EXPECT_EQ(1, CountPorts(ports_, "local", PROTO_TCP, kClientIPv6Addr)); - EXPECT_EQ(1, CountPorts(ports_, "local", PROTO_TCP, kClientIPv6Addr2)); - EXPECT_EQ(1, CountPorts(ports_, "relay", PROTO_UDP, kClientIPv6Addr)); - EXPECT_EQ(1, CountPorts(ports_, "relay", PROTO_UDP, kClientIPv6Addr2)); +// Tests that if prune_turn_ports is set, each network interface +// will has its own set of TurnPorts based on their priorities, given that +// IPv4/TCP TURN port becomes ready first. +TEST_F(BasicPortAllocatorTest, + TestEachInterfaceHasItsOwnTurnPortsWithTcpIPv4ReadyFirst) { + // IPv6/UDP have longer delay than IPv4/TCP, so that IPv4/TCP TURN port + // becomes ready last. + virtual_socket_server()->SetDelayOnAddress(kTurnTcpIntAddr, 10); + virtual_socket_server()->SetDelayOnAddress(kTurnUdpIntAddr, 100); + virtual_socket_server()->SetDelayOnAddress(kTurnTcpIntIPv6Addr, 20); + virtual_socket_server()->SetDelayOnAddress(kTurnUdpIntIPv6Addr, 300); - // We don't remove candidates, so there may be more than 10 candidates - // in |candidates_|. - EXPECT_LE(10U, candidates_.size()); - const std::vector& ready_candidates = session_->ReadyCandidates(); - EXPECT_EQ(10U, ready_candidates.size()); - EXPECT_PRED4(HasCandidate, ready_candidates, "local", "udp", kClientAddr); - EXPECT_PRED4(HasCandidate, ready_candidates, "local", "udp", kClientAddr2); - EXPECT_PRED4(HasCandidate, ready_candidates, "local", "udp", kClientIPv6Addr); - EXPECT_PRED4(HasCandidate, ready_candidates, "local", "udp", - kClientIPv6Addr2); - EXPECT_PRED4(HasCandidate, ready_candidates, "local", "tcp", kClientAddr); - EXPECT_PRED4(HasCandidate, ready_candidates, "local", "tcp", kClientAddr2); - EXPECT_PRED4(HasCandidate, ready_candidates, "local", "tcp", kClientIPv6Addr); - EXPECT_PRED4(HasCandidate, ready_candidates, "local", "tcp", - kClientIPv6Addr2); - EXPECT_PRED4(HasCandidate, ready_candidates, "relay", "udp", - rtc::SocketAddress(kTurnUdpExtAddr.ipaddr(), 0)); + TestEachInterfaceHasItsOwnTurnPorts(); } // Testing DNS resolve for the TURN server, this will test AllocationSequence