Add config to prune low-priority TURN ports for creating connections
When the flag prune_turn_ports is set, When a high-priority turn port becomes available, it will prune low-priority ones. The pruned port will not be used for creating connections locally and its candidates will not be sent over to the remove side (unless they have been sent before being pruned).
This effectively reduces the number of TURN candidates and connections created by TURN ports.
BUG=
R=deadbeef@webrtc.org, pthatcher@webrtc.org
Review URL: https://codereview.webrtc.org/2093623004 .
Cr-Original-Commit-Position: refs/heads/master@{#13335}
Cr-Mirrored-From: https://chromium.googlesource.com/external/webrtc
Cr-Mirrored-Commit: 17aac053f585e892114974d2eb248e05ad37f973
diff --git a/api/peerconnection.cc b/api/peerconnection.cc
index a337d00..be61472 100644
--- a/api/peerconnection.cc
+++ b/api/peerconnection.cc
@@ -2226,7 +2226,8 @@
// Call this last since it may create pooled allocator sessions using the
// properties set above.
port_allocator_->SetConfiguration(stun_servers, turn_servers,
- configuration.ice_candidate_pool_size);
+ configuration.ice_candidate_pool_size,
+ configuration.prune_turn_ports);
return true;
}
@@ -2242,7 +2243,8 @@
// Call this last since it may create pooled allocator sessions using the
// candidate filter set above.
port_allocator_->SetConfiguration(stun_servers, turn_servers,
- configuration.ice_candidate_pool_size);
+ configuration.ice_candidate_pool_size,
+ configuration.prune_turn_ports);
return true;
}
diff --git a/api/peerconnectioninterface.h b/api/peerconnectioninterface.h
index c72e2ec..382a4b4 100644
--- a/api/peerconnectioninterface.h
+++ b/api/peerconnectioninterface.h
@@ -300,6 +300,7 @@
rtc::Optional<bool> combined_audio_video_bwe;
rtc::Optional<bool> enable_dtls_srtp;
int ice_candidate_pool_size = 0;
+ bool prune_turn_ports = false;
};
struct RTCOfferAnswerOptions {
diff --git a/p2p/base/p2ptransportchannel.cc b/p2p/base/p2ptransportchannel.cc
index b5d6755..ebbc0ea 100644
--- a/p2p/base/p2ptransportchannel.cc
+++ b/p2p/base/p2ptransportchannel.cc
@@ -125,6 +125,7 @@
session->set_generation(static_cast<uint32_t>(allocator_sessions_.size()));
session->SignalPortReady.connect(this, &P2PTransportChannel::OnPortReady);
+ session->SignalPortPruned.connect(this, &P2PTransportChannel::OnPortPruned);
session->SignalCandidatesReady.connect(
this, &P2PTransportChannel::OnCandidatesReady);
session->SignalCandidatesAllocationDone.connect(
@@ -1616,7 +1617,7 @@
std::remove(removed_ports_.begin(), removed_ports_.end(), port),
removed_ports_.end());
- LOG(INFO) << "Removed port from p2p socket: "
+ LOG(INFO) << "Removed port because it is destroyed: "
<< static_cast<int>(ports_.size()) << " remaining";
}
@@ -1626,15 +1627,11 @@
if (!config_.gather_continually) {
return;
}
- auto it = std::find(ports_.begin(), ports_.end(), port);
- // Don't need to do anything if the port has been deleted from the port list.
- if (it == ports_.end()) {
+ if (!RemovePort(port)) {
return;
}
- removed_ports_.push_back(*it);
- ports_.erase(it);
- LOG(INFO) << "Removed port due to inactive networks: " << ports_.size()
- << " remaining";
+ LOG(INFO) << "Removed port because its network is inactive : "
+ << port->ToString() << " " << ports_.size() << " remaining";
std::vector<Candidate> candidates = port->Candidates();
for (Candidate& candidate : candidates) {
candidate.set_transport_name(transport_name());
@@ -1642,6 +1639,25 @@
SignalCandidatesRemoved(this, candidates);
}
+void P2PTransportChannel::OnPortPruned(PortAllocatorSession* session,
+ PortInterface* port) {
+ if (RemovePort(port)) {
+ LOG(INFO) << "Removed port because it is pruned: " << port->ToString()
+ << " " << ports_.size() << " remaining";
+ }
+}
+
+bool P2PTransportChannel::RemovePort(PortInterface* port) {
+ auto it = std::find(ports_.begin(), ports_.end(), port);
+ // Don't need to do anything if the port has been deleted from the port list.
+ if (it == ports_.end()) {
+ return false;
+ }
+ ports_.erase(it);
+ removed_ports_.push_back(port);
+ return true;
+}
+
// We data is available, let listeners know
void P2PTransportChannel::OnReadPacket(Connection* connection,
const char* data,
diff --git a/p2p/base/p2ptransportchannel.h b/p2p/base/p2ptransportchannel.h
index d90cc61..3d32f31 100644
--- a/p2p/base/p2ptransportchannel.h
+++ b/p2p/base/p2ptransportchannel.h
@@ -268,6 +268,9 @@
void AddConnection(Connection* connection);
void OnPortReady(PortAllocatorSession *session, PortInterface* port);
+ void OnPortPruned(PortAllocatorSession* session, PortInterface* port);
+ // Returns true if the port is found and removed from |ports_|.
+ bool RemovePort(PortInterface* port);
void OnCandidatesReady(PortAllocatorSession *session,
const std::vector<Candidate>& candidates);
void OnCandidatesAllocationDone(PortAllocatorSession* session);
diff --git a/p2p/base/p2ptransportchannel_unittest.cc b/p2p/base/p2ptransportchannel_unittest.cc
index b564500..0408552 100644
--- a/p2p/base/p2ptransportchannel_unittest.cc
+++ b/p2p/base/p2ptransportchannel_unittest.cc
@@ -1572,9 +1572,9 @@
auto& allocator_2 = GetEndpoint(1)->allocator_;
int pool_size = 1;
allocator_1->SetConfiguration(allocator_1->stun_servers(),
- allocator_1->turn_servers(), pool_size);
+ allocator_1->turn_servers(), pool_size, false);
allocator_2->SetConfiguration(allocator_2->stun_servers(),
- allocator_2->turn_servers(), pool_size);
+ allocator_2->turn_servers(), pool_size, false);
const PortAllocatorSession* pooled_session_1 =
allocator_1->GetPooledSession();
const PortAllocatorSession* pooled_session_2 =
@@ -1615,9 +1615,9 @@
auto& allocator_2 = GetEndpoint(1)->allocator_;
int pool_size = 1;
allocator_1->SetConfiguration(allocator_1->stun_servers(),
- allocator_1->turn_servers(), pool_size);
+ allocator_1->turn_servers(), pool_size, false);
allocator_2->SetConfiguration(allocator_2->stun_servers(),
- allocator_2->turn_servers(), pool_size);
+ allocator_2->turn_servers(), pool_size, false);
const PortAllocatorSession* pooled_session_1 =
allocator_1->GetPooledSession();
const PortAllocatorSession* pooled_session_2 =
diff --git a/p2p/base/port_unittest.cc b/p2p/base/port_unittest.cc
index 743cdda..e156e01 100644
--- a/p2p/base/port_unittest.cc
+++ b/p2p/base/port_unittest.cc
@@ -152,6 +152,8 @@
return true;
}
+ virtual ProtocolType GetProtocol() const { return PROTO_UDP; }
+
// Exposed for testing candidate building.
void AddCandidateAddress(const rtc::SocketAddress& addr) {
AddAddress(addr, addr, rtc::SocketAddress(), "udp", "", "", Type(),
diff --git a/p2p/base/portallocator.cc b/p2p/base/portallocator.cc
index f9f87b0..9ee08a1 100644
--- a/p2p/base/portallocator.cc
+++ b/p2p/base/portallocator.cc
@@ -32,11 +32,13 @@
void PortAllocator::SetConfiguration(
const ServerAddresses& stun_servers,
const std::vector<RelayServerConfig>& turn_servers,
- int candidate_pool_size) {
+ int candidate_pool_size,
+ bool prune_turn_ports) {
bool ice_servers_changed =
(stun_servers != stun_servers_ || turn_servers != turn_servers_);
stun_servers_ = stun_servers;
turn_servers_ = turn_servers;
+ prune_turn_ports_ = prune_turn_ports;
// If ICE servers changed, throw away any existing pooled sessions and create
// new ones.
diff --git a/p2p/base/portallocator.h b/p2p/base/portallocator.h
index 6a32b96..9b4465d 100644
--- a/p2p/base/portallocator.h
+++ b/p2p/base/portallocator.h
@@ -178,6 +178,11 @@
sigslot::signal2<PortAllocatorSession*,
const std::vector<Candidate>&> SignalCandidatesReady;
sigslot::signal1<PortAllocatorSession*> SignalCandidatesAllocationDone;
+ // A TURN port is pruned if a higher-priority TURN port becomes ready
+ // (pairable). When it is pruned, it will not be used for creating
+ // connections and its candidates will not be sent to the remote side
+ // if they have not been sent.
+ sigslot::signal2<PortAllocatorSession*, PortInterface*> SignalPortPruned;
virtual uint32_t generation() { return generation_; }
virtual void set_generation(uint32_t generation) { generation_ = generation; }
@@ -253,7 +258,8 @@
// pooled sessions will be either created or destroyed as necessary.
void SetConfiguration(const ServerAddresses& stun_servers,
const std::vector<RelayServerConfig>& turn_servers,
- int candidate_pool_size);
+ int candidate_pool_size,
+ bool prune_turn_ports);
const ServerAddresses& stun_servers() const { return stun_servers_; }
@@ -327,6 +333,8 @@
candidate_filter_ = filter;
}
+ bool prune_turn_ports() const { return prune_turn_ports_; }
+
// Gets/Sets the Origin value used for WebRTC STUN requests.
const std::string& origin() const { return origin_; }
void set_origin(const std::string& origin) { origin_ = origin; }
@@ -357,6 +365,7 @@
// both owned by this class and taken by TakePooledSession.
int allocated_pooled_session_count_ = 0;
std::deque<std::unique_ptr<PortAllocatorSession>> pooled_sessions_;
+ bool prune_turn_ports_ = false;
};
} // namespace cricket
diff --git a/p2p/base/portallocator_unittest.cc b/p2p/base/portallocator_unittest.cc
index 6a34a25..06abaad 100644
--- a/p2p/base/portallocator_unittest.cc
+++ b/p2p/base/portallocator_unittest.cc
@@ -35,7 +35,7 @@
void SetConfigurationWithPoolSize(int candidate_pool_size) {
allocator_->SetConfiguration(cricket::ServerAddresses(),
std::vector<cricket::RelayServerConfig>(),
- candidate_pool_size);
+ candidate_pool_size, false);
}
std::unique_ptr<cricket::FakePortAllocatorSession> CreateSession(
@@ -107,14 +107,14 @@
TEST_F(PortAllocatorTest, SetConfigurationUpdatesIceServers) {
cricket::ServerAddresses stun_servers_1 = {stun_server_1};
std::vector<cricket::RelayServerConfig> turn_servers_1 = {turn_server_1};
- allocator_->SetConfiguration(stun_servers_1, turn_servers_1, 0);
+ allocator_->SetConfiguration(stun_servers_1, turn_servers_1, 0, false);
EXPECT_EQ(stun_servers_1, allocator_->stun_servers());
EXPECT_EQ(turn_servers_1, allocator_->turn_servers());
// Update with a different set of servers.
cricket::ServerAddresses stun_servers_2 = {stun_server_2};
std::vector<cricket::RelayServerConfig> turn_servers_2 = {turn_server_2};
- allocator_->SetConfiguration(stun_servers_2, turn_servers_2, 0);
+ allocator_->SetConfiguration(stun_servers_2, turn_servers_2, 0, false);
EXPECT_EQ(stun_servers_2, allocator_->stun_servers());
EXPECT_EQ(turn_servers_2, allocator_->turn_servers());
}
@@ -182,14 +182,14 @@
SetConfigurationRecreatesPooledSessionsWhenIceServersChange) {
cricket::ServerAddresses stun_servers_1 = {stun_server_1};
std::vector<cricket::RelayServerConfig> turn_servers_1 = {turn_server_1};
- allocator_->SetConfiguration(stun_servers_1, turn_servers_1, 1);
+ allocator_->SetConfiguration(stun_servers_1, turn_servers_1, 1, false);
EXPECT_EQ(stun_servers_1, allocator_->stun_servers());
EXPECT_EQ(turn_servers_1, allocator_->turn_servers());
// Update with a different set of servers (and also change pool size).
cricket::ServerAddresses stun_servers_2 = {stun_server_2};
std::vector<cricket::RelayServerConfig> turn_servers_2 = {turn_server_2};
- allocator_->SetConfiguration(stun_servers_2, turn_servers_2, 2);
+ allocator_->SetConfiguration(stun_servers_2, turn_servers_2, 2, false);
EXPECT_EQ(stun_servers_2, allocator_->stun_servers());
EXPECT_EQ(turn_servers_2, allocator_->turn_servers());
auto session_1 = TakePooledSession();
diff --git a/p2p/base/portinterface.h b/p2p/base/portinterface.h
index e738619..987c3fc 100644
--- a/p2p/base/portinterface.h
+++ b/p2p/base/portinterface.h
@@ -76,6 +76,8 @@
virtual int GetOption(rtc::Socket::Option opt, int* value) = 0;
virtual int GetError() = 0;
+ virtual ProtocolType GetProtocol() const = 0;
+
virtual const std::vector<Candidate>& Candidates() const = 0;
// Sends the given packet to the given address, provided that the address is
diff --git a/p2p/base/relayport.h b/p2p/base/relayport.h
index 402736c..8fa2235 100644
--- a/p2p/base/relayport.h
+++ b/p2p/base/relayport.h
@@ -68,6 +68,12 @@
const ProtocolAddress * ServerAddress(size_t index) const;
bool IsReady() { return ready_; }
+ ProtocolType GetProtocol() const override {
+ // We shouldn't be using RelayPort, but we need to provide an
+ // implementation here.
+ return PROTO_UDP;
+ }
+
// Used for testing.
sigslot::signal1<const ProtocolAddress*> SignalConnectFailure;
sigslot::signal1<const ProtocolAddress*> SignalSoftTimeout;
diff --git a/p2p/base/stunport.h b/p2p/base/stunport.h
index 82be6fa..1bbe25f 100644
--- a/p2p/base/stunport.h
+++ b/p2p/base/stunport.h
@@ -80,8 +80,7 @@
const ServerAddresses& server_addresses() const {
return server_addresses_;
}
- void
- set_server_addresses(const ServerAddresses& addresses) {
+ void set_server_addresses(const ServerAddresses& addresses) {
server_addresses_ = addresses;
}
@@ -105,6 +104,8 @@
return protocol == UDP_PROTOCOL_NAME;
}
+ virtual ProtocolType GetProtocol() const { return PROTO_UDP; }
+
void set_stun_keepalive_delay(int delay) {
stun_keepalive_delay_ = delay;
}
diff --git a/p2p/base/tcpport.h b/p2p/base/tcpport.h
index 77bbd09..76a73f8 100644
--- a/p2p/base/tcpport.h
+++ b/p2p/base/tcpport.h
@@ -61,6 +61,8 @@
return protocol == TCP_PROTOCOL_NAME || protocol == SSLTCP_PROTOCOL_NAME;
}
+ ProtocolType GetProtocol() const override { return PROTO_TCP; }
+
protected:
TCPPort(rtc::Thread* thread,
rtc::PacketSocketFactory* factory,
diff --git a/p2p/base/turnport.h b/p2p/base/turnport.h
index 6e528aa..3bb09bc 100644
--- a/p2p/base/turnport.h
+++ b/p2p/base/turnport.h
@@ -85,6 +85,8 @@
}
const RelayCredentials& credentials() const { return credentials_; }
+ virtual ProtocolType GetProtocol() const { return server_address_.proto; }
+
virtual void PrepareAddress();
virtual Connection* CreateConnection(
const Candidate& c, PortInterface::CandidateOrigin origin);
diff --git a/p2p/client/basicportallocator.cc b/p2p/client/basicportallocator.cc
index e39a440..19f23b3 100644
--- a/p2p/client/basicportallocator.cc
+++ b/p2p/client/basicportallocator.cc
@@ -47,6 +47,36 @@
const int kNumPhases = 4;
+// Gets address family priority: IPv6 > IPv4 > Unspecified.
+int GetAddressFamilyPriority(int ip_family) {
+ switch (ip_family) {
+ case AF_INET6:
+ return 2;
+ case AF_INET:
+ return 1;
+ default:
+ RTC_DCHECK(false);
+ return 0;
+ }
+}
+
+// Returns positive if a is better, negative if b is better, and 0 otherwise.
+int ComparePort(const cricket::Port* a, const cricket::Port* b) {
+ static constexpr int a_is_better = 1;
+ static constexpr int b_is_better = -1;
+ // Protocol type is defined as UDP = 0, TCP = 1, SSLTCP = 2.
+ if (a->GetProtocol() < b->GetProtocol()) {
+ return a_is_better;
+ }
+ if (a->GetProtocol() > b->GetProtocol()) {
+ return b_is_better;
+ }
+
+ int a_family = GetAddressFamilyPriority(a->Network()->GetBestIP().family());
+ int b_family = GetAddressFamilyPriority(b->Network()->GetBestIP().family());
+ return a_family - b_family;
+}
+
} // namespace
namespace cricket {
@@ -74,7 +104,7 @@
const ServerAddresses& stun_servers)
: network_manager_(network_manager), socket_factory_(socket_factory) {
ASSERT(socket_factory_ != NULL);
- SetConfiguration(stun_servers, std::vector<RelayServerConfig>(), 0);
+ SetConfiguration(stun_servers, std::vector<RelayServerConfig>(), 0, false);
Construct();
}
@@ -101,7 +131,7 @@
turn_servers.push_back(config);
}
- SetConfiguration(stun_servers, turn_servers, 0);
+ SetConfiguration(stun_servers, turn_servers, 0, false);
Construct();
}
@@ -122,24 +152,30 @@
void BasicPortAllocator::AddTurnServer(const RelayServerConfig& turn_server) {
std::vector<RelayServerConfig> new_turn_servers = turn_servers();
new_turn_servers.push_back(turn_server);
- SetConfiguration(stun_servers(), new_turn_servers, candidate_pool_size());
+ SetConfiguration(stun_servers(), new_turn_servers, candidate_pool_size(),
+ prune_turn_ports());
}
// BasicPortAllocatorSession
BasicPortAllocatorSession::BasicPortAllocatorSession(
- BasicPortAllocator *allocator,
+ BasicPortAllocator* allocator,
const std::string& content_name,
int component,
const std::string& ice_ufrag,
const std::string& ice_pwd)
- : PortAllocatorSession(content_name, component,
- ice_ufrag, ice_pwd, allocator->flags()),
- allocator_(allocator), network_thread_(NULL),
+ : PortAllocatorSession(content_name,
+ component,
+ ice_ufrag,
+ ice_pwd,
+ allocator->flags()),
+ allocator_(allocator),
+ network_thread_(NULL),
socket_factory_(allocator->socket_factory()),
allocation_started_(false),
network_manager_started_(false),
running_(false),
- allocation_sequences_created_(false) {
+ allocation_sequences_created_(false),
+ prune_turn_ports_(allocator->prune_turn_ports()) {
allocator_->network_manager()->SignalNetworksChanged.connect(
this, &BasicPortAllocatorSession::OnNetworksChanged);
allocator_->network_manager()->StartUpdating();
@@ -217,9 +253,9 @@
std::vector<PortInterface*> BasicPortAllocatorSession::ReadyPorts() const {
std::vector<PortInterface*> ret;
- for (const PortData& port : ports_) {
- if (port.has_pairable_candidate() && !port.error()) {
- ret.push_back(port.port());
+ for (const PortData& data : ports_) {
+ if (data.ready()) {
+ ret.push_back(data.port());
}
}
return ret;
@@ -228,6 +264,10 @@
std::vector<Candidate> BasicPortAllocatorSession::ReadyCandidates() const {
std::vector<Candidate> candidates;
for (const PortData& data : ports_) {
+ if (!data.ready()) {
+ continue;
+ }
+
for (const Candidate& candidate : data.port()->Candidates()) {
if (!CheckCandidateFilter(candidate)) {
continue;
@@ -278,16 +318,11 @@
return false;
}
- // If all allocated ports are in complete state, session must have got all
+ // If all allocated ports are no longer gathering, session must have got all
// expected candidates. Session will trigger candidates allocation complete
// signal.
- if (!std::all_of(ports_.begin(), ports_.end(), [](const PortData& port) {
- return (port.complete() || port.error());
- })) {
- return false;
- }
-
- return true;
+ return std::none_of(ports_.begin(), ports_.end(),
+ [](const PortData& port) { return port.inprogress(); });
}
void BasicPortAllocatorSession::OnMessage(rtc::Message *message) {
@@ -357,7 +392,7 @@
bool send_signal = false;
for (std::vector<PortData>::iterator it = ports_.begin();
it != ports_.end(); ++it) {
- if (!it->complete() && !it->error()) {
+ if (it->inprogress()) {
// Updating port state to error, which didn't finish allocating candidates
// yet.
it->set_error();
@@ -443,11 +478,8 @@
LOG(LS_WARNING) << "Machine has no networks; no ports will be allocated";
done_signal_needed = true;
} else {
+ PortConfiguration* config = configs_.empty() ? nullptr : configs_.back();
for (uint32_t i = 0; i < networks.size(); ++i) {
- PortConfiguration* config = NULL;
- if (configs_.size() > 0)
- config = configs_.back();
-
uint32_t sequence_flags = flags();
if ((sequence_flags & DISABLE_ALL_PHASES) == DISABLE_ALL_PHASES) {
// If all the ports are disabled we should just fire the allocation
@@ -568,25 +600,8 @@
PortData* data = FindPort(port);
ASSERT(data != NULL);
// Discarding any candidate signal if port allocation status is
- // already in completed state.
- if (data->complete() || data->error()) {
- return;
- }
-
- ProtocolType pvalue;
- bool candidate_protocol_enabled =
- StringToProto(c.protocol().c_str(), &pvalue) &&
- data->sequence()->ProtocolEnabled(pvalue);
-
- if (CheckCandidateFilter(c) && candidate_protocol_enabled) {
- std::vector<Candidate> candidates;
- candidates.push_back(SanitizeRelatedAddress(c));
- SignalCandidatesReady(this, candidates);
- }
-
- // Port has already been marked as having a pairable candidate.
- // Nothing to do here.
- if (data->has_pairable_candidate()) {
+ // already done with gathering.
+ if (!data->inprogress()) {
return;
}
@@ -594,11 +609,76 @@
// usable candidate from the port, or simply because the port is bound to the
// any address and therefore has no host candidate. This will trigger the port
// to start creating candidate pairs (connections) and issue connectivity
- // checks.
- if (CandidatePairable(c, port)) {
+ // checks. If port has already been marked as having a pairable candidate,
+ // do nothing here.
+ // Note: We should check whether any candidates may become ready after this
+ // because there we will check whether the candidate is generated by the ready
+ // ports, which may include this port.
+ bool pruned_port = false;
+ if (CandidatePairable(c, port) && !data->has_pairable_candidate()) {
data->set_has_pairable_candidate(true);
- SignalPortReady(this, port);
+
+ if (prune_turn_ports_ && port->Type() == RELAY_PORT_TYPE) {
+ pruned_port = PruneTurnPorts(port);
+ }
+ // If the current port is not pruned yet, SignalPortReady.
+ if (!data->pruned()) {
+ SignalPortReady(this, port);
+ }
}
+
+ ProtocolType pvalue;
+ bool candidate_protocol_enabled =
+ StringToProto(c.protocol().c_str(), &pvalue) &&
+ data->sequence()->ProtocolEnabled(pvalue);
+
+ if (data->ready() && CheckCandidateFilter(c) && candidate_protocol_enabled) {
+ std::vector<Candidate> candidates;
+ candidates.push_back(SanitizeRelatedAddress(c));
+ SignalCandidatesReady(this, candidates);
+ }
+
+ // If we have pruned any port, maybe need to signal port allocation done.
+ if (pruned_port) {
+ MaybeSignalCandidatesAllocationDone();
+ }
+}
+
+Port* BasicPortAllocatorSession::GetBestTurnPortForNetwork(
+ const std::string& network_name) const {
+ Port* best_turn_port = nullptr;
+ for (const PortData& data : ports_) {
+ if (data.port()->Network()->name() == network_name &&
+ data.port()->Type() == RELAY_PORT_TYPE && data.ready() &&
+ (!best_turn_port || ComparePort(data.port(), best_turn_port) > 0)) {
+ best_turn_port = data.port();
+ }
+ }
+ return best_turn_port;
+}
+
+bool BasicPortAllocatorSession::PruneTurnPorts(Port* newly_pairable_turn_port) {
+ bool pruned_port = false;
+ // Note: We determine the same network based only on their network names. So
+ // if an IPv4 address and an IPv6 address have the same network name, they
+ // are considered the same network here.
+ const std::string& network_name = newly_pairable_turn_port->Network()->name();
+ Port* best_turn_port = GetBestTurnPortForNetwork(network_name);
+ // |port| is already in the list of ports, so the best port cannot be nullptr.
+ RTC_CHECK(best_turn_port != nullptr);
+
+ 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_port = true;
+ if (data.port() != newly_pairable_turn_port) {
+ SignalPortPruned(this, data.port());
+ }
+ }
+ }
+ return pruned_port;
}
void BasicPortAllocatorSession::OnPortComplete(Port* port) {
@@ -607,7 +687,7 @@
ASSERT(data != NULL);
// Ignore any late signals.
- if (data->complete() || data->error()) {
+ if (!data->inprogress()) {
return;
}
@@ -622,7 +702,7 @@
PortData* data = FindPort(port);
ASSERT(data != NULL);
// We might have already given up on this port and stopped it.
- if (data->complete() || data->error()) {
+ if (!data->inprogress()) {
return;
}
@@ -1028,13 +1108,11 @@
return;
}
- PortConfiguration::RelayList::const_iterator relay;
- for (relay = config_->relays.begin();
- relay != config_->relays.end(); ++relay) {
- if (relay->type == RELAY_GTURN) {
- CreateGturnPort(*relay);
- } else if (relay->type == RELAY_TURN) {
- CreateTurnPort(*relay);
+ for (RelayServerConfig& relay : config_->relays) {
+ if (relay.type == RELAY_GTURN) {
+ CreateGturnPort(relay);
+ } else if (relay.type == RELAY_TURN) {
+ CreateTurnPort(relay);
} else {
ASSERT(false);
}
diff --git a/p2p/client/basicportallocator.h b/p2p/client/basicportallocator.h
index 8ea66c4..bddb396 100644
--- a/p2p/client/basicportallocator.h
+++ b/p2p/client/basicportallocator.h
@@ -123,7 +123,15 @@
bool has_pairable_candidate() const { return has_pairable_candidate_; }
bool complete() const { return state_ == STATE_COMPLETE; }
bool error() const { return state_ == STATE_ERROR; }
+ bool pruned() const { return state_ == STATE_PRUNED; }
+ bool inprogress() const { return state_ == STATE_INPROGRESS; }
+ // Returns true if this port is ready to be used.
+ bool ready() const {
+ return has_pairable_candidate_ && state_ != STATE_ERROR &&
+ state_ != STATE_PRUNED;
+ }
+ void set_pruned() { state_ = STATE_PRUNED; }
void set_has_pairable_candidate(bool has_pairable_candidate) {
if (has_pairable_candidate) {
ASSERT(state_ == STATE_INPROGRESS);
@@ -142,7 +150,9 @@
enum State {
STATE_INPROGRESS, // Still gathering candidates.
STATE_COMPLETE, // All candidates allocated and ready for process.
- STATE_ERROR // Error in gathering candidates.
+ STATE_ERROR, // Error in gathering candidates.
+ STATE_PRUNED // Pruned by higher priority ports on the same network
+ // interface. Only TURN ports may be pruned.
};
Port* port_ = nullptr;
AllocationSequence* sequence_ = nullptr;
@@ -178,6 +188,10 @@
// in order to avoid leaking any information.
Candidate SanitizeRelatedAddress(const Candidate& c) const;
+ Port* GetBestTurnPortForNetwork(const std::string& network_name) const;
+ // Returns true if at least one TURN port is pruned.
+ bool PruneTurnPorts(Port* newly_pairable_turn_port);
+
BasicPortAllocator* allocator_;
rtc::Thread* network_thread_;
std::unique_ptr<rtc::PacketSocketFactory> owned_socket_factory_;
@@ -190,6 +204,8 @@
std::vector<AllocationSequence*> sequences_;
std::vector<PortData> ports_;
uint32_t candidate_filter_ = CF_ALL;
+ // Whether to prune low-priority ports, taken from the port allocator.
+ bool prune_turn_ports_;
friend class AllocationSequence;
};
diff --git a/p2p/client/basicportallocator_unittest.cc b/p2p/client/basicportallocator_unittest.cc
index 6d5d108..28a522f 100644
--- a/p2p/client/basicportallocator_unittest.cc
+++ b/p2p/client/basicportallocator_unittest.cc
@@ -39,12 +39,15 @@
using rtc::Thread;
static const SocketAddress kClientAddr("11.11.11.11", 0);
+static const SocketAddress kClientAddr2("22.22.22.22", 0);
static const SocketAddress kLoopbackAddr("127.0.0.1", 0);
static const SocketAddress kPrivateAddr("192.168.1.11", 0);
static const SocketAddress kPrivateAddr2("192.168.1.12", 0);
static const SocketAddress kClientIPv6Addr("2401:fa00:4:1000:be30:5bff:fee5:c3",
0);
-static const SocketAddress kClientAddr2("22.22.22.22", 0);
+static const SocketAddress kClientIPv6Addr2(
+ "2401:fa00:4:2000:be30:5bff:fee5:c3",
+ 0);
static const SocketAddress kNatUdpAddr("77.77.77.77", rtc::NAT_SERVER_UDP_PORT);
static const SocketAddress kNatTcpAddr("77.77.77.77", rtc::NAT_SERVER_TCP_PORT);
static const SocketAddress kRemoteClientAddr("22.22.22.22", 0);
@@ -56,7 +59,13 @@
static const SocketAddress kRelaySslTcpIntAddr("99.99.99.2", 5004);
static const SocketAddress kRelaySslTcpExtAddr("99.99.99.3", 5005);
static const SocketAddress kTurnUdpIntAddr("99.99.99.4", 3478);
+static const SocketAddress kTurnUdpIntIPv6Addr(
+ "2402:fb00:4:1000:be30:5bff:fee5:c3",
+ 3479);
static const SocketAddress kTurnTcpIntAddr("99.99.99.5", 3478);
+static const SocketAddress kTurnTcpIntIPv6Addr(
+ "2402:fb00:4:2000:be30:5bff:fee5:c3",
+ 3479);
static const SocketAddress kTurnUdpExtAddr("99.99.99.6", 0);
// Minimum and maximum port for port range tests.
@@ -184,12 +193,10 @@
turn_server.credentials = credentials;
if (!udp_turn.IsNil()) {
- turn_server.ports.push_back(
- ProtocolAddress(kTurnUdpIntAddr, PROTO_UDP, false));
+ turn_server.ports.push_back(ProtocolAddress(udp_turn, PROTO_UDP, false));
}
if (!tcp_turn.IsNil()) {
- turn_server.ports.push_back(
- ProtocolAddress(kTurnTcpIntAddr, PROTO_TCP, false));
+ turn_server.ports.push_back(ProtocolAddress(tcp_turn, PROTO_TCP, false));
}
allocator_->AddTurnServer(turn_server);
}
@@ -232,6 +239,8 @@
sid, content_name, component, ice_ufrag, ice_pwd);
session->SignalPortReady.connect(this,
&BasicPortAllocatorTest::OnPortReady);
+ session->SignalPortPruned.connect(this,
+ &BasicPortAllocatorTest::OnPortPruned);
session->SignalCandidatesReady.connect(
this, &BasicPortAllocatorTest::OnCandidatesReady);
session->SignalCandidatesAllocationDone.connect(
@@ -251,6 +260,20 @@
(pattern.port() != 0 && address.port() == pattern.port()));
}
+ // Returns the number of ports that have matching type, protocol and
+ // address.
+ static int CountPorts(const std::vector<PortInterface*>& ports,
+ const std::string& type,
+ ProtocolType protocol,
+ const SocketAddress& client_addr) {
+ return std::count_if(
+ ports.begin(), ports.end(),
+ [type, protocol, client_addr](PortInterface* port) {
+ return port->Type() == type && port->GetProtocol() == protocol &&
+ port->Network()->GetBestIP() == client_addr.ipaddr();
+ });
+ }
+
// Find a candidate and return it.
static bool FindCandidate(const std::vector<Candidate>& candidates,
const std::string& type,
@@ -392,6 +415,15 @@
EXPECT_NE(ready_ports.end(),
std::find(ready_ports.begin(), ready_ports.end(), port));
}
+ void OnPortPruned(PortAllocatorSession* ses, PortInterface* port) {
+ LOG(LS_INFO) << "OnPortPruned: " << port->ToString();
+ ports_.erase(std::remove(ports_.begin(), ports_.end(), port), ports_.end());
+ // Make sure the pruned port is not in ReadyPorts.
+ auto ready_ports = ses->ReadyPorts();
+ EXPECT_EQ(ready_ports.end(),
+ std::find(ready_ports.begin(), ready_ports.end(), port));
+ }
+
void OnCandidatesReady(PortAllocatorSession* ses,
const std::vector<Candidate>& candidates) {
for (const Candidate& candidate : candidates) {
@@ -1174,6 +1206,145 @@
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, TestUdpTurnPortDisablesTcpTurnPorts) {
+ 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));
+
+ // 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<Candidate>& 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));
+}
+
+// Tests that if prune_turn_ports is set, IPv4 TurnPort will not
+// be used if IPv6 TurnPort is used.
+TEST_F(BasicPortAllocatorTest, TestIPv6TurnPortDisablesIPv4TurnPorts) {
+ 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());
+
+ 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);
+ rtc::Thread::Current()->ProcessMessages(1000);
+ // 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));
+
+ // 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<Candidate>& 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));
+}
+
+// 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");
+ AddInterface(kClientIPv6Addr, "net1");
+ AddInterface(kClientAddr2, "net2");
+ AddInterface(kClientIPv6Addr2, "net2");
+ 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));
+
+ // We don't remove candidates, so there may be more than 10 candidates
+ // in |candidates_|.
+ EXPECT_LE(10U, candidates_.size());
+ const std::vector<Candidate>& 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));
+}
+
// Testing DNS resolve for the TURN server, this will test AllocationSequence
// handling the unresolved address signal from TurnPort.
TEST_F(BasicPortAllocatorTest, TestSharedSocketWithServerAddressResolve) {
@@ -1469,7 +1640,7 @@
AddInterface(kClientAddr);
int pool_size = 1;
allocator_->SetConfiguration(allocator_->stun_servers(),
- allocator_->turn_servers(), pool_size);
+ allocator_->turn_servers(), pool_size, false);
const PortAllocatorSession* peeked_session = allocator_->GetPooledSession();
ASSERT_NE(nullptr, peeked_session);
EXPECT_EQ_WAIT(true, peeked_session->CandidatesAllocationDone(),
@@ -1505,7 +1676,7 @@
AddInterface(kClientAddr);
int pool_size = 1;
allocator_->SetConfiguration(allocator_->stun_servers(),
- allocator_->turn_servers(), pool_size);
+ allocator_->turn_servers(), pool_size, false);
const PortAllocatorSession* peeked_session = allocator_->GetPooledSession();
ASSERT_NE(nullptr, peeked_session);
EXPECT_EQ_WAIT(true, peeked_session->CandidatesAllocationDone(),