summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/test/network
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /third_party/libwebrtc/test/network
parentInitial commit. (diff)
downloadfirefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz
firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/test/network')
-rw-r--r--third_party/libwebrtc/test/network/BUILD.gn199
-rw-r--r--third_party/libwebrtc/test/network/OWNERS1
-rw-r--r--third_party/libwebrtc/test/network/cross_traffic.cc322
-rw-r--r--third_party/libwebrtc/test/network/cross_traffic.h174
-rw-r--r--third_party/libwebrtc/test/network/cross_traffic_unittest.cc161
-rw-r--r--third_party/libwebrtc/test/network/emulated_network_manager.cc123
-rw-r--r--third_party/libwebrtc/test/network/emulated_network_manager.h83
-rw-r--r--third_party/libwebrtc/test/network/emulated_turn_server.cc191
-rw-r--r--third_party/libwebrtc/test/network/emulated_turn_server.h98
-rw-r--r--third_party/libwebrtc/test/network/fake_network_socket_server.cc319
-rw-r--r--third_party/libwebrtc/test/network/fake_network_socket_server.h63
-rw-r--r--third_party/libwebrtc/test/network/feedback_generator.cc111
-rw-r--r--third_party/libwebrtc/test/network/feedback_generator.h60
-rw-r--r--third_party/libwebrtc/test/network/feedback_generator_unittest.cc47
-rw-r--r--third_party/libwebrtc/test/network/g3doc/g3doc.lua5
-rw-r--r--third_party/libwebrtc/test/network/g3doc/index.md136
-rw-r--r--third_party/libwebrtc/test/network/g3doc/network_emulation_framework.pngbin0 -> 126248 bytes
-rw-r--r--third_party/libwebrtc/test/network/g3doc/network_injection_into_peer_connection.pngbin0 -> 65121 bytes
-rw-r--r--third_party/libwebrtc/test/network/network_emulation.cc708
-rw-r--r--third_party/libwebrtc/test/network/network_emulation.h702
-rw-r--r--third_party/libwebrtc/test/network/network_emulation_manager.cc356
-rw-r--r--third_party/libwebrtc/test/network/network_emulation_manager.h135
-rw-r--r--third_party/libwebrtc/test/network/network_emulation_pc_unittest.cc317
-rw-r--r--third_party/libwebrtc/test/network/network_emulation_unittest.cc667
-rw-r--r--third_party/libwebrtc/test/network/traffic_route.cc91
-rw-r--r--third_party/libwebrtc/test/network/traffic_route.h57
26 files changed, 5126 insertions, 0 deletions
diff --git a/third_party/libwebrtc/test/network/BUILD.gn b/third_party/libwebrtc/test/network/BUILD.gn
new file mode 100644
index 0000000000..71cf2d79f3
--- /dev/null
+++ b/third_party/libwebrtc/test/network/BUILD.gn
@@ -0,0 +1,199 @@
+# 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.
+
+import("../../webrtc.gni")
+
+rtc_library("emulated_network") {
+ visibility = [
+ ":*",
+ "../../api:create_network_emulation_manager",
+ "../../api/test/network_emulation:create_cross_traffic",
+ ]
+ if (rtc_include_tests) {
+ visibility += [
+ "../peer_scenario:*",
+ "../scenario:*",
+ ]
+ }
+ testonly = true
+ sources = [
+ "cross_traffic.cc",
+ "cross_traffic.h",
+ "emulated_network_manager.cc",
+ "emulated_network_manager.h",
+ "emulated_turn_server.cc",
+ "emulated_turn_server.h",
+ "fake_network_socket_server.cc",
+ "fake_network_socket_server.h",
+ "network_emulation.cc",
+ "network_emulation.h",
+ "network_emulation_manager.cc",
+ "network_emulation_manager.h",
+ "traffic_route.cc",
+ "traffic_route.h",
+ ]
+ deps = [
+ "../../api:array_view",
+ "../../api:field_trials_view",
+ "../../api:network_emulation_manager_api",
+ "../../api:packet_socket_factory",
+ "../../api:scoped_refptr",
+ "../../api:sequence_checker",
+ "../../api:simulated_network_api",
+ "../../api:time_controller",
+ "../../api/numerics",
+ "../../api/task_queue:pending_task_safety_flag",
+ "../../api/test/network_emulation",
+ "../../api/transport:stun_types",
+ "../../api/units:data_rate",
+ "../../api/units:data_size",
+ "../../api/units:time_delta",
+ "../../api/units:timestamp",
+ "../../call:simulated_network",
+ "../../p2p:p2p_server_utils",
+ "../../p2p:rtc_p2p",
+ "../../rtc_base",
+ "../../rtc_base:copy_on_write_buffer",
+ "../../rtc_base:ip_address",
+ "../../rtc_base:logging",
+ "../../rtc_base:macromagic",
+ "../../rtc_base:network_constants",
+ "../../rtc_base:random",
+ "../../rtc_base:rtc_base_tests_utils",
+ "../../rtc_base:rtc_event",
+ "../../rtc_base:rtc_task_queue",
+ "../../rtc_base:safe_minmax",
+ "../../rtc_base:socket",
+ "../../rtc_base:socket_address",
+ "../../rtc_base:socket_server",
+ "../../rtc_base:stringutils",
+ "../../rtc_base:task_queue_for_test",
+ "../../rtc_base:threading",
+ "../../rtc_base/memory:always_valid_pointer",
+ "../../rtc_base/synchronization:mutex",
+ "../../rtc_base/task_utils:repeating_task",
+ "../../system_wrappers",
+ "../../test:scoped_key_value_config",
+ "../scenario:column_printer",
+ "../time_controller",
+ ]
+ absl_deps = [
+ "//third_party/abseil-cpp/absl/algorithm:container",
+ "//third_party/abseil-cpp/absl/memory",
+ "//third_party/abseil-cpp/absl/strings",
+ "//third_party/abseil-cpp/absl/types:optional",
+ ]
+}
+
+rtc_library("network_emulation_unittest") {
+ testonly = true
+ sources = [ "network_emulation_unittest.cc" ]
+ deps = [
+ ":emulated_network",
+ "../:test_support",
+ "../../api:simulated_network_api",
+ "../../api/units:time_delta",
+ "../../call:simulated_network",
+ "../../rtc_base:gunit_helpers",
+ "../../rtc_base:logging",
+ "../../rtc_base:rtc_event",
+ "../../rtc_base:task_queue_for_test",
+ "../../rtc_base/synchronization:mutex",
+ ]
+}
+
+if (rtc_include_tests && !build_with_chromium) {
+ rtc_library("network_emulation_pc_unittest") {
+ testonly = true
+ sources = [ "network_emulation_pc_unittest.cc" ]
+ deps = [
+ ":emulated_network",
+ "../:test_support",
+ "../../api:callfactory_api",
+ "../../api:libjingle_peerconnection_api",
+ "../../api:scoped_refptr",
+ "../../api:simulated_network_api",
+ "../../api/rtc_event_log:rtc_event_log_factory",
+ "../../api/task_queue:default_task_queue_factory",
+ "../../api/transport:field_trial_based_config",
+ "../../call:simulated_network",
+ "../../media:rtc_audio_video",
+ "../../media:rtc_media_engine_defaults",
+ "../../modules/audio_device:audio_device_impl",
+ "../../p2p:rtc_p2p",
+ "../../pc:pc_test_utils",
+ "../../pc:peerconnection_wrapper",
+ "../../rtc_base",
+ "../../rtc_base:gunit_helpers",
+ "../../rtc_base:logging",
+ "../../rtc_base:rtc_event",
+ "../../rtc_base:task_queue_for_test",
+ ]
+ }
+}
+
+rtc_library("cross_traffic_unittest") {
+ testonly = true
+ sources = [ "cross_traffic_unittest.cc" ]
+ deps = [
+ ":emulated_network",
+ "../:test_support",
+ "../../api:network_emulation_manager_api",
+ "../../api:simulated_network_api",
+ "../../call:simulated_network",
+ "../../rtc_base",
+ "../../rtc_base:logging",
+ "../../rtc_base:network_constants",
+ "../../rtc_base:rtc_event",
+ "../time_controller",
+ ]
+ absl_deps = [
+ "//third_party/abseil-cpp/absl/memory",
+ "//third_party/abseil-cpp/absl/types:optional",
+ ]
+}
+
+if (rtc_include_tests) {
+ rtc_library("feedback_generator") {
+ testonly = true
+ sources = [
+ "feedback_generator.cc",
+ "feedback_generator.h",
+ ]
+ deps = [
+ ":emulated_network",
+ "../../api/transport:network_control",
+ "../../api/transport:test_feedback_generator_interface",
+ "../../call:simulated_network",
+ "../../rtc_base:checks",
+ "../time_controller",
+ ]
+ absl_deps = [ "//third_party/abseil-cpp/absl/memory" ]
+ }
+
+ rtc_library("feedback_generator_unittest") {
+ testonly = true
+ sources = [ "feedback_generator_unittest.cc" ]
+ deps = [
+ "../:test_support",
+ "../../api/transport:test_feedback_generator",
+ ]
+ }
+
+ if (!build_with_chromium) {
+ rtc_library("network_emulation_unittests") {
+ testonly = true
+ deps = [
+ ":cross_traffic_unittest",
+ ":feedback_generator_unittest",
+ ":network_emulation_pc_unittest",
+ ":network_emulation_unittest",
+ ]
+ }
+ }
+}
diff --git a/third_party/libwebrtc/test/network/OWNERS b/third_party/libwebrtc/test/network/OWNERS
new file mode 100644
index 0000000000..b177c4eec5
--- /dev/null
+++ b/third_party/libwebrtc/test/network/OWNERS
@@ -0,0 +1 @@
+titovartem@webrtc.org
diff --git a/third_party/libwebrtc/test/network/cross_traffic.cc b/third_party/libwebrtc/test/network/cross_traffic.cc
new file mode 100644
index 0000000000..0a817a2d39
--- /dev/null
+++ b/third_party/libwebrtc/test/network/cross_traffic.cc
@@ -0,0 +1,322 @@
+/*
+ * 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/cross_traffic.h"
+
+#include <math.h>
+
+#include <utility>
+
+#include "absl/memory/memory.h"
+#include "absl/types/optional.h"
+#include "cross_traffic.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/numerics/safe_minmax.h"
+
+namespace webrtc {
+namespace test {
+
+RandomWalkCrossTraffic::RandomWalkCrossTraffic(RandomWalkConfig config,
+ CrossTrafficRoute* traffic_route)
+ : config_(config),
+ traffic_route_(traffic_route),
+ random_(config_.random_seed) {
+ sequence_checker_.Detach();
+}
+RandomWalkCrossTraffic::~RandomWalkCrossTraffic() = default;
+
+void RandomWalkCrossTraffic::Process(Timestamp at_time) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ if (last_process_time_.IsMinusInfinity()) {
+ last_process_time_ = at_time;
+ }
+ TimeDelta delta = at_time - last_process_time_;
+ last_process_time_ = at_time;
+
+ if (at_time - last_update_time_ >= config_.update_interval) {
+ intensity_ += random_.Gaussian(config_.bias, config_.variance) *
+ sqrt((at_time - last_update_time_).seconds<double>());
+ intensity_ = rtc::SafeClamp(intensity_, 0.0, 1.0);
+ last_update_time_ = at_time;
+ }
+ pending_size_ += TrafficRate() * delta;
+
+ if (pending_size_ >= config_.min_packet_size &&
+ at_time >= last_send_time_ + config_.min_packet_interval) {
+ traffic_route_->SendPacket(pending_size_.bytes());
+ pending_size_ = DataSize::Zero();
+ last_send_time_ = at_time;
+ }
+}
+
+TimeDelta RandomWalkCrossTraffic::GetProcessInterval() const {
+ return config_.min_packet_interval;
+}
+
+DataRate RandomWalkCrossTraffic::TrafficRate() const {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ return config_.peak_rate * intensity_;
+}
+
+ColumnPrinter RandomWalkCrossTraffic::StatsPrinter() {
+ return ColumnPrinter::Lambda(
+ "random_walk_cross_traffic_rate",
+ [this](rtc::SimpleStringBuilder& sb) {
+ sb.AppendFormat("%.0lf", TrafficRate().bps() / 8.0);
+ },
+ 32);
+}
+
+PulsedPeaksCrossTraffic::PulsedPeaksCrossTraffic(
+ PulsedPeaksConfig config,
+ CrossTrafficRoute* traffic_route)
+ : config_(config), traffic_route_(traffic_route) {
+ sequence_checker_.Detach();
+}
+PulsedPeaksCrossTraffic::~PulsedPeaksCrossTraffic() = default;
+
+void PulsedPeaksCrossTraffic::Process(Timestamp at_time) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ TimeDelta time_since_toggle = at_time - last_update_time_;
+ if (time_since_toggle.IsInfinite() ||
+ (sending_ && time_since_toggle >= config_.send_duration)) {
+ sending_ = false;
+ last_update_time_ = at_time;
+ } else if (!sending_ && time_since_toggle >= config_.hold_duration) {
+ sending_ = true;
+ last_update_time_ = at_time;
+ // Start sending period.
+ last_send_time_ = at_time;
+ }
+
+ if (sending_) {
+ DataSize pending_size = config_.peak_rate * (at_time - last_send_time_);
+
+ if (pending_size >= config_.min_packet_size &&
+ at_time >= last_send_time_ + config_.min_packet_interval) {
+ traffic_route_->SendPacket(pending_size.bytes());
+ last_send_time_ = at_time;
+ }
+ }
+}
+
+TimeDelta PulsedPeaksCrossTraffic::GetProcessInterval() const {
+ return config_.min_packet_interval;
+}
+
+DataRate PulsedPeaksCrossTraffic::TrafficRate() const {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ return sending_ ? config_.peak_rate : DataRate::Zero();
+}
+
+ColumnPrinter PulsedPeaksCrossTraffic::StatsPrinter() {
+ return ColumnPrinter::Lambda(
+ "pulsed_peaks_cross_traffic_rate",
+ [this](rtc::SimpleStringBuilder& sb) {
+ sb.AppendFormat("%.0lf", TrafficRate().bps() / 8.0);
+ },
+ 32);
+}
+
+TcpMessageRouteImpl::TcpMessageRouteImpl(Clock* clock,
+ TaskQueueBase* task_queue,
+ EmulatedRoute* send_route,
+ EmulatedRoute* ret_route)
+ : clock_(clock),
+ task_queue_(task_queue),
+ request_route_(send_route,
+ [this](TcpPacket packet, Timestamp) {
+ OnRequest(std::move(packet));
+ }),
+ response_route_(ret_route,
+ [this](TcpPacket packet, Timestamp arrival_time) {
+ OnResponse(std::move(packet), arrival_time);
+ }) {}
+
+void TcpMessageRouteImpl::SendMessage(size_t size,
+ std::function<void()> on_received) {
+ task_queue_->PostTask(
+ [this, size, handler = std::move(on_received)] {
+ // If we are currently sending a message we won't reset the connection,
+ // we'll act as if the messages are sent in the same TCP stream. This is
+ // intended to simulate recreation of a TCP session for each message
+ // in the typical case while avoiding the complexity overhead of
+ // maintaining multiple virtual TCP sessions in parallel.
+ if (pending_.empty() && in_flight_.empty()) {
+ cwnd_ = 10;
+ ssthresh_ = INFINITY;
+ }
+ int64_t data_left = static_cast<int64_t>(size);
+ int64_t kMaxPacketSize = 1200;
+ int64_t kMinPacketSize = 4;
+ Message message{std::move(handler)};
+ while (data_left > 0) {
+ int64_t packet_size = std::min(data_left, kMaxPacketSize);
+ int fragment_id = next_fragment_id_++;
+ pending_.push_back(MessageFragment{
+ fragment_id,
+ static_cast<size_t>(std::max(kMinPacketSize, packet_size))});
+ message.pending_fragment_ids.insert(fragment_id);
+ data_left -= packet_size;
+ }
+ messages_.emplace_back(message);
+ SendPackets(clock_->CurrentTime());
+ });
+}
+
+void TcpMessageRouteImpl::OnRequest(TcpPacket packet_info) {
+ for (auto it = messages_.begin(); it != messages_.end(); ++it) {
+ if (it->pending_fragment_ids.count(packet_info.fragment.fragment_id) != 0) {
+ it->pending_fragment_ids.erase(packet_info.fragment.fragment_id);
+ if (it->pending_fragment_ids.empty()) {
+ it->handler();
+ messages_.erase(it);
+ }
+ break;
+ }
+ }
+ const size_t kAckPacketSize = 20;
+ response_route_.SendPacket(kAckPacketSize, packet_info);
+}
+
+void TcpMessageRouteImpl::OnResponse(TcpPacket packet_info, Timestamp at_time) {
+ auto it = in_flight_.find(packet_info.sequence_number);
+ if (it != in_flight_.end()) {
+ last_rtt_ = at_time - packet_info.send_time;
+ in_flight_.erase(it);
+ }
+ auto lost_end = in_flight_.lower_bound(packet_info.sequence_number);
+ for (auto lost_it = in_flight_.begin(); lost_it != lost_end;
+ lost_it = in_flight_.erase(lost_it)) {
+ pending_.push_front(lost_it->second.fragment);
+ }
+
+ if (packet_info.sequence_number - last_acked_seq_num_ > 1) {
+ HandleLoss(at_time);
+ } else if (cwnd_ <= ssthresh_) {
+ cwnd_ += 1;
+ } else {
+ cwnd_ += 1.0f / cwnd_;
+ }
+ last_acked_seq_num_ =
+ std::max(packet_info.sequence_number, last_acked_seq_num_);
+ SendPackets(at_time);
+}
+
+void TcpMessageRouteImpl::HandleLoss(Timestamp at_time) {
+ if (at_time - last_reduction_time_ < last_rtt_)
+ return;
+ last_reduction_time_ = at_time;
+ ssthresh_ = std::max(static_cast<int>(in_flight_.size() / 2), 2);
+ cwnd_ = ssthresh_;
+}
+
+void TcpMessageRouteImpl::SendPackets(Timestamp at_time) {
+ const TimeDelta kPacketTimeout = TimeDelta::Seconds(1);
+ int cwnd = std::ceil(cwnd_);
+ int packets_to_send = std::max(cwnd - static_cast<int>(in_flight_.size()), 0);
+ while (packets_to_send-- > 0 && !pending_.empty()) {
+ auto seq_num = next_sequence_number_++;
+ TcpPacket send;
+ send.sequence_number = seq_num;
+ send.send_time = at_time;
+ send.fragment = pending_.front();
+ pending_.pop_front();
+ request_route_.SendPacket(send.fragment.size, send);
+ in_flight_.insert({seq_num, send});
+ task_queue_->PostDelayedTask(
+ [this, seq_num] {
+ HandlePacketTimeout(seq_num, clock_->CurrentTime());
+ },
+ kPacketTimeout);
+ }
+}
+
+void TcpMessageRouteImpl::HandlePacketTimeout(int seq_num, Timestamp at_time) {
+ auto lost = in_flight_.find(seq_num);
+ if (lost != in_flight_.end()) {
+ pending_.push_front(lost->second.fragment);
+ in_flight_.erase(lost);
+ HandleLoss(at_time);
+ SendPackets(at_time);
+ }
+}
+
+FakeTcpCrossTraffic::FakeTcpCrossTraffic(FakeTcpConfig config,
+ EmulatedRoute* send_route,
+ EmulatedRoute* ret_route)
+ : conf_(config), route_(this, send_route, ret_route) {}
+
+TimeDelta FakeTcpCrossTraffic::GetProcessInterval() const {
+ return conf_.process_interval;
+}
+
+void FakeTcpCrossTraffic::Process(Timestamp at_time) {
+ SendPackets(at_time);
+}
+
+void FakeTcpCrossTraffic::OnRequest(int sequence_number, Timestamp at_time) {
+ const size_t kAckPacketSize = 20;
+ route_.SendResponse(kAckPacketSize, sequence_number);
+}
+
+void FakeTcpCrossTraffic::OnResponse(int sequence_number, Timestamp at_time) {
+ ack_received_ = true;
+ auto it = in_flight_.find(sequence_number);
+ if (it != in_flight_.end()) {
+ last_rtt_ = at_time - in_flight_.at(sequence_number);
+ in_flight_.erase(sequence_number);
+ }
+ if (sequence_number - last_acked_seq_num_ > 1) {
+ HandleLoss(at_time);
+ } else if (cwnd_ <= ssthresh_) {
+ cwnd_ += 1;
+ } else {
+ cwnd_ += 1.0f / cwnd_;
+ }
+ last_acked_seq_num_ = std::max(sequence_number, last_acked_seq_num_);
+ SendPackets(at_time);
+}
+
+void FakeTcpCrossTraffic::HandleLoss(Timestamp at_time) {
+ if (at_time - last_reduction_time_ < last_rtt_)
+ return;
+ last_reduction_time_ = at_time;
+ ssthresh_ = std::max(static_cast<int>(in_flight_.size() / 2), 2);
+ cwnd_ = ssthresh_;
+}
+
+void FakeTcpCrossTraffic::SendPackets(Timestamp at_time) {
+ int cwnd = std::ceil(cwnd_);
+ int packets_to_send = std::max(cwnd - static_cast<int>(in_flight_.size()), 0);
+ bool timeouts = false;
+ for (auto it = in_flight_.begin(); it != in_flight_.end();) {
+ if (it->second < at_time - conf_.packet_timeout) {
+ it = in_flight_.erase(it);
+ timeouts = true;
+ } else {
+ ++it;
+ }
+ }
+ if (timeouts)
+ HandleLoss(at_time);
+ for (int i = 0; i < packets_to_send; ++i) {
+ if ((total_sent_ + conf_.packet_size) > conf_.send_limit) {
+ break;
+ }
+ in_flight_.insert({next_sequence_number_, at_time});
+ route_.SendRequest(conf_.packet_size.bytes<size_t>(),
+ next_sequence_number_++);
+ total_sent_ += conf_.packet_size;
+ }
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/third_party/libwebrtc/test/network/cross_traffic.h b/third_party/libwebrtc/test/network/cross_traffic.h
new file mode 100644
index 0000000000..d21e942475
--- /dev/null
+++ b/third_party/libwebrtc/test/network/cross_traffic.h
@@ -0,0 +1,174 @@
+/*
+ * 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.
+ */
+
+#ifndef TEST_NETWORK_CROSS_TRAFFIC_H_
+#define TEST_NETWORK_CROSS_TRAFFIC_H_
+
+#include <algorithm>
+#include <map>
+#include <memory>
+
+#include "api/sequence_checker.h"
+#include "api/test/network_emulation_manager.h"
+#include "api/units/data_rate.h"
+#include "api/units/data_size.h"
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
+#include "rtc_base/random.h"
+#include "test/network/network_emulation.h"
+#include "test/scenario/column_printer.h"
+
+namespace webrtc {
+namespace test {
+
+class RandomWalkCrossTraffic final : public CrossTrafficGenerator {
+ public:
+ RandomWalkCrossTraffic(RandomWalkConfig config,
+ CrossTrafficRoute* traffic_route);
+ ~RandomWalkCrossTraffic();
+
+ void Process(Timestamp at_time) override;
+ TimeDelta GetProcessInterval() const override;
+ DataRate TrafficRate() const;
+ ColumnPrinter StatsPrinter();
+
+ private:
+ SequenceChecker sequence_checker_;
+ const RandomWalkConfig config_;
+ CrossTrafficRoute* const traffic_route_ RTC_PT_GUARDED_BY(sequence_checker_);
+ webrtc::Random random_ RTC_GUARDED_BY(sequence_checker_);
+
+ Timestamp last_process_time_ RTC_GUARDED_BY(sequence_checker_) =
+ Timestamp::MinusInfinity();
+ Timestamp last_update_time_ RTC_GUARDED_BY(sequence_checker_) =
+ Timestamp::MinusInfinity();
+ Timestamp last_send_time_ RTC_GUARDED_BY(sequence_checker_) =
+ Timestamp::MinusInfinity();
+ double intensity_ RTC_GUARDED_BY(sequence_checker_) = 0;
+ DataSize pending_size_ RTC_GUARDED_BY(sequence_checker_) = DataSize::Zero();
+};
+
+class PulsedPeaksCrossTraffic final : public CrossTrafficGenerator {
+ public:
+ PulsedPeaksCrossTraffic(PulsedPeaksConfig config,
+ CrossTrafficRoute* traffic_route);
+ ~PulsedPeaksCrossTraffic();
+
+ void Process(Timestamp at_time) override;
+ TimeDelta GetProcessInterval() const override;
+ DataRate TrafficRate() const;
+ ColumnPrinter StatsPrinter();
+
+ private:
+ SequenceChecker sequence_checker_;
+ const PulsedPeaksConfig config_;
+ CrossTrafficRoute* const traffic_route_ RTC_PT_GUARDED_BY(sequence_checker_);
+
+ Timestamp last_update_time_ RTC_GUARDED_BY(sequence_checker_) =
+ Timestamp::MinusInfinity();
+ Timestamp last_send_time_ RTC_GUARDED_BY(sequence_checker_) =
+ Timestamp::MinusInfinity();
+ bool sending_ RTC_GUARDED_BY(sequence_checker_) = false;
+};
+
+class TcpMessageRouteImpl final : public TcpMessageRoute {
+ public:
+ TcpMessageRouteImpl(Clock* clock,
+ TaskQueueBase* task_queue,
+ EmulatedRoute* send_route,
+ EmulatedRoute* ret_route);
+
+ // Sends a TCP message of the given `size` over the route, `on_received` is
+ // called when the message has been delivered. Note that the connection
+ // parameters are reset iff there's no currently pending message on the route.
+ void SendMessage(size_t size, std::function<void()> on_received) override;
+
+ private:
+ // Represents a message sent over the route. When all fragments has been
+ // delivered, the message is considered delivered and the handler is
+ // triggered. This only happen once.
+ struct Message {
+ std::function<void()> handler;
+ std::set<int> pending_fragment_ids;
+ };
+ // Represents a piece of a message that fit into a TCP packet.
+ struct MessageFragment {
+ int fragment_id;
+ size_t size;
+ };
+ // Represents a packet sent on the wire.
+ struct TcpPacket {
+ int sequence_number;
+ Timestamp send_time = Timestamp::MinusInfinity();
+ MessageFragment fragment;
+ };
+
+ void OnRequest(TcpPacket packet_info);
+ void OnResponse(TcpPacket packet_info, Timestamp at_time);
+ void HandleLoss(Timestamp at_time);
+ void SendPackets(Timestamp at_time);
+ void HandlePacketTimeout(int seq_num, Timestamp at_time);
+
+ Clock* const clock_;
+ TaskQueueBase* const task_queue_;
+ FakePacketRoute<TcpPacket> request_route_;
+ FakePacketRoute<TcpPacket> response_route_;
+
+ std::deque<MessageFragment> pending_;
+ std::map<int, TcpPacket> in_flight_;
+ std::list<Message> messages_;
+
+ double cwnd_;
+ double ssthresh_;
+
+ int last_acked_seq_num_ = 0;
+ int next_sequence_number_ = 0;
+ int next_fragment_id_ = 0;
+ Timestamp last_reduction_time_ = Timestamp::MinusInfinity();
+ TimeDelta last_rtt_ = TimeDelta::Zero();
+};
+
+class FakeTcpCrossTraffic
+ : public TwoWayFakeTrafficRoute<int, int>::TrafficHandlerInterface,
+ public CrossTrafficGenerator {
+ public:
+ FakeTcpCrossTraffic(FakeTcpConfig config,
+ EmulatedRoute* send_route,
+ EmulatedRoute* ret_route);
+
+ TimeDelta GetProcessInterval() const override;
+ void Process(Timestamp at_time) override;
+
+ void OnRequest(int sequence_number, Timestamp at_time) override;
+ void OnResponse(int sequence_number, Timestamp at_time) override;
+
+ void HandleLoss(Timestamp at_time);
+
+ void SendPackets(Timestamp at_time);
+
+ private:
+ const FakeTcpConfig conf_;
+ TwoWayFakeTrafficRoute<int, int> route_;
+
+ std::map<int, Timestamp> in_flight_;
+ double cwnd_ = 10;
+ double ssthresh_ = INFINITY;
+ bool ack_received_ = false;
+ int last_acked_seq_num_ = 0;
+ int next_sequence_number_ = 0;
+ Timestamp last_reduction_time_ = Timestamp::MinusInfinity();
+ TimeDelta last_rtt_ = TimeDelta::Zero();
+ DataSize total_sent_ = DataSize::Zero();
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // TEST_NETWORK_CROSS_TRAFFIC_H_
diff --git a/third_party/libwebrtc/test/network/cross_traffic_unittest.cc b/third_party/libwebrtc/test/network/cross_traffic_unittest.cc
new file mode 100644
index 0000000000..0c1bb46302
--- /dev/null
+++ b/third_party/libwebrtc/test/network/cross_traffic_unittest.cc
@@ -0,0 +1,161 @@
+/*
+ * Copyright 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/cross_traffic.h"
+
+#include <atomic>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "absl/memory/memory.h"
+#include "absl/types/optional.h"
+#include "api/test/network_emulation_manager.h"
+#include "api/test/simulated_network.h"
+#include "call/simulated_network.h"
+#include "rtc_base/event.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/network_constants.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+#include "test/network/network_emulation_manager.h"
+#include "test/network/traffic_route.h"
+#include "test/time_controller/simulated_time_controller.h"
+
+namespace webrtc {
+namespace test {
+namespace {
+
+constexpr uint32_t kTestIpAddress = 0xC0A80011; // 192.168.0.17
+
+class CountingReceiver : public EmulatedNetworkReceiverInterface {
+ public:
+ void OnPacketReceived(EmulatedIpPacket packet) override {
+ packets_count_++;
+ total_packets_size_ += packet.size();
+ }
+
+ std::atomic<int> packets_count_{0};
+ std::atomic<uint64_t> total_packets_size_{0};
+};
+struct TrafficCounterFixture {
+ SimulatedClock clock{0};
+ CountingReceiver counter;
+ TaskQueueForTest task_queue_;
+ EmulatedEndpointImpl endpoint{EmulatedEndpointImpl::Options{
+ /*id=*/1,
+ rtc::IPAddress(kTestIpAddress),
+ EmulatedEndpointConfig(),
+ },
+ /*is_enabled=*/true, &task_queue_, &clock};
+};
+
+} // namespace
+
+TEST(CrossTrafficTest, TriggerPacketBurst) {
+ TrafficCounterFixture fixture;
+ CrossTrafficRouteImpl traffic(&fixture.clock, &fixture.counter,
+ &fixture.endpoint);
+ traffic.TriggerPacketBurst(100, 1000);
+
+ EXPECT_EQ(fixture.counter.packets_count_, 100);
+ EXPECT_EQ(fixture.counter.total_packets_size_, 100 * 1000ul);
+}
+
+TEST(CrossTrafficTest, PulsedPeaksCrossTraffic) {
+ TrafficCounterFixture fixture;
+ CrossTrafficRouteImpl traffic(&fixture.clock, &fixture.counter,
+ &fixture.endpoint);
+
+ PulsedPeaksConfig config;
+ config.peak_rate = DataRate::KilobitsPerSec(1000);
+ config.min_packet_size = DataSize::Bytes(1);
+ config.min_packet_interval = TimeDelta::Millis(25);
+ config.send_duration = TimeDelta::Millis(500);
+ config.hold_duration = TimeDelta::Millis(250);
+ PulsedPeaksCrossTraffic pulsed_peaks(config, &traffic);
+ const auto kRunTime = TimeDelta::Seconds(1);
+ while (fixture.clock.TimeInMilliseconds() < kRunTime.ms()) {
+ pulsed_peaks.Process(Timestamp::Millis(fixture.clock.TimeInMilliseconds()));
+ fixture.clock.AdvanceTimeMilliseconds(1);
+ }
+
+ RTC_LOG(LS_INFO) << fixture.counter.packets_count_ << " packets; "
+ << fixture.counter.total_packets_size_ << " bytes";
+ // Using 50% duty cycle.
+ const auto kExpectedDataSent = kRunTime * config.peak_rate * 0.5;
+ EXPECT_NEAR(fixture.counter.total_packets_size_, kExpectedDataSent.bytes(),
+ kExpectedDataSent.bytes() * 0.1);
+}
+
+TEST(CrossTrafficTest, RandomWalkCrossTraffic) {
+ TrafficCounterFixture fixture;
+ CrossTrafficRouteImpl traffic(&fixture.clock, &fixture.counter,
+ &fixture.endpoint);
+
+ RandomWalkConfig config;
+ config.peak_rate = DataRate::KilobitsPerSec(1000);
+ config.min_packet_size = DataSize::Bytes(1);
+ config.min_packet_interval = TimeDelta::Millis(25);
+ config.update_interval = TimeDelta::Millis(500);
+ config.variance = 0.0;
+ config.bias = 1.0;
+
+ RandomWalkCrossTraffic random_walk(config, &traffic);
+ const auto kRunTime = TimeDelta::Seconds(1);
+ while (fixture.clock.TimeInMilliseconds() < kRunTime.ms()) {
+ random_walk.Process(Timestamp::Millis(fixture.clock.TimeInMilliseconds()));
+ fixture.clock.AdvanceTimeMilliseconds(1);
+ }
+
+ RTC_LOG(LS_INFO) << fixture.counter.packets_count_ << " packets; "
+ << fixture.counter.total_packets_size_ << " bytes";
+ // Sending at peak rate since bias = 1.
+ const auto kExpectedDataSent = kRunTime * config.peak_rate;
+ EXPECT_NEAR(fixture.counter.total_packets_size_, kExpectedDataSent.bytes(),
+ kExpectedDataSent.bytes() * 0.1);
+}
+
+TEST(TcpMessageRouteTest, DeliveredOnLossyNetwork) {
+ NetworkEmulationManagerImpl net(TimeMode::kSimulated);
+ BuiltInNetworkBehaviorConfig send;
+ // 800 kbps means that the 100 kB message would be delivered in ca 1 second
+ // under ideal conditions and no overhead.
+ send.link_capacity_kbps = 100 * 8;
+ send.loss_percent = 50;
+ send.queue_delay_ms = 100;
+ send.delay_standard_deviation_ms = 20;
+ send.allow_reordering = true;
+ auto ret = send;
+ ret.loss_percent = 10;
+
+ auto* tcp_route =
+ net.CreateTcpRoute(net.CreateRoute({net.CreateEmulatedNode(send)}),
+ net.CreateRoute({net.CreateEmulatedNode(ret)}));
+ int deliver_count = 0;
+ // 100 kB is more than what fits into a single packet.
+ constexpr size_t kMessageSize = 100000;
+
+ tcp_route->SendMessage(kMessageSize, [&] {
+ RTC_LOG(LS_INFO) << "Received at " << ToString(net.Now());
+ deliver_count++;
+ });
+
+ // If there was no loss, we would have delivered the message in ca 1 second,
+ // with 50% it should take much longer.
+ net.time_controller()->AdvanceTime(TimeDelta::Seconds(5));
+ ASSERT_EQ(deliver_count, 0);
+ // But given enough time the messsage will be delivered, but only once.
+ net.time_controller()->AdvanceTime(TimeDelta::Seconds(60));
+ EXPECT_EQ(deliver_count, 1);
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/third_party/libwebrtc/test/network/emulated_network_manager.cc b/third_party/libwebrtc/test/network/emulated_network_manager.cc
new file mode 100644
index 0000000000..5bc3c094cb
--- /dev/null
+++ b/third_party/libwebrtc/test/network/emulated_network_manager.cc
@@ -0,0 +1,123 @@
+/*
+ * 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/emulated_network_manager.h"
+
+#include <memory>
+#include <utility>
+
+#include "absl/memory/memory.h"
+#include "p2p/base/basic_packet_socket_factory.h"
+#include "test/network/fake_network_socket_server.h"
+
+namespace webrtc {
+namespace test {
+
+EmulatedNetworkManager::EmulatedNetworkManager(
+ TimeController* time_controller,
+ TaskQueueForTest* task_queue,
+ EndpointsContainer* endpoints_container)
+ : task_queue_(task_queue),
+ endpoints_container_(endpoints_container),
+ sent_first_update_(false),
+ start_count_(0) {
+ auto socket_server =
+ std::make_unique<FakeNetworkSocketServer>(endpoints_container);
+ packet_socket_factory_ =
+ std::make_unique<rtc::BasicPacketSocketFactory>(socket_server.get());
+ // Since we pass ownership of the socket server to `network_thread_`, we must
+ // arrange that it outlives `packet_socket_factory_` which refers to it.
+ network_thread_ =
+ time_controller->CreateThread("net_thread", std::move(socket_server));
+}
+
+void EmulatedNetworkManager::EnableEndpoint(EmulatedEndpointImpl* endpoint) {
+ RTC_CHECK(endpoints_container_->HasEndpoint(endpoint))
+ << "No such interface: " << endpoint->GetPeerLocalAddress().ToString();
+ network_thread_->PostTask([this, endpoint]() {
+ endpoint->Enable();
+ UpdateNetworksOnce();
+ });
+}
+
+void EmulatedNetworkManager::DisableEndpoint(EmulatedEndpointImpl* endpoint) {
+ RTC_CHECK(endpoints_container_->HasEndpoint(endpoint))
+ << "No such interface: " << endpoint->GetPeerLocalAddress().ToString();
+ network_thread_->PostTask([this, endpoint]() {
+ endpoint->Disable();
+ UpdateNetworksOnce();
+ });
+}
+
+// Network manager interface. All these methods are supposed to be called from
+// the same thread.
+void EmulatedNetworkManager::StartUpdating() {
+ RTC_DCHECK_RUN_ON(network_thread_.get());
+
+ if (start_count_) {
+ // If network interfaces are already discovered and signal is sent,
+ // we should trigger network signal immediately for the new clients
+ // to start allocating ports.
+ if (sent_first_update_)
+ network_thread_->PostTask([this]() { MaybeSignalNetworksChanged(); });
+ } else {
+ network_thread_->PostTask([this]() { UpdateNetworksOnce(); });
+ }
+ ++start_count_;
+}
+
+void EmulatedNetworkManager::StopUpdating() {
+ RTC_DCHECK_RUN_ON(network_thread_.get());
+ if (!start_count_)
+ return;
+
+ --start_count_;
+ if (!start_count_) {
+ sent_first_update_ = false;
+ }
+}
+
+void EmulatedNetworkManager::GetStats(
+ std::function<void(std::unique_ptr<EmulatedNetworkStats>)> stats_callback)
+ const {
+ task_queue_->PostTask([stats_callback, this]() {
+ stats_callback(endpoints_container_->GetStats());
+ });
+}
+
+void EmulatedNetworkManager::UpdateNetworksOnce() {
+ RTC_DCHECK_RUN_ON(network_thread_.get());
+
+ std::vector<std::unique_ptr<rtc::Network>> networks;
+ for (std::unique_ptr<rtc::Network>& net :
+ endpoints_container_->GetEnabledNetworks()) {
+ net->set_default_local_address_provider(this);
+ networks.push_back(std::move(net));
+ }
+
+ bool changed;
+ MergeNetworkList(std::move(networks), &changed);
+ if (changed || !sent_first_update_) {
+ MaybeSignalNetworksChanged();
+ sent_first_update_ = true;
+ }
+}
+
+void EmulatedNetworkManager::MaybeSignalNetworksChanged() {
+ RTC_DCHECK_RUN_ON(network_thread_.get());
+ // If manager is stopped we don't need to signal anything.
+ if (start_count_ == 0) {
+ return;
+ }
+ SignalNetworksChanged();
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/third_party/libwebrtc/test/network/emulated_network_manager.h b/third_party/libwebrtc/test/network/emulated_network_manager.h
new file mode 100644
index 0000000000..29d2b0943f
--- /dev/null
+++ b/third_party/libwebrtc/test/network/emulated_network_manager.h
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#ifndef TEST_NETWORK_EMULATED_NETWORK_MANAGER_H_
+#define TEST_NETWORK_EMULATED_NETWORK_MANAGER_H_
+
+#include <functional>
+#include <memory>
+#include <vector>
+
+#include "api/sequence_checker.h"
+#include "api/test/network_emulation_manager.h"
+#include "api/test/time_controller.h"
+#include "rtc_base/ip_address.h"
+#include "rtc_base/network.h"
+#include "rtc_base/socket_server.h"
+#include "rtc_base/thread.h"
+#include "test/network/network_emulation.h"
+
+namespace webrtc {
+namespace test {
+
+// Framework assumes that rtc::NetworkManager is called from network thread.
+class EmulatedNetworkManager : public rtc::NetworkManagerBase,
+ public sigslot::has_slots<>,
+ public EmulatedNetworkManagerInterface {
+ public:
+ EmulatedNetworkManager(TimeController* time_controller,
+ TaskQueueForTest* task_queue,
+ EndpointsContainer* endpoints_container);
+
+ void EnableEndpoint(EmulatedEndpointImpl* endpoint);
+ void DisableEndpoint(EmulatedEndpointImpl* endpoint);
+
+ // NetworkManager interface. All these methods are supposed to be called from
+ // the same thread.
+ void StartUpdating() override;
+ void StopUpdating() override;
+
+ // We don't support any address interfaces in the network emulation framework.
+ std::vector<const rtc::Network*> GetAnyAddressNetworks() override {
+ return {};
+ }
+
+ // EmulatedNetworkManagerInterface API
+ rtc::Thread* network_thread() override { return network_thread_.get(); }
+ rtc::NetworkManager* network_manager() override { return this; }
+ rtc::PacketSocketFactory* packet_socket_factory() override {
+ return packet_socket_factory_.get();
+ }
+ std::vector<EmulatedEndpoint*> endpoints() const override {
+ return endpoints_container_->GetEndpoints();
+ }
+ void GetStats(std::function<void(std::unique_ptr<EmulatedNetworkStats>)>
+ stats_callback) const override;
+
+ private:
+ void UpdateNetworksOnce();
+ void MaybeSignalNetworksChanged();
+
+ TaskQueueForTest* const task_queue_;
+ const EndpointsContainer* const endpoints_container_;
+ // The `network_thread_` must outlive `packet_socket_factory_`, because they
+ // both refer to a socket server that is owned by `network_thread_`. Both
+ // pointers are assigned only in the constructor, but the way they are
+ // initialized unfortunately doesn't work with const std::unique_ptr<...>.
+ std::unique_ptr<rtc::Thread> network_thread_;
+ std::unique_ptr<rtc::PacketSocketFactory> packet_socket_factory_;
+ bool sent_first_update_ RTC_GUARDED_BY(network_thread_);
+ int start_count_ RTC_GUARDED_BY(network_thread_);
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // TEST_NETWORK_EMULATED_NETWORK_MANAGER_H_
diff --git a/third_party/libwebrtc/test/network/emulated_turn_server.cc b/third_party/libwebrtc/test/network/emulated_turn_server.cc
new file mode 100644
index 0000000000..0bc7ec6e2a
--- /dev/null
+++ b/third_party/libwebrtc/test/network/emulated_turn_server.cc
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2020 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/emulated_turn_server.h"
+
+#include <string>
+#include <utility>
+
+#include "api/packet_socket_factory.h"
+#include "rtc_base/strings/string_builder.h"
+#include "rtc_base/task_queue_for_test.h"
+
+namespace {
+
+static const char kTestRealm[] = "example.org";
+static const char kTestSoftware[] = "TestTurnServer";
+
+// A wrapper class for copying data between an AsyncPacketSocket and a
+// EmulatedEndpoint. This is used by the cricket::TurnServer when
+// sending data back into the emulated network.
+class AsyncPacketSocketWrapper : public rtc::AsyncPacketSocket {
+ public:
+ AsyncPacketSocketWrapper(webrtc::test::EmulatedTURNServer* turn_server,
+ webrtc::EmulatedEndpoint* endpoint,
+ uint16_t port)
+ : turn_server_(turn_server),
+ endpoint_(endpoint),
+ local_address_(
+ rtc::SocketAddress(endpoint_->GetPeerLocalAddress(), port)) {}
+ ~AsyncPacketSocketWrapper() { turn_server_->Unbind(local_address_); }
+
+ rtc::SocketAddress GetLocalAddress() const override { return local_address_; }
+ rtc::SocketAddress GetRemoteAddress() const override {
+ return rtc::SocketAddress();
+ }
+ int Send(const void* pv,
+ size_t cb,
+ const rtc::PacketOptions& options) override {
+ RTC_CHECK(false) << "TCP not implemented";
+ return -1;
+ }
+ int SendTo(const void* pv,
+ size_t cb,
+ const rtc::SocketAddress& addr,
+ const rtc::PacketOptions& options) override {
+ // Copy from rtc::AsyncPacketSocket to EmulatedEndpoint.
+ rtc::CopyOnWriteBuffer buf(reinterpret_cast<const char*>(pv), cb);
+ endpoint_->SendPacket(local_address_, addr, buf);
+ return cb;
+ }
+ int Close() override { return 0; }
+
+ rtc::AsyncPacketSocket::State GetState() const override {
+ return rtc::AsyncPacketSocket::STATE_BOUND;
+ }
+ int GetOption(rtc::Socket::Option opt, int* value) override { return 0; }
+ int SetOption(rtc::Socket::Option opt, int value) override { return 0; }
+ int GetError() const override { return 0; }
+ void SetError(int error) override {}
+
+ private:
+ webrtc::test::EmulatedTURNServer* const turn_server_;
+ webrtc::EmulatedEndpoint* const endpoint_;
+ const rtc::SocketAddress local_address_;
+};
+
+// A wrapper class for cricket::TurnServer to allocate sockets.
+class PacketSocketFactoryWrapper : public rtc::PacketSocketFactory {
+ public:
+ explicit PacketSocketFactoryWrapper(
+ webrtc::test::EmulatedTURNServer* turn_server)
+ : turn_server_(turn_server) {}
+ ~PacketSocketFactoryWrapper() override {}
+
+ // This method is called from TurnServer when making a TURN ALLOCATION.
+ // It will create a socket on the `peer_` endpoint.
+ rtc::AsyncPacketSocket* CreateUdpSocket(const rtc::SocketAddress& address,
+ uint16_t min_port,
+ uint16_t max_port) override {
+ return turn_server_->CreatePeerSocket();
+ }
+
+ rtc::AsyncListenSocket* CreateServerTcpSocket(
+ const rtc::SocketAddress& local_address,
+ uint16_t min_port,
+ uint16_t max_port,
+ int opts) override {
+ return nullptr;
+ }
+ rtc::AsyncPacketSocket* CreateClientTcpSocket(
+ const rtc::SocketAddress& local_address,
+ const rtc::SocketAddress& remote_address,
+ const rtc::ProxyInfo& proxy_info,
+ const std::string& user_agent,
+ const rtc::PacketSocketTcpOptions& tcp_options) override {
+ return nullptr;
+ }
+ std::unique_ptr<webrtc::AsyncDnsResolverInterface> CreateAsyncDnsResolver()
+ override {
+ return nullptr;
+ }
+
+ private:
+ webrtc::test::EmulatedTURNServer* turn_server_;
+};
+
+} // namespace
+
+namespace webrtc {
+namespace test {
+
+EmulatedTURNServer::EmulatedTURNServer(std::unique_ptr<rtc::Thread> thread,
+ EmulatedEndpoint* client,
+ EmulatedEndpoint* peer)
+ : thread_(std::move(thread)), client_(client), peer_(peer) {
+ ice_config_.username = "keso";
+ ice_config_.password = "keso";
+ SendTask(thread_.get(), [=]() {
+ RTC_DCHECK_RUN_ON(thread_.get());
+ turn_server_ = std::make_unique<cricket::TurnServer>(thread_.get());
+ turn_server_->set_realm(kTestRealm);
+ turn_server_->set_realm(kTestSoftware);
+ turn_server_->set_auth_hook(this);
+
+ auto client_socket = Wrap(client_);
+ turn_server_->AddInternalSocket(client_socket, cricket::PROTO_UDP);
+ turn_server_->SetExternalSocketFactory(new PacketSocketFactoryWrapper(this),
+ rtc::SocketAddress());
+ client_address_ = client_socket->GetLocalAddress();
+ char buf[256];
+ rtc::SimpleStringBuilder str(buf);
+ str.AppendFormat("turn:%s?transport=udp",
+ client_address_.ToString().c_str());
+ ice_config_.url = str.str();
+ });
+}
+
+void EmulatedTURNServer::Stop() {
+ SendTask(thread_.get(), [=]() {
+ RTC_DCHECK_RUN_ON(thread_.get());
+ sockets_.clear();
+ });
+}
+
+EmulatedTURNServer::~EmulatedTURNServer() {
+ SendTask(thread_.get(), [=]() {
+ RTC_DCHECK_RUN_ON(thread_.get());
+ turn_server_.reset(nullptr);
+ });
+}
+
+rtc::AsyncPacketSocket* EmulatedTURNServer::Wrap(EmulatedEndpoint* endpoint) {
+ RTC_DCHECK_RUN_ON(thread_.get());
+ auto port = endpoint->BindReceiver(0, this).value();
+ auto socket = new AsyncPacketSocketWrapper(this, endpoint, port);
+ sockets_[rtc::SocketAddress(endpoint->GetPeerLocalAddress(), port)] = socket;
+ return socket;
+}
+
+void EmulatedTURNServer::OnPacketReceived(webrtc::EmulatedIpPacket packet) {
+ // Copy from EmulatedEndpoint to rtc::AsyncPacketSocket.
+ thread_->PostTask([this, packet(std::move(packet))]() {
+ RTC_DCHECK_RUN_ON(thread_.get());
+ auto it = sockets_.find(packet.to);
+ if (it != sockets_.end()) {
+ it->second->SignalReadPacket(
+ it->second, reinterpret_cast<const char*>(packet.cdata()),
+ packet.size(), packet.from, packet.arrival_time.ms());
+ }
+ });
+}
+
+void EmulatedTURNServer::Unbind(rtc::SocketAddress address) {
+ RTC_DCHECK_RUN_ON(thread_.get());
+ if (GetClientEndpoint()->GetPeerLocalAddress() == address.ipaddr()) {
+ GetClientEndpoint()->UnbindReceiver(address.port());
+ } else {
+ GetPeerEndpoint()->UnbindReceiver(address.port());
+ }
+ sockets_.erase(address);
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/third_party/libwebrtc/test/network/emulated_turn_server.h b/third_party/libwebrtc/test/network/emulated_turn_server.h
new file mode 100644
index 0000000000..9cb0ceabf6
--- /dev/null
+++ b/third_party/libwebrtc/test/network/emulated_turn_server.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2020 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.
+ */
+
+#ifndef TEST_NETWORK_EMULATED_TURN_SERVER_H_
+#define TEST_NETWORK_EMULATED_TURN_SERVER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "api/test/network_emulation_manager.h"
+#include "api/transport/stun.h"
+#include "p2p/base/turn_server.h"
+#include "rtc_base/async_packet_socket.h"
+
+namespace webrtc {
+namespace test {
+
+// EmulatedTURNServer wraps cricket::TurnServer to be used inside
+// a emulated network.
+//
+// Packets from EmulatedEndpoint (client or peer) are received in
+// EmulatedTURNServer::OnPacketReceived which performs a map lookup
+// and delivers them into cricket::TurnServer using
+// AsyncPacketSocket::SignalReadPacket
+//
+// Packets from cricket::TurnServer to EmulatedEndpoint are sent into
+// using a wrapper around AsyncPacketSocket (no lookup required as the
+// wrapper around AsyncPacketSocket keep a pointer to the EmulatedEndpoint).
+class EmulatedTURNServer : public EmulatedTURNServerInterface,
+ public cricket::TurnAuthInterface,
+ public webrtc::EmulatedNetworkReceiverInterface {
+ public:
+ // Create an EmulatedTURNServer.
+ // `thread` is a thread that will be used to run cricket::TurnServer
+ // that expects all calls to be made from a single thread.
+ EmulatedTURNServer(std::unique_ptr<rtc::Thread> thread,
+ EmulatedEndpoint* client,
+ EmulatedEndpoint* peer);
+ ~EmulatedTURNServer() override;
+
+ IceServerConfig GetIceServerConfig() const override { return ice_config_; }
+
+ EmulatedEndpoint* GetClientEndpoint() const override { return client_; }
+
+ rtc::SocketAddress GetClientEndpointAddress() const override {
+ return client_address_;
+ }
+
+ EmulatedEndpoint* GetPeerEndpoint() const override { return peer_; }
+
+ // cricket::TurnAuthInterface
+ bool GetKey(absl::string_view username,
+ absl::string_view realm,
+ std::string* key) override {
+ return cricket::ComputeStunCredentialHash(
+ std::string(username), std::string(realm), std::string(username), key);
+ }
+
+ rtc::AsyncPacketSocket* CreatePeerSocket() { return Wrap(peer_); }
+
+ // This method is called by network emulation when a packet
+ // comes from an emulated link.
+ void OnPacketReceived(webrtc::EmulatedIpPacket packet) override;
+
+ // This is called when the TURN server deletes a socket.
+ void Unbind(rtc::SocketAddress address);
+
+ // Unbind all sockets.
+ void Stop();
+
+ private:
+ std::unique_ptr<rtc::Thread> thread_;
+ rtc::SocketAddress client_address_;
+ IceServerConfig ice_config_;
+ EmulatedEndpoint* const client_;
+ EmulatedEndpoint* const peer_;
+ std::unique_ptr<cricket::TurnServer> turn_server_ RTC_GUARDED_BY(&thread_);
+ std::map<rtc::SocketAddress, rtc::AsyncPacketSocket*> sockets_
+ RTC_GUARDED_BY(&thread_);
+
+ // Wraps a EmulatedEndpoint in a AsyncPacketSocket to bridge interaction
+ // with TurnServer. cricket::TurnServer gets ownership of the socket.
+ rtc::AsyncPacketSocket* Wrap(EmulatedEndpoint* endpoint);
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // TEST_NETWORK_EMULATED_TURN_SERVER_H_
diff --git a/third_party/libwebrtc/test/network/fake_network_socket_server.cc b/third_party/libwebrtc/test/network/fake_network_socket_server.cc
new file mode 100644
index 0000000000..d4b2680fa9
--- /dev/null
+++ b/third_party/libwebrtc/test/network/fake_network_socket_server.cc
@@ -0,0 +1,319 @@
+/*
+ * 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/fake_network_socket_server.h"
+
+#include <algorithm>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "absl/algorithm/container.h"
+#include "api/scoped_refptr.h"
+#include "api/task_queue/pending_task_safety_flag.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/thread.h"
+
+namespace webrtc {
+namespace test {
+namespace {
+std::string ToString(const rtc::SocketAddress& addr) {
+ return addr.HostAsURIString() + ":" + std::to_string(addr.port());
+}
+
+} // namespace
+
+// Represents a socket, which will operate with emulated network.
+class FakeNetworkSocket : public rtc::Socket,
+ public EmulatedNetworkReceiverInterface {
+ public:
+ explicit FakeNetworkSocket(FakeNetworkSocketServer* scoket_manager,
+ rtc::Thread* thread);
+ ~FakeNetworkSocket() override;
+
+ // Will be invoked by EmulatedEndpoint to deliver packets into this socket.
+ void OnPacketReceived(EmulatedIpPacket packet) override;
+
+ // rtc::Socket methods:
+ rtc::SocketAddress GetLocalAddress() const override;
+ rtc::SocketAddress GetRemoteAddress() const override;
+ int Bind(const rtc::SocketAddress& addr) override;
+ int Connect(const rtc::SocketAddress& addr) override;
+ int Close() override;
+ int Send(const void* pv, size_t cb) override;
+ int SendTo(const void* pv,
+ size_t cb,
+ const rtc::SocketAddress& addr) override;
+ int Recv(void* pv, size_t cb, int64_t* timestamp) override;
+ int RecvFrom(void* pv,
+ size_t cb,
+ rtc::SocketAddress* paddr,
+ int64_t* timestamp) override;
+ int Listen(int backlog) override;
+ rtc::Socket* Accept(rtc::SocketAddress* paddr) override;
+ int GetError() const override;
+ void SetError(int error) override;
+ ConnState GetState() const override;
+ int GetOption(Option opt, int* value) override;
+ int SetOption(Option opt, int value) override;
+
+ private:
+ FakeNetworkSocketServer* const socket_server_;
+ rtc::Thread* const thread_;
+ EmulatedEndpointImpl* endpoint_ RTC_GUARDED_BY(&thread_);
+ rtc::SocketAddress local_addr_ RTC_GUARDED_BY(&thread_);
+ rtc::SocketAddress remote_addr_ RTC_GUARDED_BY(&thread_);
+ ConnState state_ RTC_GUARDED_BY(&thread_);
+ int error_ RTC_GUARDED_BY(&thread_);
+ std::map<Option, int> options_map_ RTC_GUARDED_BY(&thread_);
+
+ absl::optional<EmulatedIpPacket> pending_ RTC_GUARDED_BY(thread_);
+ rtc::scoped_refptr<PendingTaskSafetyFlag> alive_;
+};
+
+FakeNetworkSocket::FakeNetworkSocket(FakeNetworkSocketServer* socket_server,
+ rtc::Thread* thread)
+ : socket_server_(socket_server),
+ thread_(thread),
+ state_(CS_CLOSED),
+ error_(0),
+ alive_(PendingTaskSafetyFlag::Create()) {}
+
+FakeNetworkSocket::~FakeNetworkSocket() {
+ // Abandon all pending packets.
+ alive_->SetNotAlive();
+
+ Close();
+ socket_server_->Unregister(this);
+}
+
+void FakeNetworkSocket::OnPacketReceived(EmulatedIpPacket packet) {
+ auto task = [this, packet = std::move(packet)]() mutable {
+ RTC_DCHECK_RUN_ON(thread_);
+ if (!endpoint_->Enabled())
+ return;
+ RTC_DCHECK(!pending_);
+ pending_ = std::move(packet);
+ // Note that we expect that this will trigger exactly one call to RecvFrom()
+ // where pending_packet will be read and reset. This call is done without
+ // any thread switch (see AsyncUDPSocket::OnReadEvent) so it's safe to
+ // assume that SignalReadEvent() will block until the packet has been read.
+ SignalReadEvent(this);
+ RTC_DCHECK(!pending_);
+ };
+ thread_->PostTask(SafeTask(alive_, std::move(task)));
+ socket_server_->WakeUp();
+}
+
+
+rtc::SocketAddress FakeNetworkSocket::GetLocalAddress() const {
+ RTC_DCHECK_RUN_ON(thread_);
+ return local_addr_;
+}
+
+rtc::SocketAddress FakeNetworkSocket::GetRemoteAddress() const {
+ RTC_DCHECK_RUN_ON(thread_);
+ return remote_addr_;
+}
+
+int FakeNetworkSocket::Bind(const rtc::SocketAddress& addr) {
+ RTC_DCHECK_RUN_ON(thread_);
+ RTC_CHECK(local_addr_.IsNil())
+ << "Socket already bound to address: " << ToString(local_addr_);
+ local_addr_ = addr;
+ endpoint_ = socket_server_->GetEndpointNode(local_addr_.ipaddr());
+ if (!endpoint_) {
+ local_addr_.Clear();
+ RTC_LOG(LS_INFO) << "No endpoint for address: " << ToString(addr);
+ error_ = EADDRNOTAVAIL;
+ return 2;
+ }
+ absl::optional<uint16_t> port =
+ endpoint_->BindReceiver(local_addr_.port(), this);
+ if (!port) {
+ local_addr_.Clear();
+ RTC_LOG(LS_INFO) << "Cannot bind to in-use address: " << ToString(addr);
+ error_ = EADDRINUSE;
+ return 1;
+ }
+ local_addr_.SetPort(port.value());
+ return 0;
+}
+
+int FakeNetworkSocket::Connect(const rtc::SocketAddress& addr) {
+ RTC_DCHECK_RUN_ON(thread_);
+ RTC_CHECK(remote_addr_.IsNil())
+ << "Socket already connected to address: " << ToString(remote_addr_);
+ RTC_CHECK(!local_addr_.IsNil())
+ << "Socket have to be bind to some local address";
+ remote_addr_ = addr;
+ state_ = CS_CONNECTED;
+ return 0;
+}
+
+int FakeNetworkSocket::Send(const void* pv, size_t cb) {
+ RTC_DCHECK_RUN_ON(thread_);
+ RTC_CHECK(state_ == CS_CONNECTED) << "Socket cannot send: not connected";
+ return SendTo(pv, cb, remote_addr_);
+}
+
+int FakeNetworkSocket::SendTo(const void* pv,
+ size_t cb,
+ const rtc::SocketAddress& addr) {
+ RTC_DCHECK_RUN_ON(thread_);
+ RTC_CHECK(!local_addr_.IsNil())
+ << "Socket have to be bind to some local address";
+ if (!endpoint_->Enabled()) {
+ error_ = ENETDOWN;
+ return -1;
+ }
+ rtc::CopyOnWriteBuffer packet(static_cast<const uint8_t*>(pv), cb);
+ endpoint_->SendPacket(local_addr_, addr, packet);
+ return cb;
+}
+
+int FakeNetworkSocket::Recv(void* pv, size_t cb, int64_t* timestamp) {
+ rtc::SocketAddress paddr;
+ return RecvFrom(pv, cb, &paddr, timestamp);
+}
+
+// Reads 1 packet from internal queue. Reads up to `cb` bytes into `pv`
+// and returns the length of received packet.
+int FakeNetworkSocket::RecvFrom(void* pv,
+ size_t cb,
+ rtc::SocketAddress* paddr,
+ int64_t* timestamp) {
+ RTC_DCHECK_RUN_ON(thread_);
+
+ if (timestamp) {
+ *timestamp = -1;
+ }
+ RTC_CHECK(pending_);
+
+ *paddr = pending_->from;
+ size_t data_read = std::min(cb, pending_->size());
+ memcpy(pv, pending_->cdata(), data_read);
+ *timestamp = pending_->arrival_time.us();
+
+ // According to RECV(2) Linux Man page
+ // real socket will discard data, that won't fit into provided buffer,
+ // but we won't to skip such error, so we will assert here.
+ RTC_CHECK(data_read == pending_->size())
+ << "Too small buffer is provided for socket read. "
+ "Received data size: "
+ << pending_->size() << "; Provided buffer size: " << cb;
+
+ pending_.reset();
+
+ // According to RECV(2) Linux Man page
+ // real socket will return message length, not data read. In our case it is
+ // actually the same value.
+ return static_cast<int>(data_read);
+}
+
+int FakeNetworkSocket::Listen(int backlog) {
+ RTC_CHECK(false) << "Listen() isn't valid for SOCK_DGRAM";
+}
+
+rtc::Socket* FakeNetworkSocket::Accept(rtc::SocketAddress* /*paddr*/) {
+ RTC_CHECK(false) << "Accept() isn't valid for SOCK_DGRAM";
+}
+
+int FakeNetworkSocket::Close() {
+ RTC_DCHECK_RUN_ON(thread_);
+ state_ = CS_CLOSED;
+ if (!local_addr_.IsNil()) {
+ endpoint_->UnbindReceiver(local_addr_.port());
+ }
+ local_addr_.Clear();
+ remote_addr_.Clear();
+ return 0;
+}
+
+int FakeNetworkSocket::GetError() const {
+ RTC_DCHECK_RUN_ON(thread_);
+ return error_;
+}
+
+void FakeNetworkSocket::SetError(int error) {
+ RTC_DCHECK_RUN_ON(thread_);
+ RTC_CHECK(error == 0);
+ error_ = error;
+}
+
+rtc::Socket::ConnState FakeNetworkSocket::GetState() const {
+ RTC_DCHECK_RUN_ON(thread_);
+ return state_;
+}
+
+int FakeNetworkSocket::GetOption(Option opt, int* value) {
+ RTC_DCHECK_RUN_ON(thread_);
+ auto it = options_map_.find(opt);
+ if (it == options_map_.end()) {
+ return -1;
+ }
+ *value = it->second;
+ return 0;
+}
+
+int FakeNetworkSocket::SetOption(Option opt, int value) {
+ RTC_DCHECK_RUN_ON(thread_);
+ options_map_[opt] = value;
+ return 0;
+}
+
+FakeNetworkSocketServer::FakeNetworkSocketServer(
+ EndpointsContainer* endpoints_container)
+ : endpoints_container_(endpoints_container),
+ wakeup_(/*manual_reset=*/false, /*initially_signaled=*/false) {}
+FakeNetworkSocketServer::~FakeNetworkSocketServer() = default;
+
+EmulatedEndpointImpl* FakeNetworkSocketServer::GetEndpointNode(
+ const rtc::IPAddress& ip) {
+ return endpoints_container_->LookupByLocalAddress(ip);
+}
+
+void FakeNetworkSocketServer::Unregister(FakeNetworkSocket* socket) {
+ MutexLock lock(&lock_);
+ sockets_.erase(absl::c_find(sockets_, socket));
+}
+
+rtc::Socket* FakeNetworkSocketServer::CreateSocket(int family, int type) {
+ RTC_DCHECK(family == AF_INET || family == AF_INET6);
+ // We support only UDP sockets for now.
+ RTC_DCHECK(type == SOCK_DGRAM) << "Only UDP sockets are supported";
+ RTC_DCHECK(thread_) << "must be attached to thread before creating sockets";
+ FakeNetworkSocket* out = new FakeNetworkSocket(this, thread_);
+ {
+ MutexLock lock(&lock_);
+ sockets_.push_back(out);
+ }
+ return out;
+}
+
+void FakeNetworkSocketServer::SetMessageQueue(rtc::Thread* thread) {
+ thread_ = thread;
+}
+
+// Always returns true (if return false, it won't be invoked again...)
+bool FakeNetworkSocketServer::Wait(int cms, bool process_io) {
+ RTC_DCHECK(thread_ == rtc::Thread::Current());
+ if (cms != 0)
+ wakeup_.Wait(cms);
+ return true;
+}
+
+void FakeNetworkSocketServer::WakeUp() {
+ wakeup_.Set();
+}
+
+
+} // namespace test
+} // namespace webrtc
diff --git a/third_party/libwebrtc/test/network/fake_network_socket_server.h b/third_party/libwebrtc/test/network/fake_network_socket_server.h
new file mode 100644
index 0000000000..5b23a01eee
--- /dev/null
+++ b/third_party/libwebrtc/test/network/fake_network_socket_server.h
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+#ifndef TEST_NETWORK_FAKE_NETWORK_SOCKET_SERVER_H_
+#define TEST_NETWORK_FAKE_NETWORK_SOCKET_SERVER_H_
+
+#include <set>
+#include <vector>
+
+#include "api/units/timestamp.h"
+#include "rtc_base/event.h"
+#include "rtc_base/socket.h"
+#include "rtc_base/socket_server.h"
+#include "rtc_base/synchronization/mutex.h"
+#include "system_wrappers/include/clock.h"
+#include "test/network/network_emulation.h"
+
+namespace webrtc {
+namespace test {
+class FakeNetworkSocket;
+
+// FakeNetworkSocketServer must outlive any sockets it creates.
+class FakeNetworkSocketServer : public rtc::SocketServer {
+ public:
+ explicit FakeNetworkSocketServer(EndpointsContainer* endpoints_controller);
+ ~FakeNetworkSocketServer() override;
+
+
+ // rtc::SocketFactory methods:
+ rtc::Socket* CreateSocket(int family, int type) override;
+
+ // rtc::SocketServer methods:
+ // Called by the network thread when this server is installed, kicking off the
+ // message handler loop.
+ void SetMessageQueue(rtc::Thread* thread) override;
+ bool Wait(int cms, bool process_io) override;
+ void WakeUp() override;
+
+ protected:
+ friend class FakeNetworkSocket;
+ EmulatedEndpointImpl* GetEndpointNode(const rtc::IPAddress& ip);
+ void Unregister(FakeNetworkSocket* socket);
+
+ private:
+ const EndpointsContainer* endpoints_container_;
+ rtc::Event wakeup_;
+ rtc::Thread* thread_ = nullptr;
+
+ Mutex lock_;
+ std::vector<FakeNetworkSocket*> sockets_ RTC_GUARDED_BY(lock_);
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // TEST_NETWORK_FAKE_NETWORK_SOCKET_SERVER_H_
diff --git a/third_party/libwebrtc/test/network/feedback_generator.cc b/third_party/libwebrtc/test/network/feedback_generator.cc
new file mode 100644
index 0000000000..68fbcc02a5
--- /dev/null
+++ b/third_party/libwebrtc/test/network/feedback_generator.cc
@@ -0,0 +1,111 @@
+/*
+ * Copyright 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/feedback_generator.h"
+
+#include "absl/memory/memory.h"
+#include "api/transport/network_types.h"
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+FeedbackGeneratorImpl::FeedbackGeneratorImpl(
+ FeedbackGeneratorImpl::Config config)
+ : conf_(config),
+ net_(TimeMode::kSimulated),
+ send_link_{new SimulatedNetwork(conf_.send_link)},
+ ret_link_{new SimulatedNetwork(conf_.return_link)},
+ route_(this,
+ net_.CreateRoute(
+ {net_.CreateEmulatedNode(absl::WrapUnique(send_link_))}),
+ net_.CreateRoute(
+ {net_.CreateEmulatedNode(absl::WrapUnique(ret_link_))})) {}
+
+Timestamp FeedbackGeneratorImpl::Now() {
+ return net_.Now();
+}
+
+void FeedbackGeneratorImpl::Sleep(TimeDelta duration) {
+ net_.time_controller()->AdvanceTime(duration);
+}
+
+void FeedbackGeneratorImpl::SendPacket(size_t size) {
+ SentPacket sent;
+ sent.send_time = Now();
+ sent.size = DataSize::Bytes(size);
+ sent.sequence_number = sequence_number_++;
+ sent_packets_.push(sent);
+ route_.SendRequest(size, sent);
+}
+
+std::vector<TransportPacketsFeedback> FeedbackGeneratorImpl::PopFeedback() {
+ std::vector<TransportPacketsFeedback> ret;
+ ret.swap(feedback_);
+ return ret;
+}
+
+void FeedbackGeneratorImpl::SetSendConfig(BuiltInNetworkBehaviorConfig config) {
+ conf_.send_link = config;
+ send_link_->SetConfig(conf_.send_link);
+}
+
+void FeedbackGeneratorImpl::SetReturnConfig(
+ BuiltInNetworkBehaviorConfig config) {
+ conf_.return_link = config;
+ ret_link_->SetConfig(conf_.return_link);
+}
+
+void FeedbackGeneratorImpl::SetSendLinkCapacity(DataRate capacity) {
+ conf_.send_link.link_capacity_kbps = capacity.kbps<int>();
+ send_link_->SetConfig(conf_.send_link);
+}
+
+void FeedbackGeneratorImpl::OnRequest(SentPacket packet,
+ Timestamp arrival_time) {
+ PacketResult result;
+ result.sent_packet = packet;
+ result.receive_time = arrival_time;
+ received_packets_.push_back(result);
+ Timestamp first_recv = received_packets_.front().receive_time;
+ if (Now() - first_recv > conf_.feedback_interval) {
+ route_.SendResponse(conf_.feedback_packet_size.bytes<size_t>(),
+ std::move(received_packets_));
+ received_packets_ = {};
+ }
+}
+
+void FeedbackGeneratorImpl::OnResponse(std::vector<PacketResult> packet_results,
+ Timestamp arrival_time) {
+ TransportPacketsFeedback feedback;
+ feedback.feedback_time = arrival_time;
+ std::vector<PacketResult>::const_iterator received_packet_iterator =
+ packet_results.begin();
+ while (received_packet_iterator != packet_results.end()) {
+ RTC_DCHECK(!sent_packets_.empty() &&
+ sent_packets_.front().sequence_number <=
+ received_packet_iterator->sent_packet.sequence_number)
+ << "reordering not implemented";
+ if (sent_packets_.front().sequence_number <
+ received_packet_iterator->sent_packet.sequence_number) {
+ // Packet lost.
+ PacketResult lost;
+ lost.sent_packet = sent_packets_.front();
+ feedback.packet_feedbacks.push_back(lost);
+ }
+ if (sent_packets_.front().sequence_number ==
+ received_packet_iterator->sent_packet.sequence_number) {
+ feedback.packet_feedbacks.push_back(*received_packet_iterator);
+ ++received_packet_iterator;
+ }
+ sent_packets_.pop();
+ }
+ feedback_.push_back(feedback);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/test/network/feedback_generator.h b/third_party/libwebrtc/test/network/feedback_generator.h
new file mode 100644
index 0000000000..ecd4597d3f
--- /dev/null
+++ b/third_party/libwebrtc/test/network/feedback_generator.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 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.
+ */
+#ifndef TEST_NETWORK_FEEDBACK_GENERATOR_H_
+#define TEST_NETWORK_FEEDBACK_GENERATOR_H_
+
+#include <cstdint>
+#include <queue>
+#include <utility>
+#include <vector>
+
+#include "api/transport/network_types.h"
+#include "api/transport/test/feedback_generator_interface.h"
+#include "call/simulated_network.h"
+#include "test/network/network_emulation.h"
+#include "test/network/network_emulation_manager.h"
+#include "test/time_controller/simulated_time_controller.h"
+
+namespace webrtc {
+
+class FeedbackGeneratorImpl
+ : public FeedbackGenerator,
+ public TwoWayFakeTrafficRoute<SentPacket, std::vector<PacketResult>>::
+ TrafficHandlerInterface {
+ public:
+ explicit FeedbackGeneratorImpl(Config config);
+ Timestamp Now() override;
+ void Sleep(TimeDelta duration) override;
+ void SendPacket(size_t size) override;
+ std::vector<TransportPacketsFeedback> PopFeedback() override;
+
+ void SetSendConfig(BuiltInNetworkBehaviorConfig config) override;
+ void SetReturnConfig(BuiltInNetworkBehaviorConfig config) override;
+
+ void SetSendLinkCapacity(DataRate capacity) override;
+
+ void OnRequest(SentPacket packet, Timestamp arrival_time) override;
+ void OnResponse(std::vector<PacketResult> packet_results,
+ Timestamp arrival_time) override;
+
+ private:
+ Config conf_;
+ ::webrtc::test::NetworkEmulationManagerImpl net_;
+ SimulatedNetwork* const send_link_;
+ SimulatedNetwork* const ret_link_;
+ TwoWayFakeTrafficRoute<SentPacket, std::vector<PacketResult>> route_;
+
+ std::queue<SentPacket> sent_packets_;
+ std::vector<PacketResult> received_packets_;
+ std::vector<TransportPacketsFeedback> feedback_;
+ int64_t sequence_number_ = 1;
+};
+} // namespace webrtc
+#endif // TEST_NETWORK_FEEDBACK_GENERATOR_H_
diff --git a/third_party/libwebrtc/test/network/feedback_generator_unittest.cc b/third_party/libwebrtc/test/network/feedback_generator_unittest.cc
new file mode 100644
index 0000000000..9a577bea00
--- /dev/null
+++ b/third_party/libwebrtc/test/network/feedback_generator_unittest.cc
@@ -0,0 +1,47 @@
+/*
+ * Copyright 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 "api/transport/test/create_feedback_generator.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+TEST(FeedbackGeneratorTest, ReportsFeedbackForSentPackets) {
+ size_t kPacketSize = 1000;
+ auto gen = CreateFeedbackGenerator(FeedbackGenerator::Config());
+ for (int i = 0; i < 10; ++i) {
+ gen->SendPacket(kPacketSize);
+ gen->Sleep(TimeDelta::Millis(50));
+ }
+ auto feedback_list = gen->PopFeedback();
+ EXPECT_GT(feedback_list.size(), 0u);
+ for (const auto& feedback : feedback_list) {
+ EXPECT_GT(feedback.packet_feedbacks.size(), 0u);
+ for (const auto& packet : feedback.packet_feedbacks) {
+ EXPECT_EQ(packet.sent_packet.size.bytes<size_t>(), kPacketSize);
+ }
+ }
+}
+
+TEST(FeedbackGeneratorTest, FeedbackIncludesLostPackets) {
+ size_t kPacketSize = 1000;
+ auto gen = CreateFeedbackGenerator(FeedbackGenerator::Config());
+ BuiltInNetworkBehaviorConfig send_config_with_loss;
+ send_config_with_loss.loss_percent = 50;
+ gen->SetSendConfig(send_config_with_loss);
+ for (int i = 0; i < 20; ++i) {
+ gen->SendPacket(kPacketSize);
+ gen->Sleep(TimeDelta::Millis(5));
+ }
+ auto feedback_list = gen->PopFeedback();
+ ASSERT_GT(feedback_list.size(), 0u);
+ EXPECT_NEAR(feedback_list[0].LostWithSendInfo().size(),
+ feedback_list[0].ReceivedWithSendInfo().size(), 2);
+}
+} // namespace webrtc
diff --git a/third_party/libwebrtc/test/network/g3doc/g3doc.lua b/third_party/libwebrtc/test/network/g3doc/g3doc.lua
new file mode 100644
index 0000000000..981393c826
--- /dev/null
+++ b/third_party/libwebrtc/test/network/g3doc/g3doc.lua
@@ -0,0 +1,5 @@
+config = super()
+
+config.freshness.owner = 'titovartem'
+
+return config
diff --git a/third_party/libwebrtc/test/network/g3doc/index.md b/third_party/libwebrtc/test/network/g3doc/index.md
new file mode 100644
index 0000000000..5d511916c1
--- /dev/null
+++ b/third_party/libwebrtc/test/network/g3doc/index.md
@@ -0,0 +1,136 @@
+# Network Emulation Framework
+
+<?% config.freshness.reviewed = '2021-03-01' %?>
+
+[TOC]
+
+## Disclamer
+
+This documentation explain the implementation details of Network Emulation
+Framework. Framework's public APIs are located in:
+
+* [`/api/test/network_emulation_manager.h`](https://source.chromium.org/search?q=%2Fapi%2Ftest%2Fnetwork_emulation_manager.h)
+* [`/api/test/create_network_emulation_manager.h`](https://source.chromium.org/search?q=%2Fapi%2Ftest%2Fcreate_network_emulation_manager.h)
+* [`/api/test/network_emulation/network_emulation_interfaces.h`](https://source.chromium.org/search?q=%2Fapi%2Ftest%2Fnetwork_emulation%2Fnetwork_emulation_interfaces.h)
+* [`/api/test/simulated_network.h`](https://source.chromium.org/search?q=%2Fapi%2Ftest%2Fsimulated_network.h)
+
+## Overview
+
+Network Emulation Framework provides an ability to emulate network behavior
+between different clients, including a WebRTC PeerConnection client. To
+configure network behavior, the user can choose different options:
+
+* Use predefined implementation that can be configured with parameters such as
+ packet loss, bandwidth, delay, etc.
+* Custom implementation
+
+Conceptually the framework provides the ability to define multiple endpoints and
+routes used to connect them. All network related entities are created and
+managed by single factory class `webrtc::NetworkEmulationManager` which is
+implemented by `webrtc::test::NetworkEmulationManagerImpl` and can work in two
+modes:
+
+* Real time
+* Simulated time
+
+The manager has a dedicated task queue which pipes all packets through all
+network routes from senders to receivers. This task queue behaviour is
+determined by `webrtc::TimeController`, which is based on either in real time or
+simulated time mode.
+
+The network operates on IP level and supports only UDP for now.
+
+## Abstractions
+
+The framework contains the following public abstractions:
+
+* `webrtc::NetworkBehaviorInterface` - defines how emulated network should
+ behave. It operates on packets metadata level and is responsible for telling
+ which packet at which time have to be delivered to the next receiver.
+
+* `webrtc::EmulatedIpPacket` - represents a single packet that can be sent or
+ received via emulated network. It has source and destination address and
+ payload to transfer.
+
+* `webrtc::EmulatedNetworkReceiverInterface` - generic packet receiver
+ interface.
+
+* `webrtc::EmulatedEndpoint` - primary user facing abstraction of the
+ framework. It represents a network interface on client's machine. It has its
+ own unique IP address and can be used to send and receive packets.
+
+ `EmulatedEndpoint` implements `EmulatedNetworkReceiverInterface` to receive
+ packets from the network and provides an API to send packets to the network
+ and API to bind other `EmulatedNetworkReceiverInterface` which will be able
+ to receive packets from the endpoint. `EmulatedEndpoint` interface has the
+ only implementation: `webrtc::test::EmulatedEndpointImpl`.
+
+* `webrtc::EmulatedNetworkNode` - represents single network in the real world,
+ like a 3G network between peers, or Wi-Fi for one peer and LTE for another.
+ Each `EmulatedNetworkNode` is a single direction connetion and to form
+ bidirectional connection between endpoints two nodes should be used.
+ Multiple nodes can be joined into chain emulating a network path from one
+ peer to another.
+
+ In public API this class is forward declared and fully accessible only by
+ the framework implementation.
+
+ Internally consist of two parts: `LinkEmulation`, which is responsible for
+ behavior of current `EmulatedNetworkNode` and `NetworkRouterNode` which is
+ responsible for routing packets to the next node or to the endpoint.
+
+* `webrtc::EmulatedRoute` - represents single route from one network interface
+ on one device to another network interface on another device.
+
+ In public API this class is forward declared and fully accessible only by
+ the framework implementation.
+
+ It contains start and end endpoint and ordered list of `EmulatedNetworkNode`
+ which forms the single directional route between those endpoints.
+
+The framework has also the following private abstractions:
+
+* `webrtc::test::NetworkRouterNode` - an `EmulatedNetworkReceiverInterface`
+ that can route incoming packets to the next receiver based on internal IP
+ routing table.
+
+* `webrtc::test::LinkEmulation` - an `EmulatedNetworkReceiverInterface` that
+ can emulate network leg behavior via `webrtc::NetworkBehaviorInterface`
+ interface.
+
+For integrating with `webrtc::PeerConnection` there are helper abstractions:
+
+* `webrtc::EmulatedNetworkManagerInterface` which is implemented by
+ `webrtc::test::EmulatedNetworkManager` and provides `rtc::Thread` and
+ `rtc::NetworkManager` for WebRTC to use as network thread for
+ `PeerConnection` and for `cricket::BasicPortAllocator`.
+
+ Implementation represent framework endpoints as `rtc::Network` to WebRTC.
+
+## Architecture
+
+Let's take a look on how framework's abstractions are connected to each other.
+
+When the user wants to setup emulated network, first of all, they should create
+an instance of `NetworkEmulationManager` using
+`webrtc::CreateNetworkEmulationManager(...)` API. Then user should use a manager
+to create at least one `EmulatedEndpoint` for each client. After endpoints, the
+user should create required `EmulatedNetworkNode`s and with help of manager
+chain them into `EmulatedRoute`s conecting desired endpoints.
+
+Here is a visual overview of the emulated network architecture:
+
+![Architecture](network_emulation_framework.png "Architecture")
+
+When network is hooked into `PeerConnection` it is done through network thread
+and `NetworkManager`. In the network thread the custom `rtc::SocketServer` is
+provided: `webrtc::test::FakeNetworkSocketServer`. This custom socket server
+will construct custom sockets (`webrtc::test::FakeNetworkSocket`), which
+internally bind themselves to the required endpoint. All packets processing
+inside socket have to be done on the `PeerConnection`'s network thread. When
+packet is going from `PeerConnection` to the network it's already comming from
+the network thread and when it's comming from the emulated network switch from
+the Network Emulation Framework internal task queue and `PeerConnection`'s
+network thread is done inside socket's `OnPacketReceived(...)` method.
+
+![Network Injection](network_injection_into_peer_connection.png "Network Injection")
diff --git a/third_party/libwebrtc/test/network/g3doc/network_emulation_framework.png b/third_party/libwebrtc/test/network/g3doc/network_emulation_framework.png
new file mode 100644
index 0000000000..afec47773f
--- /dev/null
+++ b/third_party/libwebrtc/test/network/g3doc/network_emulation_framework.png
Binary files differ
diff --git a/third_party/libwebrtc/test/network/g3doc/network_injection_into_peer_connection.png b/third_party/libwebrtc/test/network/g3doc/network_injection_into_peer_connection.png
new file mode 100644
index 0000000000..c9e3bf8baf
--- /dev/null
+++ b/third_party/libwebrtc/test/network/g3doc/network_injection_into_peer_connection.png
Binary files differ
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..4cd2fda1d0
--- /dev/null
+++ b/third_party/libwebrtc/test/network/network_emulation.cc
@@ -0,0 +1,708 @@
+/*
+ * 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/test/network_emulation/network_emulation_interfaces.h"
+#include "api/units/data_size.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+DataRate EmulatedNetworkOutgoingStatsImpl::AverageSendRate() const {
+ RTC_DCHECK_GE(packets_sent_, 2);
+ RTC_DCHECK(first_packet_sent_time_.IsFinite());
+ RTC_DCHECK(last_packet_sent_time_.IsFinite());
+ return (bytes_sent_ - first_sent_packet_size_) /
+ (last_packet_sent_time_ - first_packet_sent_time_);
+}
+
+DataRate EmulatedNetworkIncomingStatsImpl::AverageReceiveRate() const {
+ RTC_DCHECK_GE(packets_received_, 2);
+ RTC_DCHECK(first_packet_received_time_.IsFinite());
+ RTC_DCHECK(last_packet_received_time_.IsFinite());
+ return (bytes_received_ - first_received_packet_size_) /
+ (last_packet_received_time_ - first_packet_received_time_);
+}
+
+std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkOutgoingStats>>
+EmulatedNetworkStatsImpl::OutgoingStatsPerDestination() const {
+ std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkOutgoingStats>> out;
+ for (const auto& entry : outgoing_stats_per_destination_) {
+ out.emplace(entry.first, std::make_unique<EmulatedNetworkOutgoingStatsImpl>(
+ *entry.second));
+ }
+ return out;
+}
+
+std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkIncomingStats>>
+EmulatedNetworkStatsImpl::IncomingStatsPerSource() const {
+ std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkIncomingStats>> out;
+ for (const auto& entry : incoming_stats_per_source_) {
+ out.emplace(entry.first, std::make_unique<EmulatedNetworkIncomingStatsImpl>(
+ *entry.second));
+ }
+ return out;
+}
+
+std::unique_ptr<EmulatedNetworkOutgoingStats>
+EmulatedNetworkStatsImpl::GetOverallOutgoingStats() const {
+ EmulatedNetworkOutgoingStatsBuilder builder;
+ for (const auto& entry : outgoing_stats_per_destination_) {
+ builder.AddOutgoingStats(*entry.second);
+ }
+ return builder.Build();
+}
+
+std::unique_ptr<EmulatedNetworkIncomingStats>
+EmulatedNetworkStatsImpl::GetOverallIncomingStats() const {
+ EmulatedNetworkIncomingStatsBuilder builder;
+ for (const auto& entry : incoming_stats_per_source_) {
+ builder.AddIncomingStats(*entry.second);
+ }
+ return builder.Build();
+}
+
+EmulatedNetworkOutgoingStatsBuilder::EmulatedNetworkOutgoingStatsBuilder() {
+ sequence_checker_.Detach();
+}
+
+void EmulatedNetworkOutgoingStatsBuilder::OnPacketSent(
+ Timestamp sent_time,
+ DataSize packet_size,
+ EmulatedEndpointConfig::StatsGatheringMode mode) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ RTC_CHECK_GE(packet_size, DataSize::Zero());
+ if (first_packet_sent_time_.IsInfinite()) {
+ first_packet_sent_time_ = sent_time;
+ first_sent_packet_size_ = packet_size;
+ }
+ last_packet_sent_time_ = sent_time;
+ packets_sent_++;
+ bytes_sent_ += packet_size;
+ if (mode == EmulatedEndpointConfig::StatsGatheringMode::kDebug) {
+ sent_packets_size_counter_.AddSample(packet_size.bytes());
+ }
+}
+
+void EmulatedNetworkOutgoingStatsBuilder::AddOutgoingStats(
+ const EmulatedNetworkOutgoingStats& stats) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ packets_sent_ += stats.PacketsSent();
+ bytes_sent_ += stats.BytesSent();
+ sent_packets_size_counter_.AddSamples(stats.SentPacketsSizeCounter());
+ if (first_packet_sent_time_ > stats.FirstPacketSentTime()) {
+ first_packet_sent_time_ = stats.FirstPacketSentTime();
+ first_sent_packet_size_ = stats.FirstSentPacketSize();
+ }
+ if (last_packet_sent_time_ < stats.LastPacketSentTime()) {
+ last_packet_sent_time_ = stats.LastPacketSentTime();
+ }
+}
+
+std::unique_ptr<EmulatedNetworkOutgoingStats>
+EmulatedNetworkOutgoingStatsBuilder::Build() const {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ return std::make_unique<EmulatedNetworkOutgoingStatsImpl>(
+ packets_sent_, bytes_sent_, sent_packets_size_counter_,
+ first_sent_packet_size_, first_packet_sent_time_, last_packet_sent_time_);
+}
+
+EmulatedNetworkIncomingStatsBuilder::EmulatedNetworkIncomingStatsBuilder() {
+ sequence_checker_.Detach();
+}
+
+void EmulatedNetworkIncomingStatsBuilder::OnPacketDropped(
+ DataSize packet_size,
+ EmulatedEndpointConfig::StatsGatheringMode mode) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ packets_dropped_++;
+ bytes_dropped_ += packet_size;
+ if (mode == EmulatedEndpointConfig::StatsGatheringMode::kDebug) {
+ dropped_packets_size_counter_.AddSample(packet_size.bytes());
+ }
+}
+
+void EmulatedNetworkIncomingStatsBuilder::OnPacketReceived(
+ Timestamp received_time,
+ DataSize packet_size,
+ EmulatedEndpointConfig::StatsGatheringMode mode) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ RTC_CHECK_GE(packet_size, DataSize::Zero());
+ if (first_packet_received_time_.IsInfinite()) {
+ first_packet_received_time_ = received_time;
+ first_received_packet_size_ = packet_size;
+ }
+ last_packet_received_time_ = received_time;
+ packets_received_++;
+ bytes_received_ += packet_size;
+ if (mode == EmulatedEndpointConfig::StatsGatheringMode::kDebug) {
+ received_packets_size_counter_.AddSample(packet_size.bytes());
+ }
+}
+
+void EmulatedNetworkIncomingStatsBuilder::AddIncomingStats(
+ const EmulatedNetworkIncomingStats& stats) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ packets_received_ += stats.PacketsReceived();
+ bytes_received_ += stats.BytesReceived();
+ received_packets_size_counter_.AddSamples(stats.ReceivedPacketsSizeCounter());
+ packets_dropped_ += stats.PacketsDropped();
+ bytes_dropped_ += stats.BytesDropped();
+ dropped_packets_size_counter_.AddSamples(stats.DroppedPacketsSizeCounter());
+ if (first_packet_received_time_ > stats.FirstPacketReceivedTime()) {
+ first_packet_received_time_ = stats.FirstPacketReceivedTime();
+ first_received_packet_size_ = stats.FirstReceivedPacketSize();
+ }
+ if (last_packet_received_time_ < stats.LastPacketReceivedTime()) {
+ last_packet_received_time_ = stats.LastPacketReceivedTime();
+ }
+}
+
+std::unique_ptr<EmulatedNetworkIncomingStats>
+EmulatedNetworkIncomingStatsBuilder::Build() const {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ return std::make_unique<EmulatedNetworkIncomingStatsImpl>(
+ packets_received_, bytes_received_, received_packets_size_counter_,
+ packets_dropped_, bytes_dropped_, dropped_packets_size_counter_,
+ first_received_packet_size_, first_packet_received_time_,
+ last_packet_received_time_);
+}
+
+EmulatedNetworkStatsBuilder::EmulatedNetworkStatsBuilder() {
+ sequence_checker_.Detach();
+}
+
+EmulatedNetworkStatsBuilder::EmulatedNetworkStatsBuilder(
+ rtc::IPAddress local_ip) {
+ 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,
+ EmulatedEndpointConfig::StatsGatheringMode mode) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ if (mode == EmulatedEndpointConfig::StatsGatheringMode::kDebug) {
+ sent_packets_queue_wait_time_us_.AddSample((sent_time - queued_time).us());
+ }
+ outgoing_stats_per_destination_[destination_ip].OnPacketSent(
+ sent_time, packet_size, mode);
+}
+
+void EmulatedNetworkStatsBuilder::OnPacketDropped(
+ rtc::IPAddress source_ip,
+ DataSize packet_size,
+ EmulatedEndpointConfig::StatsGatheringMode mode) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ incoming_stats_per_source_[source_ip].OnPacketDropped(packet_size, mode);
+}
+
+void EmulatedNetworkStatsBuilder::OnPacketReceived(
+ Timestamp received_time,
+ rtc::IPAddress source_ip,
+ DataSize packet_size,
+ EmulatedEndpointConfig::StatsGatheringMode mode) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ incoming_stats_per_source_[source_ip].OnPacketReceived(received_time,
+ packet_size, mode);
+}
+
+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.LocalAddresses()) {
+ local_addresses_.push_back(addr);
+ }
+
+ sent_packets_queue_wait_time_us_.AddSamples(
+ stats.SentPacketsQueueWaitTimeUs());
+
+ // Add outgoing stats from other endpoints to the builder.
+ const std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkOutgoingStats>>
+ outgoing_stats_per_destination = stats.OutgoingStatsPerDestination();
+ for (const auto& entry : outgoing_stats_per_destination) {
+ outgoing_stats_per_destination_[entry.first].AddOutgoingStats(
+ *entry.second);
+ }
+
+ // Add incoming stats from other endpoints to the builder.
+ const std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkIncomingStats>>
+ incoming_stats_per_source = stats.IncomingStatsPerSource();
+ for (const auto& entry : incoming_stats_per_source) {
+ incoming_stats_per_source_[entry.first].AddIncomingStats(*entry.second);
+ }
+}
+
+std::unique_ptr<EmulatedNetworkStats> EmulatedNetworkStatsBuilder::Build()
+ const {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkOutgoingStats>>
+ outgoing_stats;
+ for (const auto& entry : outgoing_stats_per_destination_) {
+ outgoing_stats.emplace(entry.first, entry.second.Build());
+ }
+ std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkIncomingStats>>
+ incoming_stats;
+ for (const auto& entry : incoming_stats_per_source_) {
+ incoming_stats.emplace(entry.first, entry.second.Build());
+ }
+ return std::make_unique<EmulatedNetworkStatsImpl>(
+ local_addresses_, sent_packets_queue_wait_time_us_,
+ std::move(outgoing_stats), std::move(incoming_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{packet_id, std::move(packet), 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;
+ });
+ });
+}
+
+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;
+
+ 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)
+ : router_(task_queue),
+ link_(clock, task_queue, std::move(network_behavior), &router_) {}
+
+void EmulatedNetworkNode::OnPacketReceived(EmulatedIpPacket packet) {
+ link_.OnPacketReceived(std::move(packet));
+}
+
+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)
+ : id(id),
+ ip(ip),
+ stats_gathering_mode(config.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) {
+ 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()),
+ options_.stats_gathering_mode);
+
+ 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()),
+ options_.stats_gathering_mode);
+ 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();
+ stats_builder_.OnPacketDropped(packet.from.ipaddr(),
+ DataSize::Bytes(packet.ip_packet_size()),
+ options_.stats_gathering_mode);
+ 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_;
+}
+
+std::unique_ptr<EmulatedNetworkStats> EmulatedEndpointImpl::stats() const {
+ RTC_DCHECK_RUN_ON(task_queue_);
+ return stats_builder_.Build();
+}
+
+EndpointsContainer::EndpointsContainer(
+ const std::vector<EmulatedEndpointImpl*>& endpoints)
+ : endpoints_(endpoints) {}
+
+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();
+}
+
+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());
+}
+
+std::unique_ptr<EmulatedNetworkStats> EndpointsContainer::GetStats() const {
+ EmulatedNetworkStatsBuilder stats_builder;
+ for (auto* endpoint : endpoints_) {
+ stats_builder.AddEmulatedNetworkStats(*endpoint->stats());
+ }
+ return stats_builder.Build();
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/test/network/network_emulation.h b/third_party/libwebrtc/test/network/network_emulation.h
new file mode 100644
index 0000000000..61dc468661
--- /dev/null
+++ b/third_party/libwebrtc/test/network/network_emulation.h
@@ -0,0 +1,702 @@
+/*
+ * 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.
+ */
+
+#ifndef TEST_NETWORK_NETWORK_EMULATION_H_
+#define TEST_NETWORK_NETWORK_EMULATION_H_
+
+#include <cstdint>
+#include <deque>
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "api/array_view.h"
+#include "api/numerics/samples_stats_counter.h"
+#include "api/sequence_checker.h"
+#include "api/test/network_emulation_manager.h"
+#include "api/test/simulated_network.h"
+#include "api/units/timestamp.h"
+#include "rtc_base/copy_on_write_buffer.h"
+#include "rtc_base/network.h"
+#include "rtc_base/network_constants.h"
+#include "rtc_base/socket_address.h"
+#include "rtc_base/synchronization/mutex.h"
+#include "rtc_base/task_queue_for_test.h"
+#include "rtc_base/task_utils/repeating_task.h"
+#include "rtc_base/thread_annotations.h"
+#include "system_wrappers/include/clock.h"
+
+namespace webrtc {
+
+// This class is immutable and so thread safe.
+class EmulatedNetworkOutgoingStatsImpl final
+ : public EmulatedNetworkOutgoingStats {
+ public:
+ EmulatedNetworkOutgoingStatsImpl(
+ int64_t packets_sent,
+ DataSize bytes_sent,
+ SamplesStatsCounter sent_packets_size_counter,
+ DataSize first_sent_packet_size,
+ Timestamp first_packet_sent_time,
+ Timestamp last_packet_sent_time)
+ : packets_sent_(packets_sent),
+ bytes_sent_(bytes_sent),
+ sent_packets_size_counter_(std::move(sent_packets_size_counter)),
+ first_sent_packet_size_(first_sent_packet_size),
+ first_packet_sent_time_(first_packet_sent_time),
+ last_packet_sent_time_(last_packet_sent_time) {}
+ explicit EmulatedNetworkOutgoingStatsImpl(
+ const EmulatedNetworkOutgoingStats& stats)
+ : packets_sent_(stats.PacketsSent()),
+ bytes_sent_(stats.BytesSent()),
+ sent_packets_size_counter_(stats.SentPacketsSizeCounter()),
+ first_sent_packet_size_(stats.FirstSentPacketSize()),
+ first_packet_sent_time_(stats.FirstPacketSentTime()),
+ last_packet_sent_time_(stats.LastPacketSentTime()) {}
+ ~EmulatedNetworkOutgoingStatsImpl() override = default;
+
+ int64_t PacketsSent() const override { return packets_sent_; }
+
+ DataSize BytesSent() const override { return bytes_sent_; }
+
+ const SamplesStatsCounter& SentPacketsSizeCounter() const override {
+ return sent_packets_size_counter_;
+ }
+
+ DataSize FirstSentPacketSize() const override {
+ return first_sent_packet_size_;
+ }
+
+ Timestamp FirstPacketSentTime() const override {
+ return first_packet_sent_time_;
+ }
+
+ Timestamp LastPacketSentTime() const override {
+ return last_packet_sent_time_;
+ }
+
+ DataRate AverageSendRate() const override;
+
+ private:
+ const int64_t packets_sent_;
+ const DataSize bytes_sent_;
+ const SamplesStatsCounter sent_packets_size_counter_;
+ const DataSize first_sent_packet_size_;
+ const Timestamp first_packet_sent_time_;
+ const Timestamp last_packet_sent_time_;
+};
+
+// This class is immutable and so thread safe.
+class EmulatedNetworkIncomingStatsImpl final
+ : public EmulatedNetworkIncomingStats {
+ public:
+ EmulatedNetworkIncomingStatsImpl(
+ int64_t packets_received,
+ DataSize bytes_received,
+ SamplesStatsCounter received_packets_size_counter,
+ int64_t packets_dropped,
+ DataSize bytes_dropped,
+ SamplesStatsCounter dropped_packets_size_counter,
+ DataSize first_received_packet_size,
+ Timestamp first_packet_received_time,
+ Timestamp last_packet_received_time)
+ : packets_received_(packets_received),
+ bytes_received_(bytes_received),
+ received_packets_size_counter_(received_packets_size_counter),
+ packets_dropped_(packets_dropped),
+ bytes_dropped_(bytes_dropped),
+ dropped_packets_size_counter_(dropped_packets_size_counter),
+ first_received_packet_size_(first_received_packet_size),
+ first_packet_received_time_(first_packet_received_time),
+ last_packet_received_time_(last_packet_received_time) {}
+ explicit EmulatedNetworkIncomingStatsImpl(
+ const EmulatedNetworkIncomingStats& stats)
+ : packets_received_(stats.PacketsReceived()),
+ bytes_received_(stats.BytesReceived()),
+ received_packets_size_counter_(stats.ReceivedPacketsSizeCounter()),
+ packets_dropped_(stats.PacketsDropped()),
+ bytes_dropped_(stats.BytesDropped()),
+ dropped_packets_size_counter_(stats.DroppedPacketsSizeCounter()),
+ first_received_packet_size_(stats.FirstReceivedPacketSize()),
+ first_packet_received_time_(stats.FirstPacketReceivedTime()),
+ last_packet_received_time_(stats.LastPacketReceivedTime()) {}
+ ~EmulatedNetworkIncomingStatsImpl() override = default;
+
+ int64_t PacketsReceived() const override { return packets_received_; }
+
+ DataSize BytesReceived() const override { return bytes_received_; }
+
+ const SamplesStatsCounter& ReceivedPacketsSizeCounter() const override {
+ return received_packets_size_counter_;
+ }
+
+ int64_t PacketsDropped() const override { return packets_dropped_; }
+
+ DataSize BytesDropped() const override { return bytes_dropped_; }
+
+ const SamplesStatsCounter& DroppedPacketsSizeCounter() const override {
+ return dropped_packets_size_counter_;
+ }
+
+ DataSize FirstReceivedPacketSize() const override {
+ return first_received_packet_size_;
+ }
+
+ Timestamp FirstPacketReceivedTime() const override {
+ return first_packet_received_time_;
+ }
+
+ Timestamp LastPacketReceivedTime() const override {
+ return last_packet_received_time_;
+ }
+
+ DataRate AverageReceiveRate() const override;
+
+ private:
+ const int64_t packets_received_;
+ const DataSize bytes_received_;
+ const SamplesStatsCounter received_packets_size_counter_;
+ const int64_t packets_dropped_;
+ const DataSize bytes_dropped_;
+ const SamplesStatsCounter dropped_packets_size_counter_;
+ const DataSize first_received_packet_size_;
+ const Timestamp first_packet_received_time_;
+ const Timestamp last_packet_received_time_;
+};
+
+// This class is immutable and so is thread safe.
+class EmulatedNetworkStatsImpl final : public EmulatedNetworkStats {
+ public:
+ EmulatedNetworkStatsImpl(
+ std::vector<rtc::IPAddress> local_addresses,
+ SamplesStatsCounter sent_packets_queue_wait_time_us,
+ std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkOutgoingStats>>
+ outgoing_stats_per_destination,
+ std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkIncomingStats>>
+ incoming_stats_per_source)
+ : local_addresses_(std::move(local_addresses)),
+ sent_packets_queue_wait_time_us_(sent_packets_queue_wait_time_us),
+ outgoing_stats_per_destination_(
+ std::move(outgoing_stats_per_destination)),
+ incoming_stats_per_source_(std::move(incoming_stats_per_source)),
+ overall_outgoing_stats_(GetOverallOutgoingStats()),
+ overall_incoming_stats_(GetOverallIncomingStats()) {}
+ ~EmulatedNetworkStatsImpl() override = default;
+
+ std::vector<rtc::IPAddress> LocalAddresses() const override {
+ return local_addresses_;
+ }
+
+ int64_t PacketsSent() const override {
+ return overall_outgoing_stats_->PacketsSent();
+ }
+
+ DataSize BytesSent() const override {
+ return overall_outgoing_stats_->BytesSent();
+ }
+
+ const SamplesStatsCounter& SentPacketsSizeCounter() const override {
+ return overall_outgoing_stats_->SentPacketsSizeCounter();
+ }
+
+ const SamplesStatsCounter& SentPacketsQueueWaitTimeUs() const override {
+ return sent_packets_queue_wait_time_us_;
+ }
+
+ DataSize FirstSentPacketSize() const override {
+ return overall_outgoing_stats_->FirstSentPacketSize();
+ }
+
+ Timestamp FirstPacketSentTime() const override {
+ return overall_outgoing_stats_->FirstPacketSentTime();
+ }
+
+ Timestamp LastPacketSentTime() const override {
+ return overall_outgoing_stats_->LastPacketSentTime();
+ }
+
+ DataRate AverageSendRate() const override {
+ return overall_outgoing_stats_->AverageSendRate();
+ }
+
+ int64_t PacketsReceived() const override {
+ return overall_incoming_stats_->PacketsReceived();
+ }
+
+ DataSize BytesReceived() const override {
+ return overall_incoming_stats_->BytesReceived();
+ }
+
+ const SamplesStatsCounter& ReceivedPacketsSizeCounter() const override {
+ return overall_incoming_stats_->ReceivedPacketsSizeCounter();
+ }
+
+ int64_t PacketsDropped() const override {
+ return overall_incoming_stats_->PacketsDropped();
+ }
+
+ DataSize BytesDropped() const override {
+ return overall_incoming_stats_->BytesDropped();
+ }
+
+ const SamplesStatsCounter& DroppedPacketsSizeCounter() const override {
+ return overall_incoming_stats_->DroppedPacketsSizeCounter();
+ }
+
+ DataSize FirstReceivedPacketSize() const override {
+ return overall_incoming_stats_->FirstReceivedPacketSize();
+ }
+
+ Timestamp FirstPacketReceivedTime() const override {
+ return overall_incoming_stats_->FirstPacketReceivedTime();
+ }
+
+ Timestamp LastPacketReceivedTime() const override {
+ return overall_incoming_stats_->LastPacketReceivedTime();
+ }
+
+ DataRate AverageReceiveRate() const override {
+ return overall_incoming_stats_->AverageReceiveRate();
+ }
+
+ std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkOutgoingStats>>
+ OutgoingStatsPerDestination() const override;
+
+ std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkIncomingStats>>
+ IncomingStatsPerSource() const override;
+
+ private:
+ std::unique_ptr<EmulatedNetworkOutgoingStats> GetOverallOutgoingStats() const;
+ std::unique_ptr<EmulatedNetworkIncomingStats> GetOverallIncomingStats() const;
+
+ const std::vector<rtc::IPAddress> local_addresses_;
+ const SamplesStatsCounter sent_packets_queue_wait_time_us_;
+ const std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkOutgoingStats>>
+ outgoing_stats_per_destination_;
+ const std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkIncomingStats>>
+ incoming_stats_per_source_;
+ const std::unique_ptr<EmulatedNetworkOutgoingStats> overall_outgoing_stats_;
+ const std::unique_ptr<EmulatedNetworkIncomingStats> overall_incoming_stats_;
+};
+
+class EmulatedNetworkOutgoingStatsBuilder {
+ public:
+ EmulatedNetworkOutgoingStatsBuilder();
+
+ void OnPacketSent(Timestamp sent_time,
+ DataSize packet_size,
+ EmulatedEndpointConfig::StatsGatheringMode mode);
+
+ void AddOutgoingStats(const EmulatedNetworkOutgoingStats& stats);
+
+ std::unique_ptr<EmulatedNetworkOutgoingStats> Build() const;
+
+ private:
+ SequenceChecker sequence_checker_;
+
+ int64_t packets_sent_ RTC_GUARDED_BY(sequence_checker_) = 0;
+ DataSize bytes_sent_ RTC_GUARDED_BY(sequence_checker_) = DataSize::Zero();
+ SamplesStatsCounter sent_packets_size_counter_
+ RTC_GUARDED_BY(sequence_checker_);
+ DataSize first_sent_packet_size_ RTC_GUARDED_BY(sequence_checker_) =
+ DataSize::Zero();
+ Timestamp first_packet_sent_time_ RTC_GUARDED_BY(sequence_checker_) =
+ Timestamp::PlusInfinity();
+ Timestamp last_packet_sent_time_ RTC_GUARDED_BY(sequence_checker_) =
+ Timestamp::MinusInfinity();
+};
+
+class EmulatedNetworkIncomingStatsBuilder {
+ public:
+ EmulatedNetworkIncomingStatsBuilder();
+
+ void OnPacketDropped(DataSize packet_size,
+ EmulatedEndpointConfig::StatsGatheringMode mode);
+
+ void OnPacketReceived(Timestamp received_time,
+ DataSize packet_size,
+ EmulatedEndpointConfig::StatsGatheringMode mode);
+
+ // Adds stats collected from another endpoints to the builder.
+ void AddIncomingStats(const EmulatedNetworkIncomingStats& stats);
+
+ std::unique_ptr<EmulatedNetworkIncomingStats> Build() const;
+
+ private:
+ SequenceChecker sequence_checker_;
+
+ int64_t packets_received_ RTC_GUARDED_BY(sequence_checker_) = 0;
+ DataSize bytes_received_ RTC_GUARDED_BY(sequence_checker_) = DataSize::Zero();
+ SamplesStatsCounter received_packets_size_counter_
+ RTC_GUARDED_BY(sequence_checker_);
+ int64_t packets_dropped_ RTC_GUARDED_BY(sequence_checker_) = 0;
+ DataSize bytes_dropped_ RTC_GUARDED_BY(sequence_checker_) = DataSize::Zero();
+ SamplesStatsCounter dropped_packets_size_counter_
+ RTC_GUARDED_BY(sequence_checker_);
+ DataSize first_received_packet_size_ RTC_GUARDED_BY(sequence_checker_) =
+ DataSize::Zero();
+ Timestamp first_packet_received_time_ RTC_GUARDED_BY(sequence_checker_) =
+ Timestamp::PlusInfinity();
+ Timestamp last_packet_received_time_ RTC_GUARDED_BY(sequence_checker_) =
+ Timestamp::MinusInfinity();
+};
+
+// All methods of EmulatedNetworkStatsBuilder have to be used on a single
+// thread. It may be created on another thread.
+class EmulatedNetworkStatsBuilder {
+ public:
+ EmulatedNetworkStatsBuilder();
+ explicit EmulatedNetworkStatsBuilder(rtc::IPAddress local_ip);
+
+ void OnPacketSent(Timestamp queued_time,
+ Timestamp sent_time,
+ rtc::IPAddress destination_ip,
+ DataSize packet_size,
+ EmulatedEndpointConfig::StatsGatheringMode mode);
+
+ void OnPacketDropped(rtc::IPAddress source_ip,
+ DataSize packet_size,
+ EmulatedEndpointConfig::StatsGatheringMode mode);
+
+ void OnPacketReceived(Timestamp received_time,
+ rtc::IPAddress source_ip,
+ DataSize packet_size,
+ EmulatedEndpointConfig::StatsGatheringMode mode);
+
+ void AddEmulatedNetworkStats(const EmulatedNetworkStats& stats);
+
+ std::unique_ptr<EmulatedNetworkStats> Build() const;
+
+ private:
+ SequenceChecker sequence_checker_;
+
+ std::vector<rtc::IPAddress> local_addresses_
+ RTC_GUARDED_BY(sequence_checker_);
+ SamplesStatsCounter sent_packets_queue_wait_time_us_;
+ std::map<rtc::IPAddress, EmulatedNetworkOutgoingStatsBuilder>
+ outgoing_stats_per_destination_ RTC_GUARDED_BY(sequence_checker_);
+ std::map<rtc::IPAddress, EmulatedNetworkIncomingStatsBuilder>
+ incoming_stats_per_source_ RTC_GUARDED_BY(sequence_checker_);
+};
+
+class LinkEmulation : public EmulatedNetworkReceiverInterface {
+ public:
+ LinkEmulation(Clock* clock,
+ rtc::TaskQueue* task_queue,
+ std::unique_ptr<NetworkBehaviorInterface> network_behavior,
+ EmulatedNetworkReceiverInterface* receiver)
+ : clock_(clock),
+ task_queue_(task_queue),
+ network_behavior_(std::move(network_behavior)),
+ receiver_(receiver) {}
+ void OnPacketReceived(EmulatedIpPacket packet) override;
+
+ private:
+ struct StoredPacket {
+ uint64_t id;
+ EmulatedIpPacket packet;
+ bool removed;
+ };
+ void Process(Timestamp at_time) RTC_RUN_ON(task_queue_);
+
+ Clock* const clock_;
+ rtc::TaskQueue* const task_queue_;
+ const std::unique_ptr<NetworkBehaviorInterface> network_behavior_
+ RTC_GUARDED_BY(task_queue_);
+ EmulatedNetworkReceiverInterface* const receiver_;
+ RepeatingTaskHandle process_task_ RTC_GUARDED_BY(task_queue_);
+ std::deque<StoredPacket> packets_ RTC_GUARDED_BY(task_queue_);
+ uint64_t next_packet_id_ RTC_GUARDED_BY(task_queue_) = 1;
+};
+
+// Represents a component responsible for routing packets based on their IP
+// address. All possible routes have to be set explicitly before packet for
+// desired destination will be seen for the first time. If route is unknown
+// the packet will be silently dropped.
+class NetworkRouterNode : public EmulatedNetworkReceiverInterface {
+ public:
+ explicit NetworkRouterNode(rtc::TaskQueue* task_queue);
+
+ void OnPacketReceived(EmulatedIpPacket packet) override;
+ void SetReceiver(const rtc::IPAddress& dest_ip,
+ EmulatedNetworkReceiverInterface* receiver);
+ void RemoveReceiver(const rtc::IPAddress& dest_ip);
+ // Sets a default receive that will be used for all incoming packets for which
+ // there is no specific receiver binded to their destination port.
+ void SetDefaultReceiver(EmulatedNetworkReceiverInterface* receiver);
+ void RemoveDefaultReceiver();
+ void SetWatcher(std::function<void(const EmulatedIpPacket&)> watcher);
+ void SetFilter(std::function<bool(const EmulatedIpPacket&)> filter);
+
+ private:
+ rtc::TaskQueue* const task_queue_;
+ absl::optional<EmulatedNetworkReceiverInterface*> default_receiver_
+ RTC_GUARDED_BY(task_queue_);
+ std::map<rtc::IPAddress, EmulatedNetworkReceiverInterface*> routing_
+ RTC_GUARDED_BY(task_queue_);
+ std::function<void(const EmulatedIpPacket&)> watcher_
+ RTC_GUARDED_BY(task_queue_);
+ std::function<bool(const EmulatedIpPacket&)> filter_
+ RTC_GUARDED_BY(task_queue_);
+};
+
+// Represents node in the emulated network. Nodes can be connected with each
+// other to form different networks with different behavior. The behavior of
+// the node itself is determined by a concrete implementation of
+// NetworkBehaviorInterface that is provided on construction.
+class EmulatedNetworkNode : public EmulatedNetworkReceiverInterface {
+ public:
+ // Creates node based on `network_behavior`. The specified `packet_overhead`
+ // is added to the size of each packet in the information provided to
+ // `network_behavior`.
+ // `task_queue` is used to process packets and to forward the packets when
+ // they are ready.
+ EmulatedNetworkNode(
+ Clock* clock,
+ rtc::TaskQueue* task_queue,
+ std::unique_ptr<NetworkBehaviorInterface> network_behavior);
+ ~EmulatedNetworkNode() override;
+
+ EmulatedNetworkNode(const EmulatedNetworkNode&) = delete;
+ EmulatedNetworkNode& operator=(const EmulatedNetworkNode&) = delete;
+
+ void OnPacketReceived(EmulatedIpPacket packet) override;
+
+ LinkEmulation* link() { return &link_; }
+ NetworkRouterNode* router() { return &router_; }
+
+ // Creates a route for the given receiver_ip over all the given nodes to the
+ // given receiver.
+ static void CreateRoute(const rtc::IPAddress& receiver_ip,
+ std::vector<EmulatedNetworkNode*> nodes,
+ EmulatedNetworkReceiverInterface* receiver);
+ static void ClearRoute(const rtc::IPAddress& receiver_ip,
+ std::vector<EmulatedNetworkNode*> nodes);
+
+ private:
+ NetworkRouterNode router_;
+ LinkEmulation link_;
+};
+
+// Represents single network interface on the device.
+// It will be used as sender from socket side to send data to the network and
+// will act as packet receiver from emulated network side to receive packets
+// from other EmulatedNetworkNodes.
+class EmulatedEndpointImpl : public EmulatedEndpoint {
+ public:
+ struct Options {
+ Options(uint64_t id,
+ const rtc::IPAddress& ip,
+ const EmulatedEndpointConfig& config);
+
+ // TODO(titovartem) check if we can remove id.
+ uint64_t id;
+ // Endpoint local IP address.
+ rtc::IPAddress ip;
+ EmulatedEndpointConfig::StatsGatheringMode stats_gathering_mode;
+ rtc::AdapterType type;
+ // Allow endpoint to send packets specifying source IP address different to
+ // the current endpoint IP address. If false endpoint will crash if attempt
+ // to send such packet will be done.
+ bool allow_send_packet_with_different_source_ip;
+ // Allow endpoint to receive packet with destination IP address different to
+ // the current endpoint IP address. If false endpoint will crash if such
+ // packet will arrive.
+ bool allow_receive_packets_with_different_dest_ip;
+ // Name of the endpoint used for logging purposes.
+ std::string log_name;
+ };
+
+ EmulatedEndpointImpl(const Options& options,
+ bool is_enabled,
+ rtc::TaskQueue* task_queue,
+ Clock* clock);
+ ~EmulatedEndpointImpl() override;
+
+ uint64_t GetId() const;
+
+ NetworkRouterNode* router() { return &router_; }
+
+ void SendPacket(const rtc::SocketAddress& from,
+ const rtc::SocketAddress& to,
+ rtc::CopyOnWriteBuffer packet_data,
+ uint16_t application_overhead = 0) override;
+
+ absl::optional<uint16_t> BindReceiver(
+ uint16_t desired_port,
+ EmulatedNetworkReceiverInterface* receiver) override;
+ // Binds a receiver, and automatically removes the binding after first call to
+ // OnPacketReceived.
+ absl::optional<uint16_t> BindOneShotReceiver(
+ uint16_t desired_port,
+ EmulatedNetworkReceiverInterface* receiver);
+ void UnbindReceiver(uint16_t port) override;
+ void BindDefaultReceiver(EmulatedNetworkReceiverInterface* receiver) override;
+ void UnbindDefaultReceiver() override;
+
+ rtc::IPAddress GetPeerLocalAddress() const override;
+
+ // Will be called to deliver packet into endpoint from network node.
+ void OnPacketReceived(EmulatedIpPacket packet) override;
+
+ void Enable();
+ void Disable();
+ bool Enabled() const;
+
+ const rtc::Network& network() const { return *network_.get(); }
+
+ std::unique_ptr<EmulatedNetworkStats> stats() const;
+
+ private:
+ struct ReceiverBinding {
+ EmulatedNetworkReceiverInterface* receiver;
+ bool is_one_shot;
+ };
+
+ absl::optional<uint16_t> BindReceiverInternal(
+ uint16_t desired_port,
+ EmulatedNetworkReceiverInterface* receiver,
+ bool is_one_shot);
+
+ static constexpr uint16_t kFirstEphemeralPort = 49152;
+ uint16_t NextPort() RTC_EXCLUSIVE_LOCKS_REQUIRED(receiver_lock_);
+
+ Mutex receiver_lock_;
+ SequenceChecker enabled_state_checker_;
+
+ const Options options_;
+ bool is_enabled_ RTC_GUARDED_BY(enabled_state_checker_);
+ Clock* const clock_;
+ rtc::TaskQueue* const task_queue_;
+ std::unique_ptr<rtc::Network> network_;
+ NetworkRouterNode router_;
+
+ uint16_t next_port_ RTC_GUARDED_BY(receiver_lock_);
+ absl::optional<EmulatedNetworkReceiverInterface*> default_receiver_
+ RTC_GUARDED_BY(receiver_lock_);
+ std::map<uint16_t, ReceiverBinding> port_to_receiver_
+ RTC_GUARDED_BY(receiver_lock_);
+
+ EmulatedNetworkStatsBuilder stats_builder_ RTC_GUARDED_BY(task_queue_);
+};
+
+class EmulatedRoute {
+ public:
+ EmulatedRoute(EmulatedEndpointImpl* from,
+ std::vector<EmulatedNetworkNode*> via_nodes,
+ EmulatedEndpointImpl* to,
+ bool is_default)
+ : from(from),
+ via_nodes(std::move(via_nodes)),
+ to(to),
+ active(true),
+ is_default(is_default) {}
+
+ EmulatedEndpointImpl* from;
+ std::vector<EmulatedNetworkNode*> via_nodes;
+ EmulatedEndpointImpl* to;
+ bool active;
+ bool is_default;
+};
+
+// This object is immutable and so thread safe.
+class EndpointsContainer {
+ public:
+ explicit EndpointsContainer(
+ const std::vector<EmulatedEndpointImpl*>& endpoints);
+
+ EmulatedEndpointImpl* LookupByLocalAddress(
+ const rtc::IPAddress& local_ip) const;
+ bool HasEndpoint(EmulatedEndpointImpl* endpoint) const;
+ // Returns list of networks for enabled endpoints. Caller takes ownership of
+ // returned rtc::Network objects.
+ std::vector<std::unique_ptr<rtc::Network>> GetEnabledNetworks() const;
+ std::vector<EmulatedEndpoint*> GetEndpoints() const;
+ std::unique_ptr<EmulatedNetworkStats> GetStats() const;
+
+ private:
+ const std::vector<EmulatedEndpointImpl*> endpoints_;
+};
+
+template <typename FakePacketType>
+class FakePacketRoute : public EmulatedNetworkReceiverInterface {
+ public:
+ FakePacketRoute(EmulatedRoute* route,
+ std::function<void(FakePacketType, Timestamp)> action)
+ : route_(route),
+ action_(std::move(action)),
+ send_addr_(route_->from->GetPeerLocalAddress(), 0),
+ recv_addr_(route_->to->GetPeerLocalAddress(),
+ *route_->to->BindReceiver(0, this)) {}
+
+ ~FakePacketRoute() { route_->to->UnbindReceiver(recv_addr_.port()); }
+
+ void SendPacket(size_t size, FakePacketType packet) {
+ RTC_CHECK_GE(size, sizeof(int));
+ sent_.emplace(next_packet_id_, packet);
+ rtc::CopyOnWriteBuffer buf(size);
+ reinterpret_cast<int*>(buf.MutableData())[0] = next_packet_id_++;
+ route_->from->SendPacket(send_addr_, recv_addr_, buf);
+ }
+
+ void OnPacketReceived(EmulatedIpPacket packet) override {
+ int packet_id = reinterpret_cast<const int*>(packet.data.data())[0];
+ action_(std::move(sent_[packet_id]), packet.arrival_time);
+ sent_.erase(packet_id);
+ }
+
+ private:
+ EmulatedRoute* const route_;
+ const std::function<void(FakePacketType, Timestamp)> action_;
+ const rtc::SocketAddress send_addr_;
+ const rtc::SocketAddress recv_addr_;
+ int next_packet_id_ = 0;
+ std::map<int, FakePacketType> sent_;
+};
+
+template <typename RequestPacketType, typename ResponsePacketType>
+class TwoWayFakeTrafficRoute {
+ public:
+ class TrafficHandlerInterface {
+ public:
+ virtual void OnRequest(RequestPacketType, Timestamp) = 0;
+ virtual void OnResponse(ResponsePacketType, Timestamp) = 0;
+ virtual ~TrafficHandlerInterface() = default;
+ };
+ TwoWayFakeTrafficRoute(TrafficHandlerInterface* handler,
+ EmulatedRoute* send_route,
+ EmulatedRoute* ret_route)
+ : handler_(handler),
+ request_handler_{send_route,
+ [&](RequestPacketType packet, Timestamp arrival_time) {
+ handler_->OnRequest(std::move(packet), arrival_time);
+ }},
+ response_handler_{
+ ret_route, [&](ResponsePacketType packet, Timestamp arrival_time) {
+ handler_->OnResponse(std::move(packet), arrival_time);
+ }} {}
+ void SendRequest(size_t size, RequestPacketType packet) {
+ request_handler_.SendPacket(size, std::move(packet));
+ }
+ void SendResponse(size_t size, ResponsePacketType packet) {
+ response_handler_.SendPacket(size, std::move(packet));
+ }
+
+ private:
+ TrafficHandlerInterface* handler_;
+ FakePacketRoute<RequestPacketType> request_handler_;
+ FakePacketRoute<ResponsePacketType> response_handler_;
+};
+} // namespace webrtc
+
+#endif // TEST_NETWORK_NETWORK_EMULATION_H_
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..a02b5f415c
--- /dev/null
+++ b/third_party/libwebrtc/test/network/network_emulation_manager.cc
@@ -0,0 +1,356 @@
+/*
+ * 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/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)
+ : time_mode_(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));
+ 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),
+ 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);
+ 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(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();
+}
+
+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
diff --git a/third_party/libwebrtc/test/network/network_emulation_manager.h b/third_party/libwebrtc/test/network/network_emulation_manager.h
new file mode 100644
index 0000000000..449441a3c1
--- /dev/null
+++ b/third_party/libwebrtc/test/network/network_emulation_manager.h
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ */
+
+#ifndef TEST_NETWORK_NETWORK_EMULATION_MANAGER_H_
+#define TEST_NETWORK_NETWORK_EMULATION_MANAGER_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "api/array_view.h"
+#include "api/test/network_emulation_manager.h"
+#include "api/test/simulated_network.h"
+#include "api/test/time_controller.h"
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/network.h"
+#include "rtc_base/task_queue_for_test.h"
+#include "rtc_base/task_utils/repeating_task.h"
+#include "rtc_base/thread.h"
+#include "system_wrappers/include/clock.h"
+#include "test/network/cross_traffic.h"
+#include "test/network/emulated_network_manager.h"
+#include "test/network/emulated_turn_server.h"
+#include "test/network/fake_network_socket_server.h"
+#include "test/network/network_emulation.h"
+
+namespace webrtc {
+namespace test {
+
+class NetworkEmulationManagerImpl : public NetworkEmulationManager {
+ public:
+ explicit NetworkEmulationManagerImpl(TimeMode mode);
+ ~NetworkEmulationManagerImpl();
+
+ EmulatedNetworkNode* CreateEmulatedNode(BuiltInNetworkBehaviorConfig config,
+ uint64_t random_seed = 1) override;
+ EmulatedNetworkNode* CreateEmulatedNode(
+ std::unique_ptr<NetworkBehaviorInterface> network_behavior) override;
+
+ SimulatedNetworkNode::Builder NodeBuilder() override;
+
+ EmulatedEndpointImpl* CreateEndpoint(EmulatedEndpointConfig config) override;
+ void EnableEndpoint(EmulatedEndpoint* endpoint) override;
+ void DisableEndpoint(EmulatedEndpoint* endpoint) override;
+
+ EmulatedRoute* CreateRoute(EmulatedEndpoint* from,
+ const std::vector<EmulatedNetworkNode*>& via_nodes,
+ EmulatedEndpoint* to) override;
+
+ EmulatedRoute* CreateRoute(
+ const std::vector<EmulatedNetworkNode*>& via_nodes) override;
+
+ EmulatedRoute* CreateDefaultRoute(
+ EmulatedEndpoint* from,
+ const std::vector<EmulatedNetworkNode*>& via_nodes,
+ EmulatedEndpoint* to) override;
+
+ void ClearRoute(EmulatedRoute* route) override;
+
+ TcpMessageRoute* CreateTcpRoute(EmulatedRoute* send_route,
+ EmulatedRoute* ret_route) override;
+
+ CrossTrafficRoute* CreateCrossTrafficRoute(
+ const std::vector<EmulatedNetworkNode*>& via_nodes) override;
+
+ CrossTrafficGenerator* StartCrossTraffic(
+ std::unique_ptr<CrossTrafficGenerator> generator) override;
+ void StopCrossTraffic(CrossTrafficGenerator* generator) override;
+
+ EmulatedNetworkManagerInterface* CreateEmulatedNetworkManagerInterface(
+ const std::vector<EmulatedEndpoint*>& endpoints) override;
+
+ void GetStats(rtc::ArrayView<EmulatedEndpoint* const> endpoints,
+ std::function<void(std::unique_ptr<EmulatedNetworkStats>)>
+ stats_callback) override;
+
+ TimeController* time_controller() override { return time_controller_.get(); }
+
+ TimeMode time_mode() const override { return time_mode_; }
+
+ Timestamp Now() const;
+
+ EmulatedTURNServerInterface* CreateTURNServer(
+ EmulatedTURNServerConfig config) override;
+
+ private:
+ using CrossTrafficSource =
+ std::pair<std::unique_ptr<CrossTrafficGenerator>, RepeatingTaskHandle>;
+
+ absl::optional<rtc::IPAddress> GetNextIPv4Address();
+
+ const TimeMode time_mode_;
+ const std::unique_ptr<TimeController> time_controller_;
+ Clock* const clock_;
+ int next_node_id_;
+
+ RepeatingTaskHandle process_task_handle_;
+
+ uint32_t next_ip4_address_;
+ std::set<rtc::IPAddress> used_ip_addresses_;
+
+ // All objects can be added to the manager only when it is idle.
+ std::vector<std::unique_ptr<EmulatedEndpoint>> endpoints_;
+ std::vector<std::unique_ptr<EmulatedNetworkNode>> network_nodes_;
+ std::vector<std::unique_ptr<EmulatedRoute>> routes_;
+ std::vector<std::unique_ptr<CrossTrafficRoute>> traffic_routes_;
+ std::vector<CrossTrafficSource> cross_traffics_;
+ std::list<std::unique_ptr<TcpMessageRouteImpl>> tcp_message_routes_;
+ std::vector<std::unique_ptr<EndpointsContainer>> endpoints_containers_;
+ std::vector<std::unique_ptr<EmulatedNetworkManager>> network_managers_;
+ std::vector<std::unique_ptr<EmulatedTURNServer>> turn_servers_;
+
+ std::map<EmulatedEndpoint*, EmulatedNetworkManager*>
+ endpoint_to_network_manager_;
+
+ // Must be the last field, so it will be deleted first, because tasks
+ // in the TaskQueue can access other fields of the instance of this class.
+ TaskQueueForTest task_queue_;
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // TEST_NETWORK_NETWORK_EMULATION_MANAGER_H_
diff --git a/third_party/libwebrtc/test/network/network_emulation_pc_unittest.cc b/third_party/libwebrtc/test/network/network_emulation_pc_unittest.cc
new file mode 100644
index 0000000000..0519dd816d
--- /dev/null
+++ b/third_party/libwebrtc/test/network/network_emulation_pc_unittest.cc
@@ -0,0 +1,317 @@
+/*
+ * Copyright 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 <cstdint>
+#include <memory>
+
+#include "api/call/call_factory_interface.h"
+#include "api/peer_connection_interface.h"
+#include "api/rtc_event_log/rtc_event_log_factory.h"
+#include "api/scoped_refptr.h"
+#include "api/task_queue/default_task_queue_factory.h"
+#include "api/transport/field_trial_based_config.h"
+#include "call/simulated_network.h"
+#include "media/engine/webrtc_media_engine.h"
+#include "media/engine/webrtc_media_engine_defaults.h"
+#include "modules/audio_device/include/test_audio_device.h"
+#include "p2p/base/basic_packet_socket_factory.h"
+#include "p2p/client/basic_port_allocator.h"
+#include "pc/peer_connection_wrapper.h"
+#include "pc/test/mock_peer_connection_observers.h"
+#include "rtc_base/gunit.h"
+#include "rtc_base/task_queue_for_test.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+#include "test/network/network_emulation.h"
+#include "test/network/network_emulation_manager.h"
+
+namespace webrtc {
+namespace test {
+namespace {
+
+constexpr int kDefaultTimeoutMs = 1000;
+constexpr int kMaxAptitude = 32000;
+constexpr int kSamplingFrequency = 48000;
+constexpr char kSignalThreadName[] = "signaling_thread";
+
+bool AddIceCandidates(PeerConnectionWrapper* peer,
+ std::vector<const IceCandidateInterface*> candidates) {
+ bool success = true;
+ for (const auto candidate : candidates) {
+ if (!peer->pc()->AddIceCandidate(candidate)) {
+ success = false;
+ }
+ }
+ return success;
+}
+
+rtc::scoped_refptr<PeerConnectionFactoryInterface> CreatePeerConnectionFactory(
+ rtc::Thread* signaling_thread,
+ rtc::Thread* network_thread) {
+ PeerConnectionFactoryDependencies pcf_deps;
+ pcf_deps.task_queue_factory = CreateDefaultTaskQueueFactory();
+ pcf_deps.call_factory = CreateCallFactory();
+ pcf_deps.event_log_factory =
+ std::make_unique<RtcEventLogFactory>(pcf_deps.task_queue_factory.get());
+ pcf_deps.network_thread = network_thread;
+ pcf_deps.signaling_thread = signaling_thread;
+ pcf_deps.trials = std::make_unique<FieldTrialBasedConfig>();
+ cricket::MediaEngineDependencies media_deps;
+ media_deps.task_queue_factory = pcf_deps.task_queue_factory.get();
+ media_deps.adm = TestAudioDeviceModule::Create(
+ media_deps.task_queue_factory,
+ TestAudioDeviceModule::CreatePulsedNoiseCapturer(kMaxAptitude,
+ kSamplingFrequency),
+ TestAudioDeviceModule::CreateDiscardRenderer(kSamplingFrequency),
+ /*speed=*/1.f);
+ media_deps.trials = pcf_deps.trials.get();
+ SetMediaEngineDefaults(&media_deps);
+ pcf_deps.media_engine = cricket::CreateMediaEngine(std::move(media_deps));
+ return CreateModularPeerConnectionFactory(std::move(pcf_deps));
+}
+
+rtc::scoped_refptr<PeerConnectionInterface> CreatePeerConnection(
+ const rtc::scoped_refptr<PeerConnectionFactoryInterface>& pcf,
+ PeerConnectionObserver* observer,
+ rtc::PacketSocketFactory* packet_socket_factory,
+ rtc::NetworkManager* network_manager,
+ EmulatedTURNServerInterface* turn_server = nullptr) {
+ PeerConnectionDependencies pc_deps(observer);
+ auto port_allocator = std::make_unique<cricket::BasicPortAllocator>(
+ network_manager, packet_socket_factory);
+
+ // This test does not support TCP
+ int flags = cricket::PORTALLOCATOR_DISABLE_TCP;
+ port_allocator->set_flags(port_allocator->flags() | flags);
+
+ pc_deps.allocator = std::move(port_allocator);
+ PeerConnectionInterface::RTCConfiguration rtc_configuration;
+ rtc_configuration.sdp_semantics = SdpSemantics::kUnifiedPlan;
+ if (turn_server != nullptr) {
+ webrtc::PeerConnectionInterface::IceServer server;
+ server.username = turn_server->GetIceServerConfig().username;
+ server.password = turn_server->GetIceServerConfig().username;
+ server.urls.push_back(turn_server->GetIceServerConfig().url);
+ rtc_configuration.servers.push_back(server);
+ }
+
+ auto result =
+ pcf->CreatePeerConnectionOrError(rtc_configuration, std::move(pc_deps));
+ if (!result.ok()) {
+ return nullptr;
+ }
+ return result.MoveValue();
+}
+
+} // namespace
+
+TEST(NetworkEmulationManagerPCTest, Run) {
+ std::unique_ptr<rtc::Thread> signaling_thread = rtc::Thread::Create();
+ signaling_thread->SetName(kSignalThreadName, nullptr);
+ signaling_thread->Start();
+
+ // Setup emulated network
+ NetworkEmulationManagerImpl emulation(TimeMode::kRealTime);
+
+ EmulatedNetworkNode* alice_node = emulation.CreateEmulatedNode(
+ std::make_unique<SimulatedNetwork>(BuiltInNetworkBehaviorConfig()));
+ EmulatedNetworkNode* bob_node = emulation.CreateEmulatedNode(
+ std::make_unique<SimulatedNetwork>(BuiltInNetworkBehaviorConfig()));
+ EmulatedEndpoint* alice_endpoint =
+ emulation.CreateEndpoint(EmulatedEndpointConfig());
+ EmulatedEndpoint* bob_endpoint =
+ emulation.CreateEndpoint(EmulatedEndpointConfig());
+ emulation.CreateRoute(alice_endpoint, {alice_node}, bob_endpoint);
+ emulation.CreateRoute(bob_endpoint, {bob_node}, alice_endpoint);
+
+ EmulatedNetworkManagerInterface* alice_network =
+ emulation.CreateEmulatedNetworkManagerInterface({alice_endpoint});
+ EmulatedNetworkManagerInterface* bob_network =
+ emulation.CreateEmulatedNetworkManagerInterface({bob_endpoint});
+
+ // Setup peer connections.
+ rtc::scoped_refptr<PeerConnectionFactoryInterface> alice_pcf;
+ rtc::scoped_refptr<PeerConnectionInterface> alice_pc;
+ std::unique_ptr<MockPeerConnectionObserver> alice_observer =
+ std::make_unique<MockPeerConnectionObserver>();
+
+ rtc::scoped_refptr<PeerConnectionFactoryInterface> bob_pcf;
+ rtc::scoped_refptr<PeerConnectionInterface> bob_pc;
+ std::unique_ptr<MockPeerConnectionObserver> bob_observer =
+ std::make_unique<MockPeerConnectionObserver>();
+
+ SendTask(signaling_thread.get(), [&]() {
+ alice_pcf = CreatePeerConnectionFactory(signaling_thread.get(),
+ alice_network->network_thread());
+ alice_pc = CreatePeerConnection(alice_pcf, alice_observer.get(),
+ alice_network->packet_socket_factory(),
+ alice_network->network_manager());
+
+ bob_pcf = CreatePeerConnectionFactory(signaling_thread.get(),
+ bob_network->network_thread());
+ bob_pc = CreatePeerConnection(bob_pcf, bob_observer.get(),
+ bob_network->packet_socket_factory(),
+ bob_network->network_manager());
+ });
+
+ std::unique_ptr<PeerConnectionWrapper> alice =
+ std::make_unique<PeerConnectionWrapper>(alice_pcf, alice_pc,
+ std::move(alice_observer));
+ std::unique_ptr<PeerConnectionWrapper> bob =
+ std::make_unique<PeerConnectionWrapper>(bob_pcf, bob_pc,
+ std::move(bob_observer));
+
+ SendTask(signaling_thread.get(), [&]() {
+ rtc::scoped_refptr<webrtc::AudioSourceInterface> source =
+ alice_pcf->CreateAudioSource(cricket::AudioOptions());
+ rtc::scoped_refptr<AudioTrackInterface> track =
+ alice_pcf->CreateAudioTrack("audio", source.get());
+ alice->AddTransceiver(track);
+
+ // Connect peers.
+ ASSERT_TRUE(alice->ExchangeOfferAnswerWith(bob.get()));
+ // Do the SDP negotiation, and also exchange ice candidates.
+ ASSERT_TRUE_WAIT(
+ alice->signaling_state() == PeerConnectionInterface::kStable,
+ kDefaultTimeoutMs);
+ ASSERT_TRUE_WAIT(alice->IsIceGatheringDone(), kDefaultTimeoutMs);
+ ASSERT_TRUE_WAIT(bob->IsIceGatheringDone(), kDefaultTimeoutMs);
+
+ // Connect an ICE candidate pairs.
+ ASSERT_TRUE(
+ AddIceCandidates(bob.get(), alice->observer()->GetAllCandidates()));
+ ASSERT_TRUE(
+ AddIceCandidates(alice.get(), bob->observer()->GetAllCandidates()));
+ // This means that ICE and DTLS are connected.
+ ASSERT_TRUE_WAIT(bob->IsIceConnected(), kDefaultTimeoutMs);
+ ASSERT_TRUE_WAIT(alice->IsIceConnected(), kDefaultTimeoutMs);
+
+ // Close peer connections
+ alice->pc()->Close();
+ bob->pc()->Close();
+
+ // Delete peers.
+ alice.reset();
+ bob.reset();
+ });
+}
+
+TEST(NetworkEmulationManagerPCTest, RunTURN) {
+ std::unique_ptr<rtc::Thread> signaling_thread = rtc::Thread::Create();
+ signaling_thread->SetName(kSignalThreadName, nullptr);
+ signaling_thread->Start();
+
+ // Setup emulated network
+ NetworkEmulationManagerImpl emulation(TimeMode::kRealTime);
+
+ EmulatedNetworkNode* alice_node = emulation.CreateEmulatedNode(
+ std::make_unique<SimulatedNetwork>(BuiltInNetworkBehaviorConfig()));
+ EmulatedNetworkNode* bob_node = emulation.CreateEmulatedNode(
+ std::make_unique<SimulatedNetwork>(BuiltInNetworkBehaviorConfig()));
+ EmulatedNetworkNode* turn_node = emulation.CreateEmulatedNode(
+ std::make_unique<SimulatedNetwork>(BuiltInNetworkBehaviorConfig()));
+ EmulatedEndpoint* alice_endpoint =
+ emulation.CreateEndpoint(EmulatedEndpointConfig());
+ EmulatedEndpoint* bob_endpoint =
+ emulation.CreateEndpoint(EmulatedEndpointConfig());
+ EmulatedTURNServerInterface* alice_turn =
+ emulation.CreateTURNServer(EmulatedTURNServerConfig());
+ EmulatedTURNServerInterface* bob_turn =
+ emulation.CreateTURNServer(EmulatedTURNServerConfig());
+
+ emulation.CreateRoute(alice_endpoint, {alice_node},
+ alice_turn->GetClientEndpoint());
+ emulation.CreateRoute(alice_turn->GetClientEndpoint(), {alice_node},
+ alice_endpoint);
+
+ emulation.CreateRoute(bob_endpoint, {bob_node},
+ bob_turn->GetClientEndpoint());
+ emulation.CreateRoute(bob_turn->GetClientEndpoint(), {bob_node},
+ bob_endpoint);
+
+ emulation.CreateRoute(alice_turn->GetPeerEndpoint(), {turn_node},
+ bob_turn->GetPeerEndpoint());
+ emulation.CreateRoute(bob_turn->GetPeerEndpoint(), {turn_node},
+ alice_turn->GetPeerEndpoint());
+
+ EmulatedNetworkManagerInterface* alice_network =
+ emulation.CreateEmulatedNetworkManagerInterface({alice_endpoint});
+ EmulatedNetworkManagerInterface* bob_network =
+ emulation.CreateEmulatedNetworkManagerInterface({bob_endpoint});
+
+ // Setup peer connections.
+ rtc::scoped_refptr<PeerConnectionFactoryInterface> alice_pcf;
+ rtc::scoped_refptr<PeerConnectionInterface> alice_pc;
+ std::unique_ptr<MockPeerConnectionObserver> alice_observer =
+ std::make_unique<MockPeerConnectionObserver>();
+
+ rtc::scoped_refptr<PeerConnectionFactoryInterface> bob_pcf;
+ rtc::scoped_refptr<PeerConnectionInterface> bob_pc;
+ std::unique_ptr<MockPeerConnectionObserver> bob_observer =
+ std::make_unique<MockPeerConnectionObserver>();
+
+ SendTask(signaling_thread.get(), [&]() {
+ alice_pcf = CreatePeerConnectionFactory(signaling_thread.get(),
+ alice_network->network_thread());
+ alice_pc = CreatePeerConnection(
+ alice_pcf, alice_observer.get(), alice_network->packet_socket_factory(),
+ alice_network->network_manager(), alice_turn);
+
+ bob_pcf = CreatePeerConnectionFactory(signaling_thread.get(),
+ bob_network->network_thread());
+ bob_pc = CreatePeerConnection(bob_pcf, bob_observer.get(),
+ bob_network->packet_socket_factory(),
+ bob_network->network_manager(), bob_turn);
+ });
+
+ std::unique_ptr<PeerConnectionWrapper> alice =
+ std::make_unique<PeerConnectionWrapper>(alice_pcf, alice_pc,
+ std::move(alice_observer));
+ std::unique_ptr<PeerConnectionWrapper> bob =
+ std::make_unique<PeerConnectionWrapper>(bob_pcf, bob_pc,
+ std::move(bob_observer));
+
+ SendTask(signaling_thread.get(), [&]() {
+ rtc::scoped_refptr<webrtc::AudioSourceInterface> source =
+ alice_pcf->CreateAudioSource(cricket::AudioOptions());
+ rtc::scoped_refptr<AudioTrackInterface> track =
+ alice_pcf->CreateAudioTrack("audio", source.get());
+ alice->AddTransceiver(track);
+
+ // Connect peers.
+ ASSERT_TRUE(alice->ExchangeOfferAnswerWith(bob.get()));
+ // Do the SDP negotiation, and also exchange ice candidates.
+ ASSERT_TRUE_WAIT(
+ alice->signaling_state() == PeerConnectionInterface::kStable,
+ kDefaultTimeoutMs);
+ ASSERT_TRUE_WAIT(alice->IsIceGatheringDone(), kDefaultTimeoutMs);
+ ASSERT_TRUE_WAIT(bob->IsIceGatheringDone(), kDefaultTimeoutMs);
+
+ // Connect an ICE candidate pairs.
+ ASSERT_TRUE(
+ AddIceCandidates(bob.get(), alice->observer()->GetAllCandidates()));
+ ASSERT_TRUE(
+ AddIceCandidates(alice.get(), bob->observer()->GetAllCandidates()));
+ // This means that ICE and DTLS are connected.
+ ASSERT_TRUE_WAIT(bob->IsIceConnected(), kDefaultTimeoutMs);
+ ASSERT_TRUE_WAIT(alice->IsIceConnected(), kDefaultTimeoutMs);
+
+ // Close peer connections
+ alice->pc()->Close();
+ bob->pc()->Close();
+
+ // Delete peers.
+ alice.reset();
+ bob.reset();
+ });
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/third_party/libwebrtc/test/network/network_emulation_unittest.cc b/third_party/libwebrtc/test/network/network_emulation_unittest.cc
new file mode 100644
index 0000000000..591cc2c473
--- /dev/null
+++ b/third_party/libwebrtc/test/network/network_emulation_unittest.cc
@@ -0,0 +1,667 @@
+/*
+ * Copyright 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.h"
+
+#include <atomic>
+#include <memory>
+#include <set>
+
+#include "api/test/simulated_network.h"
+#include "api/units/time_delta.h"
+#include "call/simulated_network.h"
+#include "rtc_base/event.h"
+#include "rtc_base/gunit.h"
+#include "rtc_base/synchronization/mutex.h"
+#include "rtc_base/task_queue_for_test.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+#include "test/network/network_emulation_manager.h"
+
+namespace webrtc {
+namespace test {
+namespace {
+
+using ::testing::ElementsAreArray;
+
+constexpr TimeDelta kNetworkPacketWaitTimeout = TimeDelta::Millis(100);
+constexpr TimeDelta kStatsWaitTimeout = TimeDelta::Seconds(1);
+constexpr int kOverheadIpv4Udp = 20 + 8;
+
+class SocketReader : public sigslot::has_slots<> {
+ public:
+ explicit SocketReader(rtc::Socket* socket, rtc::Thread* network_thread)
+ : socket_(socket), network_thread_(network_thread) {
+ socket_->SignalReadEvent.connect(this, &SocketReader::OnReadEvent);
+ size_ = 128 * 1024;
+ buf_ = new char[size_];
+ }
+ ~SocketReader() override { delete[] buf_; }
+
+ void OnReadEvent(rtc::Socket* socket) {
+ RTC_DCHECK(socket_ == socket);
+ RTC_DCHECK(network_thread_->IsCurrent());
+ int64_t timestamp;
+ len_ = socket_->Recv(buf_, size_, &timestamp);
+
+ MutexLock lock(&lock_);
+ received_count_++;
+ }
+
+ int ReceivedCount() {
+ MutexLock lock(&lock_);
+ return received_count_;
+ }
+
+ private:
+ rtc::Socket* const socket_;
+ rtc::Thread* const network_thread_;
+ char* buf_;
+ size_t size_;
+ int len_;
+
+ Mutex lock_;
+ int received_count_ RTC_GUARDED_BY(lock_) = 0;
+};
+
+class MockReceiver : public EmulatedNetworkReceiverInterface {
+ public:
+ MOCK_METHOD(void, OnPacketReceived, (EmulatedIpPacket packet), (override));
+};
+
+class NetworkEmulationManagerThreeNodesRoutingTest : public ::testing::Test {
+ public:
+ NetworkEmulationManagerThreeNodesRoutingTest() {
+ e1_ = emulation_.CreateEndpoint(EmulatedEndpointConfig());
+ e2_ = emulation_.CreateEndpoint(EmulatedEndpointConfig());
+ e3_ = emulation_.CreateEndpoint(EmulatedEndpointConfig());
+ }
+
+ void SetupRouting(
+ std::function<void(EmulatedEndpoint*,
+ EmulatedEndpoint*,
+ EmulatedEndpoint*,
+ NetworkEmulationManager*)> create_routing_func) {
+ create_routing_func(e1_, e2_, e3_, &emulation_);
+ }
+
+ void SendPacketsAndValidateDelivery() {
+ EXPECT_CALL(r_e1_e2_, OnPacketReceived(::testing::_)).Times(1);
+ EXPECT_CALL(r_e2_e1_, OnPacketReceived(::testing::_)).Times(1);
+ EXPECT_CALL(r_e1_e3_, OnPacketReceived(::testing::_)).Times(1);
+ EXPECT_CALL(r_e3_e1_, OnPacketReceived(::testing::_)).Times(1);
+
+ uint16_t common_send_port = 80;
+ uint16_t r_e1_e2_port = e2_->BindReceiver(0, &r_e1_e2_).value();
+ uint16_t r_e2_e1_port = e1_->BindReceiver(0, &r_e2_e1_).value();
+ uint16_t r_e1_e3_port = e3_->BindReceiver(0, &r_e1_e3_).value();
+ uint16_t r_e3_e1_port = e1_->BindReceiver(0, &r_e3_e1_).value();
+
+ // Next code is using API of EmulatedEndpoint, that is visible only for
+ // internals of network emulation layer. Don't use this API in other tests.
+ // Send packet from e1 to e2.
+ e1_->SendPacket(
+ rtc::SocketAddress(e1_->GetPeerLocalAddress(), common_send_port),
+ rtc::SocketAddress(e2_->GetPeerLocalAddress(), r_e1_e2_port),
+ rtc::CopyOnWriteBuffer(10));
+
+ // Send packet from e2 to e1.
+ e2_->SendPacket(
+ rtc::SocketAddress(e2_->GetPeerLocalAddress(), common_send_port),
+ rtc::SocketAddress(e1_->GetPeerLocalAddress(), r_e2_e1_port),
+ rtc::CopyOnWriteBuffer(10));
+
+ // Send packet from e1 to e3.
+ e1_->SendPacket(
+ rtc::SocketAddress(e1_->GetPeerLocalAddress(), common_send_port),
+ rtc::SocketAddress(e3_->GetPeerLocalAddress(), r_e1_e3_port),
+ rtc::CopyOnWriteBuffer(10));
+
+ // Send packet from e3 to e1.
+ e3_->SendPacket(
+ rtc::SocketAddress(e3_->GetPeerLocalAddress(), common_send_port),
+ rtc::SocketAddress(e1_->GetPeerLocalAddress(), r_e3_e1_port),
+ rtc::CopyOnWriteBuffer(10));
+
+ // Sleep at the end to wait for async packets delivery.
+ emulation_.time_controller()->AdvanceTime(kNetworkPacketWaitTimeout);
+ }
+
+ private:
+ // Receivers: r_<source endpoint>_<destination endpoint>
+ // They must be destroyed after emulation, so they should be declared before.
+ MockReceiver r_e1_e2_;
+ MockReceiver r_e2_e1_;
+ MockReceiver r_e1_e3_;
+ MockReceiver r_e3_e1_;
+
+ NetworkEmulationManagerImpl emulation_{TimeMode::kRealTime};
+ EmulatedEndpoint* e1_;
+ EmulatedEndpoint* e2_;
+ EmulatedEndpoint* e3_;
+};
+
+EmulatedNetworkNode* CreateEmulatedNodeWithDefaultBuiltInConfig(
+ NetworkEmulationManager* emulation) {
+ return emulation->CreateEmulatedNode(
+ std::make_unique<SimulatedNetwork>(BuiltInNetworkBehaviorConfig()));
+}
+
+} // namespace
+
+using ::testing::_;
+
+TEST(NetworkEmulationManagerTest, GeneratedIpv4AddressDoesNotCollide) {
+ NetworkEmulationManagerImpl network_manager(TimeMode::kRealTime);
+ std::set<rtc::IPAddress> ips;
+ EmulatedEndpointConfig config;
+ config.generated_ip_family = EmulatedEndpointConfig::IpAddressFamily::kIpv4;
+ for (int i = 0; i < 1000; i++) {
+ EmulatedEndpoint* endpoint = network_manager.CreateEndpoint(config);
+ ASSERT_EQ(endpoint->GetPeerLocalAddress().family(), AF_INET);
+ bool result = ips.insert(endpoint->GetPeerLocalAddress()).second;
+ ASSERT_TRUE(result);
+ }
+}
+
+TEST(NetworkEmulationManagerTest, GeneratedIpv6AddressDoesNotCollide) {
+ NetworkEmulationManagerImpl network_manager(TimeMode::kRealTime);
+ std::set<rtc::IPAddress> ips;
+ EmulatedEndpointConfig config;
+ config.generated_ip_family = EmulatedEndpointConfig::IpAddressFamily::kIpv6;
+ for (int i = 0; i < 1000; i++) {
+ EmulatedEndpoint* endpoint = network_manager.CreateEndpoint(config);
+ ASSERT_EQ(endpoint->GetPeerLocalAddress().family(), AF_INET6);
+ bool result = ips.insert(endpoint->GetPeerLocalAddress()).second;
+ ASSERT_TRUE(result);
+ }
+}
+
+TEST(NetworkEmulationManagerTest, Run) {
+ NetworkEmulationManagerImpl network_manager(TimeMode::kRealTime);
+
+ EmulatedNetworkNode* alice_node = network_manager.CreateEmulatedNode(
+ std::make_unique<SimulatedNetwork>(BuiltInNetworkBehaviorConfig()));
+ EmulatedNetworkNode* bob_node = network_manager.CreateEmulatedNode(
+ std::make_unique<SimulatedNetwork>(BuiltInNetworkBehaviorConfig()));
+ EmulatedEndpoint* alice_endpoint =
+ network_manager.CreateEndpoint(EmulatedEndpointConfig());
+ EmulatedEndpoint* bob_endpoint =
+ network_manager.CreateEndpoint(EmulatedEndpointConfig());
+ network_manager.CreateRoute(alice_endpoint, {alice_node}, bob_endpoint);
+ network_manager.CreateRoute(bob_endpoint, {bob_node}, alice_endpoint);
+
+ EmulatedNetworkManagerInterface* nt1 =
+ network_manager.CreateEmulatedNetworkManagerInterface({alice_endpoint});
+ EmulatedNetworkManagerInterface* nt2 =
+ network_manager.CreateEmulatedNetworkManagerInterface({bob_endpoint});
+
+ rtc::Thread* t1 = nt1->network_thread();
+ rtc::Thread* t2 = nt2->network_thread();
+
+ rtc::CopyOnWriteBuffer data("Hello");
+ for (uint64_t j = 0; j < 2; j++) {
+ rtc::Socket* s1 = nullptr;
+ rtc::Socket* s2 = nullptr;
+ SendTask(t1, [&] {
+ s1 = t1->socketserver()->CreateSocket(AF_INET, SOCK_DGRAM);
+ });
+ SendTask(t2, [&] {
+ s2 = t2->socketserver()->CreateSocket(AF_INET, SOCK_DGRAM);
+ });
+
+ SocketReader r1(s1, t1);
+ SocketReader r2(s2, t2);
+
+ rtc::SocketAddress a1(alice_endpoint->GetPeerLocalAddress(), 0);
+ rtc::SocketAddress a2(bob_endpoint->GetPeerLocalAddress(), 0);
+
+ SendTask(t1, [&] {
+ s1->Bind(a1);
+ a1 = s1->GetLocalAddress();
+ });
+ SendTask(t2, [&] {
+ s2->Bind(a2);
+ a2 = s2->GetLocalAddress();
+ });
+
+ SendTask(t1, [&] { s1->Connect(a2); });
+ SendTask(t2, [&] { s2->Connect(a1); });
+
+ for (uint64_t i = 0; i < 1000; i++) {
+ t1->PostTask([&]() { s1->Send(data.data(), data.size()); });
+ t2->PostTask([&]() { s2->Send(data.data(), data.size()); });
+ }
+
+ network_manager.time_controller()->AdvanceTime(TimeDelta::Seconds(1));
+
+ EXPECT_EQ(r1.ReceivedCount(), 1000);
+ EXPECT_EQ(r2.ReceivedCount(), 1000);
+
+ SendTask(t1, [&] { delete s1; });
+ SendTask(t2, [&] { delete s2; });
+ }
+
+ const int64_t single_packet_size = data.size() + kOverheadIpv4Udp;
+ std::atomic<int> received_stats_count{0};
+ nt1->GetStats([&](std::unique_ptr<EmulatedNetworkStats> st) {
+ EXPECT_EQ(st->PacketsSent(), 2000l);
+ EXPECT_EQ(st->BytesSent().bytes(), single_packet_size * 2000l);
+ EXPECT_THAT(st->LocalAddresses(),
+ ElementsAreArray({alice_endpoint->GetPeerLocalAddress()}));
+ EXPECT_EQ(st->PacketsReceived(), 2000l);
+ EXPECT_EQ(st->BytesReceived().bytes(), single_packet_size * 2000l);
+ EXPECT_EQ(st->PacketsDropped(), 0l);
+ EXPECT_EQ(st->BytesDropped().bytes(), 0l);
+
+ rtc::IPAddress bob_ip = bob_endpoint->GetPeerLocalAddress();
+ std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkIncomingStats>>
+ source_st = st->IncomingStatsPerSource();
+ ASSERT_EQ(source_st.size(), 1lu);
+ EXPECT_EQ(source_st.at(bob_ip)->PacketsReceived(), 2000l);
+ EXPECT_EQ(source_st.at(bob_ip)->BytesReceived().bytes(),
+ single_packet_size * 2000l);
+ EXPECT_EQ(source_st.at(bob_ip)->PacketsDropped(), 0l);
+ EXPECT_EQ(source_st.at(bob_ip)->BytesDropped().bytes(), 0l);
+
+ std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkOutgoingStats>>
+ dest_st = st->OutgoingStatsPerDestination();
+ ASSERT_EQ(dest_st.size(), 1lu);
+ EXPECT_EQ(dest_st.at(bob_ip)->PacketsSent(), 2000l);
+ EXPECT_EQ(dest_st.at(bob_ip)->BytesSent().bytes(),
+ single_packet_size * 2000l);
+
+ // No debug stats are collected by default.
+ EXPECT_TRUE(st->SentPacketsSizeCounter().IsEmpty());
+ EXPECT_TRUE(st->SentPacketsQueueWaitTimeUs().IsEmpty());
+ EXPECT_TRUE(st->ReceivedPacketsSizeCounter().IsEmpty());
+ EXPECT_TRUE(st->DroppedPacketsSizeCounter().IsEmpty());
+ EXPECT_TRUE(dest_st.at(bob_ip)->SentPacketsSizeCounter().IsEmpty());
+ EXPECT_TRUE(source_st.at(bob_ip)->ReceivedPacketsSizeCounter().IsEmpty());
+ EXPECT_TRUE(source_st.at(bob_ip)->DroppedPacketsSizeCounter().IsEmpty());
+
+ received_stats_count++;
+ });
+ nt2->GetStats([&](std::unique_ptr<EmulatedNetworkStats> st) {
+ EXPECT_EQ(st->PacketsSent(), 2000l);
+ EXPECT_EQ(st->BytesSent().bytes(), single_packet_size * 2000l);
+ EXPECT_THAT(st->LocalAddresses(),
+ ElementsAreArray({bob_endpoint->GetPeerLocalAddress()}));
+ EXPECT_EQ(st->PacketsReceived(), 2000l);
+ EXPECT_EQ(st->BytesReceived().bytes(), single_packet_size * 2000l);
+ EXPECT_EQ(st->PacketsDropped(), 0l);
+ EXPECT_EQ(st->BytesDropped().bytes(), 0l);
+ EXPECT_GT(st->FirstReceivedPacketSize(), DataSize::Zero());
+ EXPECT_TRUE(st->FirstPacketReceivedTime().IsFinite());
+ EXPECT_TRUE(st->LastPacketReceivedTime().IsFinite());
+
+ rtc::IPAddress alice_ip = alice_endpoint->GetPeerLocalAddress();
+ std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkIncomingStats>>
+ source_st = st->IncomingStatsPerSource();
+ ASSERT_EQ(source_st.size(), 1lu);
+ EXPECT_EQ(source_st.at(alice_ip)->PacketsReceived(), 2000l);
+ EXPECT_EQ(source_st.at(alice_ip)->BytesReceived().bytes(),
+ single_packet_size * 2000l);
+ EXPECT_EQ(source_st.at(alice_ip)->PacketsDropped(), 0l);
+ EXPECT_EQ(source_st.at(alice_ip)->BytesDropped().bytes(), 0l);
+
+ std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkOutgoingStats>>
+ dest_st = st->OutgoingStatsPerDestination();
+ ASSERT_EQ(dest_st.size(), 1lu);
+ EXPECT_EQ(dest_st.at(alice_ip)->PacketsSent(), 2000l);
+ EXPECT_EQ(dest_st.at(alice_ip)->BytesSent().bytes(),
+ single_packet_size * 2000l);
+
+ // No debug stats are collected by default.
+ EXPECT_TRUE(st->SentPacketsSizeCounter().IsEmpty());
+ EXPECT_TRUE(st->SentPacketsQueueWaitTimeUs().IsEmpty());
+ EXPECT_TRUE(st->ReceivedPacketsSizeCounter().IsEmpty());
+ EXPECT_TRUE(st->DroppedPacketsSizeCounter().IsEmpty());
+ EXPECT_TRUE(dest_st.at(alice_ip)->SentPacketsSizeCounter().IsEmpty());
+ EXPECT_TRUE(source_st.at(alice_ip)->ReceivedPacketsSizeCounter().IsEmpty());
+ EXPECT_TRUE(source_st.at(alice_ip)->DroppedPacketsSizeCounter().IsEmpty());
+
+ received_stats_count++;
+ });
+ ASSERT_EQ_SIMULATED_WAIT(received_stats_count.load(), 2,
+ kStatsWaitTimeout.ms(),
+ *network_manager.time_controller());
+}
+
+TEST(NetworkEmulationManagerTest, DebugStatsCollectedInDebugMode) {
+ NetworkEmulationManagerImpl network_manager(TimeMode::kSimulated);
+
+ EmulatedNetworkNode* alice_node = network_manager.CreateEmulatedNode(
+ std::make_unique<SimulatedNetwork>(BuiltInNetworkBehaviorConfig()));
+ EmulatedNetworkNode* bob_node = network_manager.CreateEmulatedNode(
+ std::make_unique<SimulatedNetwork>(BuiltInNetworkBehaviorConfig()));
+ EmulatedEndpointConfig debug_config;
+ debug_config.stats_gathering_mode =
+ EmulatedEndpointConfig::StatsGatheringMode::kDebug;
+ EmulatedEndpoint* alice_endpoint =
+ network_manager.CreateEndpoint(debug_config);
+ EmulatedEndpoint* bob_endpoint =
+ network_manager.CreateEndpoint(EmulatedEndpointConfig());
+ network_manager.CreateRoute(alice_endpoint, {alice_node}, bob_endpoint);
+ network_manager.CreateRoute(bob_endpoint, {bob_node}, alice_endpoint);
+
+ EmulatedNetworkManagerInterface* nt1 =
+ network_manager.CreateEmulatedNetworkManagerInterface({alice_endpoint});
+ EmulatedNetworkManagerInterface* nt2 =
+ network_manager.CreateEmulatedNetworkManagerInterface({bob_endpoint});
+
+ rtc::Thread* t1 = nt1->network_thread();
+ rtc::Thread* t2 = nt2->network_thread();
+
+ rtc::CopyOnWriteBuffer data("Hello");
+ for (uint64_t j = 0; j < 2; j++) {
+ rtc::Socket* s1 = nullptr;
+ rtc::Socket* s2 = nullptr;
+ SendTask(t1, [&] {
+ s1 = t1->socketserver()->CreateSocket(AF_INET, SOCK_DGRAM);
+ });
+ SendTask(t2, [&] {
+ s2 = t2->socketserver()->CreateSocket(AF_INET, SOCK_DGRAM);
+ });
+
+ SocketReader r1(s1, t1);
+ SocketReader r2(s2, t2);
+
+ rtc::SocketAddress a1(alice_endpoint->GetPeerLocalAddress(), 0);
+ rtc::SocketAddress a2(bob_endpoint->GetPeerLocalAddress(), 0);
+
+ SendTask(t1, [&] {
+ s1->Bind(a1);
+ a1 = s1->GetLocalAddress();
+ });
+ SendTask(t2, [&] {
+ s2->Bind(a2);
+ a2 = s2->GetLocalAddress();
+ });
+
+ SendTask(t1, [&] { s1->Connect(a2); });
+ SendTask(t2, [&] { s2->Connect(a1); });
+
+ for (uint64_t i = 0; i < 1000; i++) {
+ t1->PostTask([&]() { s1->Send(data.data(), data.size()); });
+ t2->PostTask([&]() { s2->Send(data.data(), data.size()); });
+ }
+
+ network_manager.time_controller()->AdvanceTime(TimeDelta::Seconds(1));
+
+ EXPECT_EQ(r1.ReceivedCount(), 1000);
+ EXPECT_EQ(r2.ReceivedCount(), 1000);
+
+ SendTask(t1, [&] { delete s1; });
+ SendTask(t2, [&] { delete s2; });
+ }
+
+ const int64_t single_packet_size = data.size() + kOverheadIpv4Udp;
+ std::atomic<int> received_stats_count{0};
+ nt1->GetStats([&](std::unique_ptr<EmulatedNetworkStats> st) {
+ rtc::IPAddress bob_ip = bob_endpoint->GetPeerLocalAddress();
+ std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkIncomingStats>>
+ source_st = st->IncomingStatsPerSource();
+ ASSERT_EQ(source_st.size(), 1lu);
+
+ std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkOutgoingStats>>
+ dest_st = st->OutgoingStatsPerDestination();
+ ASSERT_EQ(dest_st.size(), 1lu);
+
+ // No debug stats are collected by default.
+ EXPECT_EQ(st->SentPacketsSizeCounter().NumSamples(), 2000l);
+ EXPECT_EQ(st->ReceivedPacketsSizeCounter().GetAverage(),
+ single_packet_size);
+ EXPECT_EQ(st->SentPacketsQueueWaitTimeUs().NumSamples(), 2000l);
+ EXPECT_LT(st->SentPacketsQueueWaitTimeUs().GetMax(), 1);
+ EXPECT_TRUE(st->DroppedPacketsSizeCounter().IsEmpty());
+ EXPECT_EQ(dest_st.at(bob_ip)->SentPacketsSizeCounter().NumSamples(), 2000l);
+ EXPECT_EQ(dest_st.at(bob_ip)->SentPacketsSizeCounter().GetAverage(),
+ single_packet_size);
+ EXPECT_EQ(source_st.at(bob_ip)->ReceivedPacketsSizeCounter().NumSamples(),
+ 2000l);
+ EXPECT_EQ(source_st.at(bob_ip)->ReceivedPacketsSizeCounter().GetAverage(),
+ single_packet_size);
+ EXPECT_TRUE(source_st.at(bob_ip)->DroppedPacketsSizeCounter().IsEmpty());
+
+ received_stats_count++;
+ });
+ ASSERT_EQ_SIMULATED_WAIT(received_stats_count.load(), 1,
+ kStatsWaitTimeout.ms(),
+ *network_manager.time_controller());
+}
+
+TEST(NetworkEmulationManagerTest, ThroughputStats) {
+ NetworkEmulationManagerImpl network_manager(TimeMode::kRealTime);
+
+ EmulatedNetworkNode* alice_node = network_manager.CreateEmulatedNode(
+ std::make_unique<SimulatedNetwork>(BuiltInNetworkBehaviorConfig()));
+ EmulatedNetworkNode* bob_node = network_manager.CreateEmulatedNode(
+ std::make_unique<SimulatedNetwork>(BuiltInNetworkBehaviorConfig()));
+ EmulatedEndpoint* alice_endpoint =
+ network_manager.CreateEndpoint(EmulatedEndpointConfig());
+ EmulatedEndpoint* bob_endpoint =
+ network_manager.CreateEndpoint(EmulatedEndpointConfig());
+ network_manager.CreateRoute(alice_endpoint, {alice_node}, bob_endpoint);
+ network_manager.CreateRoute(bob_endpoint, {bob_node}, alice_endpoint);
+
+ EmulatedNetworkManagerInterface* nt1 =
+ network_manager.CreateEmulatedNetworkManagerInterface({alice_endpoint});
+ EmulatedNetworkManagerInterface* nt2 =
+ network_manager.CreateEmulatedNetworkManagerInterface({bob_endpoint});
+
+ rtc::Thread* t1 = nt1->network_thread();
+ rtc::Thread* t2 = nt2->network_thread();
+
+ constexpr int64_t kUdpPayloadSize = 100;
+ constexpr int64_t kSinglePacketSize = kUdpPayloadSize + kOverheadIpv4Udp;
+ rtc::CopyOnWriteBuffer data(kUdpPayloadSize);
+
+ rtc::Socket* s1 = nullptr;
+ rtc::Socket* s2 = nullptr;
+ SendTask(t1,
+ [&] { s1 = t1->socketserver()->CreateSocket(AF_INET, SOCK_DGRAM); });
+ SendTask(t2,
+ [&] { s2 = t2->socketserver()->CreateSocket(AF_INET, SOCK_DGRAM); });
+
+ SocketReader r1(s1, t1);
+ SocketReader r2(s2, t2);
+
+ rtc::SocketAddress a1(alice_endpoint->GetPeerLocalAddress(), 0);
+ rtc::SocketAddress a2(bob_endpoint->GetPeerLocalAddress(), 0);
+
+ SendTask(t1, [&] {
+ s1->Bind(a1);
+ a1 = s1->GetLocalAddress();
+ });
+ SendTask(t2, [&] {
+ s2->Bind(a2);
+ a2 = s2->GetLocalAddress();
+ });
+
+ SendTask(t1, [&] { s1->Connect(a2); });
+ SendTask(t2, [&] { s2->Connect(a1); });
+
+ // Send 11 packets, totalizing 1 second between the first and the last.
+ const int kNumPacketsSent = 11;
+ const TimeDelta kDelay = TimeDelta::Millis(100);
+ for (int i = 0; i < kNumPacketsSent; i++) {
+ t1->PostTask([&]() { s1->Send(data.data(), data.size()); });
+ t2->PostTask([&]() { s2->Send(data.data(), data.size()); });
+ network_manager.time_controller()->AdvanceTime(kDelay);
+ }
+
+ std::atomic<int> received_stats_count{0};
+ nt1->GetStats([&](std::unique_ptr<EmulatedNetworkStats> st) {
+ EXPECT_EQ(st->PacketsSent(), kNumPacketsSent);
+ EXPECT_EQ(st->BytesSent().bytes(), kSinglePacketSize * kNumPacketsSent);
+
+ const double tolerance = 0.95; // Accept 5% tolerance for timing.
+ EXPECT_GE(st->LastPacketSentTime() - st->FirstPacketSentTime(),
+ (kNumPacketsSent - 1) * kDelay * tolerance);
+ EXPECT_GT(st->AverageSendRate().bps(), 0);
+ received_stats_count++;
+ });
+
+ ASSERT_EQ_SIMULATED_WAIT(received_stats_count.load(), 1,
+ kStatsWaitTimeout.ms(),
+ *network_manager.time_controller());
+
+ EXPECT_EQ(r1.ReceivedCount(), 11);
+ EXPECT_EQ(r2.ReceivedCount(), 11);
+
+ SendTask(t1, [&] { delete s1; });
+ SendTask(t2, [&] { delete s2; });
+}
+
+// Testing that packets are delivered via all routes using a routing scheme as
+// follows:
+// * e1 -> n1 -> e2
+// * e2 -> n2 -> e1
+// * e1 -> n3 -> e3
+// * e3 -> n4 -> e1
+TEST_F(NetworkEmulationManagerThreeNodesRoutingTest,
+ PacketsAreDeliveredInBothWaysWhenConnectedToTwoPeers) {
+ SetupRouting([](EmulatedEndpoint* e1, EmulatedEndpoint* e2,
+ EmulatedEndpoint* e3, NetworkEmulationManager* emulation) {
+ auto* node1 = CreateEmulatedNodeWithDefaultBuiltInConfig(emulation);
+ auto* node2 = CreateEmulatedNodeWithDefaultBuiltInConfig(emulation);
+ auto* node3 = CreateEmulatedNodeWithDefaultBuiltInConfig(emulation);
+ auto* node4 = CreateEmulatedNodeWithDefaultBuiltInConfig(emulation);
+
+ emulation->CreateRoute(e1, {node1}, e2);
+ emulation->CreateRoute(e2, {node2}, e1);
+
+ emulation->CreateRoute(e1, {node3}, e3);
+ emulation->CreateRoute(e3, {node4}, e1);
+ });
+ SendPacketsAndValidateDelivery();
+}
+
+// Testing that packets are delivered via all routes using a routing scheme as
+// follows:
+// * e1 -> n1 -> e2
+// * e2 -> n2 -> e1
+// * e1 -> n1 -> e3
+// * e3 -> n4 -> e1
+TEST_F(NetworkEmulationManagerThreeNodesRoutingTest,
+ PacketsAreDeliveredInBothWaysWhenConnectedToTwoPeersOverSameSendLink) {
+ SetupRouting([](EmulatedEndpoint* e1, EmulatedEndpoint* e2,
+ EmulatedEndpoint* e3, NetworkEmulationManager* emulation) {
+ auto* node1 = CreateEmulatedNodeWithDefaultBuiltInConfig(emulation);
+ auto* node2 = CreateEmulatedNodeWithDefaultBuiltInConfig(emulation);
+ auto* node3 = CreateEmulatedNodeWithDefaultBuiltInConfig(emulation);
+
+ emulation->CreateRoute(e1, {node1}, e2);
+ emulation->CreateRoute(e2, {node2}, e1);
+
+ emulation->CreateRoute(e1, {node1}, e3);
+ emulation->CreateRoute(e3, {node3}, e1);
+ });
+ SendPacketsAndValidateDelivery();
+}
+
+TEST(NetworkEmulationManagerTest, EndpointLoopback) {
+ NetworkEmulationManagerImpl network_manager(TimeMode::kSimulated);
+ auto endpoint = network_manager.CreateEndpoint(EmulatedEndpointConfig());
+
+ MockReceiver receiver;
+ EXPECT_CALL(receiver, OnPacketReceived(::testing::_)).Times(1);
+ ASSERT_EQ(endpoint->BindReceiver(80, &receiver), 80);
+
+ endpoint->SendPacket(rtc::SocketAddress(endpoint->GetPeerLocalAddress(), 80),
+ rtc::SocketAddress(endpoint->GetPeerLocalAddress(), 80),
+ "Hello");
+ network_manager.time_controller()->AdvanceTime(TimeDelta::Seconds(1));
+}
+
+TEST(NetworkEmulationManagerTest, EndpointCanSendWithDifferentSourceIp) {
+ constexpr uint32_t kEndpointIp = 0xC0A80011; // 192.168.0.17
+ constexpr uint32_t kSourceIp = 0xC0A80012; // 192.168.0.18
+ NetworkEmulationManagerImpl network_manager(TimeMode::kSimulated);
+ EmulatedEndpointConfig endpoint_config;
+ endpoint_config.ip = rtc::IPAddress(kEndpointIp);
+ endpoint_config.allow_send_packet_with_different_source_ip = true;
+ auto endpoint = network_manager.CreateEndpoint(endpoint_config);
+
+ MockReceiver receiver;
+ EXPECT_CALL(receiver, OnPacketReceived(::testing::_)).Times(1);
+ ASSERT_EQ(endpoint->BindReceiver(80, &receiver), 80);
+
+ endpoint->SendPacket(rtc::SocketAddress(kSourceIp, 80),
+ rtc::SocketAddress(endpoint->GetPeerLocalAddress(), 80),
+ "Hello");
+ network_manager.time_controller()->AdvanceTime(TimeDelta::Seconds(1));
+}
+
+TEST(NetworkEmulationManagerTest,
+ EndpointCanReceiveWithDifferentDestIpThroughDefaultRoute) {
+ constexpr uint32_t kDestEndpointIp = 0xC0A80011; // 192.168.0.17
+ constexpr uint32_t kDestIp = 0xC0A80012; // 192.168.0.18
+ NetworkEmulationManagerImpl network_manager(TimeMode::kSimulated);
+ auto sender_endpoint =
+ network_manager.CreateEndpoint(EmulatedEndpointConfig());
+ EmulatedEndpointConfig endpoint_config;
+ endpoint_config.ip = rtc::IPAddress(kDestEndpointIp);
+ endpoint_config.allow_receive_packets_with_different_dest_ip = true;
+ auto receiver_endpoint = network_manager.CreateEndpoint(endpoint_config);
+
+ MockReceiver receiver;
+ EXPECT_CALL(receiver, OnPacketReceived(::testing::_)).Times(1);
+ ASSERT_EQ(receiver_endpoint->BindReceiver(80, &receiver), 80);
+
+ network_manager.CreateDefaultRoute(
+ sender_endpoint, {network_manager.NodeBuilder().Build().node},
+ receiver_endpoint);
+
+ sender_endpoint->SendPacket(
+ rtc::SocketAddress(sender_endpoint->GetPeerLocalAddress(), 80),
+ rtc::SocketAddress(kDestIp, 80), "Hello");
+ network_manager.time_controller()->AdvanceTime(TimeDelta::Seconds(1));
+}
+
+TEST(NetworkEmulationManagerTURNTest, GetIceServerConfig) {
+ NetworkEmulationManagerImpl network_manager(TimeMode::kRealTime);
+ auto turn = network_manager.CreateTURNServer(EmulatedTURNServerConfig());
+
+ EXPECT_GT(turn->GetIceServerConfig().username.size(), 0u);
+ EXPECT_GT(turn->GetIceServerConfig().password.size(), 0u);
+ EXPECT_NE(turn->GetIceServerConfig().url.find(
+ turn->GetClientEndpoint()->GetPeerLocalAddress().ToString()),
+ std::string::npos);
+}
+
+TEST(NetworkEmulationManagerTURNTest, ClientTraffic) {
+ NetworkEmulationManagerImpl emulation(TimeMode::kSimulated);
+ auto* ep = emulation.CreateEndpoint(EmulatedEndpointConfig());
+ auto* turn = emulation.CreateTURNServer(EmulatedTURNServerConfig());
+ auto* node = CreateEmulatedNodeWithDefaultBuiltInConfig(&emulation);
+ emulation.CreateRoute(ep, {node}, turn->GetClientEndpoint());
+ emulation.CreateRoute(turn->GetClientEndpoint(), {node}, ep);
+
+ MockReceiver recv;
+ int port = ep->BindReceiver(0, &recv).value();
+
+ // Construct a STUN BINDING.
+ cricket::StunMessage ping(cricket::STUN_BINDING_REQUEST);
+ rtc::ByteBufferWriter buf;
+ ping.Write(&buf);
+ rtc::CopyOnWriteBuffer packet(buf.Data(), buf.Length());
+
+ // We expect to get a ping reply.
+ EXPECT_CALL(recv, OnPacketReceived(::testing::_)).Times(1);
+
+ ep->SendPacket(rtc::SocketAddress(ep->GetPeerLocalAddress(), port),
+ turn->GetClientEndpointAddress(), packet);
+ emulation.time_controller()->AdvanceTime(TimeDelta::Seconds(1));
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/third_party/libwebrtc/test/network/traffic_route.cc b/third_party/libwebrtc/test/network/traffic_route.cc
new file mode 100644
index 0000000000..81bb8ca514
--- /dev/null
+++ b/third_party/libwebrtc/test/network/traffic_route.cc
@@ -0,0 +1,91 @@
+/*
+ * 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/traffic_route.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "absl/types/optional.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/numerics/safe_minmax.h"
+
+namespace webrtc {
+namespace test {
+namespace {
+
+class NullReceiver : public EmulatedNetworkReceiverInterface {
+ public:
+ void OnPacketReceived(EmulatedIpPacket packet) override {}
+};
+
+class ActionReceiver : public EmulatedNetworkReceiverInterface {
+ public:
+ explicit ActionReceiver(std::function<void()> action) : action_(action) {}
+ ~ActionReceiver() override = default;
+
+ void OnPacketReceived(EmulatedIpPacket packet) override {
+ action_();
+ }
+
+ private:
+ std::function<void()> action_;
+};
+
+} // namespace
+
+CrossTrafficRouteImpl::CrossTrafficRouteImpl(
+ Clock* clock,
+ EmulatedNetworkReceiverInterface* receiver,
+ EmulatedEndpointImpl* endpoint)
+ : clock_(clock), receiver_(receiver), endpoint_(endpoint) {
+ null_receiver_ = std::make_unique<NullReceiver>();
+ absl::optional<uint16_t> port =
+ endpoint_->BindReceiver(0, null_receiver_.get());
+ RTC_DCHECK(port);
+ null_receiver_port_ = port.value();
+}
+CrossTrafficRouteImpl::~CrossTrafficRouteImpl() = default;
+
+void CrossTrafficRouteImpl::TriggerPacketBurst(size_t num_packets,
+ size_t packet_size) {
+ for (size_t i = 0; i < num_packets; ++i) {
+ SendPacket(packet_size);
+ }
+}
+
+void CrossTrafficRouteImpl::NetworkDelayedAction(size_t packet_size,
+ std::function<void()> action) {
+ auto action_receiver = std::make_unique<ActionReceiver>(action);
+ // BindOneShotReceiver arranges to free the port in the endpoint after the
+ // action is done.
+ absl::optional<uint16_t> port =
+ endpoint_->BindOneShotReceiver(0, action_receiver.get());
+ RTC_DCHECK(port);
+ actions_.push_back(std::move(action_receiver));
+ SendPacket(packet_size, port.value());
+}
+
+void CrossTrafficRouteImpl::SendPacket(size_t packet_size) {
+ SendPacket(packet_size, null_receiver_port_);
+}
+
+void CrossTrafficRouteImpl::SendPacket(size_t packet_size, uint16_t dest_port) {
+ rtc::CopyOnWriteBuffer data(packet_size);
+ std::fill_n(data.MutableData(), data.size(), 0);
+ receiver_->OnPacketReceived(EmulatedIpPacket(
+ /*from=*/rtc::SocketAddress(),
+ rtc::SocketAddress(endpoint_->GetPeerLocalAddress(), dest_port), data,
+ clock_->CurrentTime()));
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/third_party/libwebrtc/test/network/traffic_route.h b/third_party/libwebrtc/test/network/traffic_route.h
new file mode 100644
index 0000000000..dbc41a694f
--- /dev/null
+++ b/third_party/libwebrtc/test/network/traffic_route.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#ifndef TEST_NETWORK_TRAFFIC_ROUTE_H_
+#define TEST_NETWORK_TRAFFIC_ROUTE_H_
+
+#include <memory>
+#include <vector>
+
+#include "api/test/network_emulation_manager.h"
+#include "rtc_base/copy_on_write_buffer.h"
+#include "system_wrappers/include/clock.h"
+#include "test/network/network_emulation.h"
+
+namespace webrtc {
+namespace test {
+
+// Represents the endpoint for cross traffic that is going through the network.
+// It can be used to emulate unexpected network load.
+class CrossTrafficRouteImpl final : public CrossTrafficRoute {
+ public:
+ CrossTrafficRouteImpl(Clock* clock,
+ EmulatedNetworkReceiverInterface* receiver,
+ EmulatedEndpointImpl* endpoint);
+ ~CrossTrafficRouteImpl();
+
+ // Triggers sending of dummy packets with size `packet_size` bytes.
+ void TriggerPacketBurst(size_t num_packets, size_t packet_size) override;
+ // Sends a packet over the nodes and runs `action` when it has been delivered.
+ void NetworkDelayedAction(size_t packet_size,
+ std::function<void()> action) override;
+
+ void SendPacket(size_t packet_size) override;
+
+ private:
+ void SendPacket(size_t packet_size, uint16_t dest_port);
+
+ Clock* const clock_;
+ EmulatedNetworkReceiverInterface* const receiver_;
+ EmulatedEndpointImpl* const endpoint_;
+
+ uint16_t null_receiver_port_;
+ std::unique_ptr<EmulatedNetworkReceiverInterface> null_receiver_;
+ std::vector<std::unique_ptr<EmulatedNetworkReceiverInterface>> actions_;
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // TEST_NETWORK_TRAFFIC_ROUTE_H_