Add ability to have multiple connected remote endpoints

Bug: webrtc:10138
Change-Id: Ic305c2f247588d75b6ced17052ba12d937d1a056
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/128864
Commit-Queue: Artem Titov <titovartem@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Sebastian Jansson <srte@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#27460}
diff --git a/api/test/network_emulation_manager.h b/api/test/network_emulation_manager.h
index 0049eb6..b409bce 100644
--- a/api/test/network_emulation_manager.h
+++ b/api/test/network_emulation_manager.h
@@ -86,18 +86,23 @@
   // Creates a route between endpoints going through specified network nodes.
   // This route is single direction only and describe how traffic that was
   // sent by network interface |from| have to be delivered to the network
-  // interface |to|. Return object can be used to remove created route.
+  // interface |to|. Return object can be used to remove created route. The
+  // route must contains at least one network node inside it.
   //
-  // Assume there are endpoints E1, E2 and E3 and network nodes A, B, C and D.
-  // Also assume, that there is a route constructed via A, B and C like this:
-  // E1 -> A -> B -> C -> E2. In such case:
-  //   * Caller mustn't use A, B and C in any route, that is leading to E2.
-  //   * If caller will then create a new route E1 -> D -> E3, then first
-  //     route will be corrupted, so if caller want to do this, first route
-  //     should be deleted by ClearRoute(...) and then a new one should be
-  //     created.
-  //   * Caller can use A, B or C for any other routes.
-  //   * Caller can create other routes leading to E2.
+  // Assume that E{0-9} are endpoints and N{0-9} are network nodes, then
+  // creation of the route have to follow these rules:
+  //   1. A route consists of a source endpoint, an ordered list of one or
+  //      more network nodes, and a destination endpoint.
+  //   2. If (E1, ..., E2) is a route, then E1 != E2.
+  //      In other words, the source and the destination may not be the same.
+  //   3. Given two simultaneously existing routes (E1, ..., E2) and
+  //      (E3, ..., E4), either E1 != E3 or E2 != E4.
+  //      In other words, there may be at most one route from any given source
+  //      endpoint to any given destination endpoint.
+  //   4. Given two simultaneously existing routes (E1, ..., N1, ..., E2)
+  //      and (E3, ..., N2, ..., E4), either N1 != N2 or E2 != E4.
+  //      In other words, a network node may not belong to two routes that lead
+  //      to the same destination endpoint.
   virtual EmulatedRoute* CreateRoute(
       EmulatedEndpoint* from,
       const std::vector<EmulatedNetworkNode*>& via_nodes,
diff --git a/test/scenario/network/BUILD.gn b/test/scenario/network/BUILD.gn
index d3ca6ab..0bcd65d 100644
--- a/test/scenario/network/BUILD.gn
+++ b/test/scenario/network/BUILD.gn
@@ -9,6 +9,13 @@
 import("../../../webrtc.gni")
 
 rtc_source_set("emulated_network") {
+  visibility = [
+    "../../../api:create_network_emulation_manager",
+    ":*",
+  ]
+  if (rtc_include_tests) {
+    visibility += [ "../:scenario" ]
+  }
   testonly = true
   sources = [
     "cross_traffic.cc",
@@ -57,8 +64,10 @@
     ":emulated_network",
     "../../../api:simulated_network_api",
     "../../../call:simulated_network",
+    "../../../rtc_base:gunit_helpers",
     "../../../rtc_base:logging",
     "../../../rtc_base:rtc_event",
+    "../../../system_wrappers:system_wrappers",
     "../../../test:test_support",
     "//third_party/abseil-cpp/absl/memory",
   ]
diff --git a/test/scenario/network/cross_traffic_unittest.cc b/test/scenario/network/cross_traffic_unittest.cc
index 450d5f8..4bff272 100644
--- a/test/scenario/network/cross_traffic_unittest.cc
+++ b/test/scenario/network/cross_traffic_unittest.cc
@@ -39,8 +39,9 @@
 struct TrafficCounterFixture {
   SimulatedClock clock{0};
   CountingReceiver counter;
-  EmulatedEndpoint endpoint{1 /*id */, rtc::IPAddress(), true /*is_enabled*/,
-                            &clock};
+  TaskQueueForTest task_queue_;
+  EmulatedEndpoint endpoint{/*id=*/1, rtc::IPAddress(), /*is_enabled=*/true,
+                            &task_queue_, &clock};
 };
 
 }  // namespace
diff --git a/test/scenario/network/network_emulation.cc b/test/scenario/network/network_emulation.cc
index fe977e5..7296439 100644
--- a/test/scenario/network/network_emulation.cc
+++ b/test/scenario/network/network_emulation.cc
@@ -161,12 +161,14 @@
 EmulatedEndpoint::EmulatedEndpoint(uint64_t id,
                                    const rtc::IPAddress& ip,
                                    bool is_enabled,
+                                   rtc::TaskQueue* task_queue,
                                    Clock* clock)
     : id_(id),
       peer_local_addr_(ip),
       is_enabled_(is_enabled),
-      send_node_(nullptr),
       clock_(clock),
+      task_queue_(task_queue),
+      router_(task_queue_),
       next_port_(kFirstEphemeralPort) {
   constexpr int kIPv4NetworkPrefixLength = 24;
   constexpr int kIPv6NetworkPrefixLength = 64;
@@ -191,18 +193,18 @@
   return id_;
 }
 
-void EmulatedEndpoint::SetSendNode(EmulatedNetworkNode* send_node) {
-  send_node_ = send_node;
-}
-
 void EmulatedEndpoint::SendPacket(const rtc::SocketAddress& from,
                                   const rtc::SocketAddress& to,
                                   rtc::CopyOnWriteBuffer packet) {
   RTC_CHECK(from.ipaddr() == peer_local_addr_);
-  RTC_CHECK(send_node_);
-  send_node_->OnPacketReceived(
-      EmulatedIpPacket(from, to, std::move(packet),
-                       Timestamp::us(clock_->TimeInMicroseconds())));
+  struct Closure {
+    void operator()() { endpoint->router_.OnPacketReceived(std::move(packet)); }
+    EmulatedEndpoint* endpoint;
+    EmulatedIpPacket packet;
+  };
+  task_queue_->PostTask(Closure{
+      this, EmulatedIpPacket(from, to, std::move(packet),
+                             Timestamp::us(clock_->TimeInMicroseconds()))});
 }
 
 absl::optional<uint16_t> EmulatedEndpoint::BindReceiver(
@@ -293,10 +295,6 @@
   return is_enabled_;
 }
 
-EmulatedNetworkNode* EmulatedEndpoint::GetSendNode() const {
-  return send_node_;
-}
-
 EndpointsContainer::EndpointsContainer(
     const std::vector<EmulatedEndpoint*>& endpoints)
     : endpoints_(endpoints) {}
diff --git a/test/scenario/network/network_emulation.h b/test/scenario/network/network_emulation.h
index 7505cbd..84fd7ae 100644
--- a/test/scenario/network/network_emulation.h
+++ b/test/scenario/network/network_emulation.h
@@ -30,13 +30,6 @@
 #include "system_wrappers/include/clock.h"
 
 namespace webrtc {
-namespace test {
-
-// Forward declare NetworkEmulationManagerImpl for friend access from
-// EmulatedEndpoint.
-class NetworkEmulationManagerImpl;
-
-}  // namespace test
 
 struct EmulatedIpPacket {
  public:
@@ -159,13 +152,13 @@
   EmulatedEndpoint(uint64_t id,
                    const rtc::IPAddress& ip,
                    bool is_enabled,
+                   rtc::TaskQueue* task_queue,
                    Clock* clock);
   ~EmulatedEndpoint() override;
 
   uint64_t GetId() const;
 
-  // Set network node, that will be used to send packets to the network.
-  void SetSendNode(EmulatedNetworkNode* send_node);
+  NetworkRouterNode* router() { return &router_; }
   // Send packet into network.
   // |from| will be used to set source address for the packet in destination
   // socket.
@@ -200,11 +193,6 @@
 
   const rtc::Network& network() const { return *network_.get(); }
 
- protected:
-  friend class test::NetworkEmulationManagerImpl;
-
-  EmulatedNetworkNode* GetSendNode() const;
-
  private:
   static constexpr uint16_t kFirstEphemeralPort = 49152;
   uint16_t NextPort() RTC_EXCLUSIVE_LOCKS_REQUIRED(receiver_lock_);
@@ -216,9 +204,10 @@
   // Peer's local IP address for this endpoint network interface.
   const rtc::IPAddress peer_local_addr_;
   bool is_enabled_ RTC_GUARDED_BY(enabled_state_checker_);
-  EmulatedNetworkNode* send_node_;
   Clock* const clock_;
+  rtc::TaskQueue* const task_queue_;
   std::unique_ptr<rtc::Network> network_;
+  NetworkRouterNode router_;
 
   uint16_t next_port_ RTC_GUARDED_BY(receiver_lock_);
   std::map<uint16_t, EmulatedNetworkReceiverInterface*> port_to_receiver_
diff --git a/test/scenario/network/network_emulation_manager.cc b/test/scenario/network/network_emulation_manager.cc
index 190a0a8..4f689c0 100644
--- a/test/scenario/network/network_emulation_manager.cc
+++ b/test/scenario/network/network_emulation_manager.cc
@@ -81,7 +81,7 @@
   bool res = used_ip_addresses_.insert(*ip).second;
   RTC_CHECK(res) << "IP=" << ip->ToString() << " already in use";
   auto node = absl::make_unique<EmulatedEndpoint>(
-      next_node_id_++, *ip, config.start_as_enabled, clock_);
+      next_node_id_++, *ip, config.start_as_enabled, &task_queue_, clock_);
   EmulatedEndpoint* out = node.get();
   endpoints_.push_back(std::move(node));
   return out;
@@ -109,7 +109,7 @@
   // provided here.
   RTC_CHECK(!via_nodes.empty());
 
-  from->SetSendNode(via_nodes[0]);
+  from->router()->SetReceiver(to->GetPeerLocalAddress(), via_nodes[0]);
   EmulatedNetworkNode* cur_node = via_nodes[0];
   for (size_t i = 1; i < via_nodes.size(); ++i) {
     cur_node->router()->SetReceiver(to->GetPeerLocalAddress(), via_nodes[i]);
@@ -131,12 +131,8 @@
     for (auto* node : route->via_nodes) {
       node->router()->RemoveReceiver(route->to->GetPeerLocalAddress());
     }
-    // Detach endpoint from current send node.
-    if (route->from->GetSendNode()) {
-      route->from->GetSendNode()->router()->RemoveReceiver(
-          route->to->GetPeerLocalAddress());
-      route->from->SetSendNode(nullptr);
-    }
+    // Remove destination endpoint from source endpoint's router.
+    route->from->router()->RemoveReceiver(route->to->GetPeerLocalAddress());
 
     route->active = false;
   });
diff --git a/test/scenario/network/network_emulation_unittest.cc b/test/scenario/network/network_emulation_unittest.cc
index 2e7b1d7..5da9ce5 100644
--- a/test/scenario/network/network_emulation_unittest.cc
+++ b/test/scenario/network/network_emulation_unittest.cc
@@ -8,13 +8,16 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
+#include <atomic>
 #include <memory>
 
 #include "absl/memory/memory.h"
 #include "api/test/simulated_network.h"
 #include "call/simulated_network.h"
 #include "rtc_base/event.h"
+#include "rtc_base/gunit.h"
 #include "rtc_base/logging.h"
+#include "system_wrappers/include/sleep.h"
 #include "test/gmock.h"
 #include "test/gtest.h"
 #include "test/scenario/network/network_emulation.h"
@@ -22,6 +25,9 @@
 
 namespace webrtc {
 namespace test {
+namespace {
+
+constexpr int kNetworkPacketWaitTimeoutMs = 100;
 
 class SocketReader : public sigslot::has_slots<> {
  public:
@@ -57,6 +63,93 @@
   int received_count_ RTC_GUARDED_BY(lock_) = 0;
 };
 
+class MockReceiver : public EmulatedNetworkReceiverInterface {
+ public:
+  MOCK_METHOD1(OnPacketReceived, void(EmulatedIpPacket packet));
+};
+
+class NetworkEmulationManagerThreeNodesRoutingTest : public ::testing::Test {
+ public:
+  NetworkEmulationManagerThreeNodesRoutingTest() {
+    e1_ = emulation_.CreateEndpoint(EmulatedEndpointConfig());
+    e2_ = emulation_.CreateEndpoint(EmulatedEndpointConfig());
+    e3_ = emulation_.CreateEndpoint(EmulatedEndpointConfig());
+  }
+
+  void SetupRouting(
+      std::function<void(EmulatedEndpoint*,
+                         EmulatedEndpoint*,
+                         EmulatedEndpoint*,
+                         NetworkEmulationManager*)> create_routing_func) {
+    create_routing_func(e1_, e2_, e3_, &emulation_);
+  }
+
+  void SendPacketsAndValidateDelivery() {
+    EXPECT_CALL(r_e1_e2_, OnPacketReceived(testing::_)).Times(1);
+    EXPECT_CALL(r_e2_e1_, OnPacketReceived(testing::_)).Times(1);
+    EXPECT_CALL(r_e1_e3_, OnPacketReceived(testing::_)).Times(1);
+    EXPECT_CALL(r_e3_e1_, OnPacketReceived(testing::_)).Times(1);
+
+    uint16_t common_send_port = 80;
+    uint16_t r_e1_e2_port = e2_->BindReceiver(0, &r_e1_e2_).value();
+    uint16_t r_e2_e1_port = e1_->BindReceiver(0, &r_e2_e1_).value();
+    uint16_t r_e1_e3_port = e3_->BindReceiver(0, &r_e1_e3_).value();
+    uint16_t r_e3_e1_port = e1_->BindReceiver(0, &r_e3_e1_).value();
+
+    // Next code is using API of EmulatedEndpoint, that is visible only for
+    // internals of network emulation layer. Don't use this API in other tests.
+    // Send packet from e1 to e2.
+    e1_->SendPacket(
+        rtc::SocketAddress(e1_->GetPeerLocalAddress(), common_send_port),
+        rtc::SocketAddress(e2_->GetPeerLocalAddress(), r_e1_e2_port),
+        rtc::CopyOnWriteBuffer(10));
+
+    // Send packet from e2 to e1.
+    e2_->SendPacket(
+        rtc::SocketAddress(e2_->GetPeerLocalAddress(), common_send_port),
+        rtc::SocketAddress(e1_->GetPeerLocalAddress(), r_e2_e1_port),
+        rtc::CopyOnWriteBuffer(10));
+
+    // Send packet from e1 to e3.
+    e1_->SendPacket(
+        rtc::SocketAddress(e1_->GetPeerLocalAddress(), common_send_port),
+        rtc::SocketAddress(e3_->GetPeerLocalAddress(), r_e1_e3_port),
+        rtc::CopyOnWriteBuffer(10));
+
+    // Send packet from e3 to e1.
+    e3_->SendPacket(
+        rtc::SocketAddress(e3_->GetPeerLocalAddress(), common_send_port),
+        rtc::SocketAddress(e1_->GetPeerLocalAddress(), r_e3_e1_port),
+        rtc::CopyOnWriteBuffer(10));
+
+    // Sleep at the end to wait for async packets delivery.
+    SleepMs(kNetworkPacketWaitTimeoutMs);
+  }
+
+ private:
+  // Receivers: r_<source endpoint>_<destination endpoint>
+  // They must be destroyed after emulation, so they should be declared before.
+  MockReceiver r_e1_e2_;
+  MockReceiver r_e2_e1_;
+  MockReceiver r_e1_e3_;
+  MockReceiver r_e3_e1_;
+
+  NetworkEmulationManagerImpl emulation_;
+  EmulatedEndpoint* e1_;
+  EmulatedEndpoint* e2_;
+  EmulatedEndpoint* e3_;
+};
+
+EmulatedNetworkNode* CreateEmulatedNodeWithDefaultBuiltInConfig(
+    NetworkEmulationManager* emulation) {
+  return emulation->CreateEmulatedNode(
+      absl::make_unique<SimulatedNetwork>(BuiltInNetworkBehaviorConfig()));
+}
+
+}  // namespace
+
+using testing::_;
+
 TEST(NetworkEmulationManagerTest, GeneratedIpv4AddressDoesNotCollide) {
   NetworkEmulationManagerImpl network_manager;
   std::set<rtc::IPAddress> ips;
@@ -136,5 +229,52 @@
   }
 }
 
+// Testing that packets are delivered via all routes using a routing scheme as
+// follows:
+//  * e1 -> n1 -> e2
+//  * e2 -> n2 -> e1
+//  * e1 -> n3 -> e3
+//  * e3 -> n4 -> e1
+TEST_F(NetworkEmulationManagerThreeNodesRoutingTest,
+       PacketsAreDeliveredInBothWaysWhenConnectedToTwoPeers) {
+  SetupRouting([](EmulatedEndpoint* e1, EmulatedEndpoint* e2,
+                  EmulatedEndpoint* e3, NetworkEmulationManager* emulation) {
+    auto* node1 = CreateEmulatedNodeWithDefaultBuiltInConfig(emulation);
+    auto* node2 = CreateEmulatedNodeWithDefaultBuiltInConfig(emulation);
+    auto* node3 = CreateEmulatedNodeWithDefaultBuiltInConfig(emulation);
+    auto* node4 = CreateEmulatedNodeWithDefaultBuiltInConfig(emulation);
+
+    emulation->CreateRoute(e1, {node1}, e2);
+    emulation->CreateRoute(e2, {node2}, e1);
+
+    emulation->CreateRoute(e1, {node3}, e3);
+    emulation->CreateRoute(e3, {node4}, e1);
+  });
+  SendPacketsAndValidateDelivery();
+}
+
+// Testing that packets are delivered via all routes using a routing scheme as
+// follows:
+//  * e1 -> n1 -> e2
+//  * e2 -> n2 -> e1
+//  * e1 -> n1 -> e3
+//  * e3 -> n4 -> e1
+TEST_F(NetworkEmulationManagerThreeNodesRoutingTest,
+       PacketsAreDeliveredInBothWaysWhenConnectedToTwoPeersOverSameSendLink) {
+  SetupRouting([](EmulatedEndpoint* e1, EmulatedEndpoint* e2,
+                  EmulatedEndpoint* e3, NetworkEmulationManager* emulation) {
+    auto* node1 = CreateEmulatedNodeWithDefaultBuiltInConfig(emulation);
+    auto* node2 = CreateEmulatedNodeWithDefaultBuiltInConfig(emulation);
+    auto* node3 = CreateEmulatedNodeWithDefaultBuiltInConfig(emulation);
+
+    emulation->CreateRoute(e1, {node1}, e2);
+    emulation->CreateRoute(e2, {node2}, e1);
+
+    emulation->CreateRoute(e1, {node1}, e3);
+    emulation->CreateRoute(e3, {node3}, e1);
+  });
+  SendPacketsAndValidateDelivery();
+}
+
 }  // namespace test
 }  // namespace webrtc