| /* | 
 |  *  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" | 
 |  | 
 | 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; | 
 | }  // 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( | 
 |     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; | 
 | } | 
 |  | 
 | 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<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 = | 
 |       std::make_unique<EmulatedRoute>(from, std::move(via_nodes), 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( | 
 |     std::vector<EmulatedNetworkNode*> send_link, | 
 |     std::vector<EmulatedNetworkNode*> ret_link) { | 
 |   auto tcp_route = std::make_unique<TcpMessageRoute>( | 
 |       clock_, task_queue_.Get(), CreateRoute(send_link), CreateRoute(ret_link)); | 
 |   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) { | 
 |   auto endpoints_container = std::make_unique<EndpointsContainer>(endpoints); | 
 |   auto network_manager = std::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 clock_->CurrentTime(); | 
 | } | 
 |  | 
 | }  // namespace test | 
 | }  // namespace webrtc |