diff --git a/p2p/client/basic_port_allocator.cc b/p2p/client/basic_port_allocator.cc index 8963d4eb8f..819bc514b5 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 @@ std::string NetworksToString(const std::vector& networks) { 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 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 @@ BasicPortAllocator::BasicPortAllocator( 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( BasicPortAllocator::BasicPortAllocator( rtc::NetworkManager* network_manager, - std::unique_ptr owned_socket_factory) - : network_manager_(network_manager), + std::unique_ptr 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( BasicPortAllocator::BasicPortAllocator( rtc::NetworkManager* network_manager, std::unique_ptr 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 @@ BasicPortAllocator::BasicPortAllocator( 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 @@ void BasicPortAllocator::AddTurnServer(const RelayServerConfig& turn_server) { 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(); - field_trials_ = owned_field_trials_.get(); - } } // BasicPortAllocatorSession @@ -795,21 +815,75 @@ std::vector BasicPortAllocatorSession::GetNetworks() { // 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 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; + } + 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; } - ++it; } return networks; } +std::vector BasicPortAllocatorSession::SelectIPv6Networks( + std::vector& all_ipv6_networks, + int max_ipv6_networks) { + if (static_cast(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 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 selected_networks; + int adapter_types_pos = 0; + + while (static_cast(selected_networks.size()) < max_ipv6_networks && + adapter_types_pos < adapter_types_cnt * max_ipv6_networks) { + int network_pos = 0; + while (network_pos < static_cast(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 9634a0eefd..173d789545 100644 --- a/p2p/client/basic_port_allocator.h +++ b/p2p/client/basic_port_allocator.h @@ -39,17 +39,21 @@ class RTC_EXPORT BasicPortAllocator : public PortAllocator { 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 owned_socket_factory); + RelayPortFactoryInterface* relay_port_factory = nullptr, + const webrtc::FieldTrialsView* field_trials = nullptr); BasicPortAllocator( rtc::NetworkManager* network_manager, std::unique_ptr owned_socket_factory, - const ServerAddresses& stun_servers); + const webrtc::FieldTrialsView* field_trials = nullptr); + BasicPortAllocator( + rtc::NetworkManager* network_manager, + std::unique_ptr 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 @@ class RTC_EXPORT BasicPortAllocator : public PortAllocator { void SetVpnList(const std::vector& 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 owned_field_trials_; + webrtc::AlwaysValidPointer + field_trials_; rtc::NetworkManager* network_manager_; const webrtc::AlwaysValidPointerNoDefault socket_factory_; @@ -163,6 +168,9 @@ class RTC_EXPORT BasicPortAllocatorSession : public PortAllocatorSession { void SetStunKeepaliveIntervalForReadyPorts( const absl::optional& stun_keepalive_interval) override; void PruneAllPorts() override; + static std::vector SelectIPv6Networks( + std::vector& 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 8faebbfcdd..d97cd33a77 100644 --- a/p2p/client/basic_port_allocator_unittest.cc +++ b/p2p/client/basic_port_allocator_unittest.cc @@ -72,6 +72,12 @@ static const SocketAddress kClientIPv6Addr2( 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 @@ class BasicPortAllocatorTestBase : public ::testing::Test, allocator_ = std::make_unique( &network_manager_, std::make_unique(fss_.get()), - stun_servers); + stun_servers, &field_trials_); allocator_->Initialize(); allocator_->set_step_delay(kMinimumStepDelay); webrtc::metrics::Reset(); @@ -375,6 +381,17 @@ class BasicPortAllocatorTestBase : public ::testing::Test, return (addr.port() >= min_port && addr.port() <= max_port); } + static bool HasNetwork(const std::vector& 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 @@ class BasicPortAllocatorTestBase : public ::testing::Test, 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 @@ class BasicPortAllocatorTestBase : public ::testing::Test, std::vector ports_; std::vector candidates_; bool candidate_allocation_done_; + webrtc::test::ScopedKeyValueConfig field_trials_; }; class BasicPortAllocatorTestWithRealClock : public BasicPortAllocatorTestBase { @@ -2472,4 +2491,284 @@ TEST_F(BasicPortAllocatorTest, TestDoNotUseTurnServerAsStunSever) { 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 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 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 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 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 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 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 889c2b19a1..6460a904f3 100644 --- a/pc/peer_connection_factory.cc +++ b/pc/peer_connection_factory.cc @@ -212,9 +212,11 @@ PeerConnectionFactory::CreatePeerConnectionOrError( network_thread()); } if (!dependencies.allocator) { + const FieldTrialsView* trials = + dependencies.trials ? dependencies.trials.get() : &field_trials(); dependencies.allocator = std::make_unique( 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);