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       passthrough_for_next_n_packets_(0),
56       // Do not require any number of successful writes before the first dropped
57       // packet.
58       num_consecutive_succesful_writes_(kMinSuccesfulWritesAfterPacketLoss),
59       fake_packet_loss_percentage_(0),
60       fake_drop_first_n_packets_(0),
61       fake_blocked_socket_percentage_(0),
62       fake_packet_reorder_percentage_(0),
63       fake_packet_delay_(QuicTime::Delta::Zero()),
64       fake_bandwidth_(QuicBandwidth::Zero()),
65       buffer_size_(0) {
66   uint64_t seed = QuicRandom::GetInstance()->RandUint64();
67   QUIC_LOG(INFO) << "Seeding packet loss with " << seed;
68   simple_random_.set_seed(seed);
69 }
70 
~PacketDroppingTestWriter()71 PacketDroppingTestWriter::~PacketDroppingTestWriter() {
72   if (write_unblocked_alarm_ != nullptr) {
73     write_unblocked_alarm_->PermanentCancel();
74   }
75   if (delay_alarm_ != nullptr) {
76     delay_alarm_->PermanentCancel();
77   }
78 }
79 
Initialize(QuicConnectionHelperInterface * helper,QuicAlarmFactory * alarm_factory,std::unique_ptr<Delegate> on_can_write)80 void PacketDroppingTestWriter::Initialize(
81     QuicConnectionHelperInterface* helper, QuicAlarmFactory* alarm_factory,
82     std::unique_ptr<Delegate> on_can_write) {
83   clock_ = helper->GetClock();
84   write_unblocked_alarm_.reset(
85       alarm_factory->CreateAlarm(new WriteUnblockedAlarm(this)));
86   delay_alarm_.reset(alarm_factory->CreateAlarm(new DelayAlarm(this)));
87   on_can_write_ = std::move(on_can_write);
88 }
89 
WritePacket(const char * buffer,size_t buf_len,const QuicIpAddress & self_address,const QuicSocketAddress & peer_address,PerPacketOptions * options,const QuicPacketWriterParams & params)90 WriteResult PacketDroppingTestWriter::WritePacket(
91     const char* buffer, size_t buf_len, const QuicIpAddress& self_address,
92     const QuicSocketAddress& peer_address, PerPacketOptions* options,
93     const QuicPacketWriterParams& params) {
94   ++num_calls_to_write_;
95   ReleaseOldPackets();
96 
97   QuicWriterMutexLock lock(&config_mutex_);
98   if (passthrough_for_next_n_packets_ > 0) {
99     --passthrough_for_next_n_packets_;
100     return QuicPacketWriterWrapper::WritePacket(buffer, buf_len, self_address,
101                                                 peer_address, options, params);
102   }
103 
104   if (fake_drop_first_n_packets_ > 0 &&
105       num_calls_to_write_ <=
106           static_cast<uint64_t>(fake_drop_first_n_packets_)) {
107     QUIC_DVLOG(1) << "Dropping first " << fake_drop_first_n_packets_
108                   << " packets (packet number " << num_calls_to_write_ << ")";
109     num_consecutive_succesful_writes_ = 0;
110     return WriteResult(WRITE_STATUS_OK, buf_len);
111   }
112 
113   // Drop every packet at 100%, otherwise always succeed for at least
114   // kMinSuccesfulWritesAfterPacketLoss packets between two dropped ones.
115   if (fake_packet_loss_percentage_ == 100 ||
116       (fake_packet_loss_percentage_ > 0 &&
117        num_consecutive_succesful_writes_ >=
118            kMinSuccesfulWritesAfterPacketLoss &&
119        (simple_random_.RandUint64() % 100 <
120         static_cast<uint64_t>(fake_packet_loss_percentage_)))) {
121     QUIC_DVLOG(1) << "Dropping packet " << num_calls_to_write_;
122     num_consecutive_succesful_writes_ = 0;
123     return WriteResult(WRITE_STATUS_OK, buf_len);
124   } else {
125     ++num_consecutive_succesful_writes_;
126   }
127 
128   if (fake_blocked_socket_percentage_ > 0 &&
129       simple_random_.RandUint64() % 100 <
130           static_cast<uint64_t>(fake_blocked_socket_percentage_)) {
131     QUICHE_CHECK(on_can_write_ != nullptr);
132     QUIC_DVLOG(1) << "Blocking socket for packet " << num_calls_to_write_;
133     if (!write_unblocked_alarm_->IsSet()) {
134       // Set the alarm to fire immediately.
135       write_unblocked_alarm_->Set(clock_->ApproximateNow());
136     }
137 
138     // Dropping this packet on retry could result in PTO timeout,
139     // make sure to avoid this.
140     num_consecutive_succesful_writes_ = 0;
141 
142     return WriteResult(WRITE_STATUS_BLOCKED, EAGAIN);
143   }
144 
145   if (!fake_packet_delay_.IsZero() || !fake_bandwidth_.IsZero()) {
146     if (buffer_size_ > 0 && buf_len + cur_buffer_size_ > buffer_size_) {
147       // Drop packets which do not fit into the buffer.
148       QUIC_DVLOG(1) << "Dropping packet because the buffer is full.";
149       return WriteResult(WRITE_STATUS_OK, buf_len);
150     }
151 
152     // Queue it to be sent.
153     QuicTime send_time = clock_->ApproximateNow() + fake_packet_delay_;
154     if (!fake_bandwidth_.IsZero()) {
155       // Calculate a time the bandwidth limit would impose.
156       QuicTime::Delta bandwidth_delay = QuicTime::Delta::FromMicroseconds(
157           (buf_len * kNumMicrosPerSecond) / fake_bandwidth_.ToBytesPerSecond());
158       send_time = delayed_packets_.empty()
159                       ? send_time + bandwidth_delay
160                       : delayed_packets_.back().send_time + bandwidth_delay;
161     }
162     std::unique_ptr<PerPacketOptions> delayed_options;
163     if (options != nullptr) {
164       delayed_options = options->Clone();
165     }
166     delayed_packets_.push_back(
167         DelayedWrite(buffer, buf_len, self_address, peer_address,
168                      std::move(delayed_options), params, send_time));
169     cur_buffer_size_ += buf_len;
170 
171     // Set the alarm if it's not yet set.
172     if (!delay_alarm_->IsSet()) {
173       delay_alarm_->Set(send_time);
174     }
175 
176     return WriteResult(WRITE_STATUS_OK, buf_len);
177   }
178 
179   return QuicPacketWriterWrapper::WritePacket(buffer, buf_len, self_address,
180                                               peer_address, options, params);
181 }
182 
IsWriteBlocked() const183 bool PacketDroppingTestWriter::IsWriteBlocked() const {
184   if (write_unblocked_alarm_ != nullptr && write_unblocked_alarm_->IsSet()) {
185     return true;
186   }
187   return QuicPacketWriterWrapper::IsWriteBlocked();
188 }
189 
SetWritable()190 void PacketDroppingTestWriter::SetWritable() {
191   if (write_unblocked_alarm_ != nullptr && write_unblocked_alarm_->IsSet()) {
192     write_unblocked_alarm_->Cancel();
193   }
194   QuicPacketWriterWrapper::SetWritable();
195 }
196 
ReleaseNextPacket()197 QuicTime PacketDroppingTestWriter::ReleaseNextPacket() {
198   if (delayed_packets_.empty()) {
199     return QuicTime::Zero();
200   }
201   QuicReaderMutexLock lock(&config_mutex_);
202   auto iter = delayed_packets_.begin();
203   // Determine if we should re-order.
204   if (delayed_packets_.size() > 1 && fake_packet_reorder_percentage_ > 0 &&
205       simple_random_.RandUint64() % 100 <
206           static_cast<uint64_t>(fake_packet_reorder_percentage_)) {
207     QUIC_DLOG(INFO) << "Reordering packets.";
208     ++iter;
209     // Swap the send times when re-ordering packets.
210     delayed_packets_.begin()->send_time = iter->send_time;
211   }
212 
213   QUIC_DVLOG(1) << "Releasing packet.  " << (delayed_packets_.size() - 1)
214                 << " remaining.";
215   // Grab the next one off the queue and send it.
216   QuicPacketWriterWrapper::WritePacket(
217       iter->buffer.data(), iter->buffer.length(), iter->self_address,
218       iter->peer_address, iter->options.get(), iter->params);
219   QUICHE_DCHECK_GE(cur_buffer_size_, iter->buffer.length());
220   cur_buffer_size_ -= iter->buffer.length();
221   delayed_packets_.erase(iter);
222 
223   // If there are others, find the time for the next to be sent.
224   if (delayed_packets_.empty()) {
225     return QuicTime::Zero();
226   }
227   return delayed_packets_.begin()->send_time;
228 }
229 
ReleaseOldPackets()230 QuicTime PacketDroppingTestWriter::ReleaseOldPackets() {
231   while (!delayed_packets_.empty()) {
232     QuicTime next_send_time = delayed_packets_.front().send_time;
233     if (next_send_time > clock_->Now()) {
234       return next_send_time;
235     }
236     ReleaseNextPacket();
237   }
238   return QuicTime::Zero();
239 }
240 
SetDelayAlarm(QuicTime new_deadline)241 void PacketDroppingTestWriter::SetDelayAlarm(QuicTime new_deadline) {
242   delay_alarm_->Set(new_deadline);
243 }
244 
OnCanWrite()245 void PacketDroppingTestWriter::OnCanWrite() { on_can_write_->OnCanWrite(); }
246 
DelayedWrite(const char * buffer,size_t buf_len,const QuicIpAddress & self_address,const QuicSocketAddress & peer_address,std::unique_ptr<PerPacketOptions> options,const QuicPacketWriterParams & params,QuicTime send_time)247 PacketDroppingTestWriter::DelayedWrite::DelayedWrite(
248     const char* buffer, size_t buf_len, const QuicIpAddress& self_address,
249     const QuicSocketAddress& peer_address,
250     std::unique_ptr<PerPacketOptions> options,
251     const QuicPacketWriterParams& params, QuicTime send_time)
252     : buffer(buffer, buf_len),
253       self_address(self_address),
254       peer_address(peer_address),
255       options(std::move(options)),
256       params(params),
257       send_time(send_time) {}
258 
259 PacketDroppingTestWriter::DelayedWrite::~DelayedWrite() = default;
260 
261 }  // namespace test
262 }  // namespace quic
263