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