webrtc_m130/test/scenario/network/network_emulation_manager.cc
Sebastian Jansson 491d29448e Only process cross traffic simulation if added.
This avoids extra processing overhead when there's no cross traffic
simulation active. To allow the use of SendTask, GlobalTimeController
is adjusted so it always overrides yield behavior.

Also adding sequence checkers to protect against races on
read access.

Bug: webrtc:10365
Change-Id: I55c6ceb22f36ec19a4fca48cff500905192d1a16
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/133167
Reviewed-by: Niels Moller <nisse@webrtc.org>
Reviewed-by: Artem Titov <titovartem@webrtc.org>
Commit-Queue: Sebastian Jansson <srte@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#27658}
2019-04-17 10:59:06 +00:00

275 lines
9.7 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/scenario/network/network_emulation_manager.h"
#include "test/time_controller/real_time_controller.h"
#include <algorithm>
#include <memory>
#include "absl/memory/memory.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "rtc_base/fake_network.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;
template <typename T, typename Closure>
class ResourceOwningTask final : public QueuedTask {
public:
ResourceOwningTask(T&& resource, Closure&& handler)
: resource_(std::move(resource)),
handler_(std::forward<Closure>(handler)) {}
bool Run() override {
handler_(std::move(resource_));
return true;
}
private:
T resource_;
Closure handler_;
};
template <typename T, typename Closure>
std::unique_ptr<QueuedTask> CreateResourceOwningTask(T resource,
Closure&& closure) {
return absl::make_unique<ResourceOwningTask<T, Closure>>(
std::forward<T>(resource), std::forward<Closure>(closure));
}
} // namespace
NetworkEmulationManagerImpl::NetworkEmulationManagerImpl()
: NetworkEmulationManagerImpl(GlobalRealTimeController()) {}
NetworkEmulationManagerImpl::NetworkEmulationManagerImpl(
TimeController* time_controller)
: 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(
std::unique_ptr<NetworkBehaviorInterface> network_behavior) {
auto node = absl::make_unique<EmulatedNetworkNode>(
clock_, &task_queue_, std::move(network_behavior));
EmulatedNetworkNode* out = node.get();
struct Closure {
void operator()() { manager->network_nodes_.push_back(std::move(node)); }
NetworkEmulationManagerImpl* manager;
std::unique_ptr<EmulatedNetworkNode> node;
};
task_queue_.PostTask(Closure{this, std::move(node)});
return out;
}
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 = absl::make_unique<EmulatedEndpoint>(
next_node_id_++, *ip, config.start_as_enabled, &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(endpoint);
}
void NetworkEmulationManagerImpl::DisableEndpoint(EmulatedEndpoint* endpoint) {
EmulatedNetworkManager* network_manager =
endpoint_to_network_manager_[endpoint];
RTC_CHECK(network_manager);
network_manager->DisableEndpoint(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());
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 =
absl::make_unique<EmulatedRoute>(from, std::move(via_nodes), to);
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) {
node->router()->RemoveReceiver(route->to->GetPeerLocalAddress());
}
// Remove destination endpoint from source endpoint's router.
route->from->router()->RemoveReceiver(route->to->GetPeerLocalAddress());
route->active = false;
});
}
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 =
absl::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 =
absl::make_unique<RandomWalkCrossTraffic>(config, traffic_route);
RandomWalkCrossTraffic* out = traffic.get();
task_queue_.PostTask(CreateResourceOwningTask(
std::move(traffic),
[this, config](std::unique_ptr<RandomWalkCrossTraffic> traffic) {
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 =
absl::make_unique<PulsedPeaksCrossTraffic>(config, traffic_route);
PulsedPeaksCrossTraffic* out = traffic.get();
task_queue_.PostTask(CreateResourceOwningTask(
std::move(traffic),
[this, config](std::unique_ptr<PulsedPeaksCrossTraffic> traffic) {
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;
}
EmulatedNetworkManagerInterface*
NetworkEmulationManagerImpl::CreateEmulatedNetworkManagerInterface(
const std::vector<EmulatedEndpoint*>& endpoints) {
auto endpoints_container = absl::make_unique<EndpointsContainer>(endpoints);
auto network_manager = absl::make_unique<EmulatedNetworkManager>(
clock_, &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;
}
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 Timestamp::us(clock_->TimeInMicroseconds());
}
} // namespace test
} // namespace webrtc