1 /*
2  *  Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 #include "media/sctp/sctp_transport.h"
11 
12 #include <memory>
13 #include <queue>
14 #include <string>
15 
16 #include "media/sctp/sctp_transport_internal.h"
17 #include "rtc_base/copy_on_write_buffer.h"
18 #include "rtc_base/gunit.h"
19 #include "rtc_base/logging.h"
20 #include "rtc_base/random.h"
21 #include "rtc_base/thread.h"
22 #include "test/gtest.h"
23 
24 namespace {
25 
26 static constexpr int kDefaultTimeout = 10000;  // 10 seconds.
27 static constexpr int kTransport1Port = 15001;
28 static constexpr int kTransport2Port = 25002;
29 static constexpr int kLogPerMessagesCount = 100;
30 
31 /**
32  * An simple packet transport implementation which can be
33  * configured to simulate uniform random packet loss and
34  * configurable random packet delay and reordering.
35  */
36 class SimulatedPacketTransport final : public rtc::PacketTransportInternal {
37  public:
SimulatedPacketTransport(std::string name,rtc::Thread * transport_thread,uint8_t packet_loss_percents,uint16_t avg_send_delay_millis)38   SimulatedPacketTransport(std::string name,
39                            rtc::Thread* transport_thread,
40                            uint8_t packet_loss_percents,
41                            uint16_t avg_send_delay_millis)
42       : transport_name_(name),
43         transport_thread_(transport_thread),
44         packet_loss_percents_(packet_loss_percents),
45         avg_send_delay_millis_(avg_send_delay_millis),
46         random_(42) {
47     RTC_DCHECK(transport_thread_);
48     RTC_DCHECK_LE(packet_loss_percents_, 100);
49     RTC_DCHECK_RUN_ON(transport_thread_);
50   }
51 
~SimulatedPacketTransport()52   ~SimulatedPacketTransport() override {
53     RTC_DCHECK_RUN_ON(transport_thread_);
54     auto destination = destination_.load();
55     if (destination != nullptr) {
56       invoker_.Flush(destination->transport_thread_);
57     }
58     invoker_.Flush(transport_thread_);
59     destination_ = nullptr;
60     SignalWritableState(this);
61   }
62 
transport_name() const63   const std::string& transport_name() const override { return transport_name_; }
64 
writable() const65   bool writable() const override { return destination_ != nullptr; }
66 
receiving() const67   bool receiving() const override { return true; }
68 
SendPacket(const char * data,size_t len,const rtc::PacketOptions & options,int flags=0)69   int SendPacket(const char* data,
70                  size_t len,
71                  const rtc::PacketOptions& options,
72                  int flags = 0) {
73     RTC_DCHECK_RUN_ON(transport_thread_);
74     auto destination = destination_.load();
75     if (destination == nullptr) {
76       return -1;
77     }
78     if (random_.Rand(100) < packet_loss_percents_) {
79       // silent packet loss
80       return 0;
81     }
82     rtc::CopyOnWriteBuffer buffer(data, len);
83     auto send_job = [this, flags, buffer = std::move(buffer)] {
84       auto destination = destination_.load();
85       if (destination == nullptr) {
86         return;
87       }
88       destination->SignalReadPacket(
89           destination, reinterpret_cast<const char*>(buffer.data()),
90           buffer.size(), rtc::Time(), flags);
91     };
92     // Introduce random send delay in range [0 .. 2 * avg_send_delay_millis_]
93     // millis, which will also work as random packet reordering mechanism.
94     uint16_t actual_send_delay = avg_send_delay_millis_;
95     int16_t reorder_delay =
96         avg_send_delay_millis_ *
97         std::min(1.0, std::max(-1.0, random_.Gaussian(0, 0.5)));
98     actual_send_delay += reorder_delay;
99 
100     if (actual_send_delay > 0) {
101       invoker_.AsyncInvokeDelayed<void>(RTC_FROM_HERE,
102                                         destination->transport_thread_,
103                                         std::move(send_job), actual_send_delay);
104     } else {
105       invoker_.AsyncInvoke<void>(RTC_FROM_HERE, destination->transport_thread_,
106                                  std::move(send_job));
107     }
108     return 0;
109   }
110 
SetOption(rtc::Socket::Option opt,int value)111   int SetOption(rtc::Socket::Option opt, int value) override { return 0; }
112 
GetOption(rtc::Socket::Option opt,int * value)113   bool GetOption(rtc::Socket::Option opt, int* value) override { return false; }
114 
GetError()115   int GetError() override { return 0; }
116 
network_route() const117   absl::optional<rtc::NetworkRoute> network_route() const override {
118     return absl::nullopt;
119   }
120 
SetDestination(SimulatedPacketTransport * destination)121   void SetDestination(SimulatedPacketTransport* destination) {
122     RTC_DCHECK_RUN_ON(transport_thread_);
123     if (destination == this) {
124       return;
125     }
126     destination_ = destination;
127     SignalWritableState(this);
128   }
129 
130  private:
131   const std::string transport_name_;
132   rtc::Thread* const transport_thread_;
133   const uint8_t packet_loss_percents_;
134   const uint16_t avg_send_delay_millis_;
135   std::atomic<SimulatedPacketTransport*> destination_ ATOMIC_VAR_INIT(nullptr);
136   rtc::AsyncInvoker invoker_;
137   webrtc::Random random_;
138   RTC_DISALLOW_COPY_AND_ASSIGN(SimulatedPacketTransport);
139 };
140 
141 /**
142  * A helper class to send specified number of messages
143  * over SctpTransport with SCTP reliability settings
144  * provided by user. The reliability settings are specified
145  * by passing a template instance of SendDataParams.
146  * When .sid field inside SendDataParams is specified to
147  * negative value it means that actual .sid will be
148  * assigned by sender itself, .sid will be assigned from
149  * range [cricket::kMinSctpSid; cricket::kMaxSctpSid].
150  * The wide range of sids are used to possibly trigger
151  * more execution paths inside usrsctp.
152  */
153 class SctpDataSender final {
154  public:
SctpDataSender(rtc::Thread * thread,cricket::SctpTransport * transport,uint64_t target_messages_count,cricket::SendDataParams send_params,uint32_t sender_id)155   SctpDataSender(rtc::Thread* thread,
156                  cricket::SctpTransport* transport,
157                  uint64_t target_messages_count,
158                  cricket::SendDataParams send_params,
159                  uint32_t sender_id)
160       : thread_(thread),
161         transport_(transport),
162         target_messages_count_(target_messages_count),
163         send_params_(send_params),
164         sender_id_(sender_id) {
165     RTC_DCHECK(thread_);
166     RTC_DCHECK(transport_);
167   }
168 
Start()169   void Start() {
170     invoker_.AsyncInvoke<void>(RTC_FROM_HERE, thread_, [this] {
171       if (started_) {
172         RTC_LOG(LS_INFO) << sender_id_ << " sender is already started";
173         return;
174       }
175       started_ = true;
176       SendNextMessage();
177     });
178   }
179 
BytesSentCount() const180   uint64_t BytesSentCount() const { return num_bytes_sent_; }
181 
MessagesSentCount() const182   uint64_t MessagesSentCount() const { return num_messages_sent_; }
183 
GetLastError()184   absl::optional<std::string> GetLastError() {
185     absl::optional<std::string> result = absl::nullopt;
186     thread_->Invoke<void>(RTC_FROM_HERE,
187                           [this, &result] { result = last_error_; });
188     return result;
189   }
190 
WaitForCompletion(int give_up_after_ms)191   bool WaitForCompletion(int give_up_after_ms) {
192     return sent_target_messages_count_.Wait(give_up_after_ms, kDefaultTimeout);
193   }
194 
195  private:
SendNextMessage()196   void SendNextMessage() {
197     RTC_DCHECK_RUN_ON(thread_);
198     if (!started_ || num_messages_sent_ >= target_messages_count_) {
199       sent_target_messages_count_.Set();
200       return;
201     }
202 
203     if (num_messages_sent_ % kLogPerMessagesCount == 0) {
204       RTC_LOG(LS_INFO) << sender_id_ << " sender will try send message "
205                        << (num_messages_sent_ + 1) << " out of "
206                        << target_messages_count_;
207     }
208 
209     cricket::SendDataParams params(send_params_);
210     if (params.sid < 0) {
211       params.sid = cricket::kMinSctpSid +
212                    (num_messages_sent_ % cricket::kMaxSctpStreams);
213     }
214 
215     cricket::SendDataResult result;
216     transport_->SendData(params, payload_, &result);
217     switch (result) {
218       case cricket::SDR_BLOCK:
219         // retry after timeout
220         invoker_.AsyncInvokeDelayed<void>(
221             RTC_FROM_HERE, thread_,
222             rtc::Bind(&SctpDataSender::SendNextMessage, this), 500);
223         break;
224       case cricket::SDR_SUCCESS:
225         // send next
226         num_bytes_sent_ += payload_.size();
227         ++num_messages_sent_;
228         invoker_.AsyncInvoke<void>(
229             RTC_FROM_HERE, thread_,
230             rtc::Bind(&SctpDataSender::SendNextMessage, this));
231         break;
232       case cricket::SDR_ERROR:
233         // give up
234         last_error_ = "SctpTransport::SendData error returned";
235         sent_target_messages_count_.Set();
236         break;
237     }
238   }
239 
240   rtc::Thread* const thread_;
241   cricket::SctpTransport* const transport_;
242   const uint64_t target_messages_count_;
243   const cricket::SendDataParams send_params_;
244   const uint32_t sender_id_;
245   rtc::CopyOnWriteBuffer payload_{std::string(1400, '.').c_str(), 1400};
246   std::atomic<bool> started_ ATOMIC_VAR_INIT(false);
247   rtc::AsyncInvoker invoker_;
248   std::atomic<uint64_t> num_messages_sent_ ATOMIC_VAR_INIT(0);
249   rtc::Event sent_target_messages_count_{true, false};
250   std::atomic<uint64_t> num_bytes_sent_ ATOMIC_VAR_INIT(0);
251   absl::optional<std::string> last_error_;
252   RTC_DISALLOW_COPY_AND_ASSIGN(SctpDataSender);
253 };
254 
255 /**
256  * A helper class which counts number of received messages
257  * and bytes over SctpTransport. Also allow waiting until
258  * specified number of messages received.
259  */
260 class SctpDataReceiver final : public sigslot::has_slots<> {
261  public:
SctpDataReceiver(uint32_t receiver_id,uint64_t target_messages_count)262   explicit SctpDataReceiver(uint32_t receiver_id,
263                             uint64_t target_messages_count)
264       : receiver_id_(receiver_id),
265         target_messages_count_(target_messages_count) {}
266 
OnDataReceived(const cricket::ReceiveDataParams & params,const rtc::CopyOnWriteBuffer & data)267   void OnDataReceived(const cricket::ReceiveDataParams& params,
268                       const rtc::CopyOnWriteBuffer& data) {
269     num_bytes_received_ += data.size();
270     if (++num_messages_received_ == target_messages_count_) {
271       received_target_messages_count_.Set();
272     }
273 
274     if (num_messages_received_ % kLogPerMessagesCount == 0) {
275       RTC_LOG(INFO) << receiver_id_ << " receiver got "
276                     << num_messages_received_ << " messages";
277     }
278   }
279 
MessagesReceivedCount() const280   uint64_t MessagesReceivedCount() const { return num_messages_received_; }
281 
BytesReceivedCount() const282   uint64_t BytesReceivedCount() const { return num_bytes_received_; }
283 
WaitForMessagesReceived(int timeout_millis)284   bool WaitForMessagesReceived(int timeout_millis) {
285     return received_target_messages_count_.Wait(timeout_millis);
286   }
287 
288  private:
289   std::atomic<uint64_t> num_messages_received_ ATOMIC_VAR_INIT(0);
290   std::atomic<uint64_t> num_bytes_received_ ATOMIC_VAR_INIT(0);
291   rtc::Event received_target_messages_count_{true, false};
292   const uint32_t receiver_id_;
293   const uint64_t target_messages_count_;
294   RTC_DISALLOW_COPY_AND_ASSIGN(SctpDataReceiver);
295 };
296 
297 /**
298  * Simple class to manage set of threads.
299  */
300 class ThreadPool final {
301  public:
ThreadPool(size_t threads_count)302   explicit ThreadPool(size_t threads_count) : random_(42) {
303     RTC_DCHECK(threads_count > 0);
304     threads_.reserve(threads_count);
305     for (size_t i = 0; i < threads_count; i++) {
306       auto thread = rtc::Thread::Create();
307       thread->SetName("Thread #" + rtc::ToString(i + 1) + " from Pool", this);
308       thread->Start();
309       threads_.emplace_back(std::move(thread));
310     }
311   }
312 
GetRandomThread()313   rtc::Thread* GetRandomThread() {
314     return threads_[random_.Rand(0U, threads_.size() - 1)].get();
315   }
316 
317  private:
318   webrtc::Random random_;
319   std::vector<std::unique_ptr<rtc::Thread>> threads_;
320   RTC_DISALLOW_COPY_AND_ASSIGN(ThreadPool);
321 };
322 
323 /**
324  * Represents single ping-pong test over SctpTransport.
325  * User can specify target number of message for bidirectional
326  * send, underlying transport packets loss and average packet delay
327  * and SCTP delivery settings.
328  */
329 class SctpPingPong final {
330  public:
SctpPingPong(uint32_t id,uint16_t port1,uint16_t port2,rtc::Thread * transport_thread1,rtc::Thread * transport_thread2,uint32_t messages_count,uint8_t packet_loss_percents,uint16_t avg_send_delay_millis,cricket::SendDataParams send_params)331   SctpPingPong(uint32_t id,
332                uint16_t port1,
333                uint16_t port2,
334                rtc::Thread* transport_thread1,
335                rtc::Thread* transport_thread2,
336                uint32_t messages_count,
337                uint8_t packet_loss_percents,
338                uint16_t avg_send_delay_millis,
339                cricket::SendDataParams send_params)
340       : id_(id),
341         port1_(port1),
342         port2_(port2),
343         transport_thread1_(transport_thread1),
344         transport_thread2_(transport_thread2),
345         messages_count_(messages_count),
346         packet_loss_percents_(packet_loss_percents),
347         avg_send_delay_millis_(avg_send_delay_millis),
348         send_params_(send_params) {
349     RTC_DCHECK(transport_thread1_ != nullptr);
350     RTC_DCHECK(transport_thread2_ != nullptr);
351   }
352 
~SctpPingPong()353   virtual ~SctpPingPong() {
354     transport_thread1_->Invoke<void>(RTC_FROM_HERE, [this] {
355       data_sender1_.reset();
356       sctp_transport1_->SetDtlsTransport(nullptr);
357       packet_transport1_->SetDestination(nullptr);
358     });
359     transport_thread2_->Invoke<void>(RTC_FROM_HERE, [this] {
360       data_sender2_.reset();
361       sctp_transport2_->SetDtlsTransport(nullptr);
362       packet_transport2_->SetDestination(nullptr);
363     });
364     transport_thread1_->Invoke<void>(RTC_FROM_HERE, [this] {
365       sctp_transport1_.reset();
366       data_receiver1_.reset();
367       packet_transport1_.reset();
368     });
369     transport_thread2_->Invoke<void>(RTC_FROM_HERE, [this] {
370       sctp_transport2_.reset();
371       data_receiver2_.reset();
372       packet_transport2_.reset();
373     });
374   }
375 
Start()376   bool Start() {
377     CreateTwoConnectedSctpTransportsWithAllStreams();
378 
379     {
380       rtc::CritScope cs(&lock_);
381       if (!errors_list_.empty()) {
382         return false;
383       }
384     }
385 
386     data_sender1_.reset(new SctpDataSender(transport_thread1_,
387                                            sctp_transport1_.get(),
388                                            messages_count_, send_params_, id_));
389     data_sender2_.reset(new SctpDataSender(transport_thread2_,
390                                            sctp_transport2_.get(),
391                                            messages_count_, send_params_, id_));
392     data_sender1_->Start();
393     data_sender2_->Start();
394     return true;
395   }
396 
GetErrorsList() const397   std::vector<std::string> GetErrorsList() const {
398     std::vector<std::string> result;
399     {
400       rtc::CritScope cs(&lock_);
401       result = errors_list_;
402     }
403     return result;
404   }
405 
WaitForCompletion(int32_t timeout_millis)406   void WaitForCompletion(int32_t timeout_millis) {
407     if (data_sender1_ == nullptr) {
408       ReportError("SctpPingPong id = " + rtc::ToString(id_) +
409                   ", sender 1 is not created");
410       return;
411     }
412     if (data_sender2_ == nullptr) {
413       ReportError("SctpPingPong id = " + rtc::ToString(id_) +
414                   ", sender 2 is not created");
415       return;
416     }
417 
418     if (!data_sender1_->WaitForCompletion(timeout_millis)) {
419       ReportError("SctpPingPong id = " + rtc::ToString(id_) +
420                   ", sender 1 failed to complete within " +
421                   rtc::ToString(timeout_millis) + " millis");
422       return;
423     }
424 
425     auto sender1_error = data_sender1_->GetLastError();
426     if (sender1_error.has_value()) {
427       ReportError("SctpPingPong id = " + rtc::ToString(id_) +
428                   ", sender 1 error: " + sender1_error.value());
429       return;
430     }
431 
432     if (!data_sender2_->WaitForCompletion(timeout_millis)) {
433       ReportError("SctpPingPong id = " + rtc::ToString(id_) +
434                   ", sender 2 failed to complete within " +
435                   rtc::ToString(timeout_millis) + " millis");
436       return;
437     }
438 
439     auto sender2_error = data_sender2_->GetLastError();
440     if (sender2_error.has_value()) {
441       ReportError("SctpPingPong id = " + rtc::ToString(id_) +
442                   ", sender 2 error: " + sender1_error.value());
443       return;
444     }
445 
446     if ((data_sender1_->MessagesSentCount() != messages_count_)) {
447       ReportError("SctpPingPong id = " + rtc::ToString(id_) +
448                   ", sender 1 sent only " +
449                   rtc::ToString(data_sender1_->MessagesSentCount()) +
450                   " out of " + rtc::ToString(messages_count_));
451       return;
452     }
453 
454     if ((data_sender2_->MessagesSentCount() != messages_count_)) {
455       ReportError("SctpPingPong id = " + rtc::ToString(id_) +
456                   ", sender 2 sent only " +
457                   rtc::ToString(data_sender2_->MessagesSentCount()) +
458                   " out of " + rtc::ToString(messages_count_));
459       return;
460     }
461 
462     if (!data_receiver1_->WaitForMessagesReceived(timeout_millis)) {
463       ReportError("SctpPingPong id = " + rtc::ToString(id_) +
464                   ", receiver 1 did not complete within " +
465                   rtc::ToString(messages_count_));
466       return;
467     }
468 
469     if (!data_receiver2_->WaitForMessagesReceived(timeout_millis)) {
470       ReportError("SctpPingPong id = " + rtc::ToString(id_) +
471                   ", receiver 2 did not complete within " +
472                   rtc::ToString(messages_count_));
473       return;
474     }
475 
476     if (data_receiver1_->BytesReceivedCount() !=
477         data_sender2_->BytesSentCount()) {
478       ReportError(
479           "SctpPingPong id = " + rtc::ToString(id_) + ", receiver 1 received " +
480           rtc::ToString(data_receiver1_->BytesReceivedCount()) +
481           " bytes, but sender 2 send " +
482           rtc::ToString(rtc::ToString(data_sender2_->BytesSentCount())));
483       return;
484     }
485 
486     if (data_receiver2_->BytesReceivedCount() !=
487         data_sender1_->BytesSentCount()) {
488       ReportError(
489           "SctpPingPong id = " + rtc::ToString(id_) + ", receiver 2 received " +
490           rtc::ToString(data_receiver2_->BytesReceivedCount()) +
491           " bytes, but sender 1 send " +
492           rtc::ToString(rtc::ToString(data_sender1_->BytesSentCount())));
493       return;
494     }
495 
496     RTC_LOG(LS_INFO) << "SctpPingPong id = " << id_ << " is done";
497   }
498 
499  private:
CreateTwoConnectedSctpTransportsWithAllStreams()500   void CreateTwoConnectedSctpTransportsWithAllStreams() {
501     transport_thread1_->Invoke<void>(RTC_FROM_HERE, [this] {
502       packet_transport1_.reset(new SimulatedPacketTransport(
503           "SctpPingPong id = " + rtc::ToString(id_) + ", packet transport 1",
504           transport_thread1_, packet_loss_percents_, avg_send_delay_millis_));
505       data_receiver1_.reset(new SctpDataReceiver(id_, messages_count_));
506       sctp_transport1_.reset(new cricket::SctpTransport(
507           transport_thread1_, packet_transport1_.get()));
508       sctp_transport1_->set_debug_name_for_testing("sctp transport 1");
509 
510       sctp_transport1_->SignalDataReceived.connect(
511           data_receiver1_.get(), &SctpDataReceiver::OnDataReceived);
512 
513       for (uint32_t i = cricket::kMinSctpSid; i <= cricket::kMaxSctpSid; i++) {
514         if (!sctp_transport1_->OpenStream(i)) {
515           ReportError("SctpPingPong id = " + rtc::ToString(id_) +
516                       ", sctp transport 1 stream " + rtc::ToString(i) +
517                       " failed to open");
518           break;
519         }
520       }
521     });
522 
523     transport_thread2_->Invoke<void>(RTC_FROM_HERE, [this] {
524       packet_transport2_.reset(new SimulatedPacketTransport(
525           "SctpPingPong id = " + rtc::ToString(id_) + "packet transport 2",
526           transport_thread2_, packet_loss_percents_, avg_send_delay_millis_));
527       data_receiver2_.reset(new SctpDataReceiver(id_, messages_count_));
528       sctp_transport2_.reset(new cricket::SctpTransport(
529           transport_thread2_, packet_transport2_.get()));
530       sctp_transport2_->set_debug_name_for_testing("sctp transport 2");
531       sctp_transport2_->SignalDataReceived.connect(
532           data_receiver2_.get(), &SctpDataReceiver::OnDataReceived);
533 
534       for (uint32_t i = cricket::kMinSctpSid; i <= cricket::kMaxSctpSid; i++) {
535         if (!sctp_transport2_->OpenStream(i)) {
536           ReportError("SctpPingPong id = " + rtc::ToString(id_) +
537                       ", sctp transport 2 stream " + rtc::ToString(i) +
538                       " failed to open");
539           break;
540         }
541       }
542     });
543 
544     transport_thread1_->Invoke<void>(RTC_FROM_HERE, [this] {
545       packet_transport1_->SetDestination(packet_transport2_.get());
546     });
547     transport_thread2_->Invoke<void>(RTC_FROM_HERE, [this] {
548       packet_transport2_->SetDestination(packet_transport1_.get());
549     });
550 
551     transport_thread1_->Invoke<void>(RTC_FROM_HERE, [this] {
552       if (!sctp_transport1_->Start(port1_, port2_,
553                                    cricket::kSctpSendBufferSize)) {
554         ReportError("SctpPingPong id = " + rtc::ToString(id_) +
555                     ", failed to start sctp transport 1");
556       }
557     });
558 
559     transport_thread2_->Invoke<void>(RTC_FROM_HERE, [this] {
560       if (!sctp_transport2_->Start(port2_, port1_,
561                                    cricket::kSctpSendBufferSize)) {
562         ReportError("SctpPingPong id = " + rtc::ToString(id_) +
563                     ", failed to start sctp transport 2");
564       }
565     });
566   }
567 
ReportError(std::string error)568   void ReportError(std::string error) {
569     rtc::CritScope cs(&lock_);
570     errors_list_.push_back(std::move(error));
571   }
572 
573   std::unique_ptr<SimulatedPacketTransport> packet_transport1_;
574   std::unique_ptr<SimulatedPacketTransport> packet_transport2_;
575   std::unique_ptr<SctpDataReceiver> data_receiver1_;
576   std::unique_ptr<SctpDataReceiver> data_receiver2_;
577   std::unique_ptr<cricket::SctpTransport> sctp_transport1_;
578   std::unique_ptr<cricket::SctpTransport> sctp_transport2_;
579   std::unique_ptr<SctpDataSender> data_sender1_;
580   std::unique_ptr<SctpDataSender> data_sender2_;
581   rtc::CriticalSection lock_;
582   std::vector<std::string> errors_list_ RTC_GUARDED_BY(lock_);
583 
584   const uint32_t id_;
585   const uint16_t port1_;
586   const uint16_t port2_;
587   rtc::Thread* const transport_thread1_;
588   rtc::Thread* const transport_thread2_;
589   const uint32_t messages_count_;
590   const uint8_t packet_loss_percents_;
591   const uint16_t avg_send_delay_millis_;
592   const cricket::SendDataParams send_params_;
593   RTC_DISALLOW_COPY_AND_ASSIGN(SctpPingPong);
594 };
595 
596 /**
597  * Helper function to calculate max number of milliseconds
598  * allowed for test to run based on test configuration.
599  */
GetExecutionTimeLimitInMillis(uint32_t total_messages,uint8_t packet_loss_percents)600 constexpr int32_t GetExecutionTimeLimitInMillis(uint32_t total_messages,
601                                                 uint8_t packet_loss_percents) {
602   return std::min<int64_t>(
603       std::numeric_limits<int32_t>::max(),
604       std::max<int64_t>(
605           1LL * total_messages * 100 *
606               std::max(1, packet_loss_percents * packet_loss_percents),
607           kDefaultTimeout));
608 }
609 
610 }  // namespace
611 
612 namespace cricket {
613 
614 /**
615  * The set of tests intended to check usrsctp reliability on
616  * stress conditions: multiple sockets, concurrent access,
617  * lossy network link. It was observed in the past that
618  * usrsctp might misbehave in concurrent environment
619  * under load on lossy networks: deadlocks and memory corruption
620  * issues might happen in non-basic usage scenarios.
621  * It's recommended to run this test whenever usrsctp version
622  * used is updated to verify it properly works in stress
623  * conditions under higher than usual load.
624  * It is also recommended to enable ASAN when these tests
625  * are executed, so whenever memory bug is happen inside usrsctp,
626  * it will be easier to understand what went wrong with ASAN
627  * provided diagnostics information.
628  * The tests cases currently disabled by default due to
629  * long execution time and due to unresolved issue inside
630  * `usrsctp` library detected by try-bots with ThreadSanitizer.
631  */
632 class UsrSctpReliabilityTest : public ::testing::Test {};
633 
634 /**
635  * A simple test which send multiple messages over reliable
636  * connection, usefull to verify test infrastructure works.
637  * Execution time is less than 1 second.
638  */
TEST_F(UsrSctpReliabilityTest,DISABLED_AllMessagesAreDeliveredOverReliableConnection)639 TEST_F(UsrSctpReliabilityTest,
640        DISABLED_AllMessagesAreDeliveredOverReliableConnection) {
641   auto thread1 = rtc::Thread::Create();
642   auto thread2 = rtc::Thread::Create();
643   thread1->Start();
644   thread2->Start();
645   constexpr uint8_t packet_loss_percents = 0;
646   constexpr uint16_t avg_send_delay_millis = 10;
647   constexpr uint32_t messages_count = 100;
648   constexpr int32_t wait_timeout =
649       GetExecutionTimeLimitInMillis(messages_count, packet_loss_percents);
650   static_assert(wait_timeout > 0,
651                 "Timeout computation must produce positive value");
652 
653   cricket::SendDataParams send_params;
654   send_params.sid = -1;
655   send_params.ordered = true;
656   send_params.reliable = true;
657   send_params.max_rtx_count = 0;
658   send_params.max_rtx_ms = 0;
659 
660   SctpPingPong test(1, kTransport1Port, kTransport2Port, thread1.get(),
661                     thread2.get(), messages_count, packet_loss_percents,
662                     avg_send_delay_millis, send_params);
663   EXPECT_TRUE(test.Start()) << rtc::join(test.GetErrorsList(), ';');
664   test.WaitForCompletion(wait_timeout);
665   auto errors_list = test.GetErrorsList();
666   EXPECT_TRUE(errors_list.empty()) << rtc::join(errors_list, ';');
667 }
668 
669 /**
670  * A test to verify that multiple messages can be reliably delivered
671  * over lossy network when usrsctp configured to guarantee reliably
672  * and in order delivery.
673  * The test case is disabled by default because it takes
674  * long time to run.
675  * Execution time is about 2.5 minutes.
676  */
TEST_F(UsrSctpReliabilityTest,DISABLED_AllMessagesAreDeliveredOverLossyConnectionReliableAndInOrder)677 TEST_F(UsrSctpReliabilityTest,
678        DISABLED_AllMessagesAreDeliveredOverLossyConnectionReliableAndInOrder) {
679   auto thread1 = rtc::Thread::Create();
680   auto thread2 = rtc::Thread::Create();
681   thread1->Start();
682   thread2->Start();
683   constexpr uint8_t packet_loss_percents = 5;
684   constexpr uint16_t avg_send_delay_millis = 16;
685   constexpr uint32_t messages_count = 10000;
686   constexpr int32_t wait_timeout =
687       GetExecutionTimeLimitInMillis(messages_count, packet_loss_percents);
688   static_assert(wait_timeout > 0,
689                 "Timeout computation must produce positive value");
690 
691   cricket::SendDataParams send_params;
692   send_params.sid = -1;
693   send_params.ordered = true;
694   send_params.reliable = true;
695   send_params.max_rtx_count = 0;
696   send_params.max_rtx_ms = 0;
697 
698   SctpPingPong test(1, kTransport1Port, kTransport2Port, thread1.get(),
699                     thread2.get(), messages_count, packet_loss_percents,
700                     avg_send_delay_millis, send_params);
701 
702   EXPECT_TRUE(test.Start()) << rtc::join(test.GetErrorsList(), ';');
703   test.WaitForCompletion(wait_timeout);
704   auto errors_list = test.GetErrorsList();
705   EXPECT_TRUE(errors_list.empty()) << rtc::join(errors_list, ';');
706 }
707 
708 /**
709  * A test to verify that multiple messages can be reliably delivered
710  * over lossy network when usrsctp configured to retransmit lost
711  * packets.
712  * The test case is disabled by default because it takes
713  * long time to run.
714  * Execution time is about 2.5 minutes.
715  */
TEST_F(UsrSctpReliabilityTest,DISABLED_AllMessagesAreDeliveredOverLossyConnectionWithRetries)716 TEST_F(UsrSctpReliabilityTest,
717        DISABLED_AllMessagesAreDeliveredOverLossyConnectionWithRetries) {
718   auto thread1 = rtc::Thread::Create();
719   auto thread2 = rtc::Thread::Create();
720   thread1->Start();
721   thread2->Start();
722   constexpr uint8_t packet_loss_percents = 5;
723   constexpr uint16_t avg_send_delay_millis = 16;
724   constexpr uint32_t messages_count = 10000;
725   constexpr int32_t wait_timeout =
726       GetExecutionTimeLimitInMillis(messages_count, packet_loss_percents);
727   static_assert(wait_timeout > 0,
728                 "Timeout computation must produce positive value");
729 
730   cricket::SendDataParams send_params;
731   send_params.sid = -1;
732   send_params.ordered = false;
733   send_params.reliable = false;
734   send_params.max_rtx_count = INT_MAX;
735   send_params.max_rtx_ms = INT_MAX;
736 
737   SctpPingPong test(1, kTransport1Port, kTransport2Port, thread1.get(),
738                     thread2.get(), messages_count, packet_loss_percents,
739                     avg_send_delay_millis, send_params);
740 
741   EXPECT_TRUE(test.Start()) << rtc::join(test.GetErrorsList(), ';');
742   test.WaitForCompletion(wait_timeout);
743   auto errors_list = test.GetErrorsList();
744   EXPECT_TRUE(errors_list.empty()) << rtc::join(errors_list, ';');
745 }
746 
747 /**
748  * This is kind of reliability stress-test of usrsctp to verify
749  * that all messages are delivered when multiple usrsctp
750  * sockets used concurrently and underlying transport is lossy.
751  *
752  * It was observed in the past that in stress condtions usrsctp
753  * might encounter deadlock and memory corruption bugs:
754  * https://github.com/sctplab/usrsctp/issues/325
755  *
756  * It is recoomended to run this test whenever usrsctp version
757  * used by WebRTC is updated.
758  *
759  * The test case is disabled by default because it takes
760  * long time to run.
761  * Execution time of this test is about 1-2 hours.
762  */
TEST_F(UsrSctpReliabilityTest,DISABLED_AllMessagesAreDeliveredOverLossyConnectionConcurrentTests)763 TEST_F(UsrSctpReliabilityTest,
764        DISABLED_AllMessagesAreDeliveredOverLossyConnectionConcurrentTests) {
765   ThreadPool pool(16);
766 
767   cricket::SendDataParams send_params;
768   send_params.sid = -1;
769   send_params.ordered = true;
770   send_params.reliable = true;
771   send_params.max_rtx_count = 0;
772   send_params.max_rtx_ms = 0;
773   constexpr uint32_t base_sctp_port = 5000;
774 
775   // The constants value below were experimentally chosen
776   // to have reasonable execution time and to reproduce
777   // particular deadlock issue inside usrsctp:
778   // https://github.com/sctplab/usrsctp/issues/325
779   // The constants values may be adjusted next time
780   // some other issue inside usrsctp need to be debugged.
781   constexpr uint32_t messages_count = 200;
782   constexpr uint8_t packet_loss_percents = 5;
783   constexpr uint16_t avg_send_delay_millis = 0;
784   constexpr uint32_t parallel_ping_pongs = 16 * 1024;
785   constexpr uint32_t total_ping_pong_tests = 16 * parallel_ping_pongs;
786 
787   constexpr int32_t wait_timeout = GetExecutionTimeLimitInMillis(
788       total_ping_pong_tests * messages_count, packet_loss_percents);
789   static_assert(wait_timeout > 0,
790                 "Timeout computation must produce positive value");
791 
792   std::queue<std::unique_ptr<SctpPingPong>> tests;
793 
794   for (uint32_t i = 0; i < total_ping_pong_tests; i++) {
795     uint32_t port1 =
796         base_sctp_port + (2 * i) % (UINT16_MAX - base_sctp_port - 1);
797 
798     auto test = std::make_unique<SctpPingPong>(
799         i, port1, port1 + 1, pool.GetRandomThread(), pool.GetRandomThread(),
800         messages_count, packet_loss_percents, avg_send_delay_millis,
801         send_params);
802 
803     EXPECT_TRUE(test->Start()) << rtc::join(test->GetErrorsList(), ';');
804     tests.emplace(std::move(test));
805 
806     while (tests.size() >= parallel_ping_pongs) {
807       auto& oldest_test = tests.front();
808       oldest_test->WaitForCompletion(wait_timeout);
809 
810       auto errors_list = oldest_test->GetErrorsList();
811       EXPECT_TRUE(errors_list.empty()) << rtc::join(errors_list, ';');
812       tests.pop();
813     }
814   }
815 
816   while (!tests.empty()) {
817     auto& oldest_test = tests.front();
818     oldest_test->WaitForCompletion(wait_timeout);
819 
820     auto errors_list = oldest_test->GetErrorsList();
821     EXPECT_TRUE(errors_list.empty()) << rtc::join(errors_list, ';');
822     tests.pop();
823   }
824 }
825 
826 }  // namespace cricket
827