summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/test/network/network_emulation_manager.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/test/network/network_emulation_manager.cc')
-rw-r--r--third_party/libwebrtc/test/network/network_emulation_manager.cc373
1 files changed, 373 insertions, 0 deletions
diff --git a/third_party/libwebrtc/test/network/network_emulation_manager.cc b/third_party/libwebrtc/test/network/network_emulation_manager.cc
new file mode 100644
index 0000000000..97c0bc1ba8
--- /dev/null
+++ b/third_party/libwebrtc/test/network/network_emulation_manager.cc
@@ -0,0 +1,373 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "test/network/network_emulation_manager.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
+#include "call/simulated_network.h"
+#include "test/network/emulated_turn_server.h"
+#include "test/network/traffic_route.h"
+#include "test/time_controller/real_time_controller.h"
+#include "test/time_controller/simulated_time_controller.h"
+
+namespace webrtc {
+namespace test {
+namespace {
+
+// uint32_t representation of 192.168.0.0 address
+constexpr uint32_t kMinIPv4Address = 0xC0A80000;
+// uint32_t representation of 192.168.255.255 address
+constexpr uint32_t kMaxIPv4Address = 0xC0A8FFFF;
+
+std::unique_ptr<TimeController> CreateTimeController(TimeMode mode) {
+ switch (mode) {
+ case TimeMode::kRealTime:
+ return std::make_unique<RealTimeController>();
+ case TimeMode::kSimulated:
+ // Using an offset of 100000 to get nice fixed width and readable
+ // timestamps in typical test scenarios.
+ const Timestamp kSimulatedStartTime = Timestamp::Seconds(100000);
+ return std::make_unique<GlobalSimulatedTimeController>(
+ kSimulatedStartTime);
+ }
+}
+} // namespace
+
+NetworkEmulationManagerImpl::NetworkEmulationManagerImpl(
+ TimeMode mode,
+ EmulatedNetworkStatsGatheringMode stats_gathering_mode)
+ : time_mode_(mode),
+ stats_gathering_mode_(stats_gathering_mode),
+ time_controller_(CreateTimeController(mode)),
+ clock_(time_controller_->GetClock()),
+ next_node_id_(1),
+ next_ip4_address_(kMinIPv4Address),
+ task_queue_(time_controller_->GetTaskQueueFactory()->CreateTaskQueue(
+ "NetworkEmulation",
+ TaskQueueFactory::Priority::NORMAL)) {}
+
+// TODO(srte): Ensure that any pending task that must be run for consistency
+// (such as stats collection tasks) are not cancelled when the task queue is
+// destroyed.
+NetworkEmulationManagerImpl::~NetworkEmulationManagerImpl() {
+ for (auto& turn_server : turn_servers_) {
+ turn_server->Stop();
+ }
+}
+
+EmulatedNetworkNode* NetworkEmulationManagerImpl::CreateEmulatedNode(
+ BuiltInNetworkBehaviorConfig config,
+ uint64_t random_seed) {
+ return CreateEmulatedNode(
+ std::make_unique<SimulatedNetwork>(config, random_seed));
+}
+
+EmulatedNetworkNode* NetworkEmulationManagerImpl::CreateEmulatedNode(
+ std::unique_ptr<NetworkBehaviorInterface> network_behavior) {
+ auto node = std::make_unique<EmulatedNetworkNode>(
+ clock_, &task_queue_, std::move(network_behavior), stats_gathering_mode_);
+ EmulatedNetworkNode* out = node.get();
+ task_queue_.PostTask([this, node = std::move(node)]() mutable {
+ network_nodes_.push_back(std::move(node));
+ });
+ return out;
+}
+
+NetworkEmulationManager::SimulatedNetworkNode::Builder
+NetworkEmulationManagerImpl::NodeBuilder() {
+ return SimulatedNetworkNode::Builder(this);
+}
+
+EmulatedEndpointImpl* NetworkEmulationManagerImpl::CreateEndpoint(
+ EmulatedEndpointConfig config) {
+ absl::optional<rtc::IPAddress> ip = config.ip;
+ if (!ip) {
+ switch (config.generated_ip_family) {
+ case EmulatedEndpointConfig::IpAddressFamily::kIpv4:
+ ip = GetNextIPv4Address();
+ RTC_CHECK(ip) << "All auto generated IPv4 addresses exhausted";
+ break;
+ case EmulatedEndpointConfig::IpAddressFamily::kIpv6:
+ ip = GetNextIPv4Address();
+ RTC_CHECK(ip) << "All auto generated IPv6 addresses exhausted";
+ ip = ip->AsIPv6Address();
+ break;
+ }
+ }
+
+ bool res = used_ip_addresses_.insert(*ip).second;
+ RTC_CHECK(res) << "IP=" << ip->ToString() << " already in use";
+ auto node = std::make_unique<EmulatedEndpointImpl>(
+ EmulatedEndpointImpl::Options(next_node_id_++, *ip, config,
+ stats_gathering_mode_),
+ config.start_as_enabled, &task_queue_, clock_);
+ EmulatedEndpointImpl* out = node.get();
+ endpoints_.push_back(std::move(node));
+ return out;
+}
+
+void NetworkEmulationManagerImpl::EnableEndpoint(EmulatedEndpoint* endpoint) {
+ EmulatedNetworkManager* network_manager =
+ endpoint_to_network_manager_[endpoint];
+ RTC_CHECK(network_manager);
+ network_manager->EnableEndpoint(static_cast<EmulatedEndpointImpl*>(endpoint));
+}
+
+void NetworkEmulationManagerImpl::DisableEndpoint(EmulatedEndpoint* endpoint) {
+ EmulatedNetworkManager* network_manager =
+ endpoint_to_network_manager_[endpoint];
+ RTC_CHECK(network_manager);
+ network_manager->DisableEndpoint(
+ static_cast<EmulatedEndpointImpl*>(endpoint));
+}
+
+EmulatedRoute* NetworkEmulationManagerImpl::CreateRoute(
+ EmulatedEndpoint* from,
+ const std::vector<EmulatedNetworkNode*>& via_nodes,
+ EmulatedEndpoint* to) {
+ // Because endpoint has no send node by default at least one should be
+ // provided here.
+ RTC_CHECK(!via_nodes.empty());
+
+ static_cast<EmulatedEndpointImpl*>(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]);
+ cur_node = via_nodes[i];
+ }
+ cur_node->router()->SetReceiver(to->GetPeerLocalAddress(), to);
+
+ std::unique_ptr<EmulatedRoute> route = std::make_unique<EmulatedRoute>(
+ static_cast<EmulatedEndpointImpl*>(from), std::move(via_nodes),
+ static_cast<EmulatedEndpointImpl*>(to), /*is_default=*/false);
+ EmulatedRoute* out = route.get();
+ routes_.push_back(std::move(route));
+ return out;
+}
+
+EmulatedRoute* NetworkEmulationManagerImpl::CreateRoute(
+ const std::vector<EmulatedNetworkNode*>& via_nodes) {
+ EmulatedEndpoint* from = CreateEndpoint(EmulatedEndpointConfig());
+ EmulatedEndpoint* to = CreateEndpoint(EmulatedEndpointConfig());
+ return CreateRoute(from, via_nodes, to);
+}
+
+EmulatedRoute* NetworkEmulationManagerImpl::CreateDefaultRoute(
+ EmulatedEndpoint* from,
+ const std::vector<EmulatedNetworkNode*>& via_nodes,
+ EmulatedEndpoint* to) {
+ // Because endpoint has no send node by default at least one should be
+ // provided here.
+ RTC_CHECK(!via_nodes.empty());
+
+ static_cast<EmulatedEndpointImpl*>(from)->router()->SetDefaultReceiver(
+ via_nodes[0]);
+ EmulatedNetworkNode* cur_node = via_nodes[0];
+ for (size_t i = 1; i < via_nodes.size(); ++i) {
+ cur_node->router()->SetDefaultReceiver(via_nodes[i]);
+ cur_node = via_nodes[i];
+ }
+ cur_node->router()->SetDefaultReceiver(to);
+
+ std::unique_ptr<EmulatedRoute> route = std::make_unique<EmulatedRoute>(
+ static_cast<EmulatedEndpointImpl*>(from), std::move(via_nodes),
+ static_cast<EmulatedEndpointImpl*>(to), /*is_default=*/true);
+ EmulatedRoute* out = route.get();
+ routes_.push_back(std::move(route));
+ return out;
+}
+
+void NetworkEmulationManagerImpl::ClearRoute(EmulatedRoute* route) {
+ RTC_CHECK(route->active) << "Route already cleared";
+ task_queue_.SendTask([route]() {
+ // Remove receiver from intermediate nodes.
+ for (auto* node : route->via_nodes) {
+ if (route->is_default) {
+ node->router()->RemoveDefaultReceiver();
+ } else {
+ node->router()->RemoveReceiver(route->to->GetPeerLocalAddress());
+ }
+ }
+ // Remove destination endpoint from source endpoint's router.
+ if (route->is_default) {
+ route->from->router()->RemoveDefaultReceiver();
+ } else {
+ route->from->router()->RemoveReceiver(route->to->GetPeerLocalAddress());
+ }
+
+ route->active = false;
+ });
+}
+
+TcpMessageRoute* NetworkEmulationManagerImpl::CreateTcpRoute(
+ EmulatedRoute* send_route,
+ EmulatedRoute* ret_route) {
+ auto tcp_route = std::make_unique<TcpMessageRouteImpl>(
+ clock_, task_queue_.Get(), send_route, ret_route);
+ auto* route_ptr = tcp_route.get();
+ task_queue_.PostTask([this, tcp_route = std::move(tcp_route)]() mutable {
+ tcp_message_routes_.push_back(std::move(tcp_route));
+ });
+ return route_ptr;
+}
+
+CrossTrafficRoute* NetworkEmulationManagerImpl::CreateCrossTrafficRoute(
+ const std::vector<EmulatedNetworkNode*>& via_nodes) {
+ RTC_CHECK(!via_nodes.empty());
+ EmulatedEndpointImpl* endpoint = CreateEndpoint(EmulatedEndpointConfig());
+
+ // Setup a route via specified nodes.
+ EmulatedNetworkNode* cur_node = via_nodes[0];
+ for (size_t i = 1; i < via_nodes.size(); ++i) {
+ cur_node->router()->SetReceiver(endpoint->GetPeerLocalAddress(),
+ via_nodes[i]);
+ cur_node = via_nodes[i];
+ }
+ cur_node->router()->SetReceiver(endpoint->GetPeerLocalAddress(), endpoint);
+
+ std::unique_ptr<CrossTrafficRoute> traffic_route =
+ std::make_unique<CrossTrafficRouteImpl>(clock_, via_nodes[0], endpoint);
+ CrossTrafficRoute* out = traffic_route.get();
+ traffic_routes_.push_back(std::move(traffic_route));
+ return out;
+}
+
+CrossTrafficGenerator* NetworkEmulationManagerImpl::StartCrossTraffic(
+ std::unique_ptr<CrossTrafficGenerator> generator) {
+ CrossTrafficGenerator* out = generator.get();
+ task_queue_.PostTask([this, generator = std::move(generator)]() mutable {
+ auto* generator_ptr = generator.get();
+
+ auto repeating_task_handle =
+ RepeatingTaskHandle::Start(task_queue_.Get(), [this, generator_ptr] {
+ generator_ptr->Process(Now());
+ return generator_ptr->GetProcessInterval();
+ });
+
+ cross_traffics_.push_back(CrossTrafficSource(
+ std::move(generator), std::move(repeating_task_handle)));
+ });
+ return out;
+}
+
+void NetworkEmulationManagerImpl::StopCrossTraffic(
+ CrossTrafficGenerator* generator) {
+ task_queue_.PostTask([=]() {
+ auto it = std::find_if(cross_traffics_.begin(), cross_traffics_.end(),
+ [=](const CrossTrafficSource& el) {
+ return el.first.get() == generator;
+ });
+ it->second.Stop();
+ cross_traffics_.erase(it);
+ });
+}
+
+EmulatedNetworkManagerInterface*
+NetworkEmulationManagerImpl::CreateEmulatedNetworkManagerInterface(
+ const std::vector<EmulatedEndpoint*>& endpoints) {
+ std::vector<EmulatedEndpointImpl*> endpoint_impls;
+ endpoint_impls.reserve(endpoints.size());
+ for (EmulatedEndpoint* endpoint : endpoints) {
+ endpoint_impls.push_back(static_cast<EmulatedEndpointImpl*>(endpoint));
+ }
+ auto endpoints_container = std::make_unique<EndpointsContainer>(
+ endpoint_impls, stats_gathering_mode_);
+ auto network_manager = std::make_unique<EmulatedNetworkManager>(
+ time_controller_.get(), &task_queue_, endpoints_container.get());
+ for (auto* endpoint : endpoints) {
+ // Associate endpoint with network manager.
+ bool insertion_result =
+ endpoint_to_network_manager_.insert({endpoint, network_manager.get()})
+ .second;
+ RTC_CHECK(insertion_result)
+ << "Endpoint ip=" << endpoint->GetPeerLocalAddress().ToString()
+ << " is already used for another network";
+ }
+
+ EmulatedNetworkManagerInterface* out = network_manager.get();
+
+ endpoints_containers_.push_back(std::move(endpoints_container));
+ network_managers_.push_back(std::move(network_manager));
+ return out;
+}
+
+void NetworkEmulationManagerImpl::GetStats(
+ rtc::ArrayView<EmulatedEndpoint* const> endpoints,
+ std::function<void(EmulatedNetworkStats)> stats_callback) {
+ task_queue_.PostTask([endpoints, stats_callback,
+ stats_gathering_mode = stats_gathering_mode_]() {
+ EmulatedNetworkStatsBuilder stats_builder(stats_gathering_mode);
+ for (auto* endpoint : endpoints) {
+ // It's safe to cast here because EmulatedEndpointImpl can be the only
+ // implementation of EmulatedEndpoint, because only it has access to
+ // EmulatedEndpoint constructor.
+ auto endpoint_impl = static_cast<EmulatedEndpointImpl*>(endpoint);
+ stats_builder.AddEmulatedNetworkStats(endpoint_impl->stats());
+ }
+ stats_callback(stats_builder.Build());
+ });
+}
+
+void NetworkEmulationManagerImpl::GetStats(
+ rtc::ArrayView<EmulatedNetworkNode* const> nodes,
+ std::function<void(EmulatedNetworkNodeStats)> stats_callback) {
+ task_queue_.PostTask(
+ [nodes, stats_callback, stats_gathering_mode = stats_gathering_mode_]() {
+ EmulatedNetworkNodeStatsBuilder stats_builder(stats_gathering_mode);
+ for (auto* node : nodes) {
+ stats_builder.AddEmulatedNetworkNodeStats(node->stats());
+ }
+ stats_callback(stats_builder.Build());
+ });
+}
+
+absl::optional<rtc::IPAddress>
+NetworkEmulationManagerImpl::GetNextIPv4Address() {
+ uint32_t addresses_count = kMaxIPv4Address - kMinIPv4Address;
+ for (uint32_t i = 0; i < addresses_count; i++) {
+ rtc::IPAddress ip(next_ip4_address_);
+ if (next_ip4_address_ == kMaxIPv4Address) {
+ next_ip4_address_ = kMinIPv4Address;
+ } else {
+ next_ip4_address_++;
+ }
+ if (used_ip_addresses_.find(ip) == used_ip_addresses_.end()) {
+ return ip;
+ }
+ }
+ return absl::nullopt;
+}
+
+Timestamp NetworkEmulationManagerImpl::Now() const {
+ return clock_->CurrentTime();
+}
+
+EmulatedTURNServerInterface* NetworkEmulationManagerImpl::CreateTURNServer(
+ EmulatedTURNServerConfig config) {
+ auto* client = CreateEndpoint(config.client_config);
+ auto* peer = CreateEndpoint(config.client_config);
+ char buf[128];
+ rtc::SimpleStringBuilder str(buf);
+ str.AppendFormat("turn_server_%u",
+ static_cast<unsigned>(turn_servers_.size()));
+ auto turn = std::make_unique<EmulatedTURNServer>(
+ time_controller_->CreateThread(str.str()), client, peer);
+ auto out = turn.get();
+ turn_servers_.push_back(std::move(turn));
+ return out;
+}
+
+} // namespace test
+} // namespace webrtc