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