1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "quiche/quic/test_tools/packet_dropping_test_writer.h"
6
7 #include "quiche/quic/platform/api/quic_logging.h"
8
9 namespace quic {
10 namespace test {
11
12 // Every dropped packet must be followed by this number of succesfully written
13 // packets. This is to avoid flaky test failures and timeouts, for example, in
14 // case both the client and the server drop every other packet (which is
15 // statistically possible even if drop percentage is less than 50%).
16 const int32_t kMinSuccesfulWritesAfterPacketLoss = 2;
17
18 // An alarm that is scheduled if a blocked socket is simulated to indicate
19 // it's writable again.
20 class WriteUnblockedAlarm : public QuicAlarm::DelegateWithoutContext {
21 public:
WriteUnblockedAlarm(PacketDroppingTestWriter * writer)22 explicit WriteUnblockedAlarm(PacketDroppingTestWriter* writer)
23 : writer_(writer) {}
24
OnAlarm()25 void OnAlarm() override {
26 QUIC_DLOG(INFO) << "Unblocking socket.";
27 writer_->OnCanWrite();
28 }
29
30 private:
31 PacketDroppingTestWriter* writer_;
32 };
33
34 // An alarm that is scheduled every time a new packet is to be written at a
35 // later point.
36 class DelayAlarm : public QuicAlarm::DelegateWithoutContext {
37 public:
DelayAlarm(PacketDroppingTestWriter * writer)38 explicit DelayAlarm(PacketDroppingTestWriter* writer) : writer_(writer) {}
39
OnAlarm()40 void OnAlarm() override {
41 QuicTime new_deadline = writer_->ReleaseOldPackets();
42 if (new_deadline.IsInitialized()) {
43 writer_->SetDelayAlarm(new_deadline);
44 }
45 }
46
47 private:
48 PacketDroppingTestWriter* writer_;
49 };
50
PacketDroppingTestWriter()51 PacketDroppingTestWriter::PacketDroppingTestWriter()
52 : clock_(nullptr),
53 cur_buffer_size_(0),
54 num_calls_to_write_(0),
55 // Do not require any number of successful writes before the first dropped
56 // packet.
57 num_consecutive_succesful_writes_(kMinSuccesfulWritesAfterPacketLoss),
58 fake_packet_loss_percentage_(0),
59 fake_drop_first_n_packets_(0),
60 fake_blocked_socket_percentage_(0),
61 fake_packet_reorder_percentage_(0),
62 fake_packet_delay_(QuicTime::Delta::Zero()),
63 fake_bandwidth_(QuicBandwidth::Zero()),
64 buffer_size_(0) {
65 uint64_t seed = QuicRandom::GetInstance()->RandUint64();
66 QUIC_LOG(INFO) << "Seeding packet loss with " << seed;
67 simple_random_.set_seed(seed);
68 }
69
~PacketDroppingTestWriter()70 PacketDroppingTestWriter::~PacketDroppingTestWriter() {
71 if (write_unblocked_alarm_ != nullptr) {
72 write_unblocked_alarm_->PermanentCancel();
73 }
74 if (delay_alarm_ != nullptr) {
75 delay_alarm_->PermanentCancel();
76 }
77 }
78
Initialize(QuicConnectionHelperInterface * helper,QuicAlarmFactory * alarm_factory,std::unique_ptr<Delegate> on_can_write)79 void PacketDroppingTestWriter::Initialize(
80 QuicConnectionHelperInterface* helper, QuicAlarmFactory* alarm_factory,
81 std::unique_ptr<Delegate> on_can_write) {
82 clock_ = helper->GetClock();
83 write_unblocked_alarm_.reset(
84 alarm_factory->CreateAlarm(new WriteUnblockedAlarm(this)));
85 delay_alarm_.reset(alarm_factory->CreateAlarm(new DelayAlarm(this)));
86 on_can_write_ = std::move(on_can_write);
87 }
88
WritePacket(const char * buffer,size_t buf_len,const QuicIpAddress & self_address,const QuicSocketAddress & peer_address,PerPacketOptions * options)89 WriteResult PacketDroppingTestWriter::WritePacket(
90 const char* buffer, size_t buf_len, const QuicIpAddress& self_address,
91 const QuicSocketAddress& peer_address, PerPacketOptions* options) {
92 ++num_calls_to_write_;
93 ReleaseOldPackets();
94
95 QuicWriterMutexLock lock(&config_mutex_);
96 if (fake_drop_first_n_packets_ > 0 &&
97 num_calls_to_write_ <=
98 static_cast<uint64_t>(fake_drop_first_n_packets_)) {
99 QUIC_DVLOG(1) << "Dropping first " << fake_drop_first_n_packets_
100 << " packets (packet number " << num_calls_to_write_ << ")";
101 num_consecutive_succesful_writes_ = 0;
102 return WriteResult(WRITE_STATUS_OK, buf_len);
103 }
104
105 // Drop every packet at 100%, otherwise always succeed for at least
106 // kMinSuccesfulWritesAfterPacketLoss packets between two dropped ones.
107 if (fake_packet_loss_percentage_ == 100 ||
108 (fake_packet_loss_percentage_ > 0 &&
109 num_consecutive_succesful_writes_ >=
110 kMinSuccesfulWritesAfterPacketLoss &&
111 (simple_random_.RandUint64() % 100 <
112 static_cast<uint64_t>(fake_packet_loss_percentage_)))) {
113 QUIC_DVLOG(1) << "Dropping packet " << num_calls_to_write_;
114 num_consecutive_succesful_writes_ = 0;
115 return WriteResult(WRITE_STATUS_OK, buf_len);
116 } else {
117 ++num_consecutive_succesful_writes_;
118 }
119
120 if (fake_blocked_socket_percentage_ > 0 &&
121 simple_random_.RandUint64() % 100 <
122 static_cast<uint64_t>(fake_blocked_socket_percentage_)) {
123 QUICHE_CHECK(on_can_write_ != nullptr);
124 QUIC_DVLOG(1) << "Blocking socket for packet " << num_calls_to_write_;
125 if (!write_unblocked_alarm_->IsSet()) {
126 // Set the alarm to fire immediately.
127 write_unblocked_alarm_->Set(clock_->ApproximateNow());
128 }
129
130 // Dropping this packet on retry could result in PTO timeout,
131 // make sure to avoid this.
132 num_consecutive_succesful_writes_ = 0;
133
134 return WriteResult(WRITE_STATUS_BLOCKED, EAGAIN);
135 }
136
137 if (!fake_packet_delay_.IsZero() || !fake_bandwidth_.IsZero()) {
138 if (buffer_size_ > 0 && buf_len + cur_buffer_size_ > buffer_size_) {
139 // Drop packets which do not fit into the buffer.
140 QUIC_DVLOG(1) << "Dropping packet because the buffer is full.";
141 return WriteResult(WRITE_STATUS_OK, buf_len);
142 }
143
144 // Queue it to be sent.
145 QuicTime send_time = clock_->ApproximateNow() + fake_packet_delay_;
146 if (!fake_bandwidth_.IsZero()) {
147 // Calculate a time the bandwidth limit would impose.
148 QuicTime::Delta bandwidth_delay = QuicTime::Delta::FromMicroseconds(
149 (buf_len * kNumMicrosPerSecond) / fake_bandwidth_.ToBytesPerSecond());
150 send_time = delayed_packets_.empty()
151 ? send_time + bandwidth_delay
152 : delayed_packets_.back().send_time + bandwidth_delay;
153 }
154 std::unique_ptr<PerPacketOptions> delayed_options;
155 if (options != nullptr) {
156 delayed_options = options->Clone();
157 }
158 delayed_packets_.push_back(
159 DelayedWrite(buffer, buf_len, self_address, peer_address,
160 std::move(delayed_options), send_time));
161 cur_buffer_size_ += buf_len;
162
163 // Set the alarm if it's not yet set.
164 if (!delay_alarm_->IsSet()) {
165 delay_alarm_->Set(send_time);
166 }
167
168 return WriteResult(WRITE_STATUS_OK, buf_len);
169 }
170
171 return QuicPacketWriterWrapper::WritePacket(buffer, buf_len, self_address,
172 peer_address, options);
173 }
174
IsWriteBlocked() const175 bool PacketDroppingTestWriter::IsWriteBlocked() const {
176 if (write_unblocked_alarm_ != nullptr && write_unblocked_alarm_->IsSet()) {
177 return true;
178 }
179 return QuicPacketWriterWrapper::IsWriteBlocked();
180 }
181
SetWritable()182 void PacketDroppingTestWriter::SetWritable() {
183 if (write_unblocked_alarm_ != nullptr && write_unblocked_alarm_->IsSet()) {
184 write_unblocked_alarm_->Cancel();
185 }
186 QuicPacketWriterWrapper::SetWritable();
187 }
188
ReleaseNextPacket()189 QuicTime PacketDroppingTestWriter::ReleaseNextPacket() {
190 if (delayed_packets_.empty()) {
191 return QuicTime::Zero();
192 }
193 QuicReaderMutexLock lock(&config_mutex_);
194 auto iter = delayed_packets_.begin();
195 // Determine if we should re-order.
196 if (delayed_packets_.size() > 1 && fake_packet_reorder_percentage_ > 0 &&
197 simple_random_.RandUint64() % 100 <
198 static_cast<uint64_t>(fake_packet_reorder_percentage_)) {
199 QUIC_DLOG(INFO) << "Reordering packets.";
200 ++iter;
201 // Swap the send times when re-ordering packets.
202 delayed_packets_.begin()->send_time = iter->send_time;
203 }
204
205 QUIC_DVLOG(1) << "Releasing packet. " << (delayed_packets_.size() - 1)
206 << " remaining.";
207 // Grab the next one off the queue and send it.
208 QuicPacketWriterWrapper::WritePacket(
209 iter->buffer.data(), iter->buffer.length(), iter->self_address,
210 iter->peer_address, iter->options.get());
211 QUICHE_DCHECK_GE(cur_buffer_size_, iter->buffer.length());
212 cur_buffer_size_ -= iter->buffer.length();
213 delayed_packets_.erase(iter);
214
215 // If there are others, find the time for the next to be sent.
216 if (delayed_packets_.empty()) {
217 return QuicTime::Zero();
218 }
219 return delayed_packets_.begin()->send_time;
220 }
221
ReleaseOldPackets()222 QuicTime PacketDroppingTestWriter::ReleaseOldPackets() {
223 while (!delayed_packets_.empty()) {
224 QuicTime next_send_time = delayed_packets_.front().send_time;
225 if (next_send_time > clock_->Now()) {
226 return next_send_time;
227 }
228 ReleaseNextPacket();
229 }
230 return QuicTime::Zero();
231 }
232
SetDelayAlarm(QuicTime new_deadline)233 void PacketDroppingTestWriter::SetDelayAlarm(QuicTime new_deadline) {
234 delay_alarm_->Set(new_deadline);
235 }
236
OnCanWrite()237 void PacketDroppingTestWriter::OnCanWrite() { on_can_write_->OnCanWrite(); }
238
DelayedWrite(const char * buffer,size_t buf_len,const QuicIpAddress & self_address,const QuicSocketAddress & peer_address,std::unique_ptr<PerPacketOptions> options,QuicTime send_time)239 PacketDroppingTestWriter::DelayedWrite::DelayedWrite(
240 const char* buffer, size_t buf_len, const QuicIpAddress& self_address,
241 const QuicSocketAddress& peer_address,
242 std::unique_ptr<PerPacketOptions> options, QuicTime send_time)
243 : buffer(buffer, buf_len),
244 self_address(self_address),
245 peer_address(peer_address),
246 options(std::move(options)),
247 send_time(send_time) {}
248
249 PacketDroppingTestWriter::DelayedWrite::~DelayedWrite() = default;
250
251 } // namespace test
252 } // namespace quic
253