webrtc_m130/p2p/base/port_allocator.cc
Philipp Hancke e3fb8122aa Reland "Let port allocator create ice tie breaker"
This is a reland of commit 3f3f991c03bb4073a06da37c822daaa9deed9307

Original change's description:
> Let port allocator create ice tie breaker
>
> Moves the responsibility for creating the ICE tie breaker from the JSEP transport controller to the port allocator. This will allow a future change to separate the ICE tie breaker (which is sent over the network and hence known to the peer) from the "port allocator random" (that is used to seed the ICE candidate foundation crc32 checksum) as an implementation detail.
>
> BUG=webrtc:14626
>
> Change-Id: I3a9a0980238d6108b1b154f45de2975b08793b1c
> Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/281660
> Reviewed-by: Harald Alvestrand <hta@webrtc.org>
> Commit-Queue: Philipp Hancke <phancke@microsoft.com>
> Cr-Commit-Position: refs/heads/main@{#41707}

Bug: webrtc:14626
Change-Id: Id3c8f257c5611958551bd66d7ce7a885bf8ba2f9
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/339320
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Philipp Hancke <phancke@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#41720}
2024-02-12 14:47:12 +00:00

325 lines
12 KiB
C++

/*
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "p2p/base/port_allocator.h"
#include <iterator>
#include <optional>
#include <set>
#include <utility>
#include "absl/strings/string_view.h"
#include "p2p/base/ice_credentials_iterator.h"
#include "rtc_base/checks.h"
namespace cricket {
RelayServerConfig::RelayServerConfig() {}
RelayServerConfig::RelayServerConfig(const rtc::SocketAddress& address,
absl::string_view username,
absl::string_view password,
ProtocolType proto)
: credentials(username, password) {
ports.push_back(ProtocolAddress(address, proto));
}
RelayServerConfig::RelayServerConfig(absl::string_view address,
int port,
absl::string_view username,
absl::string_view password,
ProtocolType proto)
: RelayServerConfig(rtc::SocketAddress(address, port),
username,
password,
proto) {}
// Legacy constructor where "secure" and PROTO_TCP implies PROTO_TLS.
RelayServerConfig::RelayServerConfig(absl::string_view address,
int port,
absl::string_view username,
absl::string_view password,
ProtocolType proto,
bool secure)
: RelayServerConfig(address,
port,
username,
password,
(proto == PROTO_TCP && secure ? PROTO_TLS : proto)) {}
RelayServerConfig::RelayServerConfig(const RelayServerConfig&) = default;
RelayServerConfig::~RelayServerConfig() = default;
PortAllocatorSession::PortAllocatorSession(absl::string_view content_name,
int component,
absl::string_view ice_ufrag,
absl::string_view ice_pwd,
uint32_t flags)
: flags_(flags),
generation_(0),
content_name_(content_name),
component_(component),
ice_ufrag_(ice_ufrag),
ice_pwd_(ice_pwd) {
// Pooled sessions are allowed to be created with empty content name,
// component, ufrag and password.
RTC_DCHECK(ice_ufrag.empty() == ice_pwd.empty());
}
PortAllocatorSession::~PortAllocatorSession() = default;
bool PortAllocatorSession::IsCleared() const {
return false;
}
bool PortAllocatorSession::IsStopped() const {
return false;
}
uint32_t PortAllocatorSession::generation() {
return generation_;
}
void PortAllocatorSession::set_generation(uint32_t generation) {
generation_ = generation;
}
PortAllocator::PortAllocator()
: flags_(kDefaultPortAllocatorFlags),
min_port_(0),
max_port_(0),
max_ipv6_networks_(kDefaultMaxIPv6Networks),
step_delay_(kDefaultStepDelay),
allow_tcp_listen_(true),
candidate_filter_(CF_ALL),
tiebreaker_(rtc::CreateRandomId64()) {
// The allocator will be attached to a thread in Initialize.
thread_checker_.Detach();
}
void PortAllocator::Initialize() {
RTC_DCHECK(thread_checker_.IsCurrent());
initialized_ = true;
}
PortAllocator::~PortAllocator() {
CheckRunOnValidThreadIfInitialized();
}
void PortAllocator::set_restrict_ice_credentials_change(bool value) {
restrict_ice_credentials_change_ = value;
}
// Deprecated
bool PortAllocator::SetConfiguration(
const ServerAddresses& stun_servers,
const std::vector<RelayServerConfig>& turn_servers,
int candidate_pool_size,
bool prune_turn_ports,
webrtc::TurnCustomizer* turn_customizer,
const absl::optional<int>& stun_candidate_keepalive_interval) {
webrtc::PortPrunePolicy turn_port_prune_policy =
prune_turn_ports ? webrtc::PRUNE_BASED_ON_PRIORITY : webrtc::NO_PRUNE;
return SetConfiguration(stun_servers, turn_servers, candidate_pool_size,
turn_port_prune_policy, turn_customizer,
stun_candidate_keepalive_interval);
}
bool PortAllocator::SetConfiguration(
const ServerAddresses& stun_servers,
const std::vector<RelayServerConfig>& turn_servers,
int candidate_pool_size,
webrtc::PortPrunePolicy turn_port_prune_policy,
webrtc::TurnCustomizer* turn_customizer,
const absl::optional<int>& stun_candidate_keepalive_interval) {
RTC_DCHECK_GE(candidate_pool_size, 0);
RTC_DCHECK_LE(candidate_pool_size, static_cast<int>(UINT16_MAX));
CheckRunOnValidThreadIfInitialized();
// A positive candidate pool size would lead to the creation of a pooled
// allocator session and starting getting ports, which we should only do on
// the network thread.
RTC_DCHECK(candidate_pool_size == 0 || thread_checker_.IsCurrent());
bool ice_servers_changed =
(stun_servers != stun_servers_ || turn_servers != turn_servers_);
stun_servers_ = stun_servers;
turn_servers_ = turn_servers;
turn_port_prune_policy_ = turn_port_prune_policy;
candidate_pool_size_ = candidate_pool_size;
// If ICE servers changed, throw away any existing pooled sessions and create
// new ones.
if (ice_servers_changed) {
pooled_sessions_.clear();
}
turn_customizer_ = turn_customizer;
// If `candidate_pool_size_` is less than the number of pooled sessions, get
// rid of the extras.
while (candidate_pool_size_ < static_cast<int>(pooled_sessions_.size())) {
pooled_sessions_.back().reset(nullptr);
pooled_sessions_.pop_back();
}
// `stun_candidate_keepalive_interval_` will be used in STUN port allocation
// in future sessions. We also update the ready ports in the pooled sessions.
// Ports in sessions that are taken and owned by P2PTransportChannel will be
// updated there via IceConfig.
stun_candidate_keepalive_interval_ = stun_candidate_keepalive_interval;
for (const auto& session : pooled_sessions_) {
session->SetStunKeepaliveIntervalForReadyPorts(
stun_candidate_keepalive_interval_);
}
// If `candidate_pool_size_` is greater than the number of pooled sessions,
// create new sessions.
while (static_cast<int>(pooled_sessions_.size()) < candidate_pool_size_) {
IceParameters iceCredentials =
IceCredentialsIterator::CreateRandomIceCredentials();
PortAllocatorSession* pooled_session =
CreateSessionInternal("", 0, iceCredentials.ufrag, iceCredentials.pwd);
pooled_session->set_pooled(true);
pooled_session->StartGettingPorts();
pooled_sessions_.push_back(
std::unique_ptr<PortAllocatorSession>(pooled_session));
}
return true;
}
std::unique_ptr<PortAllocatorSession> PortAllocator::CreateSession(
absl::string_view content_name,
int component,
absl::string_view ice_ufrag,
absl::string_view ice_pwd) {
CheckRunOnValidThreadAndInitialized();
auto session = std::unique_ptr<PortAllocatorSession>(
CreateSessionInternal(content_name, component, ice_ufrag, ice_pwd));
session->SetCandidateFilter(candidate_filter());
return session;
}
std::unique_ptr<PortAllocatorSession> PortAllocator::TakePooledSession(
absl::string_view content_name,
int component,
absl::string_view ice_ufrag,
absl::string_view ice_pwd) {
CheckRunOnValidThreadAndInitialized();
RTC_DCHECK(!ice_ufrag.empty());
RTC_DCHECK(!ice_pwd.empty());
if (pooled_sessions_.empty()) {
return nullptr;
}
IceParameters credentials(ice_ufrag, ice_pwd, false);
// If restrict_ice_credentials_change_ is TRUE, then call FindPooledSession
// with ice credentials. Otherwise call it with nullptr which means
// "find any" pooled session.
auto cit = FindPooledSession(restrict_ice_credentials_change_ ? &credentials
: nullptr);
if (cit == pooled_sessions_.end()) {
return nullptr;
}
auto it =
pooled_sessions_.begin() + std::distance(pooled_sessions_.cbegin(), cit);
std::unique_ptr<PortAllocatorSession> ret = std::move(*it);
ret->SetIceParameters(content_name, component, ice_ufrag, ice_pwd);
ret->set_pooled(false);
// According to JSEP, a pooled session should filter candidates only
// after it's taken out of the pool.
ret->SetCandidateFilter(candidate_filter());
pooled_sessions_.erase(it);
return ret;
}
const PortAllocatorSession* PortAllocator::GetPooledSession(
const IceParameters* ice_credentials) const {
CheckRunOnValidThreadAndInitialized();
auto it = FindPooledSession(ice_credentials);
if (it == pooled_sessions_.end()) {
return nullptr;
} else {
return it->get();
}
}
std::vector<std::unique_ptr<PortAllocatorSession>>::const_iterator
PortAllocator::FindPooledSession(const IceParameters* ice_credentials) const {
for (auto it = pooled_sessions_.begin(); it != pooled_sessions_.end(); ++it) {
if (ice_credentials == nullptr ||
((*it)->ice_ufrag() == ice_credentials->ufrag &&
(*it)->ice_pwd() == ice_credentials->pwd)) {
return it;
}
}
return pooled_sessions_.end();
}
void PortAllocator::DiscardCandidatePool() {
CheckRunOnValidThreadIfInitialized();
pooled_sessions_.clear();
}
void PortAllocator::SetCandidateFilter(uint32_t filter) {
CheckRunOnValidThreadIfInitialized();
if (candidate_filter_ == filter) {
return;
}
uint32_t prev_filter = candidate_filter_;
candidate_filter_ = filter;
SignalCandidateFilterChanged(prev_filter, filter);
}
void PortAllocator::GetCandidateStatsFromPooledSessions(
CandidateStatsList* candidate_stats_list) {
CheckRunOnValidThreadAndInitialized();
for (const auto& session : pooled_sessions()) {
session->GetCandidateStatsFromReadyPorts(candidate_stats_list);
}
}
std::vector<IceParameters> PortAllocator::GetPooledIceCredentials() {
CheckRunOnValidThreadAndInitialized();
std::vector<IceParameters> list;
for (const auto& session : pooled_sessions_) {
list.push_back(
IceParameters(session->ice_ufrag(), session->ice_pwd(), false));
}
return list;
}
Candidate PortAllocator::SanitizeCandidate(const Candidate& c) const {
CheckRunOnValidThreadAndInitialized();
// For a local host candidate, we need to conceal its IP address candidate if
// the mDNS obfuscation is enabled.
bool use_hostname_address =
(c.is_local() || c.is_prflx()) && MdnsObfuscationEnabled();
// If adapter enumeration is disabled or host candidates are disabled,
// clear the raddr of STUN candidates to avoid local address leakage.
bool filter_stun_related_address =
((flags() & PORTALLOCATOR_DISABLE_ADAPTER_ENUMERATION) &&
(flags() & PORTALLOCATOR_DISABLE_DEFAULT_LOCAL_CANDIDATE)) ||
!(candidate_filter_ & CF_HOST) || MdnsObfuscationEnabled();
// If the candidate filter doesn't allow reflexive addresses, empty TURN raddr
// to avoid reflexive address leakage.
bool filter_turn_related_address = !(candidate_filter_ & CF_REFLEXIVE);
// Sanitize related_address when using MDNS.
bool filter_prflx_related_address = MdnsObfuscationEnabled();
bool filter_related_address =
((c.is_stun() && filter_stun_related_address) ||
(c.is_relay() && filter_turn_related_address) ||
(c.is_prflx() && filter_prflx_related_address));
return c.ToSanitizedCopy(use_hostname_address, filter_related_address);
}
} // namespace cricket