stats() method on EmulatedEndpoint has to be called from network emulation internal task queue and user has no access to that task queue, so user can't call this method. Because of that remove it from public API and keep it only on implementation. Bug: webrtc:11756 Change-Id: I2fb7256abe94d6900965512f90c6a53a0708a7b0 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/180880 Reviewed-by: Tommi <tommi@webrtc.org> Commit-Queue: Artem Titov <titovartem@webrtc.org> Cr-Commit-Position: refs/heads/master@{#31867}
337 lines
12 KiB
C++
337 lines
12 KiB
C++
/*
|
|
* 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 "rtc_base/fake_network.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)
|
|
: 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() = default;
|
|
|
|
EmulatedNetworkNode* NetworkEmulationManagerImpl::CreateEmulatedNode(
|
|
BuiltInNetworkBehaviorConfig config) {
|
|
return CreateEmulatedNode(std::make_unique<SimulatedNetwork>(config));
|
|
}
|
|
|
|
EmulatedNetworkNode* NetworkEmulationManagerImpl::CreateEmulatedNode(
|
|
std::unique_ptr<NetworkBehaviorInterface> network_behavior) {
|
|
auto node = std::make_unique<EmulatedNetworkNode>(
|
|
clock_, &task_queue_, std::move(network_behavior));
|
|
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);
|
|
}
|
|
|
|
EmulatedEndpoint* 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>(
|
|
next_node_id_++, *ip, config.start_as_enabled, config.type, &task_queue_,
|
|
clock_);
|
|
EmulatedEndpoint* 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));
|
|
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);
|
|
}
|
|
|
|
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) {
|
|
node->router()->RemoveReceiver(route->to->GetPeerLocalAddress());
|
|
}
|
|
// Remove destination endpoint from source endpoint's router.
|
|
route->from->router()->RemoveReceiver(route->to->GetPeerLocalAddress());
|
|
|
|
route->active = false;
|
|
},
|
|
RTC_FROM_HERE);
|
|
}
|
|
|
|
TrafficRoute* NetworkEmulationManagerImpl::CreateTrafficRoute(
|
|
const std::vector<EmulatedNetworkNode*>& via_nodes) {
|
|
RTC_CHECK(!via_nodes.empty());
|
|
EmulatedEndpoint* 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<TrafficRoute> traffic_route =
|
|
std::make_unique<TrafficRoute>(clock_, via_nodes[0], endpoint);
|
|
TrafficRoute* out = traffic_route.get();
|
|
traffic_routes_.push_back(std::move(traffic_route));
|
|
return out;
|
|
}
|
|
|
|
RandomWalkCrossTraffic*
|
|
NetworkEmulationManagerImpl::CreateRandomWalkCrossTraffic(
|
|
TrafficRoute* traffic_route,
|
|
RandomWalkConfig config) {
|
|
auto traffic =
|
|
std::make_unique<RandomWalkCrossTraffic>(config, traffic_route);
|
|
RandomWalkCrossTraffic* out = traffic.get();
|
|
|
|
task_queue_.PostTask(
|
|
[this, config, traffic = std::move(traffic)]() mutable {
|
|
auto* traffic_ptr = traffic.get();
|
|
random_cross_traffics_.push_back(std::move(traffic));
|
|
RepeatingTaskHandle::Start(task_queue_.Get(),
|
|
[this, config, traffic_ptr] {
|
|
traffic_ptr->Process(Now());
|
|
return config.min_packet_interval;
|
|
});
|
|
});
|
|
return out;
|
|
}
|
|
|
|
PulsedPeaksCrossTraffic*
|
|
NetworkEmulationManagerImpl::CreatePulsedPeaksCrossTraffic(
|
|
TrafficRoute* traffic_route,
|
|
PulsedPeaksConfig config) {
|
|
auto traffic =
|
|
std::make_unique<PulsedPeaksCrossTraffic>(config, traffic_route);
|
|
PulsedPeaksCrossTraffic* out = traffic.get();
|
|
task_queue_.PostTask(
|
|
[this, config, traffic = std::move(traffic)]() mutable {
|
|
auto* traffic_ptr = traffic.get();
|
|
pulsed_cross_traffics_.push_back(std::move(traffic));
|
|
RepeatingTaskHandle::Start(task_queue_.Get(),
|
|
[this, config, traffic_ptr] {
|
|
traffic_ptr->Process(Now());
|
|
return config.min_packet_interval;
|
|
});
|
|
});
|
|
return out;
|
|
}
|
|
|
|
FakeTcpCrossTraffic* NetworkEmulationManagerImpl::StartFakeTcpCrossTraffic(
|
|
std::vector<EmulatedNetworkNode*> send_link,
|
|
std::vector<EmulatedNetworkNode*> ret_link,
|
|
FakeTcpConfig config) {
|
|
auto traffic = std::make_unique<FakeTcpCrossTraffic>(
|
|
clock_, config, CreateRoute(send_link), CreateRoute(ret_link));
|
|
auto* traffic_ptr = traffic.get();
|
|
task_queue_.PostTask([this, traffic = std::move(traffic)]() mutable {
|
|
traffic->Start(task_queue_.Get());
|
|
tcp_cross_traffics_.push_back(std::move(traffic));
|
|
});
|
|
return traffic_ptr;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
void NetworkEmulationManagerImpl::StopCrossTraffic(
|
|
FakeTcpCrossTraffic* traffic) {
|
|
task_queue_.PostTask([=]() {
|
|
traffic->Stop();
|
|
tcp_cross_traffics_.remove_if(
|
|
[=](const std::unique_ptr<FakeTcpCrossTraffic>& ptr) {
|
|
return ptr.get() == traffic;
|
|
});
|
|
});
|
|
}
|
|
|
|
EmulatedNetworkManagerInterface*
|
|
NetworkEmulationManagerImpl::CreateEmulatedNetworkManagerInterface(
|
|
const std::vector<EmulatedEndpoint*>& endpoints) {
|
|
std::vector<EmulatedEndpointImpl*> endpoint_impls;
|
|
for (EmulatedEndpoint* endpoint : endpoints) {
|
|
endpoint_impls.push_back(static_cast<EmulatedEndpointImpl*>(endpoint));
|
|
}
|
|
auto endpoints_container =
|
|
std::make_unique<EndpointsContainer>(endpoint_impls);
|
|
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*> endpoints,
|
|
std::function<void(std::unique_ptr<EmulatedNetworkStats>)> stats_callback) {
|
|
task_queue_.PostTask([endpoints, stats_callback]() {
|
|
EmulatedNetworkStatsBuilder stats_builder;
|
|
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());
|
|
});
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
} // namespace test
|
|
} // namespace webrtc
|