aboutsummaryrefslogtreecommitdiff
path: root/media/sctp
diff options
context:
space:
mode:
authorFlorent Castelli <orphis@webrtc.org>2022-03-31 19:15:10 +0200
committerWebRTC LUCI CQ <webrtc-scoped@luci-project-accounts.iam.gserviceaccount.com>2022-04-04 10:30:46 +0000
commitf2599a7f4374cb2d5b469c4d6d8e3250bad0a128 (patch)
tree3bbc76d3b3086d3adb3cc0338f77b18824cf8a10 /media/sctp
parentc128277f5600970e9d23bb2697a65551193e32e2 (diff)
downloadwebrtc-f2599a7f4374cb2d5b469c4d6d8e3250bad0a128.tar.gz
Remove usrsctp, dcSCTP is now the unique SCTP implementation
Bug: chromium:1243702 Change-Id: Id11299d26f0f8713a57781b57277837aace531f2 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/251821 Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> Reviewed-by: Victor Boivie <boivie@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Commit-Queue: Florent Castelli <orphis@webrtc.org> Cr-Commit-Position: refs/heads/main@{#36423}
Diffstat (limited to 'media/sctp')
-rw-r--r--media/sctp/sctp_transport_factory.cc31
-rw-r--r--media/sctp/sctp_transport_factory.h6
-rw-r--r--media/sctp/usrsctp_transport.cc1575
-rw-r--r--media/sctp/usrsctp_transport.h296
-rw-r--r--media/sctp/usrsctp_transport_reliability_unittest.cc809
-rw-r--r--media/sctp/usrsctp_transport_unittest.cc883
6 files changed, 7 insertions, 3593 deletions
diff --git a/media/sctp/sctp_transport_factory.cc b/media/sctp/sctp_transport_factory.cc
index 51ce372d74..457bc5f889 100644
--- a/media/sctp/sctp_transport_factory.cc
+++ b/media/sctp/sctp_transport_factory.cc
@@ -10,29 +10,18 @@
#include "media/sctp/sctp_transport_factory.h"
-#include "api/field_trials_view.h"
#include "rtc_base/system/unused.h"
#ifdef WEBRTC_HAVE_DCSCTP
-#include "media/sctp/dcsctp_transport.h" // nogncheck
-#include "system_wrappers/include/clock.h" // nogncheck
-#endif
-
-#ifdef WEBRTC_HAVE_USRSCTP
-#include "media/sctp/usrsctp_transport.h" // nogncheck
+#include "media/sctp/dcsctp_transport.h" // nogncheck
+#include "system_wrappers/include/clock.h" // nogncheck
#endif
namespace cricket {
-SctpTransportFactory::SctpTransportFactory(
- rtc::Thread* network_thread,
- const webrtc::FieldTrialsView& field_trials)
- : network_thread_(network_thread), use_usrsctp_("Disabled", false) {
+SctpTransportFactory::SctpTransportFactory(rtc::Thread* network_thread)
+ : network_thread_(network_thread) {
RTC_UNUSED(network_thread_);
-#ifdef WEBRTC_HAVE_DCSCTP
- webrtc::ParseFieldTrial({&use_usrsctp_},
- field_trials.Lookup("WebRTC-DataChannel-Dcsctp"));
-#endif
}
std::unique_ptr<SctpTransportInternal>
@@ -40,16 +29,8 @@ SctpTransportFactory::CreateSctpTransport(
rtc::PacketTransportInternal* transport) {
std::unique_ptr<SctpTransportInternal> result;
#ifdef WEBRTC_HAVE_DCSCTP
- if (!use_usrsctp_.Get()) {
- result = std::unique_ptr<SctpTransportInternal>(new webrtc::DcSctpTransport(
- network_thread_, transport, webrtc::Clock::GetRealTimeClock()));
- }
-#endif
-#ifdef WEBRTC_HAVE_USRSCTP
- if (!result) {
- result = std::unique_ptr<SctpTransportInternal>(
- new UsrsctpTransport(network_thread_, transport));
- }
+ result = std::unique_ptr<SctpTransportInternal>(new webrtc::DcSctpTransport(
+ network_thread_, transport, webrtc::Clock::GetRealTimeClock()));
#endif
return result;
}
diff --git a/media/sctp/sctp_transport_factory.h b/media/sctp/sctp_transport_factory.h
index d117fdef5c..4fff214129 100644
--- a/media/sctp/sctp_transport_factory.h
+++ b/media/sctp/sctp_transport_factory.h
@@ -13,25 +13,21 @@
#include <memory>
-#include "api/field_trials_view.h"
#include "api/transport/sctp_transport_factory_interface.h"
#include "media/sctp/sctp_transport_internal.h"
-#include "rtc_base/experiments/field_trial_parser.h"
#include "rtc_base/thread.h"
namespace cricket {
class SctpTransportFactory : public webrtc::SctpTransportFactoryInterface {
public:
- explicit SctpTransportFactory(rtc::Thread* network_thread,
- const webrtc::FieldTrialsView& field_trials);
+ explicit SctpTransportFactory(rtc::Thread* network_thread);
std::unique_ptr<SctpTransportInternal> CreateSctpTransport(
rtc::PacketTransportInternal* transport) override;
private:
rtc::Thread* network_thread_;
- webrtc::FieldTrialFlag use_usrsctp_;
};
} // namespace cricket
diff --git a/media/sctp/usrsctp_transport.cc b/media/sctp/usrsctp_transport.cc
deleted file mode 100644
index 4babf110a2..0000000000
--- a/media/sctp/usrsctp_transport.cc
+++ /dev/null
@@ -1,1575 +0,0 @@
-/*
- * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#include <errno.h>
-namespace {
-// Some ERRNO values get re-#defined to WSA* equivalents in some talk/
-// headers. We save the original ones in an enum.
-enum PreservedErrno {
- SCTP_EINPROGRESS = EINPROGRESS,
- SCTP_EWOULDBLOCK = EWOULDBLOCK
-};
-
-// Successful return value from usrsctp callbacks. Is not actually used by
-// usrsctp, but all example programs for usrsctp use 1 as their return value.
-constexpr int kSctpSuccessReturn = 1;
-constexpr int kSctpErrorReturn = 0;
-
-} // namespace
-
-#include <stdarg.h>
-#include <stdio.h>
-#include <usrsctp.h>
-
-#include <memory>
-#include <unordered_map>
-#include <utility>
-
-#include "absl/algorithm/container.h"
-#include "absl/base/attributes.h"
-#include "absl/types/optional.h"
-#include "api/sequence_checker.h"
-#include "media/base/codec.h"
-#include "media/base/media_channel.h"
-#include "media/base/media_constants.h"
-#include "media/base/stream_params.h"
-#include "media/sctp/usrsctp_transport.h"
-#include "p2p/base/dtls_transport_internal.h" // For PF_NORMAL
-#include "rtc_base/arraysize.h"
-#include "rtc_base/copy_on_write_buffer.h"
-#include "rtc_base/helpers.h"
-#include "rtc_base/logging.h"
-#include "rtc_base/numerics/safe_conversions.h"
-#include "rtc_base/string_utils.h"
-#include "rtc_base/synchronization/mutex.h"
-#include "rtc_base/task_utils/to_queued_task.h"
-#include "rtc_base/thread_annotations.h"
-#include "rtc_base/trace_event.h"
-
-namespace cricket {
-namespace {
-
-// The biggest SCTP packet. Starting from a 'safe' wire MTU value of 1280,
-// take off 85 bytes for DTLS/TURN/TCP/IP and ciphertext overhead.
-//
-// Additionally, it's possible that TURN adds an additional 4 bytes of overhead
-// after a channel has been established, so we subtract an additional 4 bytes.
-//
-// 1280 IPV6 MTU
-// -40 IPV6 header
-// -8 UDP
-// -24 GCM Cipher
-// -13 DTLS record header
-// -4 TURN ChannelData
-// = 1191 bytes.
-static constexpr size_t kSctpMtu = 1191;
-
-// Set the initial value of the static SCTP Data Engines reference count.
-ABSL_CONST_INIT int g_usrsctp_usage_count = 0;
-ABSL_CONST_INIT bool g_usrsctp_initialized_ = false;
-ABSL_CONST_INIT webrtc::GlobalMutex g_usrsctp_lock_(absl::kConstInit);
-ABSL_CONST_INIT char kZero[] = {'\0'};
-
-// DataMessageType is used for the SCTP "Payload Protocol Identifier", as
-// defined in http://tools.ietf.org/html/rfc4960#section-14.4
-//
-// For the list of IANA approved values see:
-// https://tools.ietf.org/html/rfc8831 Sec. 8
-// http://www.iana.org/assignments/sctp-parameters/sctp-parameters.xml
-// The value is not used by SCTP itself. It indicates the protocol running
-// on top of SCTP.
-enum {
- PPID_NONE = 0, // No protocol is specified.
- PPID_CONTROL = 50,
- PPID_TEXT_LAST = 51,
- PPID_BINARY_PARTIAL = 52, // Deprecated
- PPID_BINARY_LAST = 53,
- PPID_TEXT_PARTIAL = 54, // Deprecated
- PPID_TEXT_EMPTY = 56,
- PPID_BINARY_EMPTY = 57,
-};
-
-// Should only be modified by UsrSctpWrapper.
-ABSL_CONST_INIT cricket::UsrsctpTransportMap* g_transport_map_ = nullptr;
-
-// Helper that will call C's free automatically.
-// TODO(b/181900299): Figure out why unique_ptr with a custom deleter is causing
-// issues in a certain build environment.
-class AutoFreedPointer {
- public:
- explicit AutoFreedPointer(void* ptr) : ptr_(ptr) {}
- AutoFreedPointer(AutoFreedPointer&& o) : ptr_(o.ptr_) { o.ptr_ = nullptr; }
- ~AutoFreedPointer() { free(ptr_); }
-
- void* get() const { return ptr_; }
-
- private:
- void* ptr_;
-};
-
-// Helper for logging SCTP messages.
-#if defined(__GNUC__)
-__attribute__((__format__(__printf__, 1, 2)))
-#endif
-void DebugSctpPrintf(const char* format, ...) {
-#if RTC_DCHECK_IS_ON
- char s[255];
- va_list ap;
- va_start(ap, format);
- vsnprintf(s, sizeof(s), format, ap);
- RTC_LOG(LS_INFO) << "SCTP: " << s;
- va_end(ap);
-#endif
-}
-
-// Get the PPID to use for the terminating fragment of this type.
-uint32_t GetPpid(webrtc::DataMessageType type, size_t size) {
- switch (type) {
- case webrtc::DataMessageType::kControl:
- return PPID_CONTROL;
- case webrtc::DataMessageType::kBinary:
- return size > 0 ? PPID_BINARY_LAST : PPID_BINARY_EMPTY;
- case webrtc::DataMessageType::kText:
- return size > 0 ? PPID_TEXT_LAST : PPID_TEXT_EMPTY;
- }
-}
-
-bool GetDataMediaType(uint32_t ppid, webrtc::DataMessageType* dest) {
- RTC_DCHECK(dest != NULL);
- switch (ppid) {
- case PPID_BINARY_PARTIAL:
- case PPID_BINARY_LAST:
- case PPID_BINARY_EMPTY:
- *dest = webrtc::DataMessageType::kBinary;
- return true;
-
- case PPID_TEXT_PARTIAL:
- case PPID_TEXT_LAST:
- case PPID_TEXT_EMPTY:
- *dest = webrtc::DataMessageType::kText;
- return true;
-
- case PPID_CONTROL:
- *dest = webrtc::DataMessageType::kControl;
- return true;
- }
- return false;
-}
-
-bool IsEmptyPPID(uint32_t ppid) {
- return ppid == PPID_BINARY_EMPTY || ppid == PPID_TEXT_EMPTY;
-}
-
-// Log the packet in text2pcap format, if log level is at LS_VERBOSE.
-//
-// In order to turn these logs into a pcap file you can use, first filter the
-// "SCTP_PACKET" log lines:
-//
-// cat chrome_debug.log | grep SCTP_PACKET > filtered.log
-//
-// Then run through text2pcap:
-//
-// text2pcap -n -l 248 -D -t '%H:%M:%S.' filtered.log filtered.pcapng
-//
-// Command flag information:
-// -n: Outputs to a pcapng file, can specify inbound/outbound packets.
-// -l: Specifies the link layer header type. 248 means SCTP. See:
-// http://www.tcpdump.org/linktypes.html
-// -D: Text before packet specifies if it is inbound or outbound.
-// -t: Time format.
-//
-// Why do all this? Because SCTP goes over DTLS, which is encrypted. So just
-// getting a normal packet capture won't help you, unless you have the DTLS
-// keying material.
-void VerboseLogPacket(const void* data, size_t length, int direction) {
- if (RTC_LOG_CHECK_LEVEL(LS_VERBOSE) && length > 0) {
- char* dump_buf;
- // Some downstream project uses an older version of usrsctp that expects
- // a non-const "void*" as first parameter when dumping the packet, so we
- // need to cast the const away here to avoid a compiler error.
- if ((dump_buf = usrsctp_dumppacket(const_cast<void*>(data), length,
- direction)) != NULL) {
- RTC_LOG(LS_VERBOSE) << dump_buf;
- usrsctp_freedumpbuffer(dump_buf);
- }
- }
-}
-
-// Creates the sctp_sendv_spa struct used for setting flags in the
-// sctp_sendv() call.
-sctp_sendv_spa CreateSctpSendParams(int sid,
- const webrtc::SendDataParams& params,
- size_t size) {
- struct sctp_sendv_spa spa = {0};
- spa.sendv_flags |= SCTP_SEND_SNDINFO_VALID;
- spa.sendv_sndinfo.snd_sid = sid;
- spa.sendv_sndinfo.snd_ppid = rtc::HostToNetwork32(GetPpid(params.type, size));
- // Explicitly marking the EOR flag turns the usrsctp_sendv call below into a
- // non atomic operation. This means that the sctp lib might only accept the
- // message partially. This is done in order to improve throughput, so that we
- // don't have to wait for an empty buffer to send the max message length, for
- // example.
- spa.sendv_sndinfo.snd_flags |= SCTP_EOR;
-
- if (!params.ordered) {
- spa.sendv_sndinfo.snd_flags |= SCTP_UNORDERED;
- }
- if (params.max_rtx_count.has_value()) {
- RTC_DCHECK(*params.max_rtx_count >= 0 &&
- *params.max_rtx_count <= std::numeric_limits<uint16_t>::max());
- spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
- spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX;
- spa.sendv_prinfo.pr_value = *params.max_rtx_count;
- }
- if (params.max_rtx_ms.has_value()) {
- RTC_DCHECK(*params.max_rtx_ms >= 0 &&
- *params.max_rtx_ms <= std::numeric_limits<uint16_t>::max());
- spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
- spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_TTL;
- spa.sendv_prinfo.pr_value = *params.max_rtx_ms;
- }
- return spa;
-}
-
-std::string SctpErrorCauseCodeToString(SctpErrorCauseCode code) {
- switch (code) {
- case SctpErrorCauseCode::kInvalidStreamIdentifier:
- return "Invalid Stream Identifier";
- case SctpErrorCauseCode::kMissingMandatoryParameter:
- return "Missing Mandatory Parameter";
- case SctpErrorCauseCode::kStaleCookieError:
- return "Stale Cookie Error";
- case SctpErrorCauseCode::kOutOfResource:
- return "Out of Resource";
- case SctpErrorCauseCode::kUnresolvableAddress:
- return "Unresolvable Address";
- case SctpErrorCauseCode::kUnrecognizedChunkType:
- return "Unrecognized Chunk Type";
- case SctpErrorCauseCode::kInvalidMandatoryParameter:
- return "Invalid Mandatory Parameter";
- case SctpErrorCauseCode::kUnrecognizedParameters:
- return "Unrecognized Parameters";
- case SctpErrorCauseCode::kNoUserData:
- return "No User Data";
- case SctpErrorCauseCode::kCookieReceivedWhileShuttingDown:
- return "Cookie Received Whilte Shutting Down";
- case SctpErrorCauseCode::kRestartWithNewAddresses:
- return "Restart With New Addresses";
- case SctpErrorCauseCode::kUserInitiatedAbort:
- return "User Initiated Abort";
- case SctpErrorCauseCode::kProtocolViolation:
- return "Protocol Violation";
- }
- return "Unknown error";
-}
-} // namespace
-
-// Maps SCTP transport ID to UsrsctpTransport object, necessary in send
-// threshold callback and outgoing packet callback. It also provides a facility
-// to safely post a task to an UsrsctpTransport's network thread from another
-// thread.
-class UsrsctpTransportMap {
- public:
- UsrsctpTransportMap() = default;
-
- // Assigns a new unused ID to the following transport.
- uintptr_t Register(cricket::UsrsctpTransport* transport) {
- webrtc::MutexLock lock(&lock_);
- // usrsctp_connect fails with a value of 0...
- if (next_id_ == 0) {
- ++next_id_;
- }
- // In case we've wrapped around and need to find an empty spot from a
- // removed transport. Assumes we'll never be full.
- while (map_.find(next_id_) != map_.end()) {
- ++next_id_;
- if (next_id_ == 0) {
- ++next_id_;
- }
- }
- map_[next_id_] = transport;
- return next_id_++;
- }
-
- // Returns true if found.
- bool Deregister(uintptr_t id) {
- webrtc::MutexLock lock(&lock_);
- return map_.erase(id) > 0;
- }
-
- // Posts `action` to the network thread of the transport identified by `id`
- // and returns true if found, all while holding a lock to protect against the
- // transport being simultaneously deleted/deregistered, or returns false if
- // not found.
- template <typename F>
- bool PostToTransportThread(uintptr_t id, F action) const {
- webrtc::MutexLock lock(&lock_);
- UsrsctpTransport* transport = RetrieveWhileHoldingLock(id);
- if (!transport) {
- return false;
- }
- transport->network_thread_->PostTask(ToQueuedTask(
- transport->task_safety_,
- [transport, action{std::move(action)}]() { action(transport); }));
- return true;
- }
-
- private:
- UsrsctpTransport* RetrieveWhileHoldingLock(uintptr_t id) const
- RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_) {
- auto it = map_.find(id);
- if (it == map_.end()) {
- return nullptr;
- }
- return it->second;
- }
-
- mutable webrtc::Mutex lock_;
-
- uintptr_t next_id_ RTC_GUARDED_BY(lock_) = 0;
- std::unordered_map<uintptr_t, UsrsctpTransport*> map_ RTC_GUARDED_BY(lock_);
-};
-
-// Handles global init/deinit, and mapping from usrsctp callbacks to
-// UsrsctpTransport calls.
-class UsrsctpTransport::UsrSctpWrapper {
- public:
- static void InitializeUsrSctp() {
- RTC_LOG(LS_INFO) << __FUNCTION__;
- // UninitializeUsrSctp tries to call usrsctp_finish in a loop for three
- // seconds; if that failed and we were left in a still-initialized state, we
- // don't want to call usrsctp_init again as that will result in undefined
- // behavior.
- if (g_usrsctp_initialized_) {
- RTC_LOG(LS_WARNING) << "Not reinitializing usrsctp since last attempt at "
- "usrsctp_finish failed.";
- } else {
- // First argument is udp_encapsulation_port, which is not releveant for
- // our AF_CONN use of sctp.
- usrsctp_init(0, &UsrSctpWrapper::OnSctpOutboundPacket, &DebugSctpPrintf);
- g_usrsctp_initialized_ = true;
- }
-
- // To turn on/off detailed SCTP debugging. You will also need to have the
- // SCTP_DEBUG cpp defines flag, which can be turned on in media/BUILD.gn.
- // usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_ALL);
-
- // TODO(ldixon): Consider turning this on/off.
- usrsctp_sysctl_set_sctp_ecn_enable(0);
-
- // WebRTC doesn't use these features, so disable them to reduce the
- // potential attack surface.
- usrsctp_sysctl_set_sctp_asconf_enable(0);
- usrsctp_sysctl_set_sctp_auth_enable(0);
-
- // This is harmless, but we should find out when the library default
- // changes.
- int send_size = usrsctp_sysctl_get_sctp_sendspace();
- if (send_size != kSctpSendBufferSize) {
- RTC_LOG(LS_ERROR) << "Got different send size than expected: "
- << send_size;
- }
-
- // TODO(ldixon): Consider turning this on/off.
- // This is not needed right now (we don't do dynamic address changes):
- // If SCTP Auto-ASCONF is enabled, the peer is informed automatically
- // when a new address is added or removed. This feature is enabled by
- // default.
- // usrsctp_sysctl_set_sctp_auto_asconf(0);
-
- // TODO(ldixon): Consider turning this on/off.
- // Add a blackhole sysctl. Setting it to 1 results in no ABORTs
- // being sent in response to INITs, setting it to 2 results
- // in no ABORTs being sent for received OOTB packets.
- // This is similar to the TCP sysctl.
- //
- // See: http://lakerest.net/pipermail/sctp-coders/2012-January/009438.html
- // See: http://svnweb.freebsd.org/base?view=revision&revision=229805
- // usrsctp_sysctl_set_sctp_blackhole(2);
-
- // Set the number of default outgoing streams. This is the number we'll
- // send in the SCTP INIT message.
- usrsctp_sysctl_set_sctp_nr_outgoing_streams_default(kMaxSctpStreams);
-
- g_transport_map_ = new UsrsctpTransportMap();
- }
-
- static void UninitializeUsrSctp() {
- RTC_LOG(LS_INFO) << __FUNCTION__;
- // usrsctp_finish() may fail if it's called too soon after the transports
- // are
- // closed. Wait and try again until it succeeds for up to 3 seconds.
- for (size_t i = 0; i < 300; ++i) {
- if (usrsctp_finish() == 0) {
- g_usrsctp_initialized_ = false;
- delete g_transport_map_;
- g_transport_map_ = nullptr;
- return;
- }
-
- rtc::Thread::SleepMs(10);
- }
- delete g_transport_map_;
- g_transport_map_ = nullptr;
- RTC_LOG(LS_ERROR) << "Failed to shutdown usrsctp.";
- }
-
- static void IncrementUsrSctpUsageCount() {
- webrtc::GlobalMutexLock lock(&g_usrsctp_lock_);
- if (!g_usrsctp_usage_count) {
- InitializeUsrSctp();
- }
- ++g_usrsctp_usage_count;
- }
-
- static void DecrementUsrSctpUsageCount() {
- webrtc::GlobalMutexLock lock(&g_usrsctp_lock_);
- --g_usrsctp_usage_count;
- if (!g_usrsctp_usage_count) {
- UninitializeUsrSctp();
- }
- }
-
- // This is the callback usrsctp uses when there's data to send on the network
- // that has been wrapped appropriatly for the SCTP protocol.
- static int OnSctpOutboundPacket(void* addr,
- void* data,
- size_t length,
- uint8_t tos,
- uint8_t set_df) {
- if (!g_transport_map_) {
- RTC_LOG(LS_ERROR)
- << "OnSctpOutboundPacket called after usrsctp uninitialized?";
- return EINVAL;
- }
- RTC_LOG(LS_VERBOSE) << "global OnSctpOutboundPacket():"
- "addr: "
- << addr << "; length: " << length
- << "; tos: " << rtc::ToHex(tos)
- << "; set_df: " << rtc::ToHex(set_df);
-
- VerboseLogPacket(data, length, SCTP_DUMP_OUTBOUND);
-
- // Note: We have to copy the data; the caller will delete it.
- rtc::CopyOnWriteBuffer buf(reinterpret_cast<uint8_t*>(data), length);
-
- // PostsToTransportThread protects against the transport being
- // simultaneously deregistered/deleted, since this callback may come from
- // the SCTP timer thread and thus race with the network thread.
- bool found = g_transport_map_->PostToTransportThread(
- reinterpret_cast<uintptr_t>(addr), [buf](UsrsctpTransport* transport) {
- transport->OnPacketFromSctpToNetwork(buf);
- });
- if (!found) {
- RTC_LOG(LS_ERROR)
- << "OnSctpOutboundPacket: Failed to get transport for socket ID "
- << addr << "; possibly was already destroyed.";
- return EINVAL;
- }
-
- return 0;
- }
-
- // This is the callback called from usrsctp when data has been received, after
- // a packet has been interpreted and parsed by usrsctp and found to contain
- // payload data. It is called by a usrsctp thread. It is assumed this function
- // will free the memory used by 'data'.
- static int OnSctpInboundPacket(struct socket* sock,
- union sctp_sockstore addr,
- void* data,
- size_t length,
- struct sctp_rcvinfo rcv,
- int flags,
- void* ulp_info) {
- AutoFreedPointer owned_data(data);
-
- if (!g_transport_map_) {
- RTC_LOG(LS_ERROR)
- << "OnSctpInboundPacket called after usrsctp uninitialized?";
- return kSctpErrorReturn;
- }
-
- uintptr_t id = reinterpret_cast<uintptr_t>(ulp_info);
-
- // PostsToTransportThread protects against the transport being
- // simultaneously deregistered/deleted, since this callback may come from
- // the SCTP timer thread and thus race with the network thread.
- bool found = g_transport_map_->PostToTransportThread(
- id, [owned_data{std::move(owned_data)}, length, rcv,
- flags](UsrsctpTransport* transport) {
- transport->OnDataOrNotificationFromSctp(owned_data.get(), length, rcv,
- flags);
- });
- if (!found) {
- RTC_LOG(LS_ERROR)
- << "OnSctpInboundPacket: Failed to get transport for socket ID " << id
- << "; possibly was already destroyed.";
- return kSctpErrorReturn;
- }
- return kSctpSuccessReturn;
- }
-
- static int SendThresholdCallback(struct socket* sock,
- uint32_t sb_free,
- void* ulp_info) {
- // Fired on our I/O thread. UsrsctpTransport::OnPacketReceived() gets
- // a packet containing acknowledgments, which goes into usrsctp_conninput,
- // and then back here.
- if (!g_transport_map_) {
- RTC_LOG(LS_ERROR)
- << "SendThresholdCallback called after usrsctp uninitialized?";
- return 0;
- }
-
- uintptr_t id = reinterpret_cast<uintptr_t>(ulp_info);
-
- bool found = g_transport_map_->PostToTransportThread(
- id, [](UsrsctpTransport* transport) {
- transport->OnSendThresholdCallback();
- });
- if (!found) {
- RTC_LOG(LS_ERROR)
- << "SendThresholdCallback: Failed to get transport for socket ID "
- << id << "; possibly was already destroyed.";
- }
- return 0;
- }
-};
-
-UsrsctpTransport::UsrsctpTransport(rtc::Thread* network_thread,
- rtc::PacketTransportInternal* transport)
- : network_thread_(network_thread),
- transport_(transport),
- was_ever_writable_(transport ? transport->writable() : false) {
- RTC_DCHECK(network_thread_);
- RTC_DCHECK_RUN_ON(network_thread_);
- ConnectTransportSignals();
-}
-
-UsrsctpTransport::~UsrsctpTransport() {
- RTC_DCHECK_RUN_ON(network_thread_);
- // Close abruptly; no reset procedure.
- CloseSctpSocket();
- // It's not strictly necessary to reset these fields to nullptr,
- // but having these fields set to nullptr is a clear indication that
- // object was destructed. There was a bug in usrsctp when it
- // invoked OnSctpOutboundPacket callback for destructed UsrsctpTransport,
- // which caused obscure SIGSEGV on access to these fields,
- // having this fields set to nullptr will make it easier to understand
- // that UsrsctpTransport was destructed and "use-after-free" bug happen.
- // SIGSEGV error triggered on dereference these pointers will also
- // be easier to understand due to 0x0 address. All of this assumes
- // that ASAN is not enabled to detect "use-after-free", which is
- // currently default configuration.
- network_thread_ = nullptr;
- transport_ = nullptr;
-}
-
-void UsrsctpTransport::SetDtlsTransport(
- rtc::PacketTransportInternal* transport) {
- RTC_DCHECK_RUN_ON(network_thread_);
- DisconnectTransportSignals();
- transport_ = transport;
- ConnectTransportSignals();
- if (!was_ever_writable_ && transport && transport->writable()) {
- was_ever_writable_ = true;
- // New transport is writable, now we can start the SCTP connection if Start
- // was called already.
- if (started_) {
- RTC_DCHECK(!sock_);
- Connect();
- }
- }
-}
-
-bool UsrsctpTransport::Start(int local_sctp_port,
- int remote_sctp_port,
- int max_message_size) {
- RTC_DCHECK_RUN_ON(network_thread_);
- if (local_sctp_port == -1) {
- local_sctp_port = kSctpDefaultPort;
- }
- if (remote_sctp_port == -1) {
- remote_sctp_port = kSctpDefaultPort;
- }
- if (max_message_size > kSctpSendBufferSize) {
- RTC_LOG(LS_ERROR) << "Max message size of " << max_message_size
- << " is larger than send bufffer size "
- << kSctpSendBufferSize;
- return false;
- }
- if (max_message_size < 1) {
- RTC_LOG(LS_ERROR) << "Max message size of " << max_message_size
- << " is too small";
- return false;
- }
- // We allow changing max_message_size with a second Start() call,
- // but not changing the port numbers.
- max_message_size_ = max_message_size;
- if (started_) {
- if (local_sctp_port != local_port_ || remote_sctp_port != remote_port_) {
- RTC_LOG(LS_ERROR)
- << "Can't change SCTP port after SCTP association formed.";
- return false;
- }
- return true;
- }
- local_port_ = local_sctp_port;
- remote_port_ = remote_sctp_port;
- started_ = true;
- RTC_DCHECK(!sock_);
- // Only try to connect if the DTLS transport has been writable before
- // (indicating that the DTLS handshake is complete).
- if (was_ever_writable_) {
- return Connect();
- }
- return true;
-}
-
-bool UsrsctpTransport::OpenStream(int sid) {
- RTC_DCHECK_RUN_ON(network_thread_);
- if (sid > kMaxSctpSid) {
- RTC_LOG(LS_WARNING) << debug_name_
- << "->OpenStream(...): "
- "Not adding data stream "
- "with sid="
- << sid << " because sid is too high.";
- return false;
- }
- auto it = stream_status_by_sid_.find(sid);
- if (it == stream_status_by_sid_.end()) {
- stream_status_by_sid_[sid] = StreamStatus();
- return true;
- }
- if (it->second.is_open()) {
- RTC_LOG(LS_WARNING) << debug_name_
- << "->OpenStream(...): "
- "Not adding data stream "
- "with sid="
- << sid << " because stream is already open.";
- return false;
- } else {
- RTC_LOG(LS_WARNING) << debug_name_
- << "->OpenStream(...): "
- "Not adding data stream "
- " with sid="
- << sid << " because stream is still closing.";
- return false;
- }
-}
-
-bool UsrsctpTransport::ResetStream(int sid) {
- RTC_DCHECK_RUN_ON(network_thread_);
-
- auto it = stream_status_by_sid_.find(sid);
- if (it == stream_status_by_sid_.end() || !it->second.is_open()) {
- RTC_LOG(LS_WARNING) << debug_name_ << "->ResetStream(" << sid
- << "): stream not open.";
- return false;
- }
-
- RTC_LOG(LS_VERBOSE) << debug_name_ << "->ResetStream(" << sid
- << "): "
- "Queuing RE-CONFIG chunk.";
- it->second.closure_initiated = true;
-
- // Signal our stream-reset logic that it should try to send now, if it can.
- SendQueuedStreamResets();
-
- // The stream will actually get removed when we get the acknowledgment.
- return true;
-}
-
-bool UsrsctpTransport::SendData(int sid,
- const webrtc::SendDataParams& params,
- const rtc::CopyOnWriteBuffer& payload,
- SendDataResult* result) {
- RTC_DCHECK_RUN_ON(network_thread_);
-
- if (partial_outgoing_message_.has_value()) {
- if (result) {
- *result = SDR_BLOCK;
- }
- // Ready to send should get set only when SendData() call gets blocked.
- ready_to_send_data_ = false;
- return false;
- }
-
- // Do not queue data to send on a closing stream.
- auto it = stream_status_by_sid_.find(sid);
- if (it == stream_status_by_sid_.end() || !it->second.is_open()) {
- RTC_LOG(LS_WARNING)
- << debug_name_
- << "->SendData(...): "
- "Not sending data because sid is unknown or closing: "
- << sid;
- if (result) {
- *result = SDR_ERROR;
- }
- return false;
- }
-
- size_t payload_size = payload.size();
- OutgoingMessage message(payload, sid, params);
- SendDataResult send_message_result = SendMessageInternal(&message);
- if (result) {
- *result = send_message_result;
- }
- if (payload_size == message.size()) {
- // Nothing was sent.
- return false;
- }
- // If any data is sent, we accept the message. In the case that data was
- // partially accepted by the sctp library, the remaining is buffered. This
- // ensures the client does not resend the message.
- RTC_DCHECK_LT(message.size(), payload_size);
- if (message.size() > 0) {
- RTC_DCHECK(!partial_outgoing_message_.has_value());
- RTC_DLOG(LS_VERBOSE) << "Partially sent message. Buffering the remaining"
- << message.size() << "/" << payload_size << " bytes.";
-
- partial_outgoing_message_.emplace(message);
- }
- return true;
-}
-
-SendDataResult UsrsctpTransport::SendMessageInternal(OutgoingMessage* message) {
- RTC_DCHECK_RUN_ON(network_thread_);
- if (!sock_) {
- RTC_LOG(LS_WARNING) << debug_name_
- << "->SendMessageInternal(...): "
- "Not sending packet with sid="
- << message->sid() << " len=" << message->size()
- << " before Start().";
- return SDR_ERROR;
- }
- if (message->send_params().type != webrtc::DataMessageType::kControl) {
- auto it = stream_status_by_sid_.find(message->sid());
- if (it == stream_status_by_sid_.end()) {
- RTC_LOG(LS_WARNING) << debug_name_
- << "->SendMessageInternal(...): "
- "Not sending data because sid is unknown: "
- << message->sid();
- return SDR_ERROR;
- }
- }
- if (message->size() > static_cast<size_t>(max_message_size_)) {
- RTC_LOG(LS_ERROR) << "Attempting to send message of size "
- << message->size() << " which is larger than limit "
- << max_message_size_;
- return SDR_ERROR;
- }
-
- // Send data using SCTP.
- sctp_sendv_spa spa = CreateSctpSendParams(
- message->sid(), message->send_params(), message->size());
- const void* data = message->data();
- size_t data_length = message->size();
- if (message->size() == 0) {
- // Empty messages are replaced by a single NUL byte on the wire as SCTP
- // doesn't support empty messages.
- // The PPID carries the information that the payload needs to be ignored.
- data = kZero;
- data_length = 1;
- }
- // Note: this send call is not atomic because the EOR bit is set. This means
- // that usrsctp can partially accept this message and it is our duty to buffer
- // the rest.
- ssize_t send_res = usrsctp_sendv(sock_, data, data_length, NULL, 0, &spa,
- rtc::checked_cast<socklen_t>(sizeof(spa)),
- SCTP_SENDV_SPA, 0);
- if (send_res < 0) {
- if (errno == SCTP_EWOULDBLOCK) {
- ready_to_send_data_ = false;
- RTC_LOG(LS_VERBOSE) << debug_name_
- << "->SendMessageInternal(...): EWOULDBLOCK returned";
- return SDR_BLOCK;
- }
-
- RTC_LOG_ERRNO(LS_ERROR) << "ERROR:" << debug_name_
- << "->SendMessageInternal(...): "
- " usrsctp_sendv: ";
- return SDR_ERROR;
- }
-
- size_t amount_sent = static_cast<size_t>(send_res);
- RTC_DCHECK_LE(amount_sent, data_length);
- if (message->size() != 0)
- message->Advance(amount_sent);
- // Only way out now is success.
- return SDR_SUCCESS;
-}
-
-bool UsrsctpTransport::ReadyToSendData() {
- RTC_DCHECK_RUN_ON(network_thread_);
- return ready_to_send_data_;
-}
-
-void UsrsctpTransport::ConnectTransportSignals() {
- RTC_DCHECK_RUN_ON(network_thread_);
- if (!transport_) {
- return;
- }
- transport_->SignalWritableState.connect(this,
- &UsrsctpTransport::OnWritableState);
- transport_->SignalReadPacket.connect(this, &UsrsctpTransport::OnPacketRead);
- transport_->SignalClosed.connect(this, &UsrsctpTransport::OnClosed);
-}
-
-void UsrsctpTransport::DisconnectTransportSignals() {
- RTC_DCHECK_RUN_ON(network_thread_);
- if (!transport_) {
- return;
- }
- transport_->SignalWritableState.disconnect(this);
- transport_->SignalReadPacket.disconnect(this);
- transport_->SignalClosed.disconnect(this);
-}
-
-bool UsrsctpTransport::Connect() {
- RTC_DCHECK_RUN_ON(network_thread_);
- RTC_LOG(LS_VERBOSE) << debug_name_ << "->Connect().";
-
- // If we already have a socket connection (which shouldn't ever happen), just
- // return.
- RTC_DCHECK(!sock_);
- if (sock_) {
- RTC_LOG(LS_ERROR) << debug_name_
- << "->Connect(): Ignored as socket "
- "is already established.";
- return true;
- }
-
- // If no socket (it was closed) try to start it again. This can happen when
- // the socket we are connecting to closes, does an sctp shutdown handshake,
- // or behaves unexpectedly causing us to perform a CloseSctpSocket.
- if (!OpenSctpSocket()) {
- return false;
- }
-
- // Note: conversion from int to uint16_t happens on assignment.
- sockaddr_conn local_sconn = GetSctpSockAddr(local_port_);
- if (usrsctp_bind(sock_, reinterpret_cast<sockaddr*>(&local_sconn),
- sizeof(local_sconn)) < 0) {
- RTC_LOG_ERRNO(LS_ERROR)
- << debug_name_ << "->Connect(): " << ("Failed usrsctp_bind");
- CloseSctpSocket();
- return false;
- }
-
- // Note: conversion from int to uint16_t happens on assignment.
- sockaddr_conn remote_sconn = GetSctpSockAddr(remote_port_);
- int connect_result = usrsctp_connect(
- sock_, reinterpret_cast<sockaddr*>(&remote_sconn), sizeof(remote_sconn));
- if (connect_result < 0 && errno != SCTP_EINPROGRESS) {
- RTC_LOG_ERRNO(LS_ERROR) << debug_name_
- << "->Connect(): "
- "Failed usrsctp_connect. got errno="
- << errno << ", but wanted " << SCTP_EINPROGRESS;
- CloseSctpSocket();
- return false;
- }
- // Set the MTU and disable MTU discovery.
- // We can only do this after usrsctp_connect or it has no effect.
- sctp_paddrparams params = {};
- memcpy(&params.spp_address, &remote_sconn, sizeof(remote_sconn));
- params.spp_flags = SPP_PMTUD_DISABLE;
- // The MTU value provided specifies the space available for chunks in the
- // packet, so we subtract the SCTP header size.
- params.spp_pathmtu = kSctpMtu - sizeof(struct sctp_common_header);
- if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &params,
- sizeof(params))) {
- RTC_LOG_ERRNO(LS_ERROR) << debug_name_
- << "->Connect(): "
- "Failed to set SCTP_PEER_ADDR_PARAMS.";
- }
- // Since this is a fresh SCTP association, we'll always start out with empty
- // queues, so "ReadyToSendData" should be true.
- SetReadyToSendData();
- return true;
-}
-
-bool UsrsctpTransport::OpenSctpSocket() {
- RTC_DCHECK_RUN_ON(network_thread_);
- if (sock_) {
- RTC_LOG(LS_WARNING) << debug_name_
- << "->OpenSctpSocket(): "
- "Ignoring attempt to re-create existing socket.";
- return false;
- }
-
- UsrSctpWrapper::IncrementUsrSctpUsageCount();
-
- // If kSctpSendBufferSize isn't reflective of reality, we log an error, but we
- // still have to do something reasonable here. Look up what the buffer's real
- // size is and set our threshold to something reasonable.
- // TODO(bugs.webrtc.org/11824): That was previously set to 50%, not 25%, but
- // it was reduced to a recent usrsctp regression. Can return to 50% when the
- // root cause is fixed.
- static const int kSendThreshold = usrsctp_sysctl_get_sctp_sendspace() / 4;
-
- sock_ = usrsctp_socket(
- AF_CONN, SOCK_STREAM, IPPROTO_SCTP, &UsrSctpWrapper::OnSctpInboundPacket,
- &UsrSctpWrapper::SendThresholdCallback, kSendThreshold, nullptr);
- if (!sock_) {
- RTC_LOG_ERRNO(LS_ERROR) << debug_name_
- << "->OpenSctpSocket(): "
- "Failed to create SCTP socket.";
- UsrSctpWrapper::DecrementUsrSctpUsageCount();
- return false;
- }
-
- if (!ConfigureSctpSocket()) {
- usrsctp_close(sock_);
- sock_ = nullptr;
- UsrSctpWrapper::DecrementUsrSctpUsageCount();
- return false;
- }
- id_ = g_transport_map_->Register(this);
- usrsctp_set_ulpinfo(sock_, reinterpret_cast<void*>(id_));
- // Register our id as an address for usrsctp. This is used by SCTP to
- // direct the packets received (by the created socket) to this class.
- usrsctp_register_address(reinterpret_cast<void*>(id_));
- return true;
-}
-
-bool UsrsctpTransport::ConfigureSctpSocket() {
- RTC_DCHECK_RUN_ON(network_thread_);
- RTC_DCHECK(sock_);
- // Make the socket non-blocking. Connect, close, shutdown etc will not block
- // the thread waiting for the socket operation to complete.
- if (usrsctp_set_non_blocking(sock_, 1) < 0) {
- RTC_LOG_ERRNO(LS_ERROR) << debug_name_
- << "->ConfigureSctpSocket(): "
- "Failed to set SCTP to non blocking.";
- return false;
- }
-
- // This ensures that the usrsctp close call deletes the association. This
- // prevents usrsctp from calling OnSctpOutboundPacket with references to
- // this class as the address.
- linger linger_opt;
- linger_opt.l_onoff = 1;
- linger_opt.l_linger = 0;
- if (usrsctp_setsockopt(sock_, SOL_SOCKET, SO_LINGER, &linger_opt,
- sizeof(linger_opt))) {
- RTC_LOG_ERRNO(LS_ERROR) << debug_name_
- << "->ConfigureSctpSocket(): "
- "Failed to set SO_LINGER.";
- return false;
- }
-
- // Enable stream ID resets.
- struct sctp_assoc_value stream_rst;
- stream_rst.assoc_id = SCTP_ALL_ASSOC;
- stream_rst.assoc_value = 1;
- if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_ENABLE_STREAM_RESET,
- &stream_rst, sizeof(stream_rst))) {
- RTC_LOG_ERRNO(LS_ERROR) << debug_name_
- << "->ConfigureSctpSocket(): "
- "Failed to set SCTP_ENABLE_STREAM_RESET.";
- return false;
- }
-
- // Nagle.
- uint32_t nodelay = 1;
- if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_NODELAY, &nodelay,
- sizeof(nodelay))) {
- RTC_LOG_ERRNO(LS_ERROR) << debug_name_
- << "->ConfigureSctpSocket(): "
- "Failed to set SCTP_NODELAY.";
- return false;
- }
-
- // Explicit EOR.
- uint32_t eor = 1;
- if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_EXPLICIT_EOR, &eor,
- sizeof(eor))) {
- RTC_LOG_ERRNO(LS_ERROR) << debug_name_
- << "->ConfigureSctpSocket(): "
- "Failed to set SCTP_EXPLICIT_EOR.";
- return false;
- }
-
- // Subscribe to SCTP event notifications.
- // TODO(crbug.com/1137936): Subscribe to SCTP_SEND_FAILED_EVENT once deadlock
- // is fixed upstream, or we switch to the upcall API:
- // https://github.com/sctplab/usrsctp/issues/537
- int event_types[] = {SCTP_ASSOC_CHANGE, SCTP_PEER_ADDR_CHANGE,
- SCTP_SENDER_DRY_EVENT, SCTP_STREAM_RESET_EVENT};
- struct sctp_event event = {0};
- event.se_assoc_id = SCTP_ALL_ASSOC;
- event.se_on = 1;
- for (size_t i = 0; i < arraysize(event_types); i++) {
- event.se_type = event_types[i];
- if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_EVENT, &event,
- sizeof(event)) < 0) {
- RTC_LOG_ERRNO(LS_ERROR) << debug_name_
- << "->ConfigureSctpSocket(): "
- "Failed to set SCTP_EVENT type: "
- << event.se_type;
- return false;
- }
- }
- return true;
-}
-
-void UsrsctpTransport::CloseSctpSocket() {
- RTC_DCHECK_RUN_ON(network_thread_);
- if (sock_) {
- // We assume that SO_LINGER option is set to close the association when
- // close is called. This means that any pending packets in usrsctp will be
- // discarded instead of being sent.
- usrsctp_close(sock_);
- sock_ = nullptr;
- usrsctp_deregister_address(reinterpret_cast<void*>(id_));
- RTC_CHECK(g_transport_map_->Deregister(id_));
- UsrSctpWrapper::DecrementUsrSctpUsageCount();
- ready_to_send_data_ = false;
- }
-}
-
-bool UsrsctpTransport::SendQueuedStreamResets() {
- RTC_DCHECK_RUN_ON(network_thread_);
-
- auto needs_reset =
- [this](const std::map<uint32_t, StreamStatus>::value_type& stream) {
- // Ignore streams with partial outgoing messages as they are required to
- // be fully sent by the WebRTC spec
- // https://w3c.github.io/webrtc-pc/#closing-procedure
- return stream.second.need_outgoing_reset() &&
- (!partial_outgoing_message_.has_value() ||
- partial_outgoing_message_.value().sid() !=
- static_cast<int>(stream.first));
- };
- // Figure out how many streams need to be reset. We need to do this so we can
- // allocate the right amount of memory for the sctp_reset_streams structure.
- size_t num_streams = absl::c_count_if(stream_status_by_sid_, needs_reset);
- if (num_streams == 0) {
- // Nothing to reset.
- return true;
- }
-
- RTC_LOG(LS_VERBOSE) << "SendQueuedStreamResets[" << debug_name_
- << "]: Resetting " << num_streams << " outgoing streams.";
-
- const size_t num_bytes =
- sizeof(struct sctp_reset_streams) + (num_streams * sizeof(uint16_t));
- std::vector<uint8_t> reset_stream_buf(num_bytes, 0);
- struct sctp_reset_streams* resetp =
- reinterpret_cast<sctp_reset_streams*>(&reset_stream_buf[0]);
- resetp->srs_assoc_id = SCTP_ALL_ASSOC;
- resetp->srs_flags = SCTP_STREAM_RESET_OUTGOING;
- resetp->srs_number_streams = rtc::checked_cast<uint16_t>(num_streams);
- int result_idx = 0;
-
- for (const auto& stream : stream_status_by_sid_) {
- if (needs_reset(stream)) {
- resetp->srs_stream_list[result_idx++] = stream.first;
- }
- }
-
- int ret =
- usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_RESET_STREAMS, resetp,
- rtc::checked_cast<socklen_t>(reset_stream_buf.size()));
- if (ret < 0) {
- // Note that usrsctp only lets us have one reset in progress at a time
- // (even though multiple streams can be reset at once). If this happens,
- // SendQueuedStreamResets will end up called after the current in-progress
- // reset finishes, in OnStreamResetEvent.
- RTC_LOG_ERRNO(LS_WARNING) << debug_name_
- << "->SendQueuedStreamResets(): "
- "Failed to send a stream reset for "
- << num_streams << " streams";
- return false;
- }
-
- // Since the usrsctp call completed successfully, update our stream status
- // map to note that we started the outgoing reset.
- for (auto it = stream_status_by_sid_.begin();
- it != stream_status_by_sid_.end(); ++it) {
- if (it->second.need_outgoing_reset()) {
- it->second.outgoing_reset_initiated = true;
- }
- }
- return true;
-}
-
-void UsrsctpTransport::SetReadyToSendData() {
- RTC_DCHECK_RUN_ON(network_thread_);
- if (!ready_to_send_data_) {
- ready_to_send_data_ = true;
- SignalReadyToSendData();
- }
-}
-
-bool UsrsctpTransport::SendBufferedMessage() {
- RTC_DCHECK_RUN_ON(network_thread_);
- RTC_DCHECK(partial_outgoing_message_.has_value());
- RTC_DLOG(LS_VERBOSE) << "Sending partially buffered message of size "
- << partial_outgoing_message_->size() << ".";
-
- SendMessageInternal(&partial_outgoing_message_.value());
- if (partial_outgoing_message_->size() > 0) {
- // Still need to finish sending the message.
- return false;
- }
- RTC_DCHECK_EQ(0u, partial_outgoing_message_->size());
-
- int sid = partial_outgoing_message_->sid();
- partial_outgoing_message_.reset();
-
- // Send the queued stream reset if it was pending for this stream.
- auto it = stream_status_by_sid_.find(sid);
- if (it->second.need_outgoing_reset()) {
- SendQueuedStreamResets();
- }
-
- return true;
-}
-
-void UsrsctpTransport::OnWritableState(
- rtc::PacketTransportInternal* transport) {
- RTC_DCHECK_RUN_ON(network_thread_);
- RTC_DCHECK_EQ(transport_, transport);
- if (!was_ever_writable_ && transport->writable()) {
- was_ever_writable_ = true;
- if (started_) {
- Connect();
- }
- }
-}
-
-// Called by network interface when a packet has been received.
-void UsrsctpTransport::OnPacketRead(rtc::PacketTransportInternal* transport,
- const char* data,
- size_t len,
- const int64_t& /* packet_time_us */,
- int flags) {
- RTC_DCHECK_RUN_ON(network_thread_);
- RTC_DCHECK_EQ(transport_, transport);
- TRACE_EVENT0("webrtc", "UsrsctpTransport::OnPacketRead");
-
- if (flags & PF_SRTP_BYPASS) {
- // We are only interested in SCTP packets.
- return;
- }
-
- RTC_LOG(LS_VERBOSE) << debug_name_
- << "->OnPacketRead(...): "
- " length="
- << len << ", started: " << started_;
- // Only give receiving packets to usrsctp after if connected. This enables two
- // peers to each make a connect call, but for them not to receive an INIT
- // packet before they have called connect; least the last receiver of the INIT
- // packet will have called connect, and a connection will be established.
- if (sock_) {
- // Pass received packet to SCTP stack. Once processed by usrsctp, the data
- // will be will be given to the global OnSctpInboundPacket callback and
- // posted to the transport thread.
- VerboseLogPacket(data, len, SCTP_DUMP_INBOUND);
- usrsctp_conninput(reinterpret_cast<void*>(id_), data, len, 0);
- } else {
- // TODO(ldixon): Consider caching the packet for very slightly better
- // reliability.
- }
-}
-
-void UsrsctpTransport::OnClosed(rtc::PacketTransportInternal* transport) {
- webrtc::RTCError error =
- webrtc::RTCError(webrtc::RTCErrorType::OPERATION_ERROR_WITH_DATA,
- "Transport channel closed");
- error.set_error_detail(webrtc::RTCErrorDetailType::SCTP_FAILURE);
- SignalClosedAbruptly(error);
-}
-
-void UsrsctpTransport::OnSendThresholdCallback() {
- RTC_DCHECK_RUN_ON(network_thread_);
- if (partial_outgoing_message_.has_value()) {
- if (!SendBufferedMessage()) {
- // Did not finish sending the buffered message.
- return;
- }
- }
- SetReadyToSendData();
-}
-
-sockaddr_conn UsrsctpTransport::GetSctpSockAddr(int port) {
- sockaddr_conn sconn = {0};
- sconn.sconn_family = AF_CONN;
-#ifdef HAVE_SCONN_LEN
- sconn.sconn_len = sizeof(sockaddr_conn);
-#endif
- // Note: conversion from int to uint16_t happens here.
- sconn.sconn_port = rtc::HostToNetwork16(port);
- sconn.sconn_addr = reinterpret_cast<void*>(id_);
- return sconn;
-}
-
-void UsrsctpTransport::OnPacketFromSctpToNetwork(
- const rtc::CopyOnWriteBuffer& buffer) {
- RTC_DCHECK_RUN_ON(network_thread_);
- if (buffer.size() > (kSctpMtu)) {
- RTC_LOG(LS_ERROR) << debug_name_
- << "->OnPacketFromSctpToNetwork(...): "
- "SCTP seems to have made a packet that is bigger "
- "than its official MTU: "
- << buffer.size() << " vs max of " << kSctpMtu;
- }
- TRACE_EVENT0("webrtc", "UsrsctpTransport::OnPacketFromSctpToNetwork");
-
- // Don't create noise by trying to send a packet when the DTLS transport isn't
- // even writable.
- if (!transport_ || !transport_->writable()) {
- return;
- }
-
- // Bon voyage.
- transport_->SendPacket(buffer.data<char>(), buffer.size(),
- rtc::PacketOptions(), PF_NORMAL);
-}
-
-void UsrsctpTransport::InjectDataOrNotificationFromSctpForTesting(
- const void* data,
- size_t length,
- struct sctp_rcvinfo rcv,
- int flags) {
- OnDataOrNotificationFromSctp(data, length, rcv, flags);
-}
-
-void UsrsctpTransport::OnDataOrNotificationFromSctp(const void* data,
- size_t length,
- struct sctp_rcvinfo rcv,
- int flags) {
- RTC_DCHECK_RUN_ON(network_thread_);
- // If data is NULL, the SCTP association has been closed.
- if (!data) {
- RTC_LOG(LS_INFO) << debug_name_
- << "->OnDataOrNotificationFromSctp(...): "
- "No data; association closed.";
- return;
- }
-
- // Handle notifications early.
- // Note: Notifications are never split into chunks, so they can and should
- // be handled early and entirely separate from the reassembly
- // process.
- if (flags & MSG_NOTIFICATION) {
- RTC_LOG(LS_VERBOSE)
- << debug_name_
- << "->OnDataOrNotificationFromSctp(...): SCTP notification"
- << " length=" << length;
-
- rtc::CopyOnWriteBuffer notification(reinterpret_cast<const uint8_t*>(data),
- length);
- OnNotificationFromSctp(notification);
- return;
- }
-
- // Log data chunk
- const uint32_t ppid = rtc::NetworkToHost32(rcv.rcv_ppid);
- RTC_LOG(LS_VERBOSE) << debug_name_
- << "->OnDataOrNotificationFromSctp(...): SCTP data chunk"
- << " length=" << length << ", sid=" << rcv.rcv_sid
- << ", ppid=" << ppid << ", ssn=" << rcv.rcv_ssn
- << ", cum-tsn=" << rcv.rcv_cumtsn
- << ", eor=" << ((flags & MSG_EOR) ? "y" : "n");
-
- // Validate payload protocol identifier
- webrtc::DataMessageType type;
- if (!GetDataMediaType(ppid, &type)) {
- // Unexpected PPID, dropping
- RTC_LOG(LS_ERROR) << "Received an unknown PPID " << ppid
- << " on an SCTP packet. Dropping.";
- return;
- }
-
- // Expect only continuation messages belonging to the same SID. The SCTP
- // stack is expected to ensure this as long as the User Message
- // Interleaving extension (RFC 8260) is not explicitly enabled, so this
- // merely acts as a safeguard.
- if ((partial_incoming_message_.size() != 0) &&
- (rcv.rcv_sid != partial_params_.sid)) {
- RTC_LOG(LS_ERROR) << "Received a new SID without EOR in the previous"
- << " SCTP packet. Discarding the previous packet.";
- partial_incoming_message_.Clear();
- }
-
- // Copy metadata of interest
- ReceiveDataParams params;
- params.type = type;
- params.sid = rcv.rcv_sid;
- // Note that the SSN is identical for each chunk of the same message.
- // Furthermore, it is increased per stream and not on the whole
- // association.
- params.seq_num = rcv.rcv_ssn;
-
- // Append the chunk's data to the message buffer unless we have a chunk with a
- // PPID marking an empty message.
- // See: https://tools.ietf.org/html/rfc8831#section-6.6
- if (!IsEmptyPPID(ppid))
- partial_incoming_message_.AppendData(reinterpret_cast<const uint8_t*>(data),
- length);
- partial_params_ = params;
- partial_flags_ = flags;
-
- // If the message is not yet complete...
- if (!(flags & MSG_EOR)) {
- if (partial_incoming_message_.size() < kSctpSendBufferSize) {
- // We still have space in the buffer. Continue buffering chunks until
- // the message is complete before handing it out.
- return;
- } else {
- // The sender is exceeding the maximum message size that we announced.
- // Spit out a warning but still hand out the partial message. Note that
- // this behaviour is undesirable, see the discussion in issue 7774.
- //
- // TODO(lgrahl): Once sufficient time has passed and all supported
- // browser versions obey the announced maximum message size, we should
- // abort the SCTP association instead to prevent message integrity
- // violation.
- RTC_LOG(LS_ERROR) << "Handing out partial SCTP message.";
- }
- }
-
- // Dispatch the complete message and reset the message buffer.
- OnDataFromSctpToTransport(params, partial_incoming_message_);
- partial_incoming_message_.Clear();
-}
-
-void UsrsctpTransport::OnDataFromSctpToTransport(
- const ReceiveDataParams& params,
- const rtc::CopyOnWriteBuffer& buffer) {
- RTC_DCHECK_RUN_ON(network_thread_);
- RTC_LOG(LS_VERBOSE) << debug_name_
- << "->OnDataFromSctpToTransport(...): "
- "Posting with length: "
- << buffer.size() << " on stream " << params.sid;
- // Reports all received messages to upper layers, no matter whether the sid
- // is known.
- SignalDataReceived(params, buffer);
-}
-
-void UsrsctpTransport::OnNotificationFromSctp(
- const rtc::CopyOnWriteBuffer& buffer) {
- RTC_DCHECK_RUN_ON(network_thread_);
- if (buffer.size() < sizeof(sctp_notification::sn_header)) {
- RTC_LOG(LS_ERROR) << "SCTP notification is shorter than header size: "
- << buffer.size();
- return;
- }
-
- const sctp_notification& notification =
- reinterpret_cast<const sctp_notification&>(*buffer.data());
- if (buffer.size() != notification.sn_header.sn_length) {
- RTC_LOG(LS_ERROR) << "SCTP notification length (" << buffer.size()
- << ") does not match sn_length field ("
- << notification.sn_header.sn_length << ").";
- return;
- }
-
- // TODO(ldixon): handle notifications appropriately.
- switch (notification.sn_header.sn_type) {
- case SCTP_ASSOC_CHANGE:
- RTC_LOG(LS_VERBOSE) << "SCTP_ASSOC_CHANGE";
- if (buffer.size() < sizeof(notification.sn_assoc_change)) {
- RTC_LOG(LS_ERROR)
- << "SCTP_ASSOC_CHANGE notification has less than required length: "
- << buffer.size();
- return;
- }
- OnNotificationAssocChange(notification.sn_assoc_change);
- break;
- case SCTP_REMOTE_ERROR:
- RTC_LOG(LS_INFO) << "SCTP_REMOTE_ERROR";
- break;
- case SCTP_SHUTDOWN_EVENT:
- RTC_LOG(LS_INFO) << "SCTP_SHUTDOWN_EVENT";
- break;
- case SCTP_ADAPTATION_INDICATION:
- RTC_LOG(LS_INFO) << "SCTP_ADAPTATION_INDICATION";
- break;
- case SCTP_PARTIAL_DELIVERY_EVENT:
- RTC_LOG(LS_INFO) << "SCTP_PARTIAL_DELIVERY_EVENT";
- break;
- case SCTP_AUTHENTICATION_EVENT:
- RTC_LOG(LS_INFO) << "SCTP_AUTHENTICATION_EVENT";
- break;
- case SCTP_SENDER_DRY_EVENT:
- RTC_LOG(LS_VERBOSE) << "SCTP_SENDER_DRY_EVENT";
- SetReadyToSendData();
- break;
- // TODO(ldixon): Unblock after congestion.
- case SCTP_NOTIFICATIONS_STOPPED_EVENT:
- RTC_LOG(LS_INFO) << "SCTP_NOTIFICATIONS_STOPPED_EVENT";
- break;
- case SCTP_SEND_FAILED_EVENT: {
- if (buffer.size() < sizeof(notification.sn_send_failed_event)) {
- RTC_LOG(LS_ERROR) << "SCTP_SEND_FAILED_EVENT notification has less "
- "than required length: "
- << buffer.size();
- return;
- }
- const struct sctp_send_failed_event& ssfe =
- notification.sn_send_failed_event;
- RTC_LOG(LS_WARNING) << "SCTP_SEND_FAILED_EVENT: message with"
- " PPID = "
- << rtc::NetworkToHost32(ssfe.ssfe_info.snd_ppid)
- << " SID = " << ssfe.ssfe_info.snd_sid
- << " flags = " << rtc::ToHex(ssfe.ssfe_info.snd_flags)
- << " failed to sent due to error = "
- << rtc::ToHex(ssfe.ssfe_error);
- break;
- }
- case SCTP_STREAM_RESET_EVENT:
- if (buffer.size() < sizeof(notification.sn_strreset_event)) {
- RTC_LOG(LS_ERROR) << "SCTP_STREAM_RESET_EVENT notification has less "
- "than required length: "
- << buffer.size();
- return;
- }
- OnStreamResetEvent(&notification.sn_strreset_event);
- break;
- case SCTP_ASSOC_RESET_EVENT:
- RTC_LOG(LS_INFO) << "SCTP_ASSOC_RESET_EVENT";
- break;
- case SCTP_STREAM_CHANGE_EVENT:
- RTC_LOG(LS_INFO) << "SCTP_STREAM_CHANGE_EVENT";
- // An acknowledgment we get after our stream resets have gone through,
- // if they've failed. We log the message, but don't react -- we don't
- // keep around the last-transmitted set of SSIDs we wanted to close for
- // error recovery. It doesn't seem likely to occur, and if so, likely
- // harmless within the lifetime of a single SCTP association.
- break;
- case SCTP_PEER_ADDR_CHANGE:
- RTC_LOG(LS_INFO) << "SCTP_PEER_ADDR_CHANGE";
- break;
- default:
- RTC_LOG(LS_WARNING) << "Unknown SCTP event: "
- << notification.sn_header.sn_type;
- break;
- }
-}
-
-void UsrsctpTransport::OnNotificationAssocChange(
- const sctp_assoc_change& change) {
- RTC_DCHECK_RUN_ON(network_thread_);
- switch (change.sac_state) {
- case SCTP_COMM_UP:
- RTC_LOG(LS_VERBOSE) << "Association change SCTP_COMM_UP, stream # is "
- << change.sac_outbound_streams << " outbound, "
- << change.sac_inbound_streams << " inbound.";
- max_outbound_streams_ = change.sac_outbound_streams;
- max_inbound_streams_ = change.sac_inbound_streams;
- SignalAssociationChangeCommunicationUp();
- // In case someone tried to close a stream before communication
- // came up, send any queued resets.
- SendQueuedStreamResets();
- break;
- case SCTP_COMM_LOST: {
- RTC_LOG(LS_INFO) << "Association change SCTP_COMM_LOST";
- webrtc::RTCError error = webrtc::RTCError(
- webrtc::RTCErrorType::OPERATION_ERROR_WITH_DATA,
- SctpErrorCauseCodeToString(
- static_cast<SctpErrorCauseCode>(change.sac_error)));
- error.set_error_detail(webrtc::RTCErrorDetailType::SCTP_FAILURE);
- error.set_sctp_cause_code(change.sac_error);
- SignalClosedAbruptly(error);
- break;
- }
- case SCTP_RESTART:
- RTC_LOG(LS_INFO) << "Association change SCTP_RESTART";
- break;
- case SCTP_SHUTDOWN_COMP:
- RTC_LOG(LS_INFO) << "Association change SCTP_SHUTDOWN_COMP";
- break;
- case SCTP_CANT_STR_ASSOC:
- RTC_LOG(LS_INFO) << "Association change SCTP_CANT_STR_ASSOC";
- break;
- default:
- RTC_LOG(LS_INFO) << "Association change UNKNOWN";
- break;
- }
-}
-
-void UsrsctpTransport::OnStreamResetEvent(
- const struct sctp_stream_reset_event* evt) {
- RTC_DCHECK_RUN_ON(network_thread_);
-
- // This callback indicates that a reset is complete for incoming and/or
- // outgoing streams. The reset may have been initiated by us or the remote
- // side.
- const int num_sids = (evt->strreset_length - sizeof(*evt)) /
- sizeof(evt->strreset_stream_list[0]);
-
- if (evt->strreset_flags & SCTP_STREAM_RESET_FAILED) {
- // OK, just try sending any previously sent stream resets again. The stream
- // IDs sent over when the RESET_FIALED flag is set seem to be garbage
- // values. Ignore them.
- for (std::map<uint32_t, StreamStatus>::value_type& stream :
- stream_status_by_sid_) {
- stream.second.outgoing_reset_initiated = false;
- }
- SendQueuedStreamResets();
- // TODO(deadbeef): If this happens, the entire SCTP association is in quite
- // crippled state. The SCTP session should be dismantled, and the WebRTC
- // connectivity errored because is clear that the distant party is not
- // playing ball: malforms the transported data.
- return;
- }
-
- // Loop over the received events and properly update the StreamStatus map.
- for (int i = 0; i < num_sids; i++) {
- const uint32_t sid = evt->strreset_stream_list[i];
- auto it = stream_status_by_sid_.find(sid);
- if (it == stream_status_by_sid_.end()) {
- // This stream is unknown. Sometimes this can be from a
- // RESET_FAILED-related retransmit.
- RTC_LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
- << "): Unknown sid " << sid;
- continue;
- }
- StreamStatus& status = it->second;
-
- if (evt->strreset_flags & SCTP_STREAM_RESET_INCOMING_SSN) {
- RTC_LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_INCOMING_SSN(" << debug_name_
- << "): sid " << sid;
- status.incoming_reset_complete = true;
- // If we receive an incoming stream reset and we haven't started the
- // closing procedure ourselves, this means the remote side started the
- // closing procedure; fire a signal so that the relevant data channel
- // can change to "closing" (we still need to reset the outgoing stream
- // before it changes to "closed").
- if (!status.closure_initiated) {
- SignalClosingProcedureStartedRemotely(sid);
- }
- }
- if (evt->strreset_flags & SCTP_STREAM_RESET_OUTGOING_SSN) {
- RTC_LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_OUTGOING_SSN(" << debug_name_
- << "): sid " << sid;
- status.outgoing_reset_complete = true;
- }
-
- // If this reset completes the closing procedure, remove the stream from
- // our map so we can consider it closed, and fire a signal such that the
- // relevant DataChannel will change its state to "closed" and its ID can be
- // re-used.
- if (status.reset_complete()) {
- stream_status_by_sid_.erase(it);
- SignalClosingProcedureComplete(sid);
- }
- }
-
- // Always try to send any queued resets because this call indicates that the
- // last outgoing or incoming reset has made some progress.
- SendQueuedStreamResets();
-}
-
-} // namespace cricket
diff --git a/media/sctp/usrsctp_transport.h b/media/sctp/usrsctp_transport.h
deleted file mode 100644
index 7c7ce8c4a8..0000000000
--- a/media/sctp/usrsctp_transport.h
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
- * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#ifndef MEDIA_SCTP_USRSCTP_TRANSPORT_H_
-#define MEDIA_SCTP_USRSCTP_TRANSPORT_H_
-
-#include <errno.h>
-
-#include <cstdint>
-#include <map>
-#include <memory>
-#include <set>
-#include <string>
-#include <vector>
-
-#include "absl/types/optional.h"
-#include "rtc_base/buffer.h"
-#include "rtc_base/copy_on_write_buffer.h"
-#include "rtc_base/task_utils/pending_task_safety_flag.h"
-#include "rtc_base/third_party/sigslot/sigslot.h"
-#include "rtc_base/thread.h"
-// For SendDataParams/ReceiveDataParams.
-#include "media/base/media_channel.h"
-#include "media/sctp/sctp_transport_internal.h"
-
-// Defined by "usrsctplib/usrsctp.h"
-struct sockaddr_conn;
-struct sctp_assoc_change;
-struct sctp_rcvinfo;
-struct sctp_stream_reset_event;
-struct sctp_sendv_spa;
-
-// Defined by <sys/socket.h>
-struct socket;
-namespace cricket {
-
-// Holds data to be passed on to a transport.
-struct SctpInboundPacket;
-
-// From transport calls, data flows like this:
-// [network thread (although it can in princple be another thread)]
-// 1. SctpTransport::SendData(data)
-// 2. usrsctp_sendv(data)
-// [network thread returns; sctp thread then calls the following]
-// 3. OnSctpOutboundPacket(wrapped_data)
-// [sctp thread returns having async invoked on the network thread]
-// 4. SctpTransport::OnPacketFromSctpToNetwork(wrapped_data)
-// 5. DtlsTransport::SendPacket(wrapped_data)
-// 6. ... across network ... a packet is sent back ...
-// 7. SctpTransport::OnPacketReceived(wrapped_data)
-// 8. usrsctp_conninput(wrapped_data)
-// [network thread returns; sctp thread then calls the following]
-// 9. OnSctpInboundData(data)
-// 10. SctpTransport::OnDataFromSctpToTransport(data)
-// [sctp thread returns having async invoked on the network thread]
-// 11. SctpTransport::OnDataFromSctpToTransport(data)
-// 12. SctpTransport::SignalDataReceived(data)
-// [from the same thread, methods registered/connected to
-// SctpTransport are called with the received data]
-class UsrsctpTransport : public SctpTransportInternal,
- public sigslot::has_slots<> {
- public:
- // `network_thread` is where packets will be processed and callbacks from
- // this transport will be posted, and is the only thread on which public
- // methods can be called.
- // `transport` is not required (can be null).
- UsrsctpTransport(rtc::Thread* network_thread,
- rtc::PacketTransportInternal* transport);
- ~UsrsctpTransport() override;
-
- UsrsctpTransport(const UsrsctpTransport&) = delete;
- UsrsctpTransport& operator=(const UsrsctpTransport&) = delete;
-
- // SctpTransportInternal overrides (see sctptransportinternal.h for comments).
- void SetDtlsTransport(rtc::PacketTransportInternal* transport) override;
- bool Start(int local_port, int remote_port, int max_message_size) override;
- bool OpenStream(int sid) override;
- bool ResetStream(int sid) override;
- bool SendData(int sid,
- const webrtc::SendDataParams& params,
- const rtc::CopyOnWriteBuffer& payload,
- SendDataResult* result = nullptr) override;
- bool ReadyToSendData() override;
- int max_message_size() const override { return max_message_size_; }
- absl::optional<int> max_outbound_streams() const override {
- return max_outbound_streams_;
- }
- absl::optional<int> max_inbound_streams() const override {
- return max_inbound_streams_;
- }
- void set_debug_name_for_testing(const char* debug_name) override {
- debug_name_ = debug_name;
- }
- void InjectDataOrNotificationFromSctpForTesting(const void* data,
- size_t length,
- struct sctp_rcvinfo rcv,
- int flags);
-
- // Exposed to allow Post call from c-callbacks.
- // TODO(deadbeef): Remove this or at least make it return a const pointer.
- rtc::Thread* network_thread() const { return network_thread_; }
-
- private:
- // A message to be sent by the sctp library. This class is used to track the
- // progress of writing a single message to the sctp library in the presence of
- // partial writes. In this case, the Advance() function is provided in order
- // to advance over what has already been accepted by the sctp library and
- // avoid copying the remaining partial message buffer.
- class OutgoingMessage {
- public:
- OutgoingMessage(const rtc::CopyOnWriteBuffer& buffer,
- int sid,
- const webrtc::SendDataParams& send_params)
- : buffer_(buffer), sid_(sid), send_params_(send_params) {}
-
- // Advances the buffer by the incremented amount. Must not advance further
- // than the current data size.
- void Advance(size_t increment) {
- RTC_DCHECK_LE(increment + offset_, buffer_.size());
- offset_ += increment;
- }
-
- size_t size() const { return buffer_.size() - offset_; }
-
- const void* data() const { return buffer_.data() + offset_; }
-
- int sid() const { return sid_; }
- webrtc::SendDataParams send_params() const { return send_params_; }
-
- private:
- const rtc::CopyOnWriteBuffer buffer_;
- int sid_;
- const webrtc::SendDataParams send_params_;
- size_t offset_ = 0;
- };
-
- void ConnectTransportSignals();
- void DisconnectTransportSignals();
-
- // Creates the socket and connects.
- bool Connect();
-
- // Returns false when opening the socket failed.
- bool OpenSctpSocket();
- // Helpet method to set socket options.
- bool ConfigureSctpSocket();
- // Sets |sock_ |to nullptr.
- void CloseSctpSocket();
-
- // Sends a SCTP_RESET_STREAM for all streams in closing_ssids_.
- bool SendQueuedStreamResets();
-
- // Sets the "ready to send" flag and fires signal if needed.
- void SetReadyToSendData();
-
- // Sends the outgoing buffered message that was only partially accepted by the
- // sctp lib because it did not have enough space. Returns true if the entire
- // buffered message was accepted by the sctp lib.
- bool SendBufferedMessage();
-
- // Tries to send the `payload` on the usrsctp lib. The message will be
- // advanced by the amount that was sent.
- SendDataResult SendMessageInternal(OutgoingMessage* message);
-
- // Callbacks from DTLS transport.
- void OnWritableState(rtc::PacketTransportInternal* transport);
- virtual void OnPacketRead(rtc::PacketTransportInternal* transport,
- const char* data,
- size_t len,
- const int64_t& packet_time_us,
- int flags);
- void OnClosed(rtc::PacketTransportInternal* transport);
-
- // Methods related to usrsctp callbacks.
- void OnSendThresholdCallback();
- sockaddr_conn GetSctpSockAddr(int port);
-
- // Called using `invoker_` to send packet on the network.
- void OnPacketFromSctpToNetwork(const rtc::CopyOnWriteBuffer& buffer);
-
- // Called on the network thread.
- // Flags are standard socket API flags (RFC 6458).
- void OnDataOrNotificationFromSctp(const void* data,
- size_t length,
- struct sctp_rcvinfo rcv,
- int flags);
- // Called using `invoker_` to decide what to do with the data.
- void OnDataFromSctpToTransport(const ReceiveDataParams& params,
- const rtc::CopyOnWriteBuffer& buffer);
- // Called using `invoker_` to decide what to do with the notification.
- void OnNotificationFromSctp(const rtc::CopyOnWriteBuffer& buffer);
- void OnNotificationAssocChange(const sctp_assoc_change& change);
-
- void OnStreamResetEvent(const struct sctp_stream_reset_event* evt);
-
- // Responsible for marshalling incoming data to the transports listeners, and
- // outgoing data to the network interface.
- rtc::Thread* network_thread_;
- // Helps pass inbound/outbound packets asynchronously to the network thread.
- webrtc::ScopedTaskSafety task_safety_;
- // Underlying DTLS transport.
- rtc::PacketTransportInternal* transport_ = nullptr;
-
- // Track the data received from usrsctp between callbacks until the EOR bit
- // arrives.
- rtc::CopyOnWriteBuffer partial_incoming_message_;
- ReceiveDataParams partial_params_;
- int partial_flags_;
- // A message that was attempted to be sent, but was only partially accepted by
- // usrsctp lib with usrsctp_sendv() because it cannot buffer the full message.
- // This occurs because we explicitly set the EOR bit when sending, so
- // usrsctp_sendv() is not atomic.
- absl::optional<OutgoingMessage> partial_outgoing_message_;
-
- bool was_ever_writable_ = false;
- int local_port_ = kSctpDefaultPort;
- int remote_port_ = kSctpDefaultPort;
- int max_message_size_ = kSctpSendBufferSize;
- struct socket* sock_ = nullptr; // The socket created by usrsctp_socket(...).
-
- // Has Start been called? Don't create SCTP socket until it has.
- bool started_ = false;
- // Are we ready to queue data (SCTP socket created, and not blocked due to
- // congestion control)? Different than `transport_`'s "ready to send".
- bool ready_to_send_data_ = false;
-
- // Used to keep track of the status of each stream (or rather, each pair of
- // incoming/outgoing streams with matching IDs). It's specifically used to
- // keep track of the status of resets, but more information could be put here
- // later.
- //
- // See datachannel.h for a summary of the closing procedure.
- struct StreamStatus {
- // Closure initiated by application via ResetStream? Note that
- // this may be true while outgoing_reset_initiated is false if the outgoing
- // reset needed to be queued.
- bool closure_initiated = false;
- // Whether we've initiated the outgoing stream reset via
- // SCTP_RESET_STREAMS.
- bool outgoing_reset_initiated = false;
- // Whether usrsctp has indicated that the incoming/outgoing streams have
- // been reset. It's expected that the peer will reset its outgoing stream
- // (our incoming stream) after receiving the reset for our outgoing stream,
- // though older versions of chromium won't do this. See crbug.com/559394
- // for context.
- bool outgoing_reset_complete = false;
- bool incoming_reset_complete = false;
-
- // Some helper methods to improve code readability.
- bool is_open() const {
- return !closure_initiated && !incoming_reset_complete &&
- !outgoing_reset_complete;
- }
- // We need to send an outgoing reset if the application has closed the data
- // channel, or if we received a reset of the incoming stream from the
- // remote endpoint, indicating the data channel was closed remotely.
- bool need_outgoing_reset() const {
- return (incoming_reset_complete || closure_initiated) &&
- !outgoing_reset_initiated;
- }
- bool reset_complete() const {
- return outgoing_reset_complete && incoming_reset_complete;
- }
- };
-
- // Entries should only be removed from this map if `reset_complete` is
- // true.
- std::map<uint32_t, StreamStatus> stream_status_by_sid_;
-
- // A static human-readable name for debugging messages.
- const char* debug_name_ = "UsrsctpTransport";
- // Hides usrsctp interactions from this header file.
- class UsrSctpWrapper;
- // Number of channels negotiated. Not set before negotiation completes.
- absl::optional<int> max_outbound_streams_;
- absl::optional<int> max_inbound_streams_;
-
- // Used for associating this transport with the underlying sctp socket in
- // various callbacks.
- uintptr_t id_ = 0;
-
- friend class UsrsctpTransportMap;
-};
-
-class UsrsctpTransportMap;
-
-} // namespace cricket
-
-#endif // MEDIA_SCTP_USRSCTP_TRANSPORT_H_
diff --git a/media/sctp/usrsctp_transport_reliability_unittest.cc b/media/sctp/usrsctp_transport_reliability_unittest.cc
deleted file mode 100644
index 987dd04358..0000000000
--- a/media/sctp/usrsctp_transport_reliability_unittest.cc
+++ /dev/null
@@ -1,809 +0,0 @@
-/*
- * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-#include <memory>
-#include <queue>
-#include <string>
-
-#include "media/sctp/sctp_transport_internal.h"
-#include "media/sctp/usrsctp_transport.h"
-#include "rtc_base/copy_on_write_buffer.h"
-#include "rtc_base/event.h"
-#include "rtc_base/gunit.h"
-#include "rtc_base/logging.h"
-#include "rtc_base/random.h"
-#include "rtc_base/synchronization/mutex.h"
-#include "rtc_base/task_utils/pending_task_safety_flag.h"
-#include "rtc_base/task_utils/to_queued_task.h"
-#include "rtc_base/thread.h"
-#include "test/gtest.h"
-
-namespace {
-
-static constexpr int kDefaultTimeout = 10000; // 10 seconds.
-static constexpr int kTransport1Port = 15001;
-static constexpr int kTransport2Port = 25002;
-static constexpr int kLogPerMessagesCount = 100;
-
-/**
- * An simple packet transport implementation which can be
- * configured to simulate uniform random packet loss and
- * configurable random packet delay and reordering.
- */
-class SimulatedPacketTransport final : public rtc::PacketTransportInternal {
- public:
- SimulatedPacketTransport(std::string name,
- rtc::Thread* transport_thread,
- uint8_t packet_loss_percents,
- uint16_t avg_send_delay_millis)
- : transport_name_(name),
- transport_thread_(transport_thread),
- packet_loss_percents_(packet_loss_percents),
- avg_send_delay_millis_(avg_send_delay_millis),
- random_(42) {
- RTC_DCHECK(transport_thread_);
- RTC_DCHECK_LE(packet_loss_percents_, 100);
- RTC_DCHECK_RUN_ON(transport_thread_);
- }
-
- ~SimulatedPacketTransport() override {
- RTC_DCHECK_RUN_ON(transport_thread_);
- destination_ = nullptr;
- SignalWritableState(this);
- }
-
- SimulatedPacketTransport(const SimulatedPacketTransport&) = delete;
- SimulatedPacketTransport& operator=(const SimulatedPacketTransport&) = delete;
-
- const std::string& transport_name() const override { return transport_name_; }
-
- bool writable() const override { return destination_ != nullptr; }
-
- bool receiving() const override { return true; }
-
- int SendPacket(const char* data,
- size_t len,
- const rtc::PacketOptions& options,
- int flags = 0) {
- RTC_DCHECK_RUN_ON(transport_thread_);
- auto destination = destination_.load();
- if (destination == nullptr) {
- return -1;
- }
- if (random_.Rand(100) < packet_loss_percents_) {
- // silent packet loss
- return 0;
- }
- rtc::CopyOnWriteBuffer buffer(data, len);
- auto send_task = ToQueuedTask(
- destination->task_safety_.flag(),
- [destination, flags, buffer = std::move(buffer)] {
- destination->SignalReadPacket(
- destination, reinterpret_cast<const char*>(buffer.data()),
- buffer.size(), rtc::Time(), flags);
- });
- // Introduce random send delay in range [0 .. 2 * avg_send_delay_millis_]
- // millis, which will also work as random packet reordering mechanism.
- uint16_t actual_send_delay = avg_send_delay_millis_;
- int16_t reorder_delay =
- avg_send_delay_millis_ *
- std::min(1.0, std::max(-1.0, random_.Gaussian(0, 0.5)));
- actual_send_delay += reorder_delay;
-
- if (actual_send_delay > 0) {
- destination->transport_thread_->PostDelayedTask(std::move(send_task),
- actual_send_delay);
- } else {
- destination->transport_thread_->PostTask(std::move(send_task));
- }
- return 0;
- }
-
- int SetOption(rtc::Socket::Option opt, int value) override { return 0; }
-
- bool GetOption(rtc::Socket::Option opt, int* value) override { return false; }
-
- int GetError() override { return 0; }
-
- absl::optional<rtc::NetworkRoute> network_route() const override {
- return absl::nullopt;
- }
-
- void SetDestination(SimulatedPacketTransport* destination) {
- RTC_DCHECK_RUN_ON(transport_thread_);
- if (destination == this) {
- return;
- }
- destination_ = destination;
- SignalWritableState(this);
- }
-
- private:
- const std::string transport_name_;
- rtc::Thread* const transport_thread_;
- const uint8_t packet_loss_percents_;
- const uint16_t avg_send_delay_millis_;
- std::atomic<SimulatedPacketTransport*> destination_ ATOMIC_VAR_INIT(nullptr);
- webrtc::Random random_;
- webrtc::ScopedTaskSafety task_safety_;
-};
-
-/**
- * A helper class to send specified number of messages over UsrsctpTransport
- * with SCTP reliability settings provided by user. The reliability settings are
- * specified by passing a template instance of SendDataParams. The sid will be
- * assigned by sender itself and will be assigned from range
- * [cricket::kMinSctpSid; cricket::kMaxSctpSid]. The wide range of sids are used
- * to possibly trigger more execution paths inside usrsctp.
- */
-class SctpDataSender final {
- public:
- SctpDataSender(rtc::Thread* thread,
- cricket::UsrsctpTransport* transport,
- uint64_t target_messages_count,
- webrtc::SendDataParams send_params,
- uint32_t sender_id)
- : thread_(thread),
- transport_(transport),
- target_messages_count_(target_messages_count),
- send_params_(send_params),
- sender_id_(sender_id) {
- RTC_DCHECK(thread_);
- RTC_DCHECK(transport_);
- }
-
- SctpDataSender(const SctpDataSender&) = delete;
- SctpDataSender& operator=(const SctpDataSender&) = delete;
-
- void Start() {
- thread_->PostTask(ToQueuedTask(task_safety_.flag(), [this] {
- if (started_) {
- RTC_LOG(LS_INFO) << sender_id_ << " sender is already started";
- return;
- }
- started_ = true;
- SendNextMessage();
- }));
- }
-
- uint64_t BytesSentCount() const { return num_bytes_sent_; }
-
- uint64_t MessagesSentCount() const { return num_messages_sent_; }
-
- absl::optional<std::string> GetLastError() {
- absl::optional<std::string> result = absl::nullopt;
- thread_->Invoke<void>(RTC_FROM_HERE,
- [this, &result] { result = last_error_; });
- return result;
- }
-
- bool WaitForCompletion(int give_up_after_ms) {
- return sent_target_messages_count_.Wait(give_up_after_ms, kDefaultTimeout);
- }
-
- private:
- void SendNextMessage() {
- RTC_DCHECK_RUN_ON(thread_);
- if (!started_ || num_messages_sent_ >= target_messages_count_) {
- sent_target_messages_count_.Set();
- return;
- }
-
- if (num_messages_sent_ % kLogPerMessagesCount == 0) {
- RTC_LOG(LS_INFO) << sender_id_ << " sender will try send message "
- << (num_messages_sent_ + 1) << " out of "
- << target_messages_count_;
- }
-
- webrtc::SendDataParams params(send_params_);
- int sid =
- cricket::kMinSctpSid + (num_messages_sent_ % cricket::kMaxSctpStreams);
-
- cricket::SendDataResult result;
- transport_->SendData(sid, params, payload_, &result);
- switch (result) {
- case cricket::SDR_BLOCK:
- // retry after timeout
- thread_->PostDelayedTask(
- ToQueuedTask(task_safety_.flag(), [this] { SendNextMessage(); }),
- 500);
- break;
- case cricket::SDR_SUCCESS:
- // send next
- num_bytes_sent_ += payload_.size();
- ++num_messages_sent_;
- thread_->PostTask(
- ToQueuedTask(task_safety_.flag(), [this] { SendNextMessage(); }));
- break;
- case cricket::SDR_ERROR:
- // give up
- last_error_ = "UsrsctpTransport::SendData error returned";
- sent_target_messages_count_.Set();
- break;
- }
- }
-
- rtc::Thread* const thread_;
- cricket::UsrsctpTransport* const transport_;
- const uint64_t target_messages_count_;
- const webrtc::SendDataParams send_params_;
- const uint32_t sender_id_;
- rtc::CopyOnWriteBuffer payload_{std::string(1400, '.').c_str(), 1400};
- std::atomic<bool> started_ ATOMIC_VAR_INIT(false);
- std::atomic<uint64_t> num_messages_sent_ ATOMIC_VAR_INIT(0);
- rtc::Event sent_target_messages_count_{true, false};
- std::atomic<uint64_t> num_bytes_sent_ ATOMIC_VAR_INIT(0);
- absl::optional<std::string> last_error_;
- webrtc::ScopedTaskSafetyDetached task_safety_;
-};
-
-/**
- * A helper class which counts number of received messages
- * and bytes over UsrsctpTransport. Also allow waiting until
- * specified number of messages received.
- */
-class SctpDataReceiver final : public sigslot::has_slots<> {
- public:
- explicit SctpDataReceiver(uint32_t receiver_id,
- uint64_t target_messages_count)
- : receiver_id_(receiver_id),
- target_messages_count_(target_messages_count) {}
-
- SctpDataReceiver(const SctpDataReceiver&) = delete;
- SctpDataReceiver& operator=(const SctpDataReceiver&) = delete;
-
- void OnDataReceived(const cricket::ReceiveDataParams& params,
- const rtc::CopyOnWriteBuffer& data) {
- num_bytes_received_ += data.size();
- if (++num_messages_received_ == target_messages_count_) {
- received_target_messages_count_.Set();
- }
-
- if (num_messages_received_ % kLogPerMessagesCount == 0) {
- RTC_LOG(LS_INFO) << receiver_id_ << " receiver got "
- << num_messages_received_ << " messages";
- }
- }
-
- uint64_t MessagesReceivedCount() const { return num_messages_received_; }
-
- uint64_t BytesReceivedCount() const { return num_bytes_received_; }
-
- bool WaitForMessagesReceived(int timeout_millis) {
- return received_target_messages_count_.Wait(timeout_millis);
- }
-
- private:
- std::atomic<uint64_t> num_messages_received_ ATOMIC_VAR_INIT(0);
- std::atomic<uint64_t> num_bytes_received_ ATOMIC_VAR_INIT(0);
- rtc::Event received_target_messages_count_{true, false};
- const uint32_t receiver_id_;
- const uint64_t target_messages_count_;
-};
-
-/**
- * Simple class to manage set of threads.
- */
-class ThreadPool final {
- public:
- explicit ThreadPool(size_t threads_count) : random_(42) {
- RTC_DCHECK(threads_count > 0);
- threads_.reserve(threads_count);
- for (size_t i = 0; i < threads_count; i++) {
- auto thread = rtc::Thread::Create();
- thread->SetName("Thread #" + rtc::ToString(i + 1) + " from Pool", this);
- thread->Start();
- threads_.emplace_back(std::move(thread));
- }
- }
-
- ThreadPool(const ThreadPool&) = delete;
- ThreadPool& operator=(const ThreadPool&) = delete;
-
- rtc::Thread* GetRandomThread() {
- return threads_[random_.Rand(0U, threads_.size() - 1)].get();
- }
-
- private:
- webrtc::Random random_;
- std::vector<std::unique_ptr<rtc::Thread>> threads_;
-};
-
-/**
- * Represents single ping-pong test over UsrsctpTransport.
- * User can specify target number of message for bidirectional
- * send, underlying transport packets loss and average packet delay
- * and SCTP delivery settings.
- */
-class SctpPingPong final {
- 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,
- webrtc::SendDataParams send_params)
- : id_(id),
- port1_(port1),
- port2_(port2),
- transport_thread1_(transport_thread1),
- transport_thread2_(transport_thread2),
- messages_count_(messages_count),
- packet_loss_percents_(packet_loss_percents),
- avg_send_delay_millis_(avg_send_delay_millis),
- send_params_(send_params) {
- RTC_DCHECK(transport_thread1_ != nullptr);
- RTC_DCHECK(transport_thread2_ != nullptr);
- }
-
- virtual ~SctpPingPong() {
- transport_thread1_->Invoke<void>(RTC_FROM_HERE, [this] {
- data_sender1_.reset();
- sctp_transport1_->SetDtlsTransport(nullptr);
- packet_transport1_->SetDestination(nullptr);
- });
- transport_thread2_->Invoke<void>(RTC_FROM_HERE, [this] {
- data_sender2_.reset();
- sctp_transport2_->SetDtlsTransport(nullptr);
- packet_transport2_->SetDestination(nullptr);
- });
- transport_thread1_->Invoke<void>(RTC_FROM_HERE, [this] {
- sctp_transport1_.reset();
- data_receiver1_.reset();
- packet_transport1_.reset();
- });
- transport_thread2_->Invoke<void>(RTC_FROM_HERE, [this] {
- sctp_transport2_.reset();
- data_receiver2_.reset();
- packet_transport2_.reset();
- });
- }
-
- SctpPingPong(const SctpPingPong&) = delete;
- SctpPingPong& operator=(const SctpPingPong&) = delete;
-
- bool Start() {
- CreateTwoConnectedSctpTransportsWithAllStreams();
-
- {
- webrtc::MutexLock lock(&lock_);
- if (!errors_list_.empty()) {
- return false;
- }
- }
-
- data_sender1_.reset(new SctpDataSender(transport_thread1_,
- sctp_transport1_.get(),
- messages_count_, send_params_, id_));
- data_sender2_.reset(new SctpDataSender(transport_thread2_,
- sctp_transport2_.get(),
- messages_count_, send_params_, id_));
- data_sender1_->Start();
- data_sender2_->Start();
- return true;
- }
-
- std::vector<std::string> GetErrorsList() const {
- std::vector<std::string> result;
- {
- webrtc::MutexLock lock(&lock_);
- result = errors_list_;
- }
- return result;
- }
-
- void WaitForCompletion(int32_t timeout_millis) {
- if (data_sender1_ == nullptr) {
- ReportError("SctpPingPong id = " + rtc::ToString(id_) +
- ", sender 1 is not created");
- return;
- }
- if (data_sender2_ == nullptr) {
- ReportError("SctpPingPong id = " + rtc::ToString(id_) +
- ", sender 2 is not created");
- return;
- }
-
- if (!data_sender1_->WaitForCompletion(timeout_millis)) {
- ReportError("SctpPingPong id = " + rtc::ToString(id_) +
- ", sender 1 failed to complete within " +
- rtc::ToString(timeout_millis) + " millis");
- return;
- }
-
- auto sender1_error = data_sender1_->GetLastError();
- if (sender1_error.has_value()) {
- ReportError("SctpPingPong id = " + rtc::ToString(id_) +
- ", sender 1 error: " + sender1_error.value());
- return;
- }
-
- if (!data_sender2_->WaitForCompletion(timeout_millis)) {
- ReportError("SctpPingPong id = " + rtc::ToString(id_) +
- ", sender 2 failed to complete within " +
- rtc::ToString(timeout_millis) + " millis");
- return;
- }
-
- auto sender2_error = data_sender2_->GetLastError();
- if (sender2_error.has_value()) {
- ReportError("SctpPingPong id = " + rtc::ToString(id_) +
- ", sender 2 error: " + sender1_error.value());
- return;
- }
-
- if ((data_sender1_->MessagesSentCount() != messages_count_)) {
- ReportError("SctpPingPong id = " + rtc::ToString(id_) +
- ", sender 1 sent only " +
- rtc::ToString(data_sender1_->MessagesSentCount()) +
- " out of " + rtc::ToString(messages_count_));
- return;
- }
-
- if ((data_sender2_->MessagesSentCount() != messages_count_)) {
- ReportError("SctpPingPong id = " + rtc::ToString(id_) +
- ", sender 2 sent only " +
- rtc::ToString(data_sender2_->MessagesSentCount()) +
- " out of " + rtc::ToString(messages_count_));
- return;
- }
-
- if (!data_receiver1_->WaitForMessagesReceived(timeout_millis)) {
- ReportError("SctpPingPong id = " + rtc::ToString(id_) +
- ", receiver 1 did not complete within " +
- rtc::ToString(messages_count_));
- return;
- }
-
- if (!data_receiver2_->WaitForMessagesReceived(timeout_millis)) {
- ReportError("SctpPingPong id = " + rtc::ToString(id_) +
- ", receiver 2 did not complete within " +
- rtc::ToString(messages_count_));
- return;
- }
-
- if (data_receiver1_->BytesReceivedCount() !=
- data_sender2_->BytesSentCount()) {
- ReportError(
- "SctpPingPong id = " + rtc::ToString(id_) + ", receiver 1 received " +
- rtc::ToString(data_receiver1_->BytesReceivedCount()) +
- " bytes, but sender 2 send " +
- rtc::ToString(rtc::ToString(data_sender2_->BytesSentCount())));
- return;
- }
-
- if (data_receiver2_->BytesReceivedCount() !=
- data_sender1_->BytesSentCount()) {
- ReportError(
- "SctpPingPong id = " + rtc::ToString(id_) + ", receiver 2 received " +
- rtc::ToString(data_receiver2_->BytesReceivedCount()) +
- " bytes, but sender 1 send " +
- rtc::ToString(rtc::ToString(data_sender1_->BytesSentCount())));
- return;
- }
-
- RTC_LOG(LS_INFO) << "SctpPingPong id = " << id_ << " is done";
- }
-
- private:
- void CreateTwoConnectedSctpTransportsWithAllStreams() {
- transport_thread1_->Invoke<void>(RTC_FROM_HERE, [this] {
- packet_transport1_.reset(new SimulatedPacketTransport(
- "SctpPingPong id = " + rtc::ToString(id_) + ", packet transport 1",
- transport_thread1_, packet_loss_percents_, avg_send_delay_millis_));
- data_receiver1_.reset(new SctpDataReceiver(id_, messages_count_));
- sctp_transport1_.reset(new cricket::UsrsctpTransport(
- transport_thread1_, packet_transport1_.get()));
- sctp_transport1_->set_debug_name_for_testing("sctp transport 1");
-
- sctp_transport1_->SignalDataReceived.connect(
- data_receiver1_.get(), &SctpDataReceiver::OnDataReceived);
-
- for (uint32_t i = cricket::kMinSctpSid; i <= cricket::kMaxSctpSid; i++) {
- if (!sctp_transport1_->OpenStream(i)) {
- ReportError("SctpPingPong id = " + rtc::ToString(id_) +
- ", sctp transport 1 stream " + rtc::ToString(i) +
- " failed to open");
- break;
- }
- }
- });
-
- transport_thread2_->Invoke<void>(RTC_FROM_HERE, [this] {
- packet_transport2_.reset(new SimulatedPacketTransport(
- "SctpPingPong id = " + rtc::ToString(id_) + "packet transport 2",
- transport_thread2_, packet_loss_percents_, avg_send_delay_millis_));
- data_receiver2_.reset(new SctpDataReceiver(id_, messages_count_));
- sctp_transport2_.reset(new cricket::UsrsctpTransport(
- transport_thread2_, packet_transport2_.get()));
- sctp_transport2_->set_debug_name_for_testing("sctp transport 2");
- sctp_transport2_->SignalDataReceived.connect(
- data_receiver2_.get(), &SctpDataReceiver::OnDataReceived);
-
- for (uint32_t i = cricket::kMinSctpSid; i <= cricket::kMaxSctpSid; i++) {
- if (!sctp_transport2_->OpenStream(i)) {
- ReportError("SctpPingPong id = " + rtc::ToString(id_) +
- ", sctp transport 2 stream " + rtc::ToString(i) +
- " failed to open");
- break;
- }
- }
- });
-
- transport_thread1_->Invoke<void>(RTC_FROM_HERE, [this] {
- packet_transport1_->SetDestination(packet_transport2_.get());
- });
- transport_thread2_->Invoke<void>(RTC_FROM_HERE, [this] {
- packet_transport2_->SetDestination(packet_transport1_.get());
- });
-
- transport_thread1_->Invoke<void>(RTC_FROM_HERE, [this] {
- if (!sctp_transport1_->Start(port1_, port2_,
- cricket::kSctpSendBufferSize)) {
- ReportError("SctpPingPong id = " + rtc::ToString(id_) +
- ", failed to start sctp transport 1");
- }
- });
-
- transport_thread2_->Invoke<void>(RTC_FROM_HERE, [this] {
- if (!sctp_transport2_->Start(port2_, port1_,
- cricket::kSctpSendBufferSize)) {
- ReportError("SctpPingPong id = " + rtc::ToString(id_) +
- ", failed to start sctp transport 2");
- }
- });
- }
-
- void ReportError(std::string error) {
- webrtc::MutexLock lock(&lock_);
- errors_list_.push_back(std::move(error));
- }
-
- std::unique_ptr<SimulatedPacketTransport> packet_transport1_;
- std::unique_ptr<SimulatedPacketTransport> packet_transport2_;
- std::unique_ptr<SctpDataReceiver> data_receiver1_;
- std::unique_ptr<SctpDataReceiver> data_receiver2_;
- std::unique_ptr<cricket::UsrsctpTransport> sctp_transport1_;
- std::unique_ptr<cricket::UsrsctpTransport> sctp_transport2_;
- std::unique_ptr<SctpDataSender> data_sender1_;
- std::unique_ptr<SctpDataSender> data_sender2_;
- mutable webrtc::Mutex lock_;
- std::vector<std::string> errors_list_ RTC_GUARDED_BY(lock_);
-
- const uint32_t id_;
- const uint16_t port1_;
- const uint16_t port2_;
- rtc::Thread* const transport_thread1_;
- rtc::Thread* const transport_thread2_;
- const uint32_t messages_count_;
- const uint8_t packet_loss_percents_;
- const uint16_t avg_send_delay_millis_;
- const webrtc::SendDataParams send_params_;
-};
-
-/**
- * Helper function to calculate max number of milliseconds
- * allowed for test to run based on test configuration.
- */
-constexpr int32_t GetExecutionTimeLimitInMillis(uint32_t total_messages,
- uint8_t packet_loss_percents) {
- return std::min<int64_t>(
- std::numeric_limits<int32_t>::max(),
- std::max<int64_t>(
- 1LL * total_messages * 100 *
- std::max(1, packet_loss_percents * packet_loss_percents),
- kDefaultTimeout));
-}
-
-} // namespace
-
-namespace cricket {
-
-/**
- * The set of tests intended to check usrsctp reliability on
- * stress conditions: multiple sockets, concurrent access,
- * lossy network link. It was observed in the past that
- * usrsctp might misbehave in concurrent environment
- * under load on lossy networks: deadlocks and memory corruption
- * issues might happen in non-basic usage scenarios.
- * It's recommended to run this test whenever usrsctp version
- * used is updated to verify it properly works in stress
- * conditions under higher than usual load.
- * It is also recommended to enable ASAN when these tests
- * are executed, so whenever memory bug is happen inside usrsctp,
- * it will be easier to understand what went wrong with ASAN
- * provided diagnostics information.
- * The tests cases currently disabled by default due to
- * long execution time and due to unresolved issue inside
- * `usrsctp` library detected by try-bots with ThreadSanitizer.
- */
-class UsrSctpReliabilityTest : public ::testing::Test {};
-
-/**
- * A simple test which send multiple messages over reliable
- * connection, usefull to verify test infrastructure works.
- * Execution time is less than 1 second.
- */
-TEST_F(UsrSctpReliabilityTest,
- DISABLED_AllMessagesAreDeliveredOverReliableConnection) {
- auto thread1 = rtc::Thread::Create();
- auto thread2 = rtc::Thread::Create();
- thread1->Start();
- thread2->Start();
- constexpr uint8_t packet_loss_percents = 0;
- constexpr uint16_t avg_send_delay_millis = 10;
- constexpr uint32_t messages_count = 100;
- constexpr int32_t wait_timeout =
- GetExecutionTimeLimitInMillis(messages_count, packet_loss_percents);
- static_assert(wait_timeout > 0,
- "Timeout computation must produce positive value");
-
- webrtc::SendDataParams send_params;
- send_params.ordered = true;
-
- SctpPingPong test(1, kTransport1Port, kTransport2Port, thread1.get(),
- thread2.get(), messages_count, packet_loss_percents,
- avg_send_delay_millis, send_params);
- EXPECT_TRUE(test.Start()) << rtc::join(test.GetErrorsList(), ';');
- test.WaitForCompletion(wait_timeout);
- auto errors_list = test.GetErrorsList();
- EXPECT_TRUE(errors_list.empty()) << rtc::join(errors_list, ';');
-}
-
-/**
- * A test to verify that multiple messages can be reliably delivered
- * over lossy network when usrsctp configured to guarantee reliably
- * and in order delivery.
- * The test case is disabled by default because it takes
- * long time to run.
- * Execution time is about 2.5 minutes.
- */
-TEST_F(UsrSctpReliabilityTest,
- DISABLED_AllMessagesAreDeliveredOverLossyConnectionReliableAndInOrder) {
- auto thread1 = rtc::Thread::Create();
- auto thread2 = rtc::Thread::Create();
- thread1->Start();
- thread2->Start();
- constexpr uint8_t packet_loss_percents = 5;
- constexpr uint16_t avg_send_delay_millis = 16;
- constexpr uint32_t messages_count = 10000;
- constexpr int32_t wait_timeout =
- GetExecutionTimeLimitInMillis(messages_count, packet_loss_percents);
- static_assert(wait_timeout > 0,
- "Timeout computation must produce positive value");
-
- webrtc::SendDataParams send_params;
- send_params.ordered = true;
-
- SctpPingPong test(1, kTransport1Port, kTransport2Port, thread1.get(),
- thread2.get(), messages_count, packet_loss_percents,
- avg_send_delay_millis, send_params);
-
- EXPECT_TRUE(test.Start()) << rtc::join(test.GetErrorsList(), ';');
- test.WaitForCompletion(wait_timeout);
- auto errors_list = test.GetErrorsList();
- EXPECT_TRUE(errors_list.empty()) << rtc::join(errors_list, ';');
-}
-
-/**
- * A test to verify that multiple messages can be reliably delivered
- * over lossy network when usrsctp configured to retransmit lost
- * packets.
- * The test case is disabled by default because it takes
- * long time to run.
- * Execution time is about 2.5 minutes.
- */
-TEST_F(UsrSctpReliabilityTest,
- DISABLED_AllMessagesAreDeliveredOverLossyConnectionWithRetries) {
- auto thread1 = rtc::Thread::Create();
- auto thread2 = rtc::Thread::Create();
- thread1->Start();
- thread2->Start();
- constexpr uint8_t packet_loss_percents = 5;
- constexpr uint16_t avg_send_delay_millis = 16;
- constexpr uint32_t messages_count = 10000;
- constexpr int32_t wait_timeout =
- GetExecutionTimeLimitInMillis(messages_count, packet_loss_percents);
- static_assert(wait_timeout > 0,
- "Timeout computation must produce positive value");
-
- webrtc::SendDataParams send_params;
- send_params.ordered = false;
- send_params.max_rtx_count = std::numeric_limits<uint16_t>::max();
- send_params.max_rtx_ms = std::numeric_limits<uint16_t>::max();
-
- SctpPingPong test(1, kTransport1Port, kTransport2Port, thread1.get(),
- thread2.get(), messages_count, packet_loss_percents,
- avg_send_delay_millis, send_params);
-
- EXPECT_TRUE(test.Start()) << rtc::join(test.GetErrorsList(), ';');
- test.WaitForCompletion(wait_timeout);
- auto errors_list = test.GetErrorsList();
- EXPECT_TRUE(errors_list.empty()) << rtc::join(errors_list, ';');
-}
-
-/**
- * This is kind of reliability stress-test of usrsctp to verify
- * that all messages are delivered when multiple usrsctp
- * sockets used concurrently and underlying transport is lossy.
- *
- * It was observed in the past that in stress condtions usrsctp
- * might encounter deadlock and memory corruption bugs:
- * https://github.com/sctplab/usrsctp/issues/325
- *
- * It is recoomended to run this test whenever usrsctp version
- * used by WebRTC is updated.
- *
- * The test case is disabled by default because it takes
- * long time to run.
- * Execution time of this test is about 1-2 hours.
- */
-TEST_F(UsrSctpReliabilityTest,
- DISABLED_AllMessagesAreDeliveredOverLossyConnectionConcurrentTests) {
- ThreadPool pool(16);
-
- webrtc::SendDataParams send_params;
- send_params.ordered = true;
- constexpr uint32_t base_sctp_port = 5000;
-
- // The constants value below were experimentally chosen
- // to have reasonable execution time and to reproduce
- // particular deadlock issue inside usrsctp:
- // https://github.com/sctplab/usrsctp/issues/325
- // The constants values may be adjusted next time
- // some other issue inside usrsctp need to be debugged.
- constexpr uint32_t messages_count = 200;
- constexpr uint8_t packet_loss_percents = 5;
- constexpr uint16_t avg_send_delay_millis = 0;
- constexpr uint32_t parallel_ping_pongs = 16 * 1024;
- constexpr uint32_t total_ping_pong_tests = 16 * parallel_ping_pongs;
-
- constexpr int32_t wait_timeout = GetExecutionTimeLimitInMillis(
- total_ping_pong_tests * messages_count, packet_loss_percents);
- static_assert(wait_timeout > 0,
- "Timeout computation must produce positive value");
-
- std::queue<std::unique_ptr<SctpPingPong>> tests;
-
- for (uint32_t i = 0; i < total_ping_pong_tests; i++) {
- uint32_t port1 =
- base_sctp_port + (2 * i) % (UINT16_MAX - base_sctp_port - 1);
-
- auto test = std::make_unique<SctpPingPong>(
- i, port1, port1 + 1, pool.GetRandomThread(), pool.GetRandomThread(),
- messages_count, packet_loss_percents, avg_send_delay_millis,
- send_params);
-
- EXPECT_TRUE(test->Start()) << rtc::join(test->GetErrorsList(), ';');
- tests.emplace(std::move(test));
-
- while (tests.size() >= parallel_ping_pongs) {
- auto& oldest_test = tests.front();
- oldest_test->WaitForCompletion(wait_timeout);
-
- auto errors_list = oldest_test->GetErrorsList();
- EXPECT_TRUE(errors_list.empty()) << rtc::join(errors_list, ';');
- tests.pop();
- }
- }
-
- while (!tests.empty()) {
- auto& oldest_test = tests.front();
- oldest_test->WaitForCompletion(wait_timeout);
-
- auto errors_list = oldest_test->GetErrorsList();
- EXPECT_TRUE(errors_list.empty()) << rtc::join(errors_list, ';');
- tests.pop();
- }
-}
-
-} // namespace cricket
diff --git a/media/sctp/usrsctp_transport_unittest.cc b/media/sctp/usrsctp_transport_unittest.cc
deleted file mode 100644
index 8fdbabc14a..0000000000
--- a/media/sctp/usrsctp_transport_unittest.cc
+++ /dev/null
@@ -1,883 +0,0 @@
-/*
- * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#include "media/sctp/usrsctp_transport.h"
-
-#include <stdio.h>
-#include <string.h>
-#include <usrsctp.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "absl/algorithm/container.h"
-#include "media/sctp/sctp_transport_internal.h"
-#include "p2p/base/fake_dtls_transport.h"
-#include "rtc_base/copy_on_write_buffer.h"
-#include "rtc_base/gunit.h"
-#include "rtc_base/logging.h"
-#include "rtc_base/thread.h"
-#include "test/gtest.h"
-
-namespace {
-static const int kDefaultTimeout = 10000; // 10 seconds.
-// Use ports other than the default 5000 for testing.
-static const int kTransport1Port = 5001;
-static const int kTransport2Port = 5002;
-} // namespace
-
-namespace cricket {
-
-// This is essentially a buffer to hold received data. It stores only the last
-// received data. Calling OnDataReceived twice overwrites old data with the
-// newer one.
-// TODO(ldixon): Implement constraints, and allow new data to be added to old
-// instead of replacing it.
-class SctpFakeDataReceiver : public sigslot::has_slots<> {
- public:
- SctpFakeDataReceiver() : received_(false) {}
-
- void Clear() {
- received_ = false;
- last_data_ = "";
- last_params_ = ReceiveDataParams();
- num_messages_received_ = 0;
- }
-
- void OnDataReceived(const ReceiveDataParams& params,
- const rtc::CopyOnWriteBuffer& data) {
- num_messages_received_++;
- received_ = true;
- last_data_ = std::string(data.data<char>(), data.size());
- last_params_ = params;
- }
-
- bool received() const { return received_; }
- std::string last_data() const { return last_data_; }
- ReceiveDataParams last_params() const { return last_params_; }
- size_t num_messages_received() const { return num_messages_received_; }
-
- private:
- bool received_;
- std::string last_data_;
- size_t num_messages_received_ = 0;
- ReceiveDataParams last_params_;
-};
-
-class SctpTransportObserver : public sigslot::has_slots<> {
- public:
- explicit SctpTransportObserver(UsrsctpTransport* transport) {
- transport->SignalClosingProcedureComplete.connect(
- this, &SctpTransportObserver::OnClosingProcedureComplete);
- transport->SignalReadyToSendData.connect(
- this, &SctpTransportObserver::OnReadyToSend);
- }
-
- int StreamCloseCount(int stream) {
- return absl::c_count(closed_streams_, stream);
- }
-
- bool WasStreamClosed(int stream) {
- return absl::c_linear_search(closed_streams_, stream);
- }
-
- bool ReadyToSend() { return ready_to_send_; }
-
- private:
- void OnClosingProcedureComplete(int stream) {
- closed_streams_.push_back(stream);
- }
- void OnReadyToSend() { ready_to_send_ = true; }
-
- std::vector<int> closed_streams_;
- bool ready_to_send_ = false;
-};
-
-// Helper class used to immediately attempt to reopen a stream as soon as it's
-// been closed.
-class SignalTransportClosedReopener : public sigslot::has_slots<> {
- public:
- SignalTransportClosedReopener(UsrsctpTransport* transport,
- UsrsctpTransport* peer)
- : transport_(transport), peer_(peer) {}
-
- int StreamCloseCount(int stream) { return absl::c_count(streams_, stream); }
-
- private:
- void OnStreamClosed(int stream) {
- transport_->OpenStream(stream);
- peer_->OpenStream(stream);
- streams_.push_back(stream);
- }
-
- UsrsctpTransport* transport_;
- UsrsctpTransport* peer_;
- std::vector<int> streams_;
-};
-
-// SCTP Data Engine testing framework.
-class SctpTransportTest : public ::testing::Test, public sigslot::has_slots<> {
- protected:
- // usrsctp uses the NSS random number generator on non-Android platforms,
- // so we need to initialize SSL.
- static void SetUpTestSuite() {}
-
- void SetupConnectedTransportsWithTwoStreams() {
- SetupConnectedTransportsWithTwoStreams(kTransport1Port, kTransport2Port);
- }
-
- void SetupConnectedTransportsWithTwoStreams(int port1, int port2) {
- fake_dtls1_.reset(new FakeDtlsTransport("fake dtls 1", 0));
- fake_dtls2_.reset(new FakeDtlsTransport("fake dtls 2", 0));
- recv1_.reset(new SctpFakeDataReceiver());
- recv2_.reset(new SctpFakeDataReceiver());
- transport1_.reset(CreateTransport(fake_dtls1_.get(), recv1_.get()));
- transport1_->set_debug_name_for_testing("transport1");
- transport1_->SignalReadyToSendData.connect(
- this, &SctpTransportTest::OnChan1ReadyToSend);
- transport2_.reset(CreateTransport(fake_dtls2_.get(), recv2_.get()));
- transport2_->set_debug_name_for_testing("transport2");
- transport2_->SignalReadyToSendData.connect(
- this, &SctpTransportTest::OnChan2ReadyToSend);
- // Setup two connected transports ready to send and receive.
- bool asymmetric = false;
- fake_dtls1_->SetDestination(fake_dtls2_.get(), asymmetric);
-
- RTC_LOG(LS_VERBOSE) << "Transport setup ----------------------------- ";
- AddStream(1);
- AddStream(2);
-
- RTC_LOG(LS_VERBOSE)
- << "Connect the transports -----------------------------";
- // Both transports need to have started (with matching ports) for an
- // association to be formed.
- transport1_->Start(port1, port2, kSctpSendBufferSize);
- transport2_->Start(port2, port1, kSctpSendBufferSize);
- }
-
- bool AddStream(int sid) {
- bool ret = true;
- ret = ret && transport1_->OpenStream(sid);
- ret = ret && transport2_->OpenStream(sid);
- return ret;
- }
-
- UsrsctpTransport* CreateTransport(FakeDtlsTransport* fake_dtls,
- SctpFakeDataReceiver* recv) {
- UsrsctpTransport* transport =
- new UsrsctpTransport(rtc::Thread::Current(), fake_dtls);
- // When data is received, pass it to the SctpFakeDataReceiver.
- transport->SignalDataReceived.connect(
- recv, &SctpFakeDataReceiver::OnDataReceived);
- return transport;
- }
-
- bool SendData(UsrsctpTransport* chan,
- int sid,
- const std::string& msg,
- SendDataResult* result,
- bool ordered = false) {
- webrtc::SendDataParams params;
- params.ordered = ordered;
-
- return chan->SendData(
- sid, params, rtc::CopyOnWriteBuffer(&msg[0], msg.length()), result);
- }
-
- bool ReceivedData(const SctpFakeDataReceiver* recv,
- int sid,
- const std::string& msg) {
- return (recv->received() && recv->last_params().sid == sid &&
- recv->last_data() == msg);
- }
-
- bool ProcessMessagesUntilIdle() {
- rtc::Thread* thread = rtc::Thread::Current();
- while (!thread->empty()) {
- rtc::Message msg;
- if (thread->Get(&msg, rtc::Thread::kForever)) {
- thread->Dispatch(&msg);
- }
- }
- return !thread->IsQuitting();
- }
-
- UsrsctpTransport* transport1() { return transport1_.get(); }
- UsrsctpTransport* transport2() { return transport2_.get(); }
- SctpFakeDataReceiver* receiver1() { return recv1_.get(); }
- SctpFakeDataReceiver* receiver2() { return recv2_.get(); }
- FakeDtlsTransport* fake_dtls1() { return fake_dtls1_.get(); }
- FakeDtlsTransport* fake_dtls2() { return fake_dtls2_.get(); }
-
- int transport1_ready_to_send_count() {
- return transport1_ready_to_send_count_;
- }
- int transport2_ready_to_send_count() {
- return transport2_ready_to_send_count_;
- }
-
- private:
- std::unique_ptr<FakeDtlsTransport> fake_dtls1_;
- std::unique_ptr<FakeDtlsTransport> fake_dtls2_;
- std::unique_ptr<SctpFakeDataReceiver> recv1_;
- std::unique_ptr<SctpFakeDataReceiver> recv2_;
- std::unique_ptr<UsrsctpTransport> transport1_;
- std::unique_ptr<UsrsctpTransport> transport2_;
-
- int transport1_ready_to_send_count_ = 0;
- int transport2_ready_to_send_count_ = 0;
-
- void OnChan1ReadyToSend() { ++transport1_ready_to_send_count_; }
- void OnChan2ReadyToSend() { ++transport2_ready_to_send_count_; }
-};
-
-TEST_F(SctpTransportTest, MessageInterleavedWithNotification) {
- FakeDtlsTransport fake_dtls1("fake dtls 1", 0);
- FakeDtlsTransport fake_dtls2("fake dtls 2", 0);
- SctpFakeDataReceiver recv1;
- SctpFakeDataReceiver recv2;
- std::unique_ptr<UsrsctpTransport> transport1(
- CreateTransport(&fake_dtls1, &recv1));
- std::unique_ptr<UsrsctpTransport> transport2(
- CreateTransport(&fake_dtls2, &recv2));
-
- // Add a stream.
- transport1->OpenStream(1);
- transport2->OpenStream(1);
-
- // Start SCTP transports.
- transport1->Start(kSctpDefaultPort, kSctpDefaultPort, kSctpSendBufferSize);
- transport2->Start(kSctpDefaultPort, kSctpDefaultPort, kSctpSendBufferSize);
-
- // Connect the two fake DTLS transports.
- fake_dtls1.SetDestination(&fake_dtls2, false);
-
- // Ensure the SCTP association has been established
- // Note: I'd rather watch for an assoc established state here but couldn't
- // find any exposed...
- SendDataResult result;
- ASSERT_TRUE(SendData(transport2.get(), 1, "meow", &result));
- EXPECT_TRUE_WAIT(ReceivedData(&recv1, 1, "meow"), kDefaultTimeout);
-
- // Detach the DTLS transport to ensure only we will inject packets from here
- // on.
- transport1->SetDtlsTransport(nullptr);
-
- // Prepare chunk buffer and metadata
- auto chunk = rtc::CopyOnWriteBuffer(32);
- struct sctp_rcvinfo meta = {0};
- meta.rcv_sid = 1;
- meta.rcv_ssn = 1337;
- meta.rcv_ppid = rtc::HostToNetwork32(51); // text (complete)
-
- // Inject chunk 1/2.
- meta.rcv_tsn = 42;
- meta.rcv_cumtsn = 42;
- chunk.SetData("meow?", 5);
- transport1->InjectDataOrNotificationFromSctpForTesting(chunk.data(),
- chunk.size(), meta, 0);
-
- // Inject a notification in between chunks.
- union sctp_notification notification;
- memset(&notification, 0, sizeof(notification));
- // Type chosen since it's not handled apart from being logged
- notification.sn_header.sn_type = SCTP_PEER_ADDR_CHANGE;
- notification.sn_header.sn_flags = 0;
- notification.sn_header.sn_length = sizeof(notification);
- transport1->InjectDataOrNotificationFromSctpForTesting(
- &notification, sizeof(notification), {0}, MSG_NOTIFICATION);
-
- // Inject chunk 2/2
- meta.rcv_tsn = 42;
- meta.rcv_cumtsn = 43;
- chunk.SetData(" rawr!", 6);
- transport1->InjectDataOrNotificationFromSctpForTesting(
- chunk.data(), chunk.size(), meta, MSG_EOR);
-
- // Expect the message to contain both chunks.
- EXPECT_TRUE_WAIT(ReceivedData(&recv1, 1, "meow? rawr!"), kDefaultTimeout);
-}
-
-// Test that data can be sent end-to-end when an SCTP transport starts with one
-// transport (which is unwritable), and then switches to another transport. A
-// common scenario due to how BUNDLE works.
-TEST_F(SctpTransportTest, SwitchDtlsTransport) {
- FakeDtlsTransport black_hole("black hole", 0);
- FakeDtlsTransport fake_dtls1("fake dtls 1", 0);
- FakeDtlsTransport fake_dtls2("fake dtls 2", 0);
- SctpFakeDataReceiver recv1;
- SctpFakeDataReceiver recv2;
-
- // Construct transport1 with the "black hole" transport.
- std::unique_ptr<UsrsctpTransport> transport1(
- CreateTransport(&black_hole, &recv1));
- std::unique_ptr<UsrsctpTransport> transport2(
- CreateTransport(&fake_dtls2, &recv2));
-
- // Add a stream.
- transport1->OpenStream(1);
- transport2->OpenStream(1);
-
- // Tell them both to start (though transport1_ is connected to black_hole).
- transport1->Start(kTransport1Port, kTransport2Port, kSctpSendBufferSize);
- transport2->Start(kTransport2Port, kTransport1Port, kSctpSendBufferSize);
-
- // Switch transport1_ to the normal fake_dtls1_ transport.
- transport1->SetDtlsTransport(&fake_dtls1);
-
- // Connect the two fake DTLS transports.
- bool asymmetric = false;
- fake_dtls1.SetDestination(&fake_dtls2, asymmetric);
-
- // Make sure we end up able to send data.
- SendDataResult result;
- ASSERT_TRUE(SendData(transport1.get(), 1, "foo", &result));
- ASSERT_TRUE(SendData(transport2.get(), 1, "bar", &result));
- EXPECT_TRUE_WAIT(ReceivedData(&recv2, 1, "foo"), kDefaultTimeout);
- EXPECT_TRUE_WAIT(ReceivedData(&recv1, 1, "bar"), kDefaultTimeout);
-
- // Setting a null DtlsTransport should work. This could happen when an SCTP
- // data section is rejected.
- transport1->SetDtlsTransport(nullptr);
-}
-
-// Calling Start twice shouldn't do anything bad, if with the same parameters.
-TEST_F(SctpTransportTest, DuplicateStartCallsIgnored) {
- SetupConnectedTransportsWithTwoStreams();
- EXPECT_TRUE(transport1()->Start(kTransport1Port, kTransport2Port,
- kSctpSendBufferSize));
-
- // Make sure we can still send/recv data.
- SendDataResult result;
- ASSERT_TRUE(SendData(transport1(), 1, "foo", &result));
- ASSERT_TRUE(SendData(transport2(), 1, "bar", &result));
- EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "foo"), kDefaultTimeout);
- EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 1, "bar"), kDefaultTimeout);
-}
-
-// Calling Start a second time with a different port should fail.
-TEST_F(SctpTransportTest, CallingStartWithDifferentPortFails) {
- SetupConnectedTransportsWithTwoStreams();
- EXPECT_FALSE(transport1()->Start(kTransport1Port, 1234, kSctpSendBufferSize));
- EXPECT_FALSE(transport1()->Start(1234, kTransport2Port, kSctpSendBufferSize));
-}
-
-// A value of -1 for the local/remote port should be treated as the default
-// (5000).
-TEST_F(SctpTransportTest, NegativeOnePortTreatedAsDefault) {
- FakeDtlsTransport fake_dtls1("fake dtls 1", 0);
- FakeDtlsTransport fake_dtls2("fake dtls 2", 0);
- SctpFakeDataReceiver recv1;
- SctpFakeDataReceiver recv2;
- std::unique_ptr<UsrsctpTransport> transport1(
- CreateTransport(&fake_dtls1, &recv1));
- std::unique_ptr<UsrsctpTransport> transport2(
- CreateTransport(&fake_dtls2, &recv2));
-
- // Add a stream.
- transport1->OpenStream(1);
- transport2->OpenStream(1);
-
- // Tell them both to start, giving one transport the default port and the
- // other transport -1.
- transport1->Start(kSctpDefaultPort, kSctpDefaultPort, kSctpSendBufferSize);
- transport2->Start(-1, -1, kSctpSendBufferSize);
-
- // Connect the two fake DTLS transports.
- bool asymmetric = false;
- fake_dtls1.SetDestination(&fake_dtls2, asymmetric);
-
- // Make sure we end up able to send data.
- SendDataResult result;
- ASSERT_TRUE(SendData(transport1.get(), 1, "foo", &result));
- ASSERT_TRUE(SendData(transport2.get(), 1, "bar", &result));
- EXPECT_TRUE_WAIT(ReceivedData(&recv2, 1, "foo"), kDefaultTimeout);
- EXPECT_TRUE_WAIT(ReceivedData(&recv1, 1, "bar"), kDefaultTimeout);
-}
-
-TEST_F(SctpTransportTest, OpenStreamWithAlreadyOpenedStreamFails) {
- FakeDtlsTransport fake_dtls("fake dtls", 0);
- SctpFakeDataReceiver recv;
- std::unique_ptr<UsrsctpTransport> transport(
- CreateTransport(&fake_dtls, &recv));
- EXPECT_TRUE(transport->OpenStream(1));
- EXPECT_FALSE(transport->OpenStream(1));
-}
-
-TEST_F(SctpTransportTest, ResetStreamWithAlreadyResetStreamFails) {
- FakeDtlsTransport fake_dtls("fake dtls", 0);
- SctpFakeDataReceiver recv;
- std::unique_ptr<UsrsctpTransport> transport(
- CreateTransport(&fake_dtls, &recv));
- EXPECT_TRUE(transport->OpenStream(1));
- EXPECT_TRUE(transport->ResetStream(1));
- EXPECT_FALSE(transport->ResetStream(1));
-}
-
-// Test that SignalReadyToSendData is fired after Start has been called and the
-// DTLS transport is writable.
-TEST_F(SctpTransportTest, SignalReadyToSendDataAfterDtlsWritable) {
- FakeDtlsTransport fake_dtls("fake dtls", 0);
- SctpFakeDataReceiver recv;
- std::unique_ptr<UsrsctpTransport> transport(
- CreateTransport(&fake_dtls, &recv));
- SctpTransportObserver observer(transport.get());
-
- transport->Start(kSctpDefaultPort, kSctpDefaultPort, kSctpSendBufferSize);
- fake_dtls.SetWritable(true);
- EXPECT_TRUE_WAIT(observer.ReadyToSend(), kDefaultTimeout);
-}
-
-// Run the below tests using both ordered and unordered mode.
-class SctpTransportTestWithOrdered
- : public SctpTransportTest,
- public ::testing::WithParamInterface<bool> {};
-
-// Tests that a small message gets buffered and later sent by the
-// UsrsctpTransport when the sctp library only accepts the message partially.
-TEST_P(SctpTransportTestWithOrdered, SendSmallBufferedOutgoingMessage) {
- bool ordered = GetParam();
- SetupConnectedTransportsWithTwoStreams();
- // Wait for initial SCTP association to be formed.
- EXPECT_EQ_WAIT(1, transport1_ready_to_send_count(), kDefaultTimeout);
- // Make the fake transport unwritable so that messages pile up for the SCTP
- // socket.
- fake_dtls1()->SetWritable(false);
- SendDataResult result;
-
- // Fill almost all of sctp library's send buffer.
- ASSERT_TRUE(SendData(transport1(), /*sid=*/1,
- std::string(kSctpSendBufferSize - 1, 'a'), &result,
- ordered));
-
- std::string buffered_message("hello hello");
- // UsrsctpTransport accepts this message by buffering part of it.
- ASSERT_TRUE(
- SendData(transport1(), /*sid=*/1, buffered_message, &result, ordered));
- ASSERT_TRUE(transport1()->ReadyToSendData());
-
- // Sending anything else should block now.
- ASSERT_FALSE(
- SendData(transport1(), /*sid=*/1, "hello again", &result, ordered));
- ASSERT_EQ(SDR_BLOCK, result);
- ASSERT_FALSE(transport1()->ReadyToSendData());
-
- // Make sure the ready-to-send count hasn't changed.
- EXPECT_EQ(1, transport1_ready_to_send_count());
- // Make the transport writable again and expect a "SignalReadyToSendData" at
- // some point after sending the buffered message.
- fake_dtls1()->SetWritable(true);
- EXPECT_EQ_WAIT(2, transport1_ready_to_send_count(), kDefaultTimeout);
- EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, buffered_message),
- kDefaultTimeout);
- EXPECT_EQ(2u, receiver2()->num_messages_received());
-}
-
-// Tests that a large message gets buffered and later sent by the
-// UsrsctpTransport when the sctp library only accepts the message partially.
-TEST_P(SctpTransportTestWithOrdered, SendLargeBufferedOutgoingMessage) {
- bool ordered = GetParam();
- SetupConnectedTransportsWithTwoStreams();
- // Wait for initial SCTP association to be formed.
- EXPECT_EQ_WAIT(1, transport1_ready_to_send_count(), kDefaultTimeout);
- // Make the fake transport unwritable so that messages pile up for the SCTP
- // socket.
- fake_dtls1()->SetWritable(false);
- SendDataResult result;
-
- // Fill almost all of sctp library's send buffer.
- ASSERT_TRUE(SendData(transport1(), /*sid=*/1,
- std::string(kSctpSendBufferSize / 2, 'a'), &result,
- ordered));
-
- std::string buffered_message(kSctpSendBufferSize, 'b');
- // UsrsctpTransport accepts this message by buffering the second half.
- ASSERT_TRUE(
- SendData(transport1(), /*sid=*/1, buffered_message, &result, ordered));
- ASSERT_TRUE(transport1()->ReadyToSendData());
-
- // Sending anything else should block now.
- ASSERT_FALSE(
- SendData(transport1(), /*sid=*/1, "hello again", &result, ordered));
- ASSERT_EQ(SDR_BLOCK, result);
- ASSERT_FALSE(transport1()->ReadyToSendData());
-
- // Make sure the ready-to-send count hasn't changed.
- EXPECT_EQ(1, transport1_ready_to_send_count());
- // Make the transport writable again and expect a "SignalReadyToSendData" at
- // some point.
- fake_dtls1()->SetWritable(true);
- EXPECT_EQ_WAIT(2, transport1_ready_to_send_count(), kDefaultTimeout);
- EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, buffered_message),
- kDefaultTimeout);
- EXPECT_EQ(2u, receiver2()->num_messages_received());
-}
-
-// Tests that a large message gets buffered and later sent by the
-// UsrsctpTransport when the sctp library only accepts the message partially
-// during a stream reset.
-TEST_P(SctpTransportTestWithOrdered,
- SendLargeBufferedOutgoingMessageDuringReset) {
- bool ordered = GetParam();
- SetupConnectedTransportsWithTwoStreams();
- SctpTransportObserver transport2_observer(transport2());
-
- // Wait for initial SCTP association to be formed.
- EXPECT_EQ_WAIT(1, transport1_ready_to_send_count(), kDefaultTimeout);
- // Make the fake transport unwritable so that messages pile up for the SCTP
- // socket.
- fake_dtls1()->SetWritable(false);
- SendDataResult result;
-
- // Fill almost all of sctp library's send buffer.
- ASSERT_TRUE(SendData(transport1(), /*sid=*/1,
- std::string(kSctpSendBufferSize / 2, 'a'), &result,
- ordered));
-
- std::string buffered_message(kSctpSendBufferSize, 'b');
- // UsrsctpTransport accepts this message by buffering the second half.
- ASSERT_TRUE(
- SendData(transport1(), /*sid=*/1, buffered_message, &result, ordered));
- // Queue a stream reset
- transport1()->ResetStream(/*sid=*/1);
-
- // Make the transport writable again and expect a "SignalReadyToSendData" at
- // some point after sending the buffered message.
- fake_dtls1()->SetWritable(true);
- EXPECT_EQ_WAIT(2, transport1_ready_to_send_count(), kDefaultTimeout);
-
- // Queued message should be received by the receiver before receiving the
- // reset
- EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, buffered_message),
- kDefaultTimeout);
- EXPECT_EQ(2u, receiver2()->num_messages_received());
- EXPECT_TRUE_WAIT(transport2_observer.WasStreamClosed(1), kDefaultTimeout);
-}
-
-TEST_P(SctpTransportTestWithOrdered, SendData) {
- bool ordered = GetParam();
- SetupConnectedTransportsWithTwoStreams();
-
- SendDataResult result;
- RTC_LOG(LS_VERBOSE)
- << "transport1 sending: 'hello?' -----------------------------";
- ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result, ordered));
- EXPECT_EQ(SDR_SUCCESS, result);
- EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout);
- RTC_LOG(LS_VERBOSE) << "recv2.received=" << receiver2()->received()
- << ", recv2.last_params.sid="
- << receiver2()->last_params().sid
- << ", recv2.last_params.seq_num="
- << receiver2()->last_params().seq_num
- << ", recv2.last_data=" << receiver2()->last_data();
-
- RTC_LOG(LS_VERBOSE)
- << "transport2 sending: 'hi transport1' -----------------------------";
- ASSERT_TRUE(SendData(transport2(), 2, "hi transport1", &result, ordered));
- EXPECT_EQ(SDR_SUCCESS, result);
- EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi transport1"),
- kDefaultTimeout);
- RTC_LOG(LS_VERBOSE) << "recv1.received=" << receiver1()->received()
- << ", recv1.last_params.sid="
- << receiver1()->last_params().sid
- << ", recv1.last_params.seq_num="
- << receiver1()->last_params().seq_num
- << ", recv1.last_data=" << receiver1()->last_data();
-}
-
-// Sends a lot of large messages at once and verifies SDR_BLOCK is returned.
-TEST_P(SctpTransportTestWithOrdered, SendDataBlocked) {
- SetupConnectedTransportsWithTwoStreams();
-
- SendDataResult result;
- webrtc::SendDataParams params;
- params.ordered = GetParam();
-
- std::vector<char> buffer(1024 * 64, 0);
-
- for (size_t i = 0; i < 100; ++i) {
- transport1()->SendData(
- 1, params, rtc::CopyOnWriteBuffer(&buffer[0], buffer.size()), &result);
- if (result == SDR_BLOCK)
- break;
- }
-
- EXPECT_EQ(SDR_BLOCK, result);
-}
-
-// Test that after an SCTP socket's buffer is filled, SignalReadyToSendData
-// is fired after it begins to be drained.
-TEST_P(SctpTransportTestWithOrdered, SignalReadyToSendDataAfterBlocked) {
- SetupConnectedTransportsWithTwoStreams();
- // Wait for initial SCTP association to be formed.
- EXPECT_EQ_WAIT(1, transport1_ready_to_send_count(), kDefaultTimeout);
- // Make the fake transport unwritable so that messages pile up for the SCTP
- // socket.
- fake_dtls1()->SetWritable(false);
- // Send messages until we get EWOULDBLOCK.
- static const size_t kMaxMessages = 1024;
- webrtc::SendDataParams params;
- params.ordered = GetParam();
- rtc::CopyOnWriteBuffer buf(1024);
- memset(buf.MutableData(), 0, 1024);
- SendDataResult result;
- size_t message_count = 0;
- for (; message_count < kMaxMessages; ++message_count) {
- if (!transport1()->SendData(1, params, buf, &result) &&
- result == SDR_BLOCK) {
- break;
- }
- }
- ASSERT_NE(kMaxMessages, message_count)
- << "Sent max number of messages without getting SDR_BLOCK?";
- // Make sure the ready-to-send count hasn't changed.
- EXPECT_EQ(1, transport1_ready_to_send_count());
- // Make the transport writable again and expect a "SignalReadyToSendData" at
- // some point.
- fake_dtls1()->SetWritable(true);
- EXPECT_EQ_WAIT(2, transport1_ready_to_send_count(), kDefaultTimeout);
- EXPECT_EQ_WAIT(message_count, receiver2()->num_messages_received(),
- kDefaultTimeout);
-}
-
-INSTANTIATE_TEST_SUITE_P(SctpTransportTest,
- SctpTransportTestWithOrdered,
- ::testing::Bool());
-
-// This is a regression test that fails with earlier versions of SCTP in
-// unordered mode. See bugs.webrtc.org/10939.
-TEST_F(SctpTransportTest, SendsLargeDataBufferedBySctpLib) {
- SetupConnectedTransportsWithTwoStreams();
- // Wait for initial SCTP association to be formed.
- EXPECT_EQ_WAIT(1, transport1_ready_to_send_count(), kDefaultTimeout);
- // Make the fake transport unwritable so that messages pile up for the SCTP
- // socket.
- fake_dtls1()->SetWritable(false);
-
- SendDataResult result;
- std::string buffered_message(kSctpSendBufferSize - 1, 'a');
- ASSERT_TRUE(SendData(transport1(), 1, buffered_message, &result, false));
-
- fake_dtls1()->SetWritable(true);
- EXPECT_EQ_WAIT(1, transport1_ready_to_send_count(), kDefaultTimeout);
- EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, buffered_message),
- kDefaultTimeout);
-}
-
-// Trying to send data for a nonexistent stream should fail.
-TEST_F(SctpTransportTest, SendDataWithNonexistentStreamFails) {
- SetupConnectedTransportsWithTwoStreams();
- SendDataResult result;
- EXPECT_FALSE(SendData(transport2(), 123, "some data", &result));
- EXPECT_EQ(SDR_ERROR, result);
-}
-
-TEST_F(SctpTransportTest, SendDataHighPorts) {
- SetupConnectedTransportsWithTwoStreams(32768, 32769);
-
- SendDataResult result;
- ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result));
- EXPECT_EQ(SDR_SUCCESS, result);
- EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout);
-
- ASSERT_TRUE(SendData(transport2(), 2, "hi transport1", &result));
- EXPECT_EQ(SDR_SUCCESS, result);
- EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi transport1"),
- kDefaultTimeout);
-}
-
-TEST_F(SctpTransportTest, ClosesRemoteStream) {
- SetupConnectedTransportsWithTwoStreams();
- SctpTransportObserver transport1_observer(transport1());
- SctpTransportObserver transport2_observer(transport2());
-
- SendDataResult result;
- ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result));
- EXPECT_EQ(SDR_SUCCESS, result);
- EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout);
- ASSERT_TRUE(SendData(transport2(), 2, "hi transport1", &result));
- EXPECT_EQ(SDR_SUCCESS, result);
- EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi transport1"),
- kDefaultTimeout);
-
- // Close stream 1 on transport 1. Transport 2 should notify us.
- transport1()->ResetStream(1);
- EXPECT_TRUE_WAIT(transport2_observer.WasStreamClosed(1), kDefaultTimeout);
-}
-TEST_F(SctpTransportTest, ClosesRemoteStreamWithNoData) {
- SetupConnectedTransportsWithTwoStreams();
- SctpTransportObserver transport1_observer(transport1());
- SctpTransportObserver transport2_observer(transport2());
-
- // Close stream 1 on transport 1. Transport 2 should notify us.
- transport1()->ResetStream(1);
- EXPECT_TRUE_WAIT(transport2_observer.WasStreamClosed(1), kDefaultTimeout);
-}
-
-TEST_F(SctpTransportTest, ClosesTwoRemoteStreams) {
- SetupConnectedTransportsWithTwoStreams();
- AddStream(3);
- SctpTransportObserver transport1_observer(transport1());
- SctpTransportObserver transport2_observer(transport2());
-
- SendDataResult result;
- ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result));
- EXPECT_EQ(SDR_SUCCESS, result);
- EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout);
- ASSERT_TRUE(SendData(transport2(), 2, "hi transport1", &result));
- EXPECT_EQ(SDR_SUCCESS, result);
- EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi transport1"),
- kDefaultTimeout);
-
- // Close two streams on one side.
- transport2()->ResetStream(2);
- transport2()->ResetStream(3);
- EXPECT_TRUE_WAIT(transport2_observer.WasStreamClosed(2), kDefaultTimeout);
- EXPECT_TRUE_WAIT(transport2_observer.WasStreamClosed(3), kDefaultTimeout);
-}
-
-TEST_F(SctpTransportTest, ClosesStreamsOnBothSides) {
- SetupConnectedTransportsWithTwoStreams();
- AddStream(3);
- AddStream(4);
- SctpTransportObserver transport1_observer(transport1());
- SctpTransportObserver transport2_observer(transport2());
-
- SendDataResult result;
- ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result));
- EXPECT_EQ(SDR_SUCCESS, result);
- EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout);
- ASSERT_TRUE(SendData(transport2(), 2, "hi transport1", &result));
- EXPECT_EQ(SDR_SUCCESS, result);
- EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi transport1"),
- kDefaultTimeout);
-
- // Close one stream on transport1(), while closing three streams on
- // transport2(). They will conflict (only one side can close anything at a
- // time, apparently). Test the resolution of the conflict.
- transport1()->ResetStream(1);
-
- transport2()->ResetStream(2);
- transport2()->ResetStream(3);
- transport2()->ResetStream(4);
- EXPECT_TRUE_WAIT(transport2_observer.WasStreamClosed(1), kDefaultTimeout);
- EXPECT_TRUE_WAIT(transport1_observer.WasStreamClosed(2), kDefaultTimeout);
- EXPECT_TRUE_WAIT(transport1_observer.WasStreamClosed(3), kDefaultTimeout);
- EXPECT_TRUE_WAIT(transport1_observer.WasStreamClosed(4), kDefaultTimeout);
-}
-
-TEST_F(SctpTransportTest, RefusesHighNumberedTransports) {
- SetupConnectedTransportsWithTwoStreams();
- EXPECT_TRUE(AddStream(kMaxSctpSid));
- EXPECT_FALSE(AddStream(kMaxSctpSid + 1));
-}
-
-TEST_F(SctpTransportTest, ReusesAStream) {
- // Shut down transport 1, then open it up again for reuse.
- SetupConnectedTransportsWithTwoStreams();
- SendDataResult result;
- SctpTransportObserver transport2_observer(transport2());
-
- ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result));
- EXPECT_EQ(SDR_SUCCESS, result);
- EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout);
-
- transport1()->ResetStream(1);
- EXPECT_TRUE_WAIT(transport2_observer.WasStreamClosed(1), kDefaultTimeout);
- // Transport 1 is gone now.
-
- // Create a new transport 1.
- AddStream(1);
- ASSERT_TRUE(SendData(transport1(), 1, "hi?", &result));
- EXPECT_EQ(SDR_SUCCESS, result);
- EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hi?"), kDefaultTimeout);
- transport1()->ResetStream(1);
- EXPECT_EQ_WAIT(2, transport2_observer.StreamCloseCount(1), kDefaultTimeout);
-}
-
-TEST_F(SctpTransportTest, RejectsTooLargeMessageSize) {
- FakeDtlsTransport fake_dtls("fake dtls", 0);
- SctpFakeDataReceiver recv;
- std::unique_ptr<UsrsctpTransport> transport(
- CreateTransport(&fake_dtls, &recv));
-
- EXPECT_FALSE(transport->Start(kSctpDefaultPort, kSctpDefaultPort,
- kSctpSendBufferSize + 1));
-}
-
-TEST_F(SctpTransportTest, RejectsTooSmallMessageSize) {
- FakeDtlsTransport fake_dtls("fake dtls", 0);
- SctpFakeDataReceiver recv;
- std::unique_ptr<UsrsctpTransport> transport(
- CreateTransport(&fake_dtls, &recv));
-
- EXPECT_FALSE(transport->Start(kSctpDefaultPort, kSctpDefaultPort, 0));
-}
-
-TEST_F(SctpTransportTest, RejectsSendTooLargeMessages) {
- SetupConnectedTransportsWithTwoStreams();
- // Use "Start" to reduce the max message size
- transport1()->Start(kTransport1Port, kTransport2Port, 10);
- EXPECT_EQ(10, transport1()->max_message_size());
- const char eleven_characters[] = "12345678901";
- SendDataResult result;
- EXPECT_FALSE(SendData(transport1(), 1, eleven_characters, &result));
-}
-
-// Regression test for: crbug.com/1137936
-TEST_F(SctpTransportTest, SctpRestartWithPendingDataDoesNotDeadlock) {
- // In order to trigger a restart, we'll connect two transports, then
- // disconnect them and connect the first to a third, which will initiate the
- // new handshake.
- FakeDtlsTransport fake_dtls1("fake dtls 1", 0);
- FakeDtlsTransport fake_dtls2("fake dtls 2", 0);
- FakeDtlsTransport fake_dtls3("fake dtls 3", 0);
- SctpFakeDataReceiver recv1;
- SctpFakeDataReceiver recv2;
- SctpFakeDataReceiver recv3;
-
- std::unique_ptr<UsrsctpTransport> transport1(
- CreateTransport(&fake_dtls1, &recv1));
- std::unique_ptr<UsrsctpTransport> transport2(
- CreateTransport(&fake_dtls2, &recv2));
- std::unique_ptr<UsrsctpTransport> transport3(
- CreateTransport(&fake_dtls3, &recv3));
- SctpTransportObserver observer(transport1.get());
-
- // Connect the first two transports.
- fake_dtls1.SetDestination(&fake_dtls2, /*asymmetric=*/false);
- transport1->OpenStream(1);
- transport2->OpenStream(1);
- transport1->Start(5000, 5000, kSctpSendBufferSize);
- transport2->Start(5000, 5000, kSctpSendBufferSize);
-
- // Sanity check that we can send data.
- SendDataResult result;
- ASSERT_TRUE(SendData(transport1.get(), 1, "foo", &result));
- ASSERT_TRUE_WAIT(ReceivedData(&recv2, 1, "foo"), kDefaultTimeout);
-
- // Disconnect the transports and attempt to send a message, which will be
- // stored in an output queue; this is necessary to reproduce the bug.
- fake_dtls1.SetDestination(nullptr, /*asymmetric=*/false);
- EXPECT_TRUE(SendData(transport1.get(), 1, "bar", &result));
-
- // Now connect to the third transport.
- fake_dtls1.SetDestination(&fake_dtls3, /*asymmetric=*/false);
- transport3->OpenStream(1);
- transport3->Start(5000, 5000, kSctpSendBufferSize);
-
- // Send data from the new endpoint to the original endpoint. If data is
- // received that means the restart must have been successful.
- EXPECT_TRUE(SendData(transport3.get(), 1, "baz", &result));
- EXPECT_TRUE_WAIT(ReceivedData(&recv1, 1, "baz"), kDefaultTimeout);
-}
-
-} // namespace cricket