Improve IPv6 selection logic when gathering candidates.
- If there are more than 5 IPv6 networks, then diversify IPv6 interface types selection.
- Passing field_trial from peer_connection_factory.cc when creating BasicPortAllocator object.
Bug: webrtc:14334
Change-Id: I7d100d944f4e60414e3421f422997bc3f168cc24
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/271581
Reviewed-by: Per Kjellander <perkj@webrtc.org>
Reviewed-by: Jonas Oreland <jonaso@webrtc.org>
Commit-Queue: Diep Bui <diepbp@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#37924}
diff --git a/p2p/client/basic_port_allocator.cc b/p2p/client/basic_port_allocator.cc
index 8963d4e..819bc51 100644
--- a/p2p/client/basic_port_allocator.cc
+++ b/p2p/client/basic_port_allocator.cc
@@ -31,8 +31,10 @@
#include "p2p/base/turn_port.h"
#include "p2p/base/udp_port.h"
#include "rtc_base/checks.h"
+#include "rtc_base/experiments/field_trial_parser.h"
#include "rtc_base/helpers.h"
#include "rtc_base/logging.h"
+#include "rtc_base/network_constants.h"
#include "rtc_base/strings/string_builder.h"
#include "rtc_base/trace_event.h"
#include "system_wrappers/include/metrics.h"
@@ -152,6 +154,21 @@
return ost.Release();
}
+bool IsDiversifyIpv6InterfacesEnabled(
+ const webrtc::FieldTrialsView* field_trials) {
+ // webrtc:14334: Improve IPv6 network resolution and candidate creation
+ if (field_trials &&
+ field_trials->IsEnabled("WebRTC-IPv6NetworkResolutionFixes")) {
+ webrtc::FieldTrialParameter<bool> diversify_ipv6_interfaces(
+ "DiversifyIpv6Interfaces", false);
+ webrtc::ParseFieldTrial(
+ {&diversify_ipv6_interfaces},
+ field_trials->Lookup("WebRTC-IPv6NetworkResolutionFixes"));
+ return diversify_ipv6_interfaces;
+ }
+ return false;
+}
+
} // namespace
const uint32_t DISABLE_ALL_PHASES =
@@ -163,9 +180,12 @@
rtc::NetworkManager* network_manager,
rtc::PacketSocketFactory* socket_factory,
webrtc::TurnCustomizer* customizer,
- RelayPortFactoryInterface* relay_port_factory)
- : network_manager_(network_manager), socket_factory_(socket_factory) {
- Init(relay_port_factory, nullptr);
+ RelayPortFactoryInterface* relay_port_factory,
+ const webrtc::FieldTrialsView* field_trials)
+ : field_trials_(field_trials),
+ network_manager_(network_manager),
+ socket_factory_(socket_factory) {
+ Init(relay_port_factory);
RTC_DCHECK(relay_port_factory_ != nullptr);
RTC_DCHECK(network_manager_ != nullptr);
RTC_CHECK(socket_factory_ != nullptr);
@@ -175,10 +195,12 @@
BasicPortAllocator::BasicPortAllocator(
rtc::NetworkManager* network_manager,
- std::unique_ptr<rtc::PacketSocketFactory> owned_socket_factory)
- : network_manager_(network_manager),
+ std::unique_ptr<rtc::PacketSocketFactory> owned_socket_factory,
+ const webrtc::FieldTrialsView* field_trials)
+ : field_trials_(field_trials),
+ network_manager_(network_manager),
socket_factory_(std::move(owned_socket_factory)) {
- Init(nullptr, nullptr);
+ Init(nullptr);
RTC_DCHECK(relay_port_factory_ != nullptr);
RTC_DCHECK(network_manager_ != nullptr);
RTC_CHECK(socket_factory_ != nullptr);
@@ -187,10 +209,12 @@
BasicPortAllocator::BasicPortAllocator(
rtc::NetworkManager* network_manager,
std::unique_ptr<rtc::PacketSocketFactory> owned_socket_factory,
- const ServerAddresses& stun_servers)
- : network_manager_(network_manager),
+ const ServerAddresses& stun_servers,
+ const webrtc::FieldTrialsView* field_trials)
+ : field_trials_(field_trials),
+ network_manager_(network_manager),
socket_factory_(std::move(owned_socket_factory)) {
- Init(nullptr, nullptr);
+ Init(nullptr);
RTC_DCHECK(relay_port_factory_ != nullptr);
RTC_DCHECK(network_manager_ != nullptr);
RTC_CHECK(socket_factory_ != nullptr);
@@ -198,11 +222,15 @@
webrtc::NO_PRUNE, nullptr);
}
-BasicPortAllocator::BasicPortAllocator(rtc::NetworkManager* network_manager,
- rtc::PacketSocketFactory* socket_factory,
- const ServerAddresses& stun_servers)
- : network_manager_(network_manager), socket_factory_(socket_factory) {
- Init(nullptr, nullptr);
+BasicPortAllocator::BasicPortAllocator(
+ rtc::NetworkManager* network_manager,
+ rtc::PacketSocketFactory* socket_factory,
+ const ServerAddresses& stun_servers,
+ const webrtc::FieldTrialsView* field_trials)
+ : field_trials_(field_trials),
+ network_manager_(network_manager),
+ socket_factory_(socket_factory) {
+ Init(nullptr);
RTC_DCHECK(relay_port_factory_ != nullptr);
RTC_DCHECK(network_manager_ != nullptr);
RTC_CHECK(socket_factory_ != nullptr);
@@ -278,21 +306,13 @@
turn_port_prune_policy(), turn_customizer());
}
-void BasicPortAllocator::Init(RelayPortFactoryInterface* relay_port_factory,
- const webrtc::FieldTrialsView* field_trials) {
+void BasicPortAllocator::Init(RelayPortFactoryInterface* relay_port_factory) {
if (relay_port_factory != nullptr) {
relay_port_factory_ = relay_port_factory;
} else {
default_relay_port_factory_.reset(new TurnPortFactory());
relay_port_factory_ = default_relay_port_factory_.get();
}
-
- if (field_trials != nullptr) {
- field_trials_ = field_trials;
- } else {
- owned_field_trials_ = std::make_unique<webrtc::FieldTrialBasedConfig>();
- field_trials_ = owned_field_trials_.get();
- }
}
// BasicPortAllocatorSession
@@ -795,21 +815,75 @@
// hard to define what that means though; it's not just "lowest cost".
// Alternatively, we could just focus on making our ICE pinging logic smarter
// such that this filtering isn't necessary in the first place.
- int ipv6_networks = 0;
- for (auto it = networks.begin(); it != networks.end();) {
- if ((*it)->prefix().family() == AF_INET6) {
- if (ipv6_networks >= allocator_->max_ipv6_networks()) {
+ const webrtc::FieldTrialsView* field_trials = allocator_->field_trials();
+ if (IsDiversifyIpv6InterfacesEnabled(field_trials)) {
+ std::vector<const rtc::Network*> ipv6_networks;
+ for (auto it = networks.begin(); it != networks.end();) {
+ if ((*it)->prefix().family() == AF_INET6) {
+ ipv6_networks.push_back(*it);
it = networks.erase(it);
continue;
- } else {
- ++ipv6_networks;
}
+ ++it;
}
- ++it;
+ ipv6_networks =
+ SelectIPv6Networks(ipv6_networks, allocator_->max_ipv6_networks());
+ networks.insert(networks.end(), ipv6_networks.begin(), ipv6_networks.end());
+ } else {
+ int ipv6_networks = 0;
+ for (auto it = networks.begin(); it != networks.end();) {
+ if ((*it)->prefix().family() == AF_INET6) {
+ if (ipv6_networks >= allocator_->max_ipv6_networks()) {
+ it = networks.erase(it);
+ continue;
+ } else {
+ ++ipv6_networks;
+ }
+ }
+ ++it;
+ }
}
return networks;
}
+std::vector<const rtc::Network*> BasicPortAllocatorSession::SelectIPv6Networks(
+ std::vector<const rtc::Network*>& all_ipv6_networks,
+ int max_ipv6_networks) {
+ if (static_cast<int>(all_ipv6_networks.size()) <= max_ipv6_networks) {
+ return all_ipv6_networks;
+ }
+ // Adapter types are placed in priority order. Cellular type is an alias of
+ // cellular, 2G..5G types.
+ std::vector<rtc::AdapterType> adapter_types = {
+ rtc::ADAPTER_TYPE_ETHERNET, rtc::ADAPTER_TYPE_LOOPBACK,
+ rtc::ADAPTER_TYPE_WIFI, rtc::ADAPTER_TYPE_CELLULAR,
+ rtc::ADAPTER_TYPE_VPN, rtc::ADAPTER_TYPE_UNKNOWN,
+ rtc::ADAPTER_TYPE_ANY};
+ int adapter_types_cnt = adapter_types.size();
+ std::vector<const rtc::Network*> selected_networks;
+ int adapter_types_pos = 0;
+
+ while (static_cast<int>(selected_networks.size()) < max_ipv6_networks &&
+ adapter_types_pos < adapter_types_cnt * max_ipv6_networks) {
+ int network_pos = 0;
+ while (network_pos < static_cast<int>(all_ipv6_networks.size())) {
+ if (adapter_types[adapter_types_pos % adapter_types_cnt] ==
+ all_ipv6_networks[network_pos]->type() ||
+ (adapter_types[adapter_types_pos % adapter_types_cnt] ==
+ rtc::ADAPTER_TYPE_CELLULAR &&
+ all_ipv6_networks[network_pos]->IsCellular())) {
+ selected_networks.push_back(all_ipv6_networks[network_pos]);
+ all_ipv6_networks.erase(all_ipv6_networks.begin() + network_pos);
+ break;
+ }
+ network_pos++;
+ }
+ adapter_types_pos++;
+ }
+
+ return selected_networks;
+}
+
// For each network, see if we have a sequence that covers it already. If not,
// create a new sequence to create the appropriate ports.
void BasicPortAllocatorSession::DoAllocate(bool disable_equivalent) {
diff --git a/p2p/client/basic_port_allocator.h b/p2p/client/basic_port_allocator.h
index 9634a0e..173d789 100644
--- a/p2p/client/basic_port_allocator.h
+++ b/p2p/client/basic_port_allocator.h
@@ -39,17 +39,21 @@
BasicPortAllocator(rtc::NetworkManager* network_manager,
rtc::PacketSocketFactory* socket_factory,
webrtc::TurnCustomizer* customizer = nullptr,
- RelayPortFactoryInterface* relay_port_factory = nullptr);
- BasicPortAllocator(
- rtc::NetworkManager* network_manager,
- std::unique_ptr<rtc::PacketSocketFactory> owned_socket_factory);
+ RelayPortFactoryInterface* relay_port_factory = nullptr,
+ const webrtc::FieldTrialsView* field_trials = nullptr);
BasicPortAllocator(
rtc::NetworkManager* network_manager,
std::unique_ptr<rtc::PacketSocketFactory> owned_socket_factory,
- const ServerAddresses& stun_servers);
+ const webrtc::FieldTrialsView* field_trials = nullptr);
+ BasicPortAllocator(
+ rtc::NetworkManager* network_manager,
+ std::unique_ptr<rtc::PacketSocketFactory> owned_socket_factory,
+ const ServerAddresses& stun_servers,
+ const webrtc::FieldTrialsView* field_trials = nullptr);
BasicPortAllocator(rtc::NetworkManager* network_manager,
rtc::PacketSocketFactory* socket_factory,
- const ServerAddresses& stun_servers);
+ const ServerAddresses& stun_servers,
+ const webrtc::FieldTrialsView* field_trials = nullptr);
~BasicPortAllocator() override;
// Set to kDefaultNetworkIgnoreMask by default.
@@ -84,21 +88,22 @@
void SetVpnList(const std::vector<rtc::NetworkMask>& vpn_list) override;
- const webrtc::FieldTrialsView* field_trials() const { return field_trials_; }
+ const webrtc::FieldTrialsView* field_trials() const {
+ return field_trials_.get();
+ }
private:
void OnIceRegathering(PortAllocatorSession* session,
IceRegatheringReason reason);
- // This function makes sure that relay_port_factory_ and field_trials_ is set
- // properly.
- void Init(RelayPortFactoryInterface* relay_port_factory,
- const webrtc::FieldTrialsView* field_trials);
+ // This function makes sure that relay_port_factory_ is set properly.
+ void Init(RelayPortFactoryInterface* relay_port_factory);
bool MdnsObfuscationEnabled() const override;
- const webrtc::FieldTrialsView* field_trials_;
- std::unique_ptr<webrtc::FieldTrialsView> owned_field_trials_;
+ webrtc::AlwaysValidPointer<const webrtc::FieldTrialsView,
+ webrtc::FieldTrialBasedConfig>
+ field_trials_;
rtc::NetworkManager* network_manager_;
const webrtc::AlwaysValidPointerNoDefault<rtc::PacketSocketFactory>
socket_factory_;
@@ -163,6 +168,9 @@
void SetStunKeepaliveIntervalForReadyPorts(
const absl::optional<int>& stun_keepalive_interval) override;
void PruneAllPorts() override;
+ static std::vector<const rtc::Network*> SelectIPv6Networks(
+ std::vector<const rtc::Network*>& all_ipv6_networks,
+ int max_ipv6_networks);
protected:
void UpdateIceParametersInternal() override;
diff --git a/p2p/client/basic_port_allocator_unittest.cc b/p2p/client/basic_port_allocator_unittest.cc
index 8faebbf..d97cd33 100644
--- a/p2p/client/basic_port_allocator_unittest.cc
+++ b/p2p/client/basic_port_allocator_unittest.cc
@@ -72,6 +72,12 @@
static const SocketAddress kClientIPv6Addr3(
"2401:fa00:4:3000:be30:5bff:fee5:c3",
0);
+static const SocketAddress kClientIPv6Addr4(
+ "2401:fa00:4:4000:be30:5bff:fee5:c3",
+ 0);
+static const SocketAddress kClientIPv6Addr5(
+ "2401:fa00:4:5000: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);
@@ -165,7 +171,7 @@
allocator_ = std::make_unique<BasicPortAllocator>(
&network_manager_,
std::make_unique<rtc::BasicPacketSocketFactory>(fss_.get()),
- stun_servers);
+ stun_servers, &field_trials_);
allocator_->Initialize();
allocator_->set_step_delay(kMinimumStepDelay);
webrtc::metrics::Reset();
@@ -375,6 +381,17 @@
return (addr.port() >= min_port && addr.port() <= max_port);
}
+ static bool HasNetwork(const std::vector<const rtc::Network*>& networks,
+ const rtc::Network& to_be_found) {
+ auto it =
+ absl::c_find_if(networks, [to_be_found](const rtc::Network* network) {
+ return network->description() == to_be_found.description() &&
+ network->name() == to_be_found.name() &&
+ network->prefix() == to_be_found.prefix();
+ });
+ return it != networks.end();
+ }
+
void OnCandidatesAllocationDone(PortAllocatorSession* session) {
// We should only get this callback once, except in the mux test where
// we have multiple port allocation sessions.
@@ -486,8 +503,9 @@
if (!stun_server.IsNil()) {
stun_servers.insert(stun_server);
}
- allocator_.reset(new BasicPortAllocator(
- &network_manager_, nat_socket_factory_.get(), stun_servers));
+ allocator_.reset(new BasicPortAllocator(&network_manager_,
+ nat_socket_factory_.get(),
+ stun_servers, &field_trials_));
allocator_->Initialize();
allocator_->set_step_delay(kMinimumStepDelay);
}
@@ -506,6 +524,7 @@
std::vector<PortInterface*> ports_;
std::vector<Candidate> candidates_;
bool candidate_allocation_done_;
+ webrtc::test::ScopedKeyValueConfig field_trials_;
};
class BasicPortAllocatorTestWithRealClock : public BasicPortAllocatorTestBase {
@@ -2472,4 +2491,284 @@
EXPECT_EQ(1U, port_config.StunServers().size());
}
+// Test that no more than allocator.max_ipv6_networks() IPv6 networks are used
+// to gather candidates.
+TEST_F(BasicPortAllocatorTest, TwoIPv6AreSelectedBecauseOfMaxIpv6Limit) {
+ rtc::Network wifi1("wifi1", "Test NetworkAdapter 1", kClientIPv6Addr.ipaddr(),
+ 64, rtc::ADAPTER_TYPE_WIFI);
+ rtc::Network ethe1("ethe1", "Test NetworkAdapter 2",
+ kClientIPv6Addr2.ipaddr(), 64, rtc::ADAPTER_TYPE_ETHERNET);
+ rtc::Network wifi2("wifi2", "Test NetworkAdapter 3",
+ kClientIPv6Addr3.ipaddr(), 64, rtc::ADAPTER_TYPE_WIFI);
+ std::vector<const rtc::Network*> networks = {&wifi1, ðe1, &wifi2};
+
+ // Ensure that only 2 interfaces were selected.
+ EXPECT_EQ(2U, BasicPortAllocatorSession::SelectIPv6Networks(
+ networks, /*max_ipv6_networks=*/2)
+ .size());
+}
+
+// Test that if the number of available IPv6 networks is less than
+// allocator.max_ipv6_networks(), all IPv6 networks will be selected.
+TEST_F(BasicPortAllocatorTest, AllIPv6AreSelected) {
+ rtc::Network wifi1("wifi1", "Test NetworkAdapter 1", kClientIPv6Addr.ipaddr(),
+ 64, rtc::ADAPTER_TYPE_WIFI);
+ rtc::Network ethe1("ethe1", "Test NetworkAdapter 2",
+ kClientIPv6Addr2.ipaddr(), 64, rtc::ADAPTER_TYPE_ETHERNET);
+ std::vector<const rtc::Network*> networks = {&wifi1, ðe1};
+
+ // Ensure that all 2 interfaces were selected.
+ EXPECT_EQ(2U, BasicPortAllocatorSession::SelectIPv6Networks(
+ networks, /*max_ipv6_networks=*/3)
+ .size());
+}
+
+// If there are some IPv6 networks with different types, diversify IPv6
+// networks.
+TEST_F(BasicPortAllocatorTest, TwoIPv6WifiAreSelectedIfThereAreTwo) {
+ rtc::Network wifi1("wifi1", "Test NetworkAdapter 1", kClientIPv6Addr.ipaddr(),
+ 64, rtc::ADAPTER_TYPE_WIFI);
+ rtc::Network ethe1("ethe1", "Test NetworkAdapter 2",
+ kClientIPv6Addr2.ipaddr(), 64, rtc::ADAPTER_TYPE_ETHERNET);
+ rtc::Network ethe2("ethe2", "Test NetworkAdapter 3",
+ kClientIPv6Addr3.ipaddr(), 64, rtc::ADAPTER_TYPE_ETHERNET);
+ rtc::Network unknown1("unknown1", "Test NetworkAdapter 4",
+ kClientIPv6Addr2.ipaddr(), 64,
+ rtc::ADAPTER_TYPE_UNKNOWN);
+ rtc::Network cell1("cell1", "Test NetworkAdapter 5",
+ kClientIPv6Addr3.ipaddr(), 64,
+ rtc::ADAPTER_TYPE_CELLULAR_4G);
+ std::vector<const rtc::Network*> networks = {&wifi1, ðe1, ðe2,
+ &unknown1, &cell1};
+
+ networks = BasicPortAllocatorSession::SelectIPv6Networks(
+ networks, /*max_ipv6_networks=*/4);
+
+ EXPECT_EQ(4U, networks.size());
+ // Ensure the expected 4 interfaces (wifi1, ethe1, cell1, unknown1) were
+ // selected.
+ EXPECT_TRUE(HasNetwork(networks, wifi1));
+ EXPECT_TRUE(HasNetwork(networks, ethe1));
+ EXPECT_TRUE(HasNetwork(networks, cell1));
+ EXPECT_TRUE(HasNetwork(networks, unknown1));
+}
+
+// If there are some IPv6 networks with the same type, select them because there
+// is no other option.
+TEST_F(BasicPortAllocatorTest, IPv6WithSameTypeAreSelectedIfNoOtherOption) {
+ // Add 5 cellular interfaces
+ rtc::Network cell1("cell1", "Test NetworkAdapter 1", kClientIPv6Addr.ipaddr(),
+ 64, rtc::ADAPTER_TYPE_CELLULAR_2G);
+ rtc::Network cell2("cell2", "Test NetworkAdapter 2",
+ kClientIPv6Addr2.ipaddr(), 64,
+ rtc::ADAPTER_TYPE_CELLULAR_3G);
+ rtc::Network cell3("cell3", "Test NetworkAdapter 3",
+ kClientIPv6Addr3.ipaddr(), 64,
+ rtc::ADAPTER_TYPE_CELLULAR_4G);
+ rtc::Network cell4("cell4", "Test NetworkAdapter 4",
+ kClientIPv6Addr2.ipaddr(), 64,
+ rtc::ADAPTER_TYPE_CELLULAR_5G);
+ rtc::Network cell5("cell5", "Test NetworkAdapter 5",
+ kClientIPv6Addr3.ipaddr(), 64,
+ rtc::ADAPTER_TYPE_CELLULAR_3G);
+ std::vector<const rtc::Network*> networks = {&cell1, &cell2, &cell3, &cell4,
+ &cell5};
+
+ // Ensure that 4 interfaces were selected.
+ EXPECT_EQ(4U, BasicPortAllocatorSession::SelectIPv6Networks(
+ networks, /*max_ipv6_networks=*/4)
+ .size());
+}
+
+TEST_F(BasicPortAllocatorTest, IPv6EthernetHasHigherPriorityThanWifi) {
+ rtc::Network wifi1("wifi1", "Test NetworkAdapter 1", kClientIPv6Addr.ipaddr(),
+ 64, rtc::ADAPTER_TYPE_WIFI);
+ rtc::Network ethe1("ethe1", "Test NetworkAdapter 2",
+ kClientIPv6Addr2.ipaddr(), 64, rtc::ADAPTER_TYPE_ETHERNET);
+ rtc::Network wifi2("wifi2", "Test NetworkAdapter 3",
+ kClientIPv6Addr3.ipaddr(), 64, rtc::ADAPTER_TYPE_WIFI);
+ std::vector<const rtc::Network*> networks = {&wifi1, ðe1, &wifi2};
+
+ networks = BasicPortAllocatorSession::SelectIPv6Networks(
+ networks, /*max_ipv6_networks=*/1);
+
+ EXPECT_EQ(1U, networks.size());
+ // Ensure ethe1 was selected.
+ EXPECT_TRUE(HasNetwork(networks, ethe1));
+}
+
+TEST_F(BasicPortAllocatorTest, IPv6EtherAndWifiHaveHigherPriorityThanOthers) {
+ rtc::Network cell1("cell1", "Test NetworkAdapter 1", kClientIPv6Addr.ipaddr(),
+ 64, rtc::ADAPTER_TYPE_CELLULAR_3G);
+ rtc::Network ethe1("ethe1", "Test NetworkAdapter 2",
+ kClientIPv6Addr2.ipaddr(), 64, rtc::ADAPTER_TYPE_ETHERNET);
+ rtc::Network wifi1("wifi1", "Test NetworkAdapter 3",
+ kClientIPv6Addr3.ipaddr(), 64, rtc::ADAPTER_TYPE_WIFI);
+ rtc::Network unknown("unknown", "Test NetworkAdapter 4",
+ kClientIPv6Addr2.ipaddr(), 64,
+ rtc::ADAPTER_TYPE_UNKNOWN);
+ rtc::Network vpn1("vpn1", "Test NetworkAdapter 5", kClientIPv6Addr3.ipaddr(),
+ 64, rtc::ADAPTER_TYPE_VPN);
+ std::vector<const rtc::Network*> networks = {&cell1, ðe1, &wifi1, &unknown,
+ &vpn1};
+
+ networks = BasicPortAllocatorSession::SelectIPv6Networks(
+ networks, /*max_ipv6_networks=*/2);
+
+ EXPECT_EQ(2U, networks.size());
+ // Ensure ethe1 and wifi1 were selected.
+ EXPECT_TRUE(HasNetwork(networks, wifi1));
+ EXPECT_TRUE(HasNetwork(networks, ethe1));
+}
+
+// Do not change the default IPv6 selection behavior if
+// IPv6NetworkResolutionFixes is disabled.
+TEST_F(BasicPortAllocatorTest,
+ NotDiversifyIPv6NetworkTypesIfIPv6NetworkResolutionFixesDisabled) {
+ webrtc::test::ScopedKeyValueConfig field_trials(
+ field_trials_, "WebRTC-IPv6NetworkResolutionFixes/Disabled/");
+ // Add three IPv6 network interfaces, but tell the allocator to only use two.
+ allocator().set_max_ipv6_networks(2);
+ AddInterface(kClientIPv6Addr, "ethe1", rtc::ADAPTER_TYPE_ETHERNET);
+ AddInterface(kClientIPv6Addr2, "ethe2", rtc::ADAPTER_TYPE_ETHERNET);
+ AddInterface(kClientIPv6Addr3, "wifi1", rtc::ADAPTER_TYPE_WIFI);
+ // To simplify the test, only gather UDP host candidates.
+ allocator().set_flags(PORTALLOCATOR_ENABLE_IPV6 | PORTALLOCATOR_DISABLE_TCP |
+ PORTALLOCATOR_DISABLE_STUN |
+ PORTALLOCATOR_DISABLE_RELAY |
+ PORTALLOCATOR_ENABLE_IPV6_ON_WIFI);
+
+ ASSERT_TRUE(CreateSession(cricket::ICE_CANDIDATE_COMPONENT_RTP));
+ session_->StartGettingPorts();
+ EXPECT_TRUE_SIMULATED_WAIT(candidate_allocation_done_,
+ kDefaultAllocationTimeout, fake_clock);
+
+ EXPECT_EQ(2U, candidates_.size());
+ // Wifi1 was not selected because it comes after ethe1 and ethe2.
+ EXPECT_FALSE(HasCandidate(candidates_, "local", "udp", kClientIPv6Addr3));
+}
+
+// Do not change the default IPv6 selection behavior if
+// IPv6NetworkResolutionFixes is enabled but DiversifyIpv6Interfaces is not
+// enabled.
+TEST_F(BasicPortAllocatorTest,
+ NotDiversifyIPv6NetworkTypesIfDiversifyIpv6InterfacesDisabled) {
+ webrtc::test::ScopedKeyValueConfig field_trials(
+ field_trials_,
+ "WebRTC-IPv6NetworkResolutionFixes/"
+ "Enabled,DiversifyIpv6Interfaces:false/");
+ // Add three IPv6 network interfaces, but tell the allocator to only use two.
+ allocator().set_max_ipv6_networks(2);
+ AddInterface(kClientIPv6Addr, "ethe1", rtc::ADAPTER_TYPE_ETHERNET);
+ AddInterface(kClientIPv6Addr2, "ethe2", rtc::ADAPTER_TYPE_ETHERNET);
+ AddInterface(kClientIPv6Addr3, "wifi1", rtc::ADAPTER_TYPE_WIFI);
+ // To simplify the test, only gather UDP host candidates.
+ allocator().set_flags(PORTALLOCATOR_ENABLE_IPV6 | PORTALLOCATOR_DISABLE_TCP |
+ PORTALLOCATOR_DISABLE_STUN |
+ PORTALLOCATOR_DISABLE_RELAY |
+ PORTALLOCATOR_ENABLE_IPV6_ON_WIFI);
+
+ ASSERT_TRUE(CreateSession(cricket::ICE_CANDIDATE_COMPONENT_RTP));
+ session_->StartGettingPorts();
+ EXPECT_TRUE_SIMULATED_WAIT(candidate_allocation_done_,
+ kDefaultAllocationTimeout, fake_clock);
+
+ EXPECT_EQ(2U, candidates_.size());
+ // Wifi1 was not selected because it comes after ethe1 and ethe2.
+ EXPECT_FALSE(HasCandidate(candidates_, "local", "udp", kClientIPv6Addr3));
+}
+
+TEST_F(BasicPortAllocatorTest,
+ Select2DifferentIntefacesIfDiversifyIpv6InterfacesEnabled) {
+ webrtc::test::ScopedKeyValueConfig field_trials(
+ field_trials_,
+ "WebRTC-IPv6NetworkResolutionFixes/"
+ "Enabled,DiversifyIpv6Interfaces:true/");
+ allocator().set_max_ipv6_networks(2);
+ AddInterface(kClientIPv6Addr, "ethe1", rtc::ADAPTER_TYPE_ETHERNET);
+ AddInterface(kClientIPv6Addr2, "ethe2", rtc::ADAPTER_TYPE_ETHERNET);
+ AddInterface(kClientIPv6Addr3, "wifi1", rtc::ADAPTER_TYPE_WIFI);
+ AddInterface(kClientIPv6Addr4, "wifi2", rtc::ADAPTER_TYPE_WIFI);
+ AddInterface(kClientIPv6Addr5, "cell1", rtc::ADAPTER_TYPE_CELLULAR_3G);
+
+ // To simplify the test, only gather UDP host candidates.
+ allocator().set_flags(PORTALLOCATOR_ENABLE_IPV6 | PORTALLOCATOR_DISABLE_TCP |
+ PORTALLOCATOR_DISABLE_STUN |
+ PORTALLOCATOR_DISABLE_RELAY |
+ PORTALLOCATOR_ENABLE_IPV6_ON_WIFI);
+
+ ASSERT_TRUE(CreateSession(cricket::ICE_CANDIDATE_COMPONENT_RTP));
+ session_->StartGettingPorts();
+ EXPECT_TRUE_SIMULATED_WAIT(candidate_allocation_done_,
+ kDefaultAllocationTimeout, fake_clock);
+
+ EXPECT_EQ(2U, candidates_.size());
+ // ethe1 and wifi1 were selected.
+ EXPECT_TRUE(HasCandidate(candidates_, "local", "udp", kClientIPv6Addr));
+ EXPECT_TRUE(HasCandidate(candidates_, "local", "udp", kClientIPv6Addr3));
+}
+
+TEST_F(BasicPortAllocatorTest,
+ Select3DifferentIntefacesIfDiversifyIpv6InterfacesEnabled) {
+ webrtc::test::ScopedKeyValueConfig field_trials(
+ field_trials_,
+ "WebRTC-IPv6NetworkResolutionFixes/"
+ "Enabled,DiversifyIpv6Interfaces:true/");
+ allocator().set_max_ipv6_networks(3);
+ AddInterface(kClientIPv6Addr, "ethe1", rtc::ADAPTER_TYPE_ETHERNET);
+ AddInterface(kClientIPv6Addr2, "ethe2", rtc::ADAPTER_TYPE_ETHERNET);
+ AddInterface(kClientIPv6Addr3, "wifi1", rtc::ADAPTER_TYPE_WIFI);
+ AddInterface(kClientIPv6Addr4, "wifi2", rtc::ADAPTER_TYPE_WIFI);
+ AddInterface(kClientIPv6Addr5, "cell1", rtc::ADAPTER_TYPE_CELLULAR_3G);
+
+ // To simplify the test, only gather UDP host candidates.
+ allocator().set_flags(PORTALLOCATOR_ENABLE_IPV6 | PORTALLOCATOR_DISABLE_TCP |
+ PORTALLOCATOR_DISABLE_STUN |
+ PORTALLOCATOR_DISABLE_RELAY |
+ PORTALLOCATOR_ENABLE_IPV6_ON_WIFI);
+
+ ASSERT_TRUE(CreateSession(cricket::ICE_CANDIDATE_COMPONENT_RTP));
+ session_->StartGettingPorts();
+ EXPECT_TRUE_SIMULATED_WAIT(candidate_allocation_done_,
+ kDefaultAllocationTimeout, fake_clock);
+
+ EXPECT_EQ(3U, candidates_.size());
+ // ethe1, wifi1, and cell1 were selected.
+ EXPECT_TRUE(HasCandidate(candidates_, "local", "udp", kClientIPv6Addr));
+ EXPECT_TRUE(HasCandidate(candidates_, "local", "udp", kClientIPv6Addr3));
+ EXPECT_TRUE(HasCandidate(candidates_, "local", "udp", kClientIPv6Addr5));
+}
+
+TEST_F(BasicPortAllocatorTest,
+ Select4DifferentIntefacesIfDiversifyIpv6InterfacesEnabled) {
+ webrtc::test::ScopedKeyValueConfig field_trials(
+ field_trials_,
+ "WebRTC-IPv6NetworkResolutionFixes/"
+ "Enabled,DiversifyIpv6Interfaces:true/");
+ allocator().set_max_ipv6_networks(4);
+ AddInterface(kClientIPv6Addr, "ethe1", rtc::ADAPTER_TYPE_ETHERNET);
+ AddInterface(kClientIPv6Addr2, "ethe2", rtc::ADAPTER_TYPE_ETHERNET);
+ AddInterface(kClientIPv6Addr3, "wifi1", rtc::ADAPTER_TYPE_WIFI);
+ AddInterface(kClientIPv6Addr4, "wifi2", rtc::ADAPTER_TYPE_WIFI);
+ AddInterface(kClientIPv6Addr5, "cell1", rtc::ADAPTER_TYPE_CELLULAR_3G);
+
+ // To simplify the test, only gather UDP host candidates.
+ allocator().set_flags(PORTALLOCATOR_ENABLE_IPV6 | PORTALLOCATOR_DISABLE_TCP |
+ PORTALLOCATOR_DISABLE_STUN |
+ PORTALLOCATOR_DISABLE_RELAY |
+ PORTALLOCATOR_ENABLE_IPV6_ON_WIFI);
+
+ ASSERT_TRUE(CreateSession(cricket::ICE_CANDIDATE_COMPONENT_RTP));
+ session_->StartGettingPorts();
+ EXPECT_TRUE_SIMULATED_WAIT(candidate_allocation_done_,
+ kDefaultAllocationTimeout, fake_clock);
+
+ EXPECT_EQ(4U, candidates_.size());
+ // ethe1, ethe2, wifi1, and cell1 were selected.
+ EXPECT_TRUE(HasCandidate(candidates_, "local", "udp", kClientIPv6Addr));
+ EXPECT_TRUE(HasCandidate(candidates_, "local", "udp", kClientIPv6Addr2));
+ EXPECT_TRUE(HasCandidate(candidates_, "local", "udp", kClientIPv6Addr3));
+ EXPECT_TRUE(HasCandidate(candidates_, "local", "udp", kClientIPv6Addr5));
+}
+
} // namespace cricket
diff --git a/pc/peer_connection_factory.cc b/pc/peer_connection_factory.cc
index 889c2b1..6460a90 100644
--- a/pc/peer_connection_factory.cc
+++ b/pc/peer_connection_factory.cc
@@ -212,9 +212,11 @@
network_thread());
}
if (!dependencies.allocator) {
+ const FieldTrialsView* trials =
+ dependencies.trials ? dependencies.trials.get() : &field_trials();
dependencies.allocator = std::make_unique<cricket::BasicPortAllocator>(
context_->default_network_manager(), context_->default_socket_factory(),
- configuration.turn_customizer);
+ configuration.turn_customizer, /*relay_port_factory=*/nullptr, trials);
dependencies.allocator->SetPortRange(
configuration.port_allocator_config.min_port,
configuration.port_allocator_config.max_port);