diff --git a/api/test/network_emulation/BUILD.gn b/api/test/network_emulation/BUILD.gn index 3534ed5fd5..1042ca1525 100644 --- a/api/test/network_emulation/BUILD.gn +++ b/api/test/network_emulation/BUILD.gn @@ -38,6 +38,8 @@ rtc_library("network_emulation") { sources = [ "cross_traffic.h", + "ecn_marking_counter.cc", + "ecn_marking_counter.h", "network_emulation_interfaces.cc", "network_emulation_interfaces.h", ] diff --git a/api/test/network_emulation/ecn_marking_counter.cc b/api/test/network_emulation/ecn_marking_counter.cc new file mode 100644 index 0000000000..759ad6c8eb --- /dev/null +++ b/api/test/network_emulation/ecn_marking_counter.cc @@ -0,0 +1,40 @@ +/* + * Copyright 2024 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/test/network_emulation/ecn_marking_counter.h" + +namespace webrtc { + +void EcnMarkingCounter::Add(EcnMarking ecn) { + switch (ecn) { + case EcnMarking::kNotEct: + ++not_ect_; + break; + case EcnMarking::kEct0: + ++ect_0_; + break; + case EcnMarking::kEct1: + ++ect_1_; + break; + case EcnMarking::kCe: + ++ce_; + break; + } +} + +EcnMarkingCounter& EcnMarkingCounter::operator+=( + const EcnMarkingCounter& counter) { + not_ect_ += counter.not_ect(); + ect_0_ += counter.ect_0(); + ect_1_ += counter.ect_1(); + ce_ += counter.ce(); + return *this; +} + +} // namespace webrtc diff --git a/api/test/network_emulation/ecn_marking_counter.h b/api/test/network_emulation/ecn_marking_counter.h new file mode 100644 index 0000000000..24c8bffb72 --- /dev/null +++ b/api/test/network_emulation/ecn_marking_counter.h @@ -0,0 +1,41 @@ +/* + * Copyright 2024 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 API_TEST_NETWORK_EMULATION_ECN_MARKING_COUNTER_H_ +#define API_TEST_NETWORK_EMULATION_ECN_MARKING_COUNTER_H_ + +#include "api/transport/ecn_marking.h" + +namespace webrtc { + +// Counts Explicit Congestion Notifaction marks in IP packets. +// https://www.rfc-editor.org/rfc/rfc9331.html +class EcnMarkingCounter { + public: + // Number of packets without ECT explicitly set sent through the network. + int not_ect() const { return not_ect_; } + // Number of packets with ECT(1) sent through the network. + int ect_0() const { return ect_0_; } + // Number of packets with ECT(1) sent through the network. + int ect_1() const { return ect_1_; } + // Number of packets the network has marked as CE (congestion experienced). + int ce() const { return ce_; } + + void Add(EcnMarking ecn); + EcnMarkingCounter& operator+=(const EcnMarkingCounter& counter); + + private: + int not_ect_ = 0; + int ect_0_ = 0; // Not used by WebRTC or L4S. + int ect_1_ = 0; + int ce_ = 0; +}; + +} // namespace webrtc +#endif // API_TEST_NETWORK_EMULATION_ECN_MARKING_COUNTER_H_ diff --git a/api/test/network_emulation/network_emulation_interfaces.h b/api/test/network_emulation/network_emulation_interfaces.h index d02d7394a2..7d0b81c088 100644 --- a/api/test/network_emulation/network_emulation_interfaces.h +++ b/api/test/network_emulation/network_emulation_interfaces.h @@ -18,6 +18,7 @@ #include #include "api/numerics/samples_stats_counter.h" +#include "api/test/network_emulation/ecn_marking_counter.h" #include "api/transport/ecn_marking.h" #include "api/units/data_rate.h" #include "api/units/data_size.h" @@ -83,6 +84,8 @@ struct EmulatedNetworkOutgoingStats { // Time of the last packet sent or infinite value if no packets were sent. Timestamp last_packet_sent_time = Timestamp::MinusInfinity(); + EcnMarkingCounter ecn_count; + // Returns average send rate. Requires that at least 2 packets were sent. DataRate AverageSendRate() const; }; @@ -118,6 +121,8 @@ struct EmulatedNetworkIncomingStats { // received. Timestamp last_packet_received_time = Timestamp::MinusInfinity(); + EcnMarkingCounter ecn_count; + DataRate AverageReceiveRate() const; }; diff --git a/test/network/network_emulation.cc b/test/network/network_emulation.cc index 62168c2e52..bbbfdaf33e 100644 --- a/test/network/network_emulation.cc +++ b/test/network/network_emulation.cc @@ -31,6 +31,7 @@ #include "api/transport/ecn_marking.h" #include "api/units/data_size.h" #include "api/units/time_delta.h" +#include "api/units/timestamp.h" #include "rtc_base/logging.h" namespace webrtc { @@ -77,19 +78,21 @@ EmulatedNetworkOutgoingStatsBuilder::EmulatedNetworkOutgoingStatsBuilder( sequence_checker_.Detach(); } -void EmulatedNetworkOutgoingStatsBuilder::OnPacketSent(Timestamp sent_time, - DataSize packet_size) { +void EmulatedNetworkOutgoingStatsBuilder::OnPacketSent( + Timestamp sent_time, + const EmulatedIpPacket& packet) { RTC_DCHECK_RUN_ON(&sequence_checker_); - RTC_CHECK_GE(packet_size, DataSize::Zero()); + RTC_CHECK_GE(packet.size(), 0); if (stats_.first_packet_sent_time.IsInfinite()) { stats_.first_packet_sent_time = sent_time; - stats_.first_sent_packet_size = packet_size; + stats_.first_sent_packet_size = DataSize::Bytes(packet.ip_packet_size()); } stats_.last_packet_sent_time = sent_time; stats_.packets_sent++; - stats_.bytes_sent += packet_size; + stats_.bytes_sent += DataSize::Bytes(packet.ip_packet_size()); + stats_.ecn_count.Add(packet.ecn); if (stats_gathering_mode_ == EmulatedNetworkStatsGatheringMode::kDebug) { - stats_.sent_packets_size.AddSample(packet_size.bytes()); + stats_.sent_packets_size.AddSample(packet.ip_packet_size()); } } @@ -106,6 +109,7 @@ void EmulatedNetworkOutgoingStatsBuilder::AddOutgoingStats( if (stats_.last_packet_sent_time < stats.last_packet_sent_time) { stats_.last_packet_sent_time = stats.last_packet_sent_time; } + stats_.ecn_count += stats.ecn_count; } EmulatedNetworkOutgoingStats EmulatedNetworkOutgoingStatsBuilder::Build() @@ -132,18 +136,20 @@ void EmulatedNetworkIncomingStatsBuilder::OnPacketDropped( void EmulatedNetworkIncomingStatsBuilder::OnPacketReceived( Timestamp received_time, - DataSize packet_size) { + const EmulatedIpPacket& packet) { RTC_DCHECK_RUN_ON(&sequence_checker_); - RTC_CHECK_GE(packet_size, DataSize::Zero()); + RTC_CHECK_GE(packet.size(), 0); if (stats_.first_packet_received_time.IsInfinite()) { stats_.first_packet_received_time = received_time; - stats_.first_received_packet_size = packet_size; + stats_.first_received_packet_size = + DataSize::Bytes(packet.ip_packet_size()); } stats_.last_packet_received_time = received_time; stats_.packets_received++; - stats_.bytes_received += packet_size; + stats_.ecn_count.Add(packet.ecn); + stats_.bytes_received += DataSize::Bytes(packet.ip_packet_size()); if (stats_gathering_mode_ == EmulatedNetworkStatsGatheringMode::kDebug) { - stats_.received_packets_size.AddSample(packet_size.bytes()); + stats_.received_packets_size.AddSample(packet.ip_packet_size()); } } @@ -164,6 +170,7 @@ void EmulatedNetworkIncomingStatsBuilder::AddIncomingStats( if (stats_.last_packet_received_time < stats.last_packet_received_time) { stats_.last_packet_received_time = stats.last_packet_received_time; } + stats_.ecn_count += stats.ecn_count; } EmulatedNetworkIncomingStats EmulatedNetworkIncomingStatsBuilder::Build() @@ -186,23 +193,22 @@ EmulatedNetworkStatsBuilder::EmulatedNetworkStatsBuilder( sequence_checker_.Detach(); } -void EmulatedNetworkStatsBuilder::OnPacketSent(Timestamp queued_time, - Timestamp sent_time, - rtc::IPAddress destination_ip, - DataSize packet_size) { +void EmulatedNetworkStatsBuilder::OnPacketSent(Timestamp sent_time, + const EmulatedIpPacket& packet) { RTC_DCHECK_RUN_ON(&sequence_checker_); if (stats_gathering_mode_ == EmulatedNetworkStatsGatheringMode::kDebug) { - sent_packets_queue_wait_time_us_.AddSample((sent_time - queued_time).us()); + sent_packets_queue_wait_time_us_.AddSample( + (sent_time - packet.arrival_time).us()); } - auto it = outgoing_stats_per_destination_.find(destination_ip); + auto it = outgoing_stats_per_destination_.find(packet.to.ipaddr()); if (it == outgoing_stats_per_destination_.end()) { outgoing_stats_per_destination_ - .emplace(destination_ip, + .emplace(packet.to.ipaddr(), std::make_unique( stats_gathering_mode_)) - .first->second->OnPacketSent(sent_time, packet_size); + .first->second->OnPacketSent(sent_time, packet); } else { - it->second->OnPacketSent(sent_time, packet_size); + it->second->OnPacketSent(sent_time, packet); } } @@ -221,19 +227,19 @@ void EmulatedNetworkStatsBuilder::OnPacketDropped(rtc::IPAddress source_ip, } } -void EmulatedNetworkStatsBuilder::OnPacketReceived(Timestamp received_time, - rtc::IPAddress source_ip, - DataSize packet_size) { +void EmulatedNetworkStatsBuilder::OnPacketReceived( + Timestamp received_time, + const EmulatedIpPacket& packet) { RTC_DCHECK_RUN_ON(&sequence_checker_); - auto it = incoming_stats_per_source_.find(source_ip); + auto it = incoming_stats_per_source_.find(packet.from.ipaddr()); if (it == incoming_stats_per_source_.end()) { incoming_stats_per_source_ - .emplace(source_ip, + .emplace(packet.from.ipaddr(), std::make_unique( stats_gathering_mode_)) - .first->second->OnPacketReceived(received_time, packet_size); + .first->second->OnPacketReceived(received_time, packet); } else { - it->second->OnPacketReceived(received_time, packet_size); + it->second->OnPacketReceived(received_time, packet); } } @@ -627,10 +633,7 @@ void EmulatedEndpointImpl::SendPacket(const rtc::SocketAddress& from, clock_->CurrentTime(), application_overhead, ecn); 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())); - + stats_builder_.OnPacketSent(clock_->CurrentTime(), packet); if (packet.to.ipaddr() == options_.ip) { OnPacketReceived(std::move(packet)); } else { @@ -734,8 +737,7 @@ void EmulatedEndpointImpl::OnPacketReceived(EmulatedIpPacket packet) { << "; Receiver options_.ip=" << options_.ip.ToString(); } MutexLock lock(&receiver_lock_); - stats_builder_.OnPacketReceived(clock_->CurrentTime(), packet.from.ipaddr(), - DataSize::Bytes(packet.ip_packet_size())); + stats_builder_.OnPacketReceived(clock_->CurrentTime(), packet); auto it = port_to_receiver_.find(packet.to.port()); if (it == port_to_receiver_.end()) { if (default_receiver_.has_value()) { diff --git a/test/network/network_emulation.h b/test/network/network_emulation.h index 10bca422f4..c625d06072 100644 --- a/test/network/network_emulation.h +++ b/test/network/network_emulation.h @@ -52,7 +52,7 @@ class EmulatedNetworkOutgoingStatsBuilder { explicit EmulatedNetworkOutgoingStatsBuilder( EmulatedNetworkStatsGatheringMode stats_gathering_mode); - void OnPacketSent(Timestamp sent_time, DataSize packet_size); + void OnPacketSent(Timestamp sent_time, const EmulatedIpPacket& packet); void AddOutgoingStats(const EmulatedNetworkOutgoingStats& stats); @@ -74,7 +74,8 @@ class EmulatedNetworkIncomingStatsBuilder { void OnPacketDropped(DataSize packet_size); - void OnPacketReceived(Timestamp received_time, DataSize packet_size); + void OnPacketReceived(Timestamp received_time, + const EmulatedIpPacket& packet); // Adds stats collected from another endpoints to the builder. void AddIncomingStats(const EmulatedNetworkIncomingStats& stats); @@ -98,16 +99,12 @@ class EmulatedNetworkStatsBuilder { rtc::IPAddress local_ip, EmulatedNetworkStatsGatheringMode stats_gathering_mode); - void OnPacketSent(Timestamp queued_time, - Timestamp sent_time, - rtc::IPAddress destination_ip, - DataSize packet_size); + void OnPacketSent(Timestamp send_time, const EmulatedIpPacket& packet); void OnPacketDropped(rtc::IPAddress source_ip, DataSize packet_size); void OnPacketReceived(Timestamp received_time, - rtc::IPAddress source_ip, - DataSize packet_size); + const EmulatedIpPacket& packet); void AddEmulatedNetworkStats(const EmulatedNetworkStats& stats); diff --git a/test/network/network_emulation_unittest.cc b/test/network/network_emulation_unittest.cc index dfacbf2203..610dc17825 100644 --- a/test/network/network_emulation_unittest.cc +++ b/test/network/network_emulation_unittest.cc @@ -423,6 +423,27 @@ TEST(NetworkEmulationManagerTest, EcnMarkingIsPropagated) { EXPECT_EQ(r2.ReceivedCount(), 1); EXPECT_EQ(r2.LastEcnMarking(), webrtc::EcnMarking::kEct1); + std::atomic received_stats_count{0}; + nt1->GetStats([&](EmulatedNetworkStats st) { + EXPECT_EQ(st.overall_incoming_stats.packets_received, 0); + EXPECT_EQ(st.overall_outgoing_stats.packets_sent, 1); + EXPECT_EQ(st.overall_outgoing_stats.ecn_count.ect_1(), 1); + EXPECT_EQ(st.overall_outgoing_stats.ecn_count.ce(), 0); + EXPECT_EQ(st.overall_outgoing_stats.ecn_count.not_ect(), 0); + ++received_stats_count; + }); + nt2->GetStats([&](EmulatedNetworkStats st) { + EXPECT_EQ(st.overall_incoming_stats.packets_received, 1); + EXPECT_EQ(st.overall_outgoing_stats.packets_sent, 0); + EXPECT_EQ(st.overall_incoming_stats.ecn_count.ect_1(), 1); + EXPECT_EQ(st.overall_incoming_stats.ecn_count.ce(), 0); + EXPECT_EQ(st.overall_incoming_stats.ecn_count.not_ect(), 0); + ++received_stats_count; + }); + ASSERT_EQ_SIMULATED_WAIT(received_stats_count.load(), 2, + kStatsWaitTimeout.ms(), + *network_manager.time_controller()); + SendTask(t1, [&] { delete s1; }); SendTask(t2, [&] { delete s2; }); }