summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/test/network/network_emulation.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--third_party/libwebrtc/test/network/network_emulation.cc767
1 files changed, 767 insertions, 0 deletions
diff --git a/third_party/libwebrtc/test/network/network_emulation.cc b/third_party/libwebrtc/test/network/network_emulation.cc
new file mode 100644
index 0000000000..f1c9ca80dd
--- /dev/null
+++ b/third_party/libwebrtc/test/network/network_emulation.cc
@@ -0,0 +1,767 @@
+/*
+ * Copyright (c) 2018 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.h"
+
+#include <algorithm>
+#include <limits>
+#include <memory>
+#include <utility>
+
+#include "absl/types/optional.h"
+#include "api/numerics/samples_stats_counter.h"
+#include "api/sequence_checker.h"
+#include "api/test/network_emulation/network_emulation_interfaces.h"
+#include "api/test/network_emulation_manager.h"
+#include "api/units/data_size.h"
+#include "api/units/time_delta.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace {
+
+EmulatedNetworkOutgoingStats GetOverallOutgoingStats(
+ const std::map<rtc::IPAddress, EmulatedNetworkOutgoingStats>&
+ outgoing_stats,
+ EmulatedNetworkStatsGatheringMode mode) {
+ EmulatedNetworkOutgoingStatsBuilder builder(mode);
+ for (const auto& entry : outgoing_stats) {
+ builder.AddOutgoingStats(entry.second);
+ }
+ return builder.Build();
+}
+
+EmulatedNetworkIncomingStats GetOverallIncomingStats(
+ const std::map<rtc::IPAddress, EmulatedNetworkIncomingStats>&
+ incoming_stats,
+ EmulatedNetworkStatsGatheringMode mode) {
+ EmulatedNetworkIncomingStatsBuilder builder(mode);
+ for (const auto& entry : incoming_stats) {
+ builder.AddIncomingStats(entry.second);
+ }
+ return builder.Build();
+}
+
+} // namespace
+
+EmulatedNetworkOutgoingStatsBuilder::EmulatedNetworkOutgoingStatsBuilder(
+ EmulatedNetworkStatsGatheringMode stats_gathering_mode)
+ : stats_gathering_mode_(stats_gathering_mode) {
+ sequence_checker_.Detach();
+}
+
+void EmulatedNetworkOutgoingStatsBuilder::OnPacketSent(Timestamp sent_time,
+ DataSize packet_size) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ RTC_CHECK_GE(packet_size, DataSize::Zero());
+ if (stats_.first_packet_sent_time.IsInfinite()) {
+ stats_.first_packet_sent_time = sent_time;
+ stats_.first_sent_packet_size = packet_size;
+ }
+ stats_.last_packet_sent_time = sent_time;
+ stats_.packets_sent++;
+ stats_.bytes_sent += packet_size;
+ if (stats_gathering_mode_ == EmulatedNetworkStatsGatheringMode::kDebug) {
+ stats_.sent_packets_size.AddSample(packet_size.bytes());
+ }
+}
+
+void EmulatedNetworkOutgoingStatsBuilder::AddOutgoingStats(
+ const EmulatedNetworkOutgoingStats& stats) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ stats_.packets_sent += stats.packets_sent;
+ stats_.bytes_sent += stats.bytes_sent;
+ stats_.sent_packets_size.AddSamples(stats.sent_packets_size);
+ if (stats_.first_packet_sent_time > stats.first_packet_sent_time) {
+ stats_.first_packet_sent_time = stats.first_packet_sent_time;
+ stats_.first_sent_packet_size = stats.first_sent_packet_size;
+ }
+ if (stats_.last_packet_sent_time < stats.last_packet_sent_time) {
+ stats_.last_packet_sent_time = stats.last_packet_sent_time;
+ }
+}
+
+EmulatedNetworkOutgoingStats EmulatedNetworkOutgoingStatsBuilder::Build()
+ const {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ return stats_;
+}
+
+EmulatedNetworkIncomingStatsBuilder::EmulatedNetworkIncomingStatsBuilder(
+ EmulatedNetworkStatsGatheringMode stats_gathering_mode)
+ : stats_gathering_mode_(stats_gathering_mode) {
+ sequence_checker_.Detach();
+}
+
+void EmulatedNetworkIncomingStatsBuilder::OnPacketDropped(
+ DataSize packet_size) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ stats_.packets_discarded_no_receiver++;
+ stats_.bytes_discarded_no_receiver += packet_size;
+ if (stats_gathering_mode_ == EmulatedNetworkStatsGatheringMode::kDebug) {
+ stats_.packets_discarded_no_receiver_size.AddSample(packet_size.bytes());
+ }
+}
+
+void EmulatedNetworkIncomingStatsBuilder::OnPacketReceived(
+ Timestamp received_time,
+ DataSize packet_size) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ RTC_CHECK_GE(packet_size, DataSize::Zero());
+ if (stats_.first_packet_received_time.IsInfinite()) {
+ stats_.first_packet_received_time = received_time;
+ stats_.first_received_packet_size = packet_size;
+ }
+ stats_.last_packet_received_time = received_time;
+ stats_.packets_received++;
+ stats_.bytes_received += packet_size;
+ if (stats_gathering_mode_ == EmulatedNetworkStatsGatheringMode::kDebug) {
+ stats_.received_packets_size.AddSample(packet_size.bytes());
+ }
+}
+
+void EmulatedNetworkIncomingStatsBuilder::AddIncomingStats(
+ const EmulatedNetworkIncomingStats& stats) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ stats_.packets_received += stats.packets_received;
+ stats_.bytes_received += stats.bytes_received;
+ stats_.received_packets_size.AddSamples(stats.received_packets_size);
+ stats_.packets_discarded_no_receiver += stats.packets_discarded_no_receiver;
+ stats_.bytes_discarded_no_receiver += stats.bytes_discarded_no_receiver;
+ stats_.packets_discarded_no_receiver_size.AddSamples(
+ stats.packets_discarded_no_receiver_size);
+ if (stats_.first_packet_received_time > stats.first_packet_received_time) {
+ stats_.first_packet_received_time = stats.first_packet_received_time;
+ stats_.first_received_packet_size = stats.first_received_packet_size;
+ }
+ if (stats_.last_packet_received_time < stats.last_packet_received_time) {
+ stats_.last_packet_received_time = stats.last_packet_received_time;
+ }
+}
+
+EmulatedNetworkIncomingStats EmulatedNetworkIncomingStatsBuilder::Build()
+ const {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ return stats_;
+}
+
+EmulatedNetworkStatsBuilder::EmulatedNetworkStatsBuilder(
+ EmulatedNetworkStatsGatheringMode stats_gathering_mode)
+ : stats_gathering_mode_(stats_gathering_mode) {
+ sequence_checker_.Detach();
+}
+
+EmulatedNetworkStatsBuilder::EmulatedNetworkStatsBuilder(
+ rtc::IPAddress local_ip,
+ EmulatedNetworkStatsGatheringMode stats_gathering_mode)
+ : stats_gathering_mode_(stats_gathering_mode) {
+ local_addresses_.push_back(local_ip);
+ sequence_checker_.Detach();
+}
+
+void EmulatedNetworkStatsBuilder::OnPacketSent(Timestamp queued_time,
+ Timestamp sent_time,
+ rtc::IPAddress destination_ip,
+ DataSize packet_size) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ if (stats_gathering_mode_ == EmulatedNetworkStatsGatheringMode::kDebug) {
+ sent_packets_queue_wait_time_us_.AddSample((sent_time - queued_time).us());
+ }
+ auto it = outgoing_stats_per_destination_.find(destination_ip);
+ if (it == outgoing_stats_per_destination_.end()) {
+ outgoing_stats_per_destination_
+ .emplace(destination_ip,
+ std::make_unique<EmulatedNetworkOutgoingStatsBuilder>(
+ stats_gathering_mode_))
+ .first->second->OnPacketSent(sent_time, packet_size);
+ } else {
+ it->second->OnPacketSent(sent_time, packet_size);
+ }
+}
+
+void EmulatedNetworkStatsBuilder::OnPacketDropped(rtc::IPAddress source_ip,
+ DataSize packet_size) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ auto it = incoming_stats_per_source_.find(source_ip);
+ if (it == incoming_stats_per_source_.end()) {
+ incoming_stats_per_source_
+ .emplace(source_ip,
+ std::make_unique<EmulatedNetworkIncomingStatsBuilder>(
+ stats_gathering_mode_))
+ .first->second->OnPacketDropped(packet_size);
+ } else {
+ it->second->OnPacketDropped(packet_size);
+ }
+}
+
+void EmulatedNetworkStatsBuilder::OnPacketReceived(Timestamp received_time,
+ rtc::IPAddress source_ip,
+ DataSize packet_size) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ auto it = incoming_stats_per_source_.find(source_ip);
+ if (it == incoming_stats_per_source_.end()) {
+ incoming_stats_per_source_
+ .emplace(source_ip,
+ std::make_unique<EmulatedNetworkIncomingStatsBuilder>(
+ stats_gathering_mode_))
+ .first->second->OnPacketReceived(received_time, packet_size);
+ } else {
+ it->second->OnPacketReceived(received_time, packet_size);
+ }
+}
+
+void EmulatedNetworkStatsBuilder::AddEmulatedNetworkStats(
+ const EmulatedNetworkStats& stats) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+
+ // Append IPs from other endpoints stats to the builder.
+ for (const rtc::IPAddress& addr : stats.local_addresses) {
+ local_addresses_.push_back(addr);
+ }
+
+ sent_packets_queue_wait_time_us_.AddSamples(
+ stats.sent_packets_queue_wait_time_us);
+
+ // Add outgoing stats from other endpoints to the builder.
+ for (const auto& entry : stats.outgoing_stats_per_destination) {
+ auto it = outgoing_stats_per_destination_.find(entry.first);
+ if (it == outgoing_stats_per_destination_.end()) {
+ outgoing_stats_per_destination_
+ .emplace(entry.first,
+ std::make_unique<EmulatedNetworkOutgoingStatsBuilder>(
+ stats_gathering_mode_))
+ .first->second->AddOutgoingStats(entry.second);
+ } else {
+ it->second->AddOutgoingStats(entry.second);
+ }
+ }
+
+ // Add incoming stats from other endpoints to the builder.
+ for (const auto& entry : stats.incoming_stats_per_source) {
+ auto it = incoming_stats_per_source_.find(entry.first);
+ if (it == incoming_stats_per_source_.end()) {
+ incoming_stats_per_source_
+ .emplace(entry.first,
+ std::make_unique<EmulatedNetworkIncomingStatsBuilder>(
+ stats_gathering_mode_))
+ .first->second->AddIncomingStats(entry.second);
+ } else {
+ it->second->AddIncomingStats(entry.second);
+ }
+ }
+}
+
+EmulatedNetworkStats EmulatedNetworkStatsBuilder::Build() const {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ std::map<rtc::IPAddress, EmulatedNetworkOutgoingStats> outgoing_stats;
+ for (const auto& entry : outgoing_stats_per_destination_) {
+ outgoing_stats.emplace(entry.first, entry.second->Build());
+ }
+ std::map<rtc::IPAddress, EmulatedNetworkIncomingStats> incoming_stats;
+ for (const auto& entry : incoming_stats_per_source_) {
+ incoming_stats.emplace(entry.first, entry.second->Build());
+ }
+ return EmulatedNetworkStats{
+ .local_addresses = local_addresses_,
+ .overall_outgoing_stats =
+ GetOverallOutgoingStats(outgoing_stats, stats_gathering_mode_),
+ .overall_incoming_stats =
+ GetOverallIncomingStats(incoming_stats, stats_gathering_mode_),
+ .outgoing_stats_per_destination = std::move(outgoing_stats),
+ .incoming_stats_per_source = std::move(incoming_stats),
+ .sent_packets_queue_wait_time_us = sent_packets_queue_wait_time_us_};
+}
+
+EmulatedNetworkNodeStatsBuilder::EmulatedNetworkNodeStatsBuilder(
+ EmulatedNetworkStatsGatheringMode stats_gathering_mode)
+ : stats_gathering_mode_(stats_gathering_mode) {
+ sequence_checker_.Detach();
+}
+
+void EmulatedNetworkNodeStatsBuilder::AddPacketTransportTime(
+ TimeDelta time,
+ size_t packet_size) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ if (stats_gathering_mode_ == EmulatedNetworkStatsGatheringMode::kDebug) {
+ stats_.packet_transport_time.AddSample(time.ms<double>());
+ stats_.size_to_packet_transport_time.AddSample(packet_size /
+ time.ms<double>());
+ }
+}
+
+void EmulatedNetworkNodeStatsBuilder::AddEmulatedNetworkNodeStats(
+ const EmulatedNetworkNodeStats& stats) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ stats_.packet_transport_time.AddSamples(stats.packet_transport_time);
+ stats_.size_to_packet_transport_time.AddSamples(
+ stats.size_to_packet_transport_time);
+}
+
+EmulatedNetworkNodeStats EmulatedNetworkNodeStatsBuilder::Build() const {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ return stats_;
+}
+
+void LinkEmulation::OnPacketReceived(EmulatedIpPacket packet) {
+ task_queue_->PostTask([this, packet = std::move(packet)]() mutable {
+ RTC_DCHECK_RUN_ON(task_queue_);
+
+ uint64_t packet_id = next_packet_id_++;
+ bool sent = network_behavior_->EnqueuePacket(PacketInFlightInfo(
+ packet.ip_packet_size(), packet.arrival_time.us(), packet_id));
+ if (sent) {
+ packets_.emplace_back(StoredPacket{.id = packet_id,
+ .sent_time = clock_->CurrentTime(),
+ .packet = std::move(packet),
+ .removed = false});
+ }
+ if (process_task_.Running())
+ return;
+ absl::optional<int64_t> next_time_us =
+ network_behavior_->NextDeliveryTimeUs();
+ if (!next_time_us)
+ return;
+ Timestamp current_time = clock_->CurrentTime();
+ process_task_ = RepeatingTaskHandle::DelayedStart(
+ task_queue_->Get(),
+ std::max(TimeDelta::Zero(),
+ Timestamp::Micros(*next_time_us) - current_time),
+ [this]() {
+ RTC_DCHECK_RUN_ON(task_queue_);
+ Timestamp current_time = clock_->CurrentTime();
+ Process(current_time);
+ absl::optional<int64_t> next_time_us =
+ network_behavior_->NextDeliveryTimeUs();
+ if (!next_time_us) {
+ process_task_.Stop();
+ return TimeDelta::Zero(); // This is ignored.
+ }
+ RTC_DCHECK_GE(*next_time_us, current_time.us());
+ return Timestamp::Micros(*next_time_us) - current_time;
+ });
+ });
+}
+
+EmulatedNetworkNodeStats LinkEmulation::stats() const {
+ RTC_DCHECK_RUN_ON(task_queue_);
+ return stats_builder_.Build();
+}
+
+void LinkEmulation::Process(Timestamp at_time) {
+ std::vector<PacketDeliveryInfo> delivery_infos =
+ network_behavior_->DequeueDeliverablePackets(at_time.us());
+ for (PacketDeliveryInfo& delivery_info : delivery_infos) {
+ StoredPacket* packet = nullptr;
+ for (auto& stored_packet : packets_) {
+ if (stored_packet.id == delivery_info.packet_id) {
+ packet = &stored_packet;
+ break;
+ }
+ }
+ RTC_CHECK(packet);
+ RTC_DCHECK(!packet->removed);
+ packet->removed = true;
+ stats_builder_.AddPacketTransportTime(
+ clock_->CurrentTime() - packet->sent_time,
+ packet->packet.ip_packet_size());
+
+ if (delivery_info.receive_time_us != PacketDeliveryInfo::kNotReceived) {
+ packet->packet.arrival_time =
+ Timestamp::Micros(delivery_info.receive_time_us);
+ receiver_->OnPacketReceived(std::move(packet->packet));
+ }
+ while (!packets_.empty() && packets_.front().removed) {
+ packets_.pop_front();
+ }
+ }
+}
+
+NetworkRouterNode::NetworkRouterNode(rtc::TaskQueue* task_queue)
+ : task_queue_(task_queue) {}
+
+void NetworkRouterNode::OnPacketReceived(EmulatedIpPacket packet) {
+ RTC_DCHECK_RUN_ON(task_queue_);
+ if (watcher_) {
+ watcher_(packet);
+ }
+ if (filter_) {
+ if (!filter_(packet))
+ return;
+ }
+ auto receiver_it = routing_.find(packet.to.ipaddr());
+ if (receiver_it == routing_.end()) {
+ if (default_receiver_.has_value()) {
+ (*default_receiver_)->OnPacketReceived(std::move(packet));
+ }
+ return;
+ }
+ RTC_CHECK(receiver_it != routing_.end());
+
+ receiver_it->second->OnPacketReceived(std::move(packet));
+}
+
+void NetworkRouterNode::SetReceiver(
+ const rtc::IPAddress& dest_ip,
+ EmulatedNetworkReceiverInterface* receiver) {
+ task_queue_->PostTask([=] {
+ RTC_DCHECK_RUN_ON(task_queue_);
+ EmulatedNetworkReceiverInterface* cur_receiver = routing_[dest_ip];
+ RTC_CHECK(cur_receiver == nullptr || cur_receiver == receiver)
+ << "Routing for dest_ip=" << dest_ip.ToString() << " already exists";
+ routing_[dest_ip] = receiver;
+ });
+}
+
+void NetworkRouterNode::RemoveReceiver(const rtc::IPAddress& dest_ip) {
+ RTC_DCHECK_RUN_ON(task_queue_);
+ routing_.erase(dest_ip);
+}
+
+void NetworkRouterNode::SetDefaultReceiver(
+ EmulatedNetworkReceiverInterface* receiver) {
+ task_queue_->PostTask([=] {
+ RTC_DCHECK_RUN_ON(task_queue_);
+ if (default_receiver_.has_value()) {
+ RTC_CHECK_EQ(*default_receiver_, receiver)
+ << "Router already default receiver";
+ }
+ default_receiver_ = receiver;
+ });
+}
+
+void NetworkRouterNode::RemoveDefaultReceiver() {
+ RTC_DCHECK_RUN_ON(task_queue_);
+ default_receiver_ = absl::nullopt;
+}
+
+void NetworkRouterNode::SetWatcher(
+ std::function<void(const EmulatedIpPacket&)> watcher) {
+ task_queue_->PostTask([=] {
+ RTC_DCHECK_RUN_ON(task_queue_);
+ watcher_ = watcher;
+ });
+}
+
+void NetworkRouterNode::SetFilter(
+ std::function<bool(const EmulatedIpPacket&)> filter) {
+ task_queue_->PostTask([=] {
+ RTC_DCHECK_RUN_ON(task_queue_);
+ filter_ = filter;
+ });
+}
+
+EmulatedNetworkNode::EmulatedNetworkNode(
+ Clock* clock,
+ rtc::TaskQueue* task_queue,
+ std::unique_ptr<NetworkBehaviorInterface> network_behavior,
+ EmulatedNetworkStatsGatheringMode stats_gathering_mode)
+ : router_(task_queue),
+ link_(clock,
+ task_queue,
+ std::move(network_behavior),
+ &router_,
+ stats_gathering_mode) {}
+
+void EmulatedNetworkNode::OnPacketReceived(EmulatedIpPacket packet) {
+ link_.OnPacketReceived(std::move(packet));
+}
+
+EmulatedNetworkNodeStats EmulatedNetworkNode::stats() const {
+ return link_.stats();
+}
+
+void EmulatedNetworkNode::CreateRoute(
+ const rtc::IPAddress& receiver_ip,
+ std::vector<EmulatedNetworkNode*> nodes,
+ EmulatedNetworkReceiverInterface* receiver) {
+ RTC_CHECK(!nodes.empty());
+ for (size_t i = 0; i + 1 < nodes.size(); ++i)
+ nodes[i]->router()->SetReceiver(receiver_ip, nodes[i + 1]);
+ nodes.back()->router()->SetReceiver(receiver_ip, receiver);
+}
+
+void EmulatedNetworkNode::ClearRoute(const rtc::IPAddress& receiver_ip,
+ std::vector<EmulatedNetworkNode*> nodes) {
+ for (EmulatedNetworkNode* node : nodes)
+ node->router()->RemoveReceiver(receiver_ip);
+}
+
+EmulatedNetworkNode::~EmulatedNetworkNode() = default;
+
+EmulatedEndpointImpl::Options::Options(
+ uint64_t id,
+ const rtc::IPAddress& ip,
+ const EmulatedEndpointConfig& config,
+ EmulatedNetworkStatsGatheringMode stats_gathering_mode)
+ : id(id),
+ ip(ip),
+ stats_gathering_mode(stats_gathering_mode),
+ type(config.type),
+ allow_send_packet_with_different_source_ip(
+ config.allow_send_packet_with_different_source_ip),
+ allow_receive_packets_with_different_dest_ip(
+ config.allow_receive_packets_with_different_dest_ip),
+ log_name(ip.ToString() + " (" + config.name.value_or("") + ")") {}
+
+EmulatedEndpointImpl::EmulatedEndpointImpl(const Options& options,
+ bool is_enabled,
+ rtc::TaskQueue* task_queue,
+ Clock* clock)
+ : options_(options),
+ is_enabled_(is_enabled),
+ clock_(clock),
+ task_queue_(task_queue),
+ router_(task_queue_),
+ next_port_(kFirstEphemeralPort),
+ stats_builder_(options_.ip, options_.stats_gathering_mode) {
+ constexpr int kIPv4NetworkPrefixLength = 24;
+ constexpr int kIPv6NetworkPrefixLength = 64;
+
+ int prefix_length = 0;
+ if (options_.ip.family() == AF_INET) {
+ prefix_length = kIPv4NetworkPrefixLength;
+ } else if (options_.ip.family() == AF_INET6) {
+ prefix_length = kIPv6NetworkPrefixLength;
+ }
+ rtc::IPAddress prefix = TruncateIP(options_.ip, prefix_length);
+ network_ = std::make_unique<rtc::Network>(
+ options_.ip.ToString(), "Endpoint id=" + std::to_string(options_.id),
+ prefix, prefix_length, options_.type);
+ network_->AddIP(options_.ip);
+
+ enabled_state_checker_.Detach();
+ RTC_LOG(LS_INFO) << "Created emulated endpoint " << options_.log_name
+ << "; id=" << options_.id;
+}
+EmulatedEndpointImpl::~EmulatedEndpointImpl() = default;
+
+uint64_t EmulatedEndpointImpl::GetId() const {
+ return options_.id;
+}
+
+void EmulatedEndpointImpl::SendPacket(const rtc::SocketAddress& from,
+ const rtc::SocketAddress& to,
+ rtc::CopyOnWriteBuffer packet_data,
+ uint16_t application_overhead) {
+ if (!options_.allow_send_packet_with_different_source_ip) {
+ RTC_CHECK(from.ipaddr() == options_.ip);
+ }
+ EmulatedIpPacket packet(from, to, std::move(packet_data),
+ clock_->CurrentTime(), application_overhead);
+ task_queue_->PostTask([this, packet = std::move(packet)]() mutable {
+ RTC_DCHECK_RUN_ON(task_queue_);
+ stats_builder_.OnPacketSent(packet.arrival_time, clock_->CurrentTime(),
+ packet.to.ipaddr(),
+ DataSize::Bytes(packet.ip_packet_size()));
+
+ if (packet.to.ipaddr() == options_.ip) {
+ OnPacketReceived(std::move(packet));
+ } else {
+ router_.OnPacketReceived(std::move(packet));
+ }
+ });
+}
+
+absl::optional<uint16_t> EmulatedEndpointImpl::BindReceiver(
+ uint16_t desired_port,
+ EmulatedNetworkReceiverInterface* receiver) {
+ return BindReceiverInternal(desired_port, receiver, /*is_one_shot=*/false);
+}
+
+absl::optional<uint16_t> EmulatedEndpointImpl::BindOneShotReceiver(
+ uint16_t desired_port,
+ EmulatedNetworkReceiverInterface* receiver) {
+ return BindReceiverInternal(desired_port, receiver, /*is_one_shot=*/true);
+}
+
+absl::optional<uint16_t> EmulatedEndpointImpl::BindReceiverInternal(
+ uint16_t desired_port,
+ EmulatedNetworkReceiverInterface* receiver,
+ bool is_one_shot) {
+ MutexLock lock(&receiver_lock_);
+ uint16_t port = desired_port;
+ if (port == 0) {
+ // Because client can specify its own port, next_port_ can be already in
+ // use, so we need to find next available port.
+ int ports_pool_size =
+ std::numeric_limits<uint16_t>::max() - kFirstEphemeralPort + 1;
+ for (int i = 0; i < ports_pool_size; ++i) {
+ uint16_t next_port = NextPort();
+ if (port_to_receiver_.find(next_port) == port_to_receiver_.end()) {
+ port = next_port;
+ break;
+ }
+ }
+ }
+ RTC_CHECK(port != 0) << "Can't find free port for receiver in endpoint "
+ << options_.log_name << "; id=" << options_.id;
+ bool result =
+ port_to_receiver_.insert({port, {receiver, is_one_shot}}).second;
+ if (!result) {
+ RTC_LOG(LS_INFO) << "Can't bind receiver to used port " << desired_port
+ << " in endpoint " << options_.log_name
+ << "; id=" << options_.id;
+ return absl::nullopt;
+ }
+ RTC_LOG(LS_INFO) << "New receiver is binded to endpoint " << options_.log_name
+ << "; id=" << options_.id << " on port " << port;
+ return port;
+}
+
+uint16_t EmulatedEndpointImpl::NextPort() {
+ uint16_t out = next_port_;
+ if (next_port_ == std::numeric_limits<uint16_t>::max()) {
+ next_port_ = kFirstEphemeralPort;
+ } else {
+ next_port_++;
+ }
+ return out;
+}
+
+void EmulatedEndpointImpl::UnbindReceiver(uint16_t port) {
+ MutexLock lock(&receiver_lock_);
+ RTC_LOG(LS_INFO) << "Receiver is removed on port " << port
+ << " from endpoint " << options_.log_name
+ << "; id=" << options_.id;
+ port_to_receiver_.erase(port);
+}
+
+void EmulatedEndpointImpl::BindDefaultReceiver(
+ EmulatedNetworkReceiverInterface* receiver) {
+ MutexLock lock(&receiver_lock_);
+ RTC_CHECK(!default_receiver_.has_value())
+ << "Endpoint " << options_.log_name << "; id=" << options_.id
+ << " already has default receiver";
+ RTC_LOG(LS_INFO) << "Default receiver is binded to endpoint "
+ << options_.log_name << "; id=" << options_.id;
+ default_receiver_ = receiver;
+}
+
+void EmulatedEndpointImpl::UnbindDefaultReceiver() {
+ MutexLock lock(&receiver_lock_);
+ RTC_LOG(LS_INFO) << "Default receiver is removed from endpoint "
+ << options_.log_name << "; id=" << options_.id;
+ default_receiver_ = absl::nullopt;
+}
+
+rtc::IPAddress EmulatedEndpointImpl::GetPeerLocalAddress() const {
+ return options_.ip;
+}
+
+void EmulatedEndpointImpl::OnPacketReceived(EmulatedIpPacket packet) {
+ RTC_DCHECK_RUN_ON(task_queue_);
+ if (!options_.allow_receive_packets_with_different_dest_ip) {
+ RTC_CHECK(packet.to.ipaddr() == options_.ip)
+ << "Routing error: wrong destination endpoint. Packet.to.ipaddr()=: "
+ << packet.to.ipaddr().ToString()
+ << "; Receiver options_.ip=" << options_.ip.ToString();
+ }
+ MutexLock lock(&receiver_lock_);
+ stats_builder_.OnPacketReceived(clock_->CurrentTime(), packet.from.ipaddr(),
+ DataSize::Bytes(packet.ip_packet_size()));
+ auto it = port_to_receiver_.find(packet.to.port());
+ if (it == port_to_receiver_.end()) {
+ if (default_receiver_.has_value()) {
+ (*default_receiver_)->OnPacketReceived(std::move(packet));
+ return;
+ }
+ // It can happen, that remote peer closed connection, but there still some
+ // packets, that are going to it. It can happen during peer connection close
+ // process: one peer closed connection, second still sending data.
+ RTC_LOG(LS_INFO) << "Drop packet: no receiver registered in "
+ << options_.log_name << "; id=" << options_.id
+ << " on port " << packet.to.port()
+ << ". Packet source: " << packet.from.ToString();
+ stats_builder_.OnPacketDropped(packet.from.ipaddr(),
+ DataSize::Bytes(packet.ip_packet_size()));
+ return;
+ }
+ // Endpoint holds lock during packet processing to ensure that a call to
+ // UnbindReceiver followed by a delete of the receiver cannot race with this
+ // call to OnPacketReceived.
+ it->second.receiver->OnPacketReceived(std::move(packet));
+
+ if (it->second.is_one_shot) {
+ port_to_receiver_.erase(it);
+ }
+}
+
+void EmulatedEndpointImpl::Enable() {
+ RTC_DCHECK_RUN_ON(&enabled_state_checker_);
+ RTC_CHECK(!is_enabled_);
+ is_enabled_ = true;
+}
+
+void EmulatedEndpointImpl::Disable() {
+ RTC_DCHECK_RUN_ON(&enabled_state_checker_);
+ RTC_CHECK(is_enabled_);
+ is_enabled_ = false;
+}
+
+bool EmulatedEndpointImpl::Enabled() const {
+ RTC_DCHECK_RUN_ON(&enabled_state_checker_);
+ return is_enabled_;
+}
+
+EmulatedNetworkStats EmulatedEndpointImpl::stats() const {
+ RTC_DCHECK_RUN_ON(task_queue_);
+ return stats_builder_.Build();
+}
+
+EmulatedEndpointImpl* EndpointsContainer::LookupByLocalAddress(
+ const rtc::IPAddress& local_ip) const {
+ for (auto* endpoint : endpoints_) {
+ rtc::IPAddress peer_local_address = endpoint->GetPeerLocalAddress();
+ if (peer_local_address == local_ip) {
+ return endpoint;
+ }
+ }
+ RTC_CHECK(false) << "No network found for address" << local_ip.ToString();
+}
+
+EndpointsContainer::EndpointsContainer(
+ const std::vector<EmulatedEndpointImpl*>& endpoints,
+ EmulatedNetworkStatsGatheringMode stats_gathering_mode)
+ : endpoints_(endpoints), stats_gathering_mode_(stats_gathering_mode) {}
+
+bool EndpointsContainer::HasEndpoint(EmulatedEndpointImpl* endpoint) const {
+ for (auto* e : endpoints_) {
+ if (e->GetId() == endpoint->GetId()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+std::vector<std::unique_ptr<rtc::Network>>
+EndpointsContainer::GetEnabledNetworks() const {
+ std::vector<std::unique_ptr<rtc::Network>> networks;
+ for (auto* endpoint : endpoints_) {
+ if (endpoint->Enabled()) {
+ networks.emplace_back(
+ std::make_unique<rtc::Network>(endpoint->network()));
+ }
+ }
+ return networks;
+}
+
+std::vector<EmulatedEndpoint*> EndpointsContainer::GetEndpoints() const {
+ return std::vector<EmulatedEndpoint*>(endpoints_.begin(), endpoints_.end());
+}
+
+EmulatedNetworkStats EndpointsContainer::GetStats() const {
+ EmulatedNetworkStatsBuilder stats_builder(stats_gathering_mode_);
+ for (auto* endpoint : endpoints_) {
+ stats_builder.AddEmulatedNetworkStats(endpoint->stats());
+ }
+ return stats_builder.Build();
+}
+
+} // namespace webrtc