Add feature to skip RELAY to non-RELAY connections

This patch adds a feature enabled using webrtc field trial
that remove connections between RELAY and non-RELAY candidates.

Bug: webrtc:11021
Change-Id: I924076277a843bffc1d25f6de14d2165f7012c4c
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/156083
Reviewed-by: Honghai Zhang <honghaiz@webrtc.org>
Commit-Queue: Jonas Oreland <jonaso@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29464}
diff --git a/p2p/BUILD.gn b/p2p/BUILD.gn
index d9a9209..913560d 100644
--- a/p2p/BUILD.gn
+++ b/p2p/BUILD.gn
@@ -106,6 +106,7 @@
     "../logging:ice_log",
     "../rtc_base",
     "../rtc_base:checks",
+    "../rtc_base/experiments:field_trial_parser",
     "//third_party/abseil-cpp/absl/memory",
 
     # Needed by pseudo_tcp, which should move to a separate target.
diff --git a/p2p/base/p2p_transport_channel.cc b/p2p/base/p2p_transport_channel.cc
index c6f2f20..6e68aa3 100644
--- a/p2p/base/p2p_transport_channel.cc
+++ b/p2p/base/p2p_transport_channel.cc
@@ -23,6 +23,7 @@
 #include "p2p/base/port.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/crc32.h"
+#include "rtc_base/experiments/struct_parameters_parser.h"
 #include "rtc_base/logging.h"
 #include "rtc_base/net_helper.h"
 #include "rtc_base/net_helpers.h"
@@ -694,6 +695,15 @@
     RTC_LOG(LS_INFO) << "Set WebRTC-TurnAddMultiMapping: Enabled";
   }
 
+  webrtc::StructParametersParser::Create(
+      "skip_relay_to_non_relay_connections",
+      &field_trials_.skip_relay_to_non_relay_connections)
+      ->Parse(webrtc::field_trial::FindFullName("WebRTC-IceFieldTrials"));
+
+  if (field_trials_.skip_relay_to_non_relay_connections) {
+    RTC_LOG(LS_INFO) << "Set skip_relay_to_non_relay_connections";
+  }
+
   webrtc::BasicRegatheringController::Config regathering_config(
       config_.regather_all_networks_interval_range,
       config_.regather_on_failed_networks_interval_or_default());
@@ -1323,6 +1333,17 @@
   if (!port->SupportsProtocol(remote_candidate.protocol())) {
     return false;
   }
+
+  if (field_trials_.skip_relay_to_non_relay_connections) {
+    if ((port->Type() != remote_candidate.type()) &&
+        (port->Type() == RELAY_PORT_TYPE ||
+         remote_candidate.type() == RELAY_PORT_TYPE)) {
+      RTC_LOG(LS_INFO) << ToString() << ": skip creating connection "
+                       << port->Type() << " to " << remote_candidate.type();
+      return false;
+    }
+  }
+
   // Look for an existing connection with this remote address.  If one is not
   // found or it is found but the existing remote candidate has an older
   // generation, then we can create a new connection for this address.
diff --git a/p2p/base/p2p_transport_channel.h b/p2p/base/p2p_transport_channel.h
index 9f70e65..1fe68ec 100644
--- a/p2p/base/p2p_transport_channel.h
+++ b/p2p/base/p2p_transport_channel.h
@@ -75,6 +75,10 @@
   PortInterface* origin_port_;
 };
 
+struct IceFieldTrials {
+  bool skip_relay_to_non_relay_connections = false;
+};
+
 // P2PTransportChannel manages the candidates and connection process to keep
 // two P2P clients connected to each other.
 class RTC_EXPORT P2PTransportChannel : public IceTransportInternal {
@@ -501,6 +505,8 @@
   // Number of times the selected_connection_ has been modified.
   uint32_t selected_candidate_pair_changes_ = 0;
 
+  IceFieldTrials field_trials_;
+
   RTC_DISALLOW_COPY_AND_ASSIGN(P2PTransportChannel);
 };
 
diff --git a/p2p/base/p2p_transport_channel_unittest.cc b/p2p/base/p2p_transport_channel_unittest.cc
index 46ce535..1f21bc2 100644
--- a/p2p/base/p2p_transport_channel_unittest.cc
+++ b/p2p/base/p2p_transport_channel_unittest.cc
@@ -4705,6 +4705,26 @@
   VerifyNextPingableConnection(RELAY_PORT_TYPE, RELAY_PORT_TYPE);
 }
 
+// Test skip_relay_to_non_relay_connections field-trial.
+// I.e that we never create connection between relay and non-relay.
+TEST_F(P2PTransportChannelMostLikelyToWorkFirstTest,
+       TestSkipRelayToNonRelayConnectionsFieldTrial) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-IceFieldTrials/skip_relay_to_non_relay_connections:true/");
+  P2PTransportChannel& ch = StartTransportChannel(true, 500);
+  EXPECT_TRUE_WAIT(ch.ports().size() == 2, kDefaultTimeout);
+  EXPECT_EQ(ch.ports()[0]->Type(), LOCAL_PORT_TYPE);
+  EXPECT_EQ(ch.ports()[1]->Type(), RELAY_PORT_TYPE);
+
+  // Remote Relay candidate arrives.
+  ch.AddRemoteCandidate(CreateUdpCandidate(RELAY_PORT_TYPE, "1.1.1.1", 1, 1));
+  EXPECT_TRUE_WAIT(ch.connections().size() == 1, kDefaultTimeout);
+
+  // Remote Local candidate arrives.
+  ch.AddRemoteCandidate(CreateUdpCandidate(LOCAL_PORT_TYPE, "2.2.2.2", 2, 2));
+  EXPECT_TRUE_WAIT(ch.connections().size() == 2, kDefaultTimeout);
+}
+
 // Test the ping sequence is UDP Relay/Relay followed by TCP Relay/Relay,
 // followed by the rest.
 TEST_F(P2PTransportChannelMostLikelyToWorkFirstTest, TestTcpTurn) {