diff --git a/modules/congestion_controller/goog_cc/goog_cc_network_control_unittest.cc b/modules/congestion_controller/goog_cc/goog_cc_network_control_unittest.cc index 5ec15e97c4..9b2be1c61b 100644 --- a/modules/congestion_controller/goog_cc/goog_cc_network_control_unittest.cc +++ b/modules/congestion_controller/goog_cc/goog_cc_network_control_unittest.cc @@ -685,5 +685,29 @@ TEST_F(GoogCcNetworkControllerTest, NoRttBackoffCollapseWhenVideoStops) { EXPECT_GT(client->send_bandwidth().kbps(), 1000); } +TEST_F(GoogCcNetworkControllerTest, IsFairToTCP) { + Scenario s("googcc_unit/tcp_fairness"); + NetworkSimulationConfig net_conf; + net_conf.bandwidth = DataRate::kbps(1000); + net_conf.delay = TimeDelta::ms(50); + auto* client = s.CreateClient("send", [&](CallClientConfig* c) { + c->transport.rates.start_rate = DataRate::kbps(1000); + }); + auto send_net = {s.CreateSimulationNode(net_conf)}; + auto ret_net = {s.CreateSimulationNode(net_conf)}; + auto* route = s.CreateRoutes( + client, send_net, s.CreateClient("return", CallClientConfig()), ret_net); + s.CreateVideoStream(route->forward(), VideoStreamConfig()); + s.net()->StartFakeTcpCrossTraffic(s.net()->CreateRoute(send_net), + s.net()->CreateRoute(ret_net), + FakeTcpConfig()); + s.RunFor(TimeDelta::seconds(10)); + + // Currently only testing for the upper limit as we in practice back out + // quite a lot in this scenario. If this behavior is fixed, we should add a + // lower bound to ensure it stays fixed. + EXPECT_LT(client->send_bandwidth().kbps(), 750); +} + } // namespace test } // namespace webrtc diff --git a/test/scenario/network/cross_traffic.cc b/test/scenario/network/cross_traffic.cc index 1d4efef866..105ad48076 100644 --- a/test/scenario/network/cross_traffic.cc +++ b/test/scenario/network/cross_traffic.cc @@ -114,5 +114,70 @@ ColumnPrinter PulsedPeaksCrossTraffic::StatsPrinter() { 32); } +FakeTcpCrossTraffic::FakeTcpCrossTraffic(FakeTcpConfig config, + EmulatedRoute* send_route, + EmulatedRoute* ret_route) + : conf_(config), route_(this, send_route, ret_route) {} + +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(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(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(), + next_sequence_number_++); + total_sent_ += conf_.packet_size; + } +} + } // namespace test } // namespace webrtc diff --git a/test/scenario/network/cross_traffic.h b/test/scenario/network/cross_traffic.h index e88827466c..29625b8d3b 100644 --- a/test/scenario/network/cross_traffic.h +++ b/test/scenario/network/cross_traffic.h @@ -90,6 +90,43 @@ class PulsedPeaksCrossTraffic { bool sending_ RTC_GUARDED_BY(sequence_checker_) = false; }; +struct FakeTcpConfig { + DataSize packet_size = DataSize::bytes(1200); + DataSize send_limit = DataSize::PlusInfinity(); + int packet_window; + TimeDelta process_interval = TimeDelta::ms(200); + TimeDelta packet_timeout = TimeDelta::seconds(1); +}; + +class FakeTcpCrossTraffic + : public TwoWayFakeTrafficRoute::TrafficHandlerInterface { + public: + FakeTcpCrossTraffic(FakeTcpConfig config, + EmulatedRoute* send_route, + EmulatedRoute* ret_route); + void Process(Timestamp at_time); + 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 route_; + + std::map 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 diff --git a/test/scenario/network/feedback_generator.cc b/test/scenario/network/feedback_generator.cc index 4ab7c2efc0..9e968051bd 100644 --- a/test/scenario/network/feedback_generator.cc +++ b/test/scenario/network/feedback_generator.cc @@ -21,29 +21,11 @@ FeedbackGeneratorImpl::FeedbackGeneratorImpl( net_{&time_controller_}, send_link_{new SimulatedNetwork(conf_.send_link)}, ret_link_{new SimulatedNetwork(conf_.return_link)}, - send_ep_{net_.CreateEndpoint(EmulatedEndpointConfig())}, - ret_ep_{net_.CreateEndpoint(EmulatedEndpointConfig())}, - send_route_{net_.CreateRoute( - send_ep_, - {net_.CreateEmulatedNode(absl::WrapUnique(send_link_))}, - ret_ep_)}, - ret_route_{net_.CreateRoute( - ret_ep_, - {net_.CreateEmulatedNode(absl::WrapUnique(ret_link_))}, - send_ep_)}, - send_addr_{rtc::SocketAddress(send_ep_->GetPeerLocalAddress(), 0)}, - ret_addr_{rtc::SocketAddress(ret_ep_->GetPeerLocalAddress(), 0)}, - received_packet_handler_{send_route_, - [&](SentPacket packet, Timestamp arrival_time) { - OnPacketReceived(std::move(packet), - arrival_time); - }}, - received_feedback_handler_{ - ret_route_, - [&](TransportPacketsFeedback packet, Timestamp arrival_time) { - packet.feedback_time = arrival_time; - feedback_.push_back(packet); - }} {} + route_(this, + net_.CreateRoute( + {net_.CreateEmulatedNode(absl::WrapUnique(send_link_))}), + net_.CreateRoute( + {net_.CreateEmulatedNode(absl::WrapUnique(ret_link_))})) {} Timestamp FeedbackGeneratorImpl::Now() { return Timestamp::ms(time_controller_.GetClock()->TimeInMilliseconds()); @@ -57,7 +39,7 @@ void FeedbackGeneratorImpl::SendPacket(size_t size) { SentPacket sent; sent.send_time = Now(); sent.size = DataSize::bytes(size); - received_packet_handler_.SendPacket(send_ep_, size, sent); + route_.SendRequest(size, sent); } std::vector FeedbackGeneratorImpl::PopFeedback() { @@ -82,18 +64,23 @@ void FeedbackGeneratorImpl::SetSendLinkCapacity(DataRate capacity) { send_link_->SetConfig(conf_.send_link); } -void FeedbackGeneratorImpl::OnPacketReceived(SentPacket packet, - Timestamp arrival_time) { +void FeedbackGeneratorImpl::OnRequest(SentPacket packet, + Timestamp arrival_time) { PacketResult result; result.sent_packet = packet; result.receive_time = arrival_time; builder_.packet_feedbacks.push_back(result); Timestamp first_recv = builder_.packet_feedbacks.front().receive_time; if (Now() - first_recv > conf_.feedback_interval) { - received_feedback_handler_.SendPacket( - ret_ep_, conf_.feedback_packet_size.bytes(), builder_); + route_.SendResponse(conf_.feedback_packet_size.bytes(), builder_); builder_ = {}; } } +void FeedbackGeneratorImpl::OnResponse(TransportPacketsFeedback packet, + Timestamp arrival_time) { + packet.feedback_time = arrival_time; + feedback_.push_back(packet); +} + } // namespace webrtc diff --git a/test/scenario/network/feedback_generator.h b/test/scenario/network/feedback_generator.h index 5ff69a9883..094ada55fc 100644 --- a/test/scenario/network/feedback_generator.h +++ b/test/scenario/network/feedback_generator.h @@ -22,42 +22,10 @@ namespace webrtc { -template -class FakePacketRoute : public EmulatedNetworkReceiverInterface { - public: - FakePacketRoute(EmulatedRoute* route, - std::function action) - : route_(route), - action_(std::move(action)), - send_addr_(route_->from->GetPeerLocalAddress(), 0), - recv_addr_(route_->to->GetPeerLocalAddress(), - *route_->to->BindReceiver(0, this)) {} - - void SendPacket(EmulatedEndpoint* from, size_t size, FakePacketType packet) { - RTC_CHECK_GE(size, sizeof(int)); - sent_.emplace(next_packet_id_, packet); - rtc::CopyOnWriteBuffer buf(size); - reinterpret_cast(buf.data())[0] = next_packet_id_++; - from->SendPacket(send_addr_, recv_addr_, buf); - } - - void OnPacketReceived(EmulatedIpPacket packet) override { - int packet_id = reinterpret_cast(packet.data.data())[0]; - action_(std::move(sent_[packet_id]), packet.arrival_time); - sent_.erase(packet_id); - } - - private: - EmulatedRoute* const route_; - const std::function action_; - const rtc::SocketAddress send_addr_; - const rtc::SocketAddress recv_addr_; - - int next_packet_id_ = 0; - std::map sent_; -}; - -class FeedbackGeneratorImpl : public FeedbackGenerator { +class FeedbackGeneratorImpl + : public FeedbackGenerator, + public TwoWayFakeTrafficRoute:: + TrafficHandlerInterface { public: explicit FeedbackGeneratorImpl(Config config); Timestamp Now() override; @@ -70,21 +38,17 @@ class FeedbackGeneratorImpl : public FeedbackGenerator { void SetSendLinkCapacity(DataRate capacity) override; + void OnRequest(SentPacket packet, Timestamp arrival_time) override; + void OnResponse(TransportPacketsFeedback packet, + Timestamp arrival_time) override; + private: - void OnPacketReceived(SentPacket packet, Timestamp arrival_time); Config conf_; GlobalSimulatedTimeController time_controller_; ::webrtc::test::NetworkEmulationManagerImpl net_; SimulatedNetwork* const send_link_; SimulatedNetwork* const ret_link_; - EmulatedEndpoint* const send_ep_; - EmulatedEndpoint* const ret_ep_; - EmulatedRoute* const send_route_; - EmulatedRoute* const ret_route_; - const rtc::SocketAddress send_addr_; - const rtc::SocketAddress ret_addr_; - FakePacketRoute received_packet_handler_; - FakePacketRoute received_feedback_handler_; + TwoWayFakeTrafficRoute route_; TransportPacketsFeedback builder_; std::vector feedback_; diff --git a/test/scenario/network/network_emulation.h b/test/scenario/network/network_emulation.h index b3fe5ebf43..ff8444bf7e 100644 --- a/test/scenario/network/network_emulation.h +++ b/test/scenario/network/network_emulation.h @@ -233,10 +233,9 @@ class EmulatedRoute { EmulatedEndpoint* to; bool active; }; - class EndpointsContainer { public: - EndpointsContainer(const std::vector& endpoints); + explicit EndpointsContainer(const std::vector& endpoints); EmulatedEndpoint* LookupByLocalAddress(const rtc::IPAddress& local_ip) const; bool HasEndpoint(EmulatedEndpoint* endpoint) const; @@ -249,6 +248,73 @@ class EndpointsContainer { const std::vector endpoints_; }; +template +class FakePacketRoute : public EmulatedNetworkReceiverInterface { + public: + FakePacketRoute(EmulatedRoute* route, + std::function action) + : route_(route), + action_(std::move(action)), + send_addr_(route_->from->GetPeerLocalAddress(), 0), + recv_addr_(route_->to->GetPeerLocalAddress(), + *route_->to->BindReceiver(0, this)) {} + + 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(buf.data())[0] = next_packet_id_++; + route_->from->SendPacket(send_addr_, recv_addr_, buf); + } + + void OnPacketReceived(EmulatedIpPacket packet) override { + int packet_id = reinterpret_cast(packet.data.data())[0]; + action_(std::move(sent_[packet_id]), packet.arrival_time); + sent_.erase(packet_id); + } + + private: + EmulatedRoute* const route_; + const std::function action_; + const rtc::SocketAddress send_addr_; + const rtc::SocketAddress recv_addr_; + int next_packet_id_ = 0; + std::map sent_; +}; + +template +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 request_handler_; + FakePacketRoute response_handler_; +}; } // namespace webrtc #endif // TEST_SCENARIO_NETWORK_NETWORK_EMULATION_H_ diff --git a/test/scenario/network/network_emulation_manager.cc b/test/scenario/network/network_emulation_manager.cc index 8e1dc76924..d85a6ff640 100644 --- a/test/scenario/network/network_emulation_manager.cc +++ b/test/scenario/network/network_emulation_manager.cc @@ -145,6 +145,13 @@ EmulatedRoute* NetworkEmulationManagerImpl::CreateRoute( return out; } +EmulatedRoute* NetworkEmulationManagerImpl::CreateRoute( + const std::vector& via_nodes) { + EmulatedEndpoint* from = CreateEndpoint(EmulatedEndpointConfig()); + EmulatedEndpoint* to = CreateEndpoint(EmulatedEndpointConfig()); + return CreateRoute(from, via_nodes, to); +} + void NetworkEmulationManagerImpl::ClearRoute(EmulatedRoute* route) { RTC_CHECK(route->active) << "Route already cleared"; task_queue_.SendTask([route]() { @@ -223,6 +230,24 @@ NetworkEmulationManagerImpl::CreatePulsedPeaksCrossTraffic( return out; } +void NetworkEmulationManagerImpl::StartFakeTcpCrossTraffic( + EmulatedRoute* send_route, + EmulatedRoute* ret_route, + FakeTcpConfig config) { + task_queue_.PostTask([=]() { + auto traffic = + absl::make_unique(config, send_route, ret_route); + auto* traffic_ptr = traffic.get(); + tcp_cross_traffics_.push_back(std::move(traffic)); + TimeDelta process_interval = config.process_interval; + RepeatingTaskHandle::Start(task_queue_.Get(), + [this, process_interval, traffic_ptr] { + traffic_ptr->Process(Now()); + return process_interval; + }); + }); +} + EmulatedNetworkManagerInterface* NetworkEmulationManagerImpl::CreateEmulatedNetworkManagerInterface( const std::vector& endpoints) { diff --git a/test/scenario/network/network_emulation_manager.h b/test/scenario/network/network_emulation_manager.h index cb1ace7c2c..a10eca733e 100644 --- a/test/scenario/network/network_emulation_manager.h +++ b/test/scenario/network/network_emulation_manager.h @@ -51,6 +51,10 @@ class NetworkEmulationManagerImpl : public NetworkEmulationManager { EmulatedRoute* CreateRoute(EmulatedEndpoint* from, const std::vector& via_nodes, EmulatedEndpoint* to) override; + + EmulatedRoute* CreateRoute( + const std::vector& via_nodes); + void ClearRoute(EmulatedRoute* route) override; TrafficRoute* CreateTrafficRoute( @@ -61,6 +65,9 @@ class NetworkEmulationManagerImpl : public NetworkEmulationManager { PulsedPeaksCrossTraffic* CreatePulsedPeaksCrossTraffic( TrafficRoute* traffic_route, PulsedPeaksConfig config); + void StartFakeTcpCrossTraffic(EmulatedRoute* send_route, + EmulatedRoute* ret_route, + FakeTcpConfig config); EmulatedNetworkManagerInterface* CreateEmulatedNetworkManagerInterface( const std::vector& endpoints) override; @@ -84,6 +91,7 @@ class NetworkEmulationManagerImpl : public NetworkEmulationManager { std::vector> traffic_routes_; std::vector> random_cross_traffics_; std::vector> pulsed_cross_traffics_; + std::vector> tcp_cross_traffics_; std::vector> endpoints_containers_; std::vector> network_managers_;