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, &ethe1, &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, &ethe1};
+
+  // 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, &ethe1, &ethe2,
+                                               &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, &ethe1, &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, &ethe1, &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);