2013-07-10 00:45:36 +00:00
|
|
|
/*
|
2016-02-07 20:46:45 -08:00
|
|
|
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
2013-07-10 00:45:36 +00:00
|
|
|
*
|
2016-02-07 20:46:45 -08:00
|
|
|
* 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.
|
2013-07-10 00:45:36 +00:00
|
|
|
*/
|
|
|
|
|
|
Move talk/media to webrtc/media
I removed the 'libjingle' target in talk/libjingle.gyp and replaced
all users of it with base/base.gyp:rtc_base. It seems the jsoncpp
and expat dependencies were not used by it's previous references.
The files in talk/media/testdata were uploaded to Google Storage and
added .sha1 files in resources/media instead of simply moving them.
The previously disabled warnings that were inherited from
talk/build/common.gypi are now replaced by target-specific disabling
of only the failing warnings. Additional disabling was needed since the stricter
compilation warnings that applies to code in webrtc/.
License headers will be updated in a follow-up CL in order to not
break Git history.
Other modifications:
* Updated the header guards.
* Sorted the includes using chromium/src/tools/sort-headers.py
except for these files:
talk/app/webrtc/peerconnectionendtoend_unittest.cc
talk/app/webrtc/java/jni/androidmediadecoder_jni.cc
talk/app/webrtc/java/jni/androidmediaencoder_jni.cc
webrtc/media/devices/win32devicemanager.cc.
* Unused GYP reference to libjingle_tests_additional_deps was removed.
* Removed duplicated GYP entries of
webrtc/base/testutils.cc
webrtc/base/testutils.h
The HAVE_WEBRTC_VIDEO and HAVE_WEBRTC_VOICE defines were used by only talk/media,
so they were moved to the media.gyp.
I also checked that none of
EXPAT_RELATIVE_PATH,
FEATURE_ENABLE_VOICEMAIL,
GTEST_RELATIVE_PATH,
JSONCPP_RELATIVE_PATH,
LOGGING=1,
SRTP_RELATIVE_PATH,
FEATURE_ENABLE_SSL,
FEATURE_ENABLE_VOICEMAIL,
FEATURE_ENABLE_PSTN,
HAVE_SCTP,
HAVE_SRTP,
are used by the talk/media code.
For Chromium, the following changes will need to be applied to the roll CL that updates the
DEPS for WebRTC and libjingle: https://codereview.chromium.org/1604303002/
BUG=webrtc:5420
NOPRESUBMIT=True
TBR=tommi@webrtc.org
Review URL: https://codereview.webrtc.org/1587193006
Cr-Commit-Position: refs/heads/master@{#11495}
2016-02-04 23:52:28 -08:00
|
|
|
#include "webrtc/media/sctp/sctpdataengine.h"
|
2013-07-10 00:45:36 +00:00
|
|
|
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
#include <stdio.h>
|
2016-02-26 03:00:35 -08:00
|
|
|
|
|
|
|
|
#include <memory>
|
2014-01-03 22:08:47 +00:00
|
|
|
#include <sstream>
|
2013-07-10 00:45:36 +00:00
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
#include "usrsctplib/usrsctp.h"
|
2015-11-10 23:44:30 -08:00
|
|
|
#include "webrtc/base/arraysize.h"
|
2016-03-20 06:15:43 -07:00
|
|
|
#include "webrtc/base/copyonwritebuffer.h"
|
2016-05-19 19:58:38 +02:00
|
|
|
#include "webrtc/base/criticalsection.h"
|
2014-08-13 17:26:08 +00:00
|
|
|
#include "webrtc/base/helpers.h"
|
|
|
|
|
#include "webrtc/base/logging.h"
|
Revert of Safe numeric library: base/numerics (copied from Chromium) (patchset #11 id:250001 of https://codereview.webrtc.org/1753293002/ )
Reason for revert:
Looks like the Chrome iOS build is broken because of these two changes. So I'm going to have to revert. Here's the error:
https://build.chromium.org/p/tryserver.chromium.mac/builders/ios_rel_device_ninja/builds/185624/steps/compile/logs/stdio
FAILED: rm -f arch/libsafe_numerics.arm64.a && ./gyp-mac-tool filter-libtool libtool -static -o arch/libsafe_numerics.arm64.a
error: /Applications/Xcode7.0.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/libtool: no files specified
Usage: /Applications/Xcode7.0.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/libtool -static [-] file [...] [-filelist listfile[,dirname]] [-arch_only arch] [-sacLT] [-no_warning_for_no_symbols]
Usage: /Applications/Xcode7.0.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/libtool -dynamic [-] file [...] [-filelist listfile[,dirname]] [-arch_only arch] [-o output] [-install_name name] [-compatibility_version #] [-current_version #] [-seg1addr 0x#] [-segs_read_only_addr 0x#] [-segs_read_write_addr 0x#] [-seg_addr_table <filename>] [-seg_addr_table_filename <file_system_path>] [-all_load] [-noall_load]
FAILED: rm -f arch/libsafe_numerics.armv7.a && ./gyp-mac-tool filter-libtool libtool -static -o arch/libsafe_numerics.armv7.a
error: /Applications/Xcode7.0.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/libtool: no files specified
Usage: /Applications/Xcode7.0.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/libtool -static [-] file [...] [-filelist listfile[,dirname]] [-arch_only arch] [-sacLT] [-no_warning_for_no_symbols]
Usage: /Applications/Xcode7.0.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/libtool -dynamic [-] file [...] [-filelist listfile[,dirname]] [-arch_only arch] [-o output] [-install_name name] [-compatibility_version #] [-current_version #] [-seg1addr 0x#] [-segs_read_only_addr 0x#] [-segs_read_write_addr 0x#] [-seg_addr_table <filename>] [-seg_addr_table_filename <file_system_path>] [-all_load] [-noall_load]
ninja: build stopped: subcommand failed.
Original issue's description:
> Safe numeric library added: base/numerics (copied from Chromium)
>
> This copies the contents (unittest excluded) of base/numerics in
> chromium to base/numerics in webrtc. Files added:
> - safe_conversions.h
> - safe_conversions_impl.h
> - safe_math.h
> - safe_math_impl.h
>
> A really old version of safe_conversions[_impl].h previously existed in
> base/, this has been deleted and sources using it have been updated
> to include the new base/numerics/safe_converions.h.
>
> This CL also adds a DEPS file to webrtc/base.
>
> NOPRESUBMIT=True
> BUG=webrtc:5548, webrtc:5623
>
> Committed: https://crrev.com/de1c81b2d2196be611674aa6019b9db3a9329042
> Cr-Commit-Position: refs/heads/master@{#11907}
TBR=kjellander@webrtc.org,kwiberg@webrtc.org,tina.legrand@webrtc.org,hbos@webrtc.org
# Not skipping CQ checks because original CL landed more than 1 days ago.
BUG=webrtc:5548, webrtc:5623
Review URL: https://codereview.webrtc.org/1792613002 .
Cr-Commit-Position: refs/heads/master@{#11965}
2016-03-11 17:12:32 -08:00
|
|
|
#include "webrtc/base/safe_conversions.h"
|
Move talk/media to webrtc/media
I removed the 'libjingle' target in talk/libjingle.gyp and replaced
all users of it with base/base.gyp:rtc_base. It seems the jsoncpp
and expat dependencies were not used by it's previous references.
The files in talk/media/testdata were uploaded to Google Storage and
added .sha1 files in resources/media instead of simply moving them.
The previously disabled warnings that were inherited from
talk/build/common.gypi are now replaced by target-specific disabling
of only the failing warnings. Additional disabling was needed since the stricter
compilation warnings that applies to code in webrtc/.
License headers will be updated in a follow-up CL in order to not
break Git history.
Other modifications:
* Updated the header guards.
* Sorted the includes using chromium/src/tools/sort-headers.py
except for these files:
talk/app/webrtc/peerconnectionendtoend_unittest.cc
talk/app/webrtc/java/jni/androidmediadecoder_jni.cc
talk/app/webrtc/java/jni/androidmediaencoder_jni.cc
webrtc/media/devices/win32devicemanager.cc.
* Unused GYP reference to libjingle_tests_additional_deps was removed.
* Removed duplicated GYP entries of
webrtc/base/testutils.cc
webrtc/base/testutils.h
The HAVE_WEBRTC_VIDEO and HAVE_WEBRTC_VOICE defines were used by only talk/media,
so they were moved to the media.gyp.
I also checked that none of
EXPAT_RELATIVE_PATH,
FEATURE_ENABLE_VOICEMAIL,
GTEST_RELATIVE_PATH,
JSONCPP_RELATIVE_PATH,
LOGGING=1,
SRTP_RELATIVE_PATH,
FEATURE_ENABLE_SSL,
FEATURE_ENABLE_VOICEMAIL,
FEATURE_ENABLE_PSTN,
HAVE_SCTP,
HAVE_SRTP,
are used by the talk/media code.
For Chromium, the following changes will need to be applied to the roll CL that updates the
DEPS for WebRTC and libjingle: https://codereview.chromium.org/1604303002/
BUG=webrtc:5420
NOPRESUBMIT=True
TBR=tommi@webrtc.org
Review URL: https://codereview.webrtc.org/1587193006
Cr-Commit-Position: refs/heads/master@{#11495}
2016-02-04 23:52:28 -08:00
|
|
|
#include "webrtc/media/base/codec.h"
|
2016-03-02 05:42:30 -08:00
|
|
|
#include "webrtc/media/base/mediaconstants.h"
|
Move talk/media to webrtc/media
I removed the 'libjingle' target in talk/libjingle.gyp and replaced
all users of it with base/base.gyp:rtc_base. It seems the jsoncpp
and expat dependencies were not used by it's previous references.
The files in talk/media/testdata were uploaded to Google Storage and
added .sha1 files in resources/media instead of simply moving them.
The previously disabled warnings that were inherited from
talk/build/common.gypi are now replaced by target-specific disabling
of only the failing warnings. Additional disabling was needed since the stricter
compilation warnings that applies to code in webrtc/.
License headers will be updated in a follow-up CL in order to not
break Git history.
Other modifications:
* Updated the header guards.
* Sorted the includes using chromium/src/tools/sort-headers.py
except for these files:
talk/app/webrtc/peerconnectionendtoend_unittest.cc
talk/app/webrtc/java/jni/androidmediadecoder_jni.cc
talk/app/webrtc/java/jni/androidmediaencoder_jni.cc
webrtc/media/devices/win32devicemanager.cc.
* Unused GYP reference to libjingle_tests_additional_deps was removed.
* Removed duplicated GYP entries of
webrtc/base/testutils.cc
webrtc/base/testutils.h
The HAVE_WEBRTC_VIDEO and HAVE_WEBRTC_VOICE defines were used by only talk/media,
so they were moved to the media.gyp.
I also checked that none of
EXPAT_RELATIVE_PATH,
FEATURE_ENABLE_VOICEMAIL,
GTEST_RELATIVE_PATH,
JSONCPP_RELATIVE_PATH,
LOGGING=1,
SRTP_RELATIVE_PATH,
FEATURE_ENABLE_SSL,
FEATURE_ENABLE_VOICEMAIL,
FEATURE_ENABLE_PSTN,
HAVE_SCTP,
HAVE_SRTP,
are used by the talk/media code.
For Chromium, the following changes will need to be applied to the roll CL that updates the
DEPS for WebRTC and libjingle: https://codereview.chromium.org/1604303002/
BUG=webrtc:5420
NOPRESUBMIT=True
TBR=tommi@webrtc.org
Review URL: https://codereview.webrtc.org/1587193006
Cr-Commit-Position: refs/heads/master@{#11495}
2016-02-04 23:52:28 -08:00
|
|
|
#include "webrtc/media/base/streamparams.h"
|
2013-07-10 00:45:36 +00:00
|
|
|
|
2016-05-19 19:58:38 +02:00
|
|
|
namespace cricket {
|
2016-08-24 16:26:55 -07:00
|
|
|
// The biggest SCTP packet. Starting from a 'safe' wire MTU value of 1280,
|
2016-05-19 19:58:38 +02:00
|
|
|
// take off 80 bytes for DTLS/TURN/TCP/IP overhead.
|
2016-08-24 16:26:55 -07:00
|
|
|
static constexpr size_t kSctpMtu = 1200;
|
2016-05-19 19:58:38 +02:00
|
|
|
|
|
|
|
|
// The size of the SCTP association send buffer. 256kB, the usrsctp default.
|
2016-08-24 16:26:55 -07:00
|
|
|
static constexpr int kSendBufferSize = 262144;
|
2016-05-19 19:58:38 +02:00
|
|
|
|
|
|
|
|
struct SctpInboundPacket {
|
|
|
|
|
rtc::CopyOnWriteBuffer buffer;
|
|
|
|
|
ReceiveDataParams params;
|
|
|
|
|
// The |flags| parameter is used by SCTP to distinguish notification packets
|
|
|
|
|
// from other types of packets.
|
|
|
|
|
int flags;
|
|
|
|
|
};
|
|
|
|
|
|
2014-01-03 22:08:47 +00:00
|
|
|
namespace {
|
2016-05-19 19:58:38 +02:00
|
|
|
// Set the initial value of the static SCTP Data Engines reference count.
|
|
|
|
|
int g_usrsctp_usage_count = 0;
|
|
|
|
|
rtc::GlobalLockPod g_usrsctp_lock_;
|
|
|
|
|
|
|
|
|
|
typedef SctpDataMediaChannel::StreamSet StreamSet;
|
|
|
|
|
|
2014-01-03 22:08:47 +00:00
|
|
|
// Returns a comma-separated, human-readable list of the stream IDs in 's'
|
|
|
|
|
std::string ListStreams(const StreamSet& s) {
|
|
|
|
|
std::stringstream result;
|
|
|
|
|
bool first = true;
|
2014-01-07 19:32:40 +00:00
|
|
|
for (StreamSet::const_iterator it = s.begin(); it != s.end(); ++it) {
|
2014-01-03 22:08:47 +00:00
|
|
|
if (!first) {
|
|
|
|
|
result << ", " << *it;
|
|
|
|
|
} else {
|
|
|
|
|
result << *it;
|
|
|
|
|
first = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result.str();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns a pipe-separated, human-readable list of the SCTP_STREAM_RESET
|
|
|
|
|
// flags in 'flags'
|
|
|
|
|
std::string ListFlags(int flags) {
|
|
|
|
|
std::stringstream result;
|
|
|
|
|
bool first = true;
|
|
|
|
|
// Skip past the first 12 chars (strlen("SCTP_STREAM_"))
|
|
|
|
|
#define MAKEFLAG(X) { X, #X + 12}
|
|
|
|
|
struct flaginfo_t {
|
|
|
|
|
int value;
|
|
|
|
|
const char* name;
|
|
|
|
|
} flaginfo[] = {
|
|
|
|
|
MAKEFLAG(SCTP_STREAM_RESET_INCOMING_SSN),
|
|
|
|
|
MAKEFLAG(SCTP_STREAM_RESET_OUTGOING_SSN),
|
|
|
|
|
MAKEFLAG(SCTP_STREAM_RESET_DENIED),
|
|
|
|
|
MAKEFLAG(SCTP_STREAM_RESET_FAILED),
|
|
|
|
|
MAKEFLAG(SCTP_STREAM_CHANGE_DENIED)
|
|
|
|
|
};
|
|
|
|
|
#undef MAKEFLAG
|
Move talk/media to webrtc/media
I removed the 'libjingle' target in talk/libjingle.gyp and replaced
all users of it with base/base.gyp:rtc_base. It seems the jsoncpp
and expat dependencies were not used by it's previous references.
The files in talk/media/testdata were uploaded to Google Storage and
added .sha1 files in resources/media instead of simply moving them.
The previously disabled warnings that were inherited from
talk/build/common.gypi are now replaced by target-specific disabling
of only the failing warnings. Additional disabling was needed since the stricter
compilation warnings that applies to code in webrtc/.
License headers will be updated in a follow-up CL in order to not
break Git history.
Other modifications:
* Updated the header guards.
* Sorted the includes using chromium/src/tools/sort-headers.py
except for these files:
talk/app/webrtc/peerconnectionendtoend_unittest.cc
talk/app/webrtc/java/jni/androidmediadecoder_jni.cc
talk/app/webrtc/java/jni/androidmediaencoder_jni.cc
webrtc/media/devices/win32devicemanager.cc.
* Unused GYP reference to libjingle_tests_additional_deps was removed.
* Removed duplicated GYP entries of
webrtc/base/testutils.cc
webrtc/base/testutils.h
The HAVE_WEBRTC_VIDEO and HAVE_WEBRTC_VOICE defines were used by only talk/media,
so they were moved to the media.gyp.
I also checked that none of
EXPAT_RELATIVE_PATH,
FEATURE_ENABLE_VOICEMAIL,
GTEST_RELATIVE_PATH,
JSONCPP_RELATIVE_PATH,
LOGGING=1,
SRTP_RELATIVE_PATH,
FEATURE_ENABLE_SSL,
FEATURE_ENABLE_VOICEMAIL,
FEATURE_ENABLE_PSTN,
HAVE_SCTP,
HAVE_SRTP,
are used by the talk/media code.
For Chromium, the following changes will need to be applied to the roll CL that updates the
DEPS for WebRTC and libjingle: https://codereview.chromium.org/1604303002/
BUG=webrtc:5420
NOPRESUBMIT=True
TBR=tommi@webrtc.org
Review URL: https://codereview.webrtc.org/1587193006
Cr-Commit-Position: refs/heads/master@{#11495}
2016-02-04 23:52:28 -08:00
|
|
|
for (uint32_t i = 0; i < arraysize(flaginfo); ++i) {
|
2014-01-03 22:08:47 +00:00
|
|
|
if (flags & flaginfo[i].value) {
|
|
|
|
|
if (!first) result << " | ";
|
|
|
|
|
result << flaginfo[i].name;
|
|
|
|
|
first = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result.str();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns a comma-separated, human-readable list of the integers in 'array'.
|
|
|
|
|
// All 'num_elems' of them.
|
Use suffixed {uint,int}{8,16,32,64}_t types.
Removes the use of uint8, etc. in favor of uint8_t.
BUG=webrtc:5024
R=henrik.lundin@webrtc.org, henrikg@webrtc.org, perkj@webrtc.org, solenberg@webrtc.org, stefan@webrtc.org, tina.legrand@webrtc.org
Review URL: https://codereview.webrtc.org/1362503003 .
Cr-Commit-Position: refs/heads/master@{#10196}
2015-10-07 12:23:21 +02:00
|
|
|
std::string ListArray(const uint16_t* array, int num_elems) {
|
2014-01-03 22:08:47 +00:00
|
|
|
std::stringstream result;
|
|
|
|
|
for (int i = 0; i < num_elems; ++i) {
|
|
|
|
|
if (i) {
|
|
|
|
|
result << ", " << array[i];
|
|
|
|
|
} else {
|
|
|
|
|
result << array[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result.str();
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-29 17:36:52 +00:00
|
|
|
typedef rtc::ScopedMessageData<SctpInboundPacket> InboundPacketMessage;
|
2016-03-20 06:15:43 -07:00
|
|
|
typedef rtc::ScopedMessageData<rtc::CopyOnWriteBuffer> OutboundPacketMessage;
|
2013-07-10 00:45:36 +00:00
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
MSG_SCTPINBOUNDPACKET = 1, // MessageData is SctpInboundPacket
|
2014-07-29 17:36:52 +00:00
|
|
|
MSG_SCTPOUTBOUNDPACKET = 2, // MessageData is rtc:Buffer
|
2013-07-10 00:45:36 +00:00
|
|
|
};
|
|
|
|
|
|
2013-09-23 20:34:45 +00:00
|
|
|
// Helper for logging SCTP messages.
|
2016-05-19 19:58:38 +02:00
|
|
|
void DebugSctpPrintf(const char* format, ...) {
|
2016-10-04 13:46:56 -07:00
|
|
|
#if RTC_DCHECK_IS_ON
|
2013-07-10 00:45:36 +00:00
|
|
|
char s[255];
|
|
|
|
|
va_list ap;
|
|
|
|
|
va_start(ap, format);
|
|
|
|
|
vsnprintf(s, sizeof(s), format, ap);
|
2013-09-23 20:34:45 +00:00
|
|
|
LOG(LS_INFO) << "SCTP: " << s;
|
2013-07-10 00:45:36 +00:00
|
|
|
va_end(ap);
|
2016-05-19 19:58:38 +02:00
|
|
|
#endif
|
2013-07-10 00:45:36 +00:00
|
|
|
}
|
|
|
|
|
|
2013-09-23 20:34:45 +00:00
|
|
|
// Get the PPID to use for the terminating fragment of this type.
|
2016-05-19 19:58:38 +02:00
|
|
|
SctpDataMediaChannel::PayloadProtocolIdentifier GetPpid(DataMessageType type) {
|
2013-09-23 20:34:45 +00:00
|
|
|
switch (type) {
|
|
|
|
|
default:
|
2016-05-19 19:58:38 +02:00
|
|
|
case DMT_NONE:
|
2013-09-23 20:34:45 +00:00
|
|
|
return SctpDataMediaChannel::PPID_NONE;
|
2016-05-19 19:58:38 +02:00
|
|
|
case DMT_CONTROL:
|
2013-09-23 20:34:45 +00:00
|
|
|
return SctpDataMediaChannel::PPID_CONTROL;
|
2016-05-19 19:58:38 +02:00
|
|
|
case DMT_BINARY:
|
2013-09-23 20:34:45 +00:00
|
|
|
return SctpDataMediaChannel::PPID_BINARY_LAST;
|
2016-05-19 19:58:38 +02:00
|
|
|
case DMT_TEXT:
|
2013-09-23 20:34:45 +00:00
|
|
|
return SctpDataMediaChannel::PPID_TEXT_LAST;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-19 19:58:38 +02:00
|
|
|
bool GetDataMediaType(SctpDataMediaChannel::PayloadProtocolIdentifier ppid,
|
|
|
|
|
DataMessageType* dest) {
|
2013-09-23 20:34:45 +00:00
|
|
|
ASSERT(dest != NULL);
|
|
|
|
|
switch (ppid) {
|
|
|
|
|
case SctpDataMediaChannel::PPID_BINARY_PARTIAL:
|
|
|
|
|
case SctpDataMediaChannel::PPID_BINARY_LAST:
|
2016-05-19 19:58:38 +02:00
|
|
|
*dest = DMT_BINARY;
|
2013-09-23 20:34:45 +00:00
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
case SctpDataMediaChannel::PPID_TEXT_PARTIAL:
|
|
|
|
|
case SctpDataMediaChannel::PPID_TEXT_LAST:
|
2016-05-19 19:58:38 +02:00
|
|
|
*dest = DMT_TEXT;
|
2013-09-23 20:34:45 +00:00
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
case SctpDataMediaChannel::PPID_CONTROL:
|
2016-05-19 19:58:38 +02:00
|
|
|
*dest = DMT_CONTROL;
|
2013-09-23 20:34:45 +00:00
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
case SctpDataMediaChannel::PPID_NONE:
|
2016-05-19 19:58:38 +02:00
|
|
|
*dest = DMT_NONE;
|
2013-09-23 20:34:45 +00:00
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
2013-07-10 00:45:36 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-08 14:39:04 -04:00
|
|
|
// Log the packet in text2pcap format, if log level is at LS_VERBOSE.
|
2016-05-19 19:58:38 +02:00
|
|
|
void VerboseLogPacket(const void* data, size_t length, int direction) {
|
2015-05-08 14:39:04 -04:00
|
|
|
if (LOG_CHECK_LEVEL(LS_VERBOSE) && length > 0) {
|
|
|
|
|
char *dump_buf;
|
2016-03-20 06:15:43 -07:00
|
|
|
// 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.
|
2015-05-08 14:39:04 -04:00
|
|
|
if ((dump_buf = usrsctp_dumppacket(
|
2016-03-20 06:15:43 -07:00
|
|
|
const_cast<void*>(data), length, direction)) != NULL) {
|
2015-05-08 14:39:04 -04:00
|
|
|
LOG(LS_VERBOSE) << dump_buf;
|
|
|
|
|
usrsctp_freedumpbuffer(dump_buf);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-10 00:45:36 +00:00
|
|
|
// This is the callback usrsctp uses when there's data to send on the network
|
|
|
|
|
// that has been wrapped appropriatly for the SCTP protocol.
|
2016-05-19 19:58:38 +02:00
|
|
|
int OnSctpOutboundPacket(void* addr,
|
|
|
|
|
void* data,
|
|
|
|
|
size_t length,
|
|
|
|
|
uint8_t tos,
|
|
|
|
|
uint8_t set_df) {
|
2013-07-10 00:45:36 +00:00
|
|
|
SctpDataMediaChannel* channel = static_cast<SctpDataMediaChannel*>(addr);
|
|
|
|
|
LOG(LS_VERBOSE) << "global OnSctpOutboundPacket():"
|
|
|
|
|
<< "addr: " << addr << "; length: " << length
|
|
|
|
|
<< "; tos: " << std::hex << static_cast<int>(tos)
|
2013-09-23 20:34:45 +00:00
|
|
|
<< "; set_df: " << std::hex << static_cast<int>(set_df);
|
2015-05-08 14:39:04 -04:00
|
|
|
|
2016-06-13 17:30:32 -07:00
|
|
|
VerboseLogPacket(data, length, SCTP_DUMP_OUTBOUND);
|
2013-07-10 00:45:36 +00:00
|
|
|
// Note: We have to copy the data; the caller will delete it.
|
rtc::Buffer improvements
1. Constructors, SetData(), and AppendData() now accept uint8_t*,
int8_t*, and char*. Previously, they accepted void*, meaning that
any kind of pointer was accepted. I think requiring an explicit
cast in cases where the input array isn't already of a byte-sized
type is a better compromise between convenience and safety.
2. data() can now return a uint8_t* instead of a char*, which seems
more appropriate for a byte array, and is harder to mix up with
zero-terminated C strings. data<int8_t>() is also available so
that callers that want that type instead won't have to cast, as
is data<char>() (which remains the default until all existing
callers have been fixed).
3. Constructors, SetData(), and AppendData() now accept arrays
natively, not just decayed to pointers. The advantage of this is
that callers don't have to pass the size separately.
4. There are new constructors that allow setting size and capacity
without initializing the array. Previously, this had to be done
separately after construction.
5. Instead of TransferTo(), Buffer now supports swap(), and move
construction and assignment, and has a Pass() method that works
just like std::move(). (The Pass method is modeled after
scoped_ptr::Pass().)
R=jmarusic@webrtc.org, tommi@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/42989004
Cr-Commit-Position: refs/heads/master@{#9033}
2015-04-20 14:03:07 +02:00
|
|
|
auto* msg = new OutboundPacketMessage(
|
2016-03-20 06:15:43 -07:00
|
|
|
new rtc::CopyOnWriteBuffer(reinterpret_cast<uint8_t*>(data), length));
|
2016-06-10 14:17:27 -07:00
|
|
|
channel->worker_thread()->Post(RTC_FROM_HERE, channel, MSG_SCTPOUTBOUNDPACKET,
|
|
|
|
|
msg);
|
2013-07-10 00:45:36 +00:00
|
|
|
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'.
|
2016-05-19 19:58:38 +02:00
|
|
|
int OnSctpInboundPacket(struct socket* sock,
|
|
|
|
|
union sctp_sockstore addr,
|
|
|
|
|
void* data,
|
|
|
|
|
size_t length,
|
|
|
|
|
struct sctp_rcvinfo rcv,
|
|
|
|
|
int flags,
|
|
|
|
|
void* ulp_info) {
|
2013-07-10 00:45:36 +00:00
|
|
|
SctpDataMediaChannel* channel = static_cast<SctpDataMediaChannel*>(ulp_info);
|
|
|
|
|
// Post data to the channel's receiver thread (copying it).
|
|
|
|
|
// TODO(ldixon): Unclear if copy is needed as this method is responsible for
|
|
|
|
|
// memory cleanup. But this does simplify code.
|
2013-09-23 20:34:45 +00:00
|
|
|
const SctpDataMediaChannel::PayloadProtocolIdentifier ppid =
|
|
|
|
|
static_cast<SctpDataMediaChannel::PayloadProtocolIdentifier>(
|
2014-07-29 17:36:52 +00:00
|
|
|
rtc::HostToNetwork32(rcv.rcv_ppid));
|
2016-05-19 19:58:38 +02:00
|
|
|
DataMessageType type = DMT_NONE;
|
2013-09-23 20:34:45 +00:00
|
|
|
if (!GetDataMediaType(ppid, &type) && !(flags & MSG_NOTIFICATION)) {
|
|
|
|
|
// It's neither a notification nor a recognized data packet. Drop it.
|
|
|
|
|
LOG(LS_ERROR) << "Received an unknown PPID " << ppid
|
|
|
|
|
<< " on an SCTP packet. Dropping.";
|
|
|
|
|
} else {
|
|
|
|
|
SctpInboundPacket* packet = new SctpInboundPacket;
|
rtc::Buffer improvements
1. Constructors, SetData(), and AppendData() now accept uint8_t*,
int8_t*, and char*. Previously, they accepted void*, meaning that
any kind of pointer was accepted. I think requiring an explicit
cast in cases where the input array isn't already of a byte-sized
type is a better compromise between convenience and safety.
2. data() can now return a uint8_t* instead of a char*, which seems
more appropriate for a byte array, and is harder to mix up with
zero-terminated C strings. data<int8_t>() is also available so
that callers that want that type instead won't have to cast, as
is data<char>() (which remains the default until all existing
callers have been fixed).
3. Constructors, SetData(), and AppendData() now accept arrays
natively, not just decayed to pointers. The advantage of this is
that callers don't have to pass the size separately.
4. There are new constructors that allow setting size and capacity
without initializing the array. Previously, this had to be done
separately after construction.
5. Instead of TransferTo(), Buffer now supports swap(), and move
construction and assignment, and has a Pass() method that works
just like std::move(). (The Pass method is modeled after
scoped_ptr::Pass().)
R=jmarusic@webrtc.org, tommi@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/42989004
Cr-Commit-Position: refs/heads/master@{#9033}
2015-04-20 14:03:07 +02:00
|
|
|
packet->buffer.SetData(reinterpret_cast<uint8_t*>(data), length);
|
2013-09-23 20:34:45 +00:00
|
|
|
packet->params.ssrc = rcv.rcv_sid;
|
|
|
|
|
packet->params.seq_num = rcv.rcv_ssn;
|
|
|
|
|
packet->params.timestamp = rcv.rcv_tsn;
|
|
|
|
|
packet->params.type = type;
|
|
|
|
|
packet->flags = flags;
|
2014-01-03 22:08:47 +00:00
|
|
|
// The ownership of |packet| transfers to |msg|.
|
|
|
|
|
InboundPacketMessage* msg = new InboundPacketMessage(packet);
|
2016-06-10 14:17:27 -07:00
|
|
|
channel->worker_thread()->Post(RTC_FROM_HERE, channel,
|
|
|
|
|
MSG_SCTPINBOUNDPACKET, msg);
|
2013-09-23 20:34:45 +00:00
|
|
|
}
|
2013-07-10 00:45:36 +00:00
|
|
|
free(data);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-19 19:58:38 +02:00
|
|
|
void InitializeUsrSctp() {
|
|
|
|
|
LOG(LS_INFO) << __FUNCTION__;
|
|
|
|
|
// First argument is udp_encapsulation_port, which is not releveant for our
|
|
|
|
|
// AF_CONN use of sctp.
|
|
|
|
|
usrsctp_init(0, &OnSctpOutboundPacket, &DebugSctpPrintf);
|
2015-08-28 14:54:37 -04:00
|
|
|
|
2016-05-19 19:58:38 +02:00
|
|
|
// To turn on/off detailed SCTP debugging. You will also need to have the
|
|
|
|
|
// SCTP_DEBUG cpp defines flag.
|
|
|
|
|
// usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_ALL);
|
2013-07-10 00:45:36 +00:00
|
|
|
|
2016-05-19 19:58:38 +02:00
|
|
|
// TODO(ldixon): Consider turning this on/off.
|
|
|
|
|
usrsctp_sysctl_set_sctp_ecn_enable(0);
|
2014-06-18 21:30:40 +00:00
|
|
|
|
2016-05-19 19:58:38 +02:00
|
|
|
// This is harmless, but we should find out when the library default
|
|
|
|
|
// changes.
|
|
|
|
|
int send_size = usrsctp_sysctl_get_sctp_sendspace();
|
|
|
|
|
if (send_size != kSendBufferSize) {
|
|
|
|
|
LOG(LS_ERROR) << "Got different send size than expected: " << send_size;
|
|
|
|
|
}
|
2014-06-18 21:30:40 +00:00
|
|
|
|
2016-05-19 19:58:38 +02:00
|
|
|
// 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);
|
|
|
|
|
|
2016-08-24 13:15:27 -07:00
|
|
|
// 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);
|
2016-05-19 19:58:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UninitializeUsrSctp() {
|
|
|
|
|
LOG(LS_INFO) << __FUNCTION__;
|
|
|
|
|
// usrsctp_finish() may fail if it's called too soon after the channels 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) {
|
|
|
|
|
return;
|
2014-06-18 21:30:40 +00:00
|
|
|
}
|
2016-05-19 19:58:38 +02:00
|
|
|
|
|
|
|
|
rtc::Thread::SleepMs(10);
|
|
|
|
|
}
|
|
|
|
|
LOG(LS_ERROR) << "Failed to shutdown usrsctp.";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void IncrementUsrSctpUsageCount() {
|
|
|
|
|
rtc::GlobalLockScope lock(&g_usrsctp_lock_);
|
|
|
|
|
if (!g_usrsctp_usage_count) {
|
|
|
|
|
InitializeUsrSctp();
|
|
|
|
|
}
|
|
|
|
|
++g_usrsctp_usage_count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DecrementUsrSctpUsageCount() {
|
|
|
|
|
rtc::GlobalLockScope lock(&g_usrsctp_lock_);
|
|
|
|
|
--g_usrsctp_usage_count;
|
|
|
|
|
if (!g_usrsctp_usage_count) {
|
|
|
|
|
UninitializeUsrSctp();
|
2014-06-18 21:30:40 +00:00
|
|
|
}
|
2013-07-10 00:45:36 +00:00
|
|
|
}
|
|
|
|
|
|
2016-05-19 19:58:38 +02:00
|
|
|
DataCodec GetSctpDataCodec() {
|
2016-10-08 13:02:44 -07:00
|
|
|
DataCodec codec(kGoogleSctpDataCodecPlType, kGoogleSctpDataCodecName);
|
2016-05-19 19:58:38 +02:00
|
|
|
codec.SetParam(kCodecParamPort, kSctpDefaultPort);
|
|
|
|
|
return codec;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
SctpDataEngine::SctpDataEngine() : codecs_(1, GetSctpDataCodec()) {}
|
|
|
|
|
|
|
|
|
|
SctpDataEngine::~SctpDataEngine() {}
|
|
|
|
|
|
|
|
|
|
// Called on the worker thread.
|
2013-07-10 00:45:36 +00:00
|
|
|
DataMediaChannel* SctpDataEngine::CreateChannel(
|
|
|
|
|
DataChannelType data_channel_type) {
|
|
|
|
|
if (data_channel_type != DCT_SCTP) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2015-08-27 04:29:58 -07:00
|
|
|
return new SctpDataMediaChannel(rtc::Thread::Current());
|
2013-07-10 00:45:36 +00:00
|
|
|
}
|
|
|
|
|
|
2015-08-28 14:54:37 -04:00
|
|
|
// static
|
2016-05-19 19:58:38 +02:00
|
|
|
SctpDataMediaChannel* SctpDataMediaChannel::GetChannelFromSocket(
|
2015-08-28 14:54:37 -04:00
|
|
|
struct socket* sock) {
|
|
|
|
|
struct sockaddr* addrs = nullptr;
|
|
|
|
|
int naddrs = usrsctp_getladdrs(sock, 0, &addrs);
|
|
|
|
|
if (naddrs <= 0 || addrs[0].sa_family != AF_CONN) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
// usrsctp_getladdrs() returns the addresses bound to this socket, which
|
|
|
|
|
// contains the SctpDataMediaChannel* as sconn_addr. Read the pointer,
|
|
|
|
|
// then free the list of addresses once we have the pointer. We only open
|
|
|
|
|
// AF_CONN sockets, and they should all have the sconn_addr set to the
|
|
|
|
|
// pointer that created them, so [0] is as good as any other.
|
|
|
|
|
struct sockaddr_conn* sconn =
|
|
|
|
|
reinterpret_cast<struct sockaddr_conn*>(&addrs[0]);
|
|
|
|
|
SctpDataMediaChannel* channel =
|
|
|
|
|
reinterpret_cast<SctpDataMediaChannel*>(sconn->sconn_addr);
|
|
|
|
|
usrsctp_freeladdrs(addrs);
|
|
|
|
|
|
|
|
|
|
return channel;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// static
|
2016-05-19 19:58:38 +02:00
|
|
|
int SctpDataMediaChannel::SendThresholdCallback(struct socket* sock,
|
|
|
|
|
uint32_t sb_free) {
|
2015-08-28 14:54:37 -04:00
|
|
|
// Fired on our I/O thread. SctpDataMediaChannel::OnPacketReceived() gets
|
|
|
|
|
// a packet containing acknowledgments, which goes into usrsctp_conninput,
|
|
|
|
|
// and then back here.
|
|
|
|
|
SctpDataMediaChannel* channel = GetChannelFromSocket(sock);
|
|
|
|
|
if (!channel) {
|
|
|
|
|
LOG(LS_ERROR) << "SendThresholdCallback: Failed to get channel for socket "
|
|
|
|
|
<< sock;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
channel->OnSendThresholdCallback();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-29 17:36:52 +00:00
|
|
|
SctpDataMediaChannel::SctpDataMediaChannel(rtc::Thread* thread)
|
2013-07-10 00:45:36 +00:00
|
|
|
: worker_thread_(thread),
|
2014-05-01 18:30:30 +00:00
|
|
|
local_port_(kSctpDefaultPort),
|
|
|
|
|
remote_port_(kSctpDefaultPort),
|
2013-07-10 00:45:36 +00:00
|
|
|
sock_(NULL),
|
|
|
|
|
sending_(false),
|
|
|
|
|
receiving_(false),
|
|
|
|
|
debug_name_("SctpDataMediaChannel") {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SctpDataMediaChannel::~SctpDataMediaChannel() {
|
|
|
|
|
CloseSctpSocket();
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-28 14:54:37 -04:00
|
|
|
void SctpDataMediaChannel::OnSendThresholdCallback() {
|
2015-09-17 00:24:34 -07:00
|
|
|
RTC_DCHECK(rtc::Thread::Current() == worker_thread_);
|
2015-08-28 14:54:37 -04:00
|
|
|
SignalReadyToSend(true);
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-10 00:45:36 +00:00
|
|
|
sockaddr_conn SctpDataMediaChannel::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.
|
2014-07-29 17:36:52 +00:00
|
|
|
sconn.sconn_port = rtc::HostToNetwork16(port);
|
2013-07-10 00:45:36 +00:00
|
|
|
sconn.sconn_addr = this;
|
|
|
|
|
return sconn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SctpDataMediaChannel::OpenSctpSocket() {
|
|
|
|
|
if (sock_) {
|
|
|
|
|
LOG(LS_VERBOSE) << debug_name_
|
|
|
|
|
<< "->Ignoring attempt to re-create existing socket.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2015-08-28 14:54:37 -04:00
|
|
|
|
2016-05-19 19:58:38 +02:00
|
|
|
IncrementUsrSctpUsageCount();
|
|
|
|
|
|
2015-08-28 14:54:37 -04:00
|
|
|
// If kSendBufferSize 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.
|
|
|
|
|
const static int kSendThreshold = usrsctp_sysctl_get_sctp_sendspace() / 2;
|
|
|
|
|
|
2016-05-19 19:58:38 +02:00
|
|
|
sock_ = usrsctp_socket(
|
|
|
|
|
AF_CONN, SOCK_STREAM, IPPROTO_SCTP, OnSctpInboundPacket,
|
|
|
|
|
&SctpDataMediaChannel::SendThresholdCallback, kSendThreshold, this);
|
2013-07-10 00:45:36 +00:00
|
|
|
if (!sock_) {
|
|
|
|
|
LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to create SCTP socket.";
|
2016-05-19 19:58:38 +02:00
|
|
|
DecrementUsrSctpUsageCount();
|
2013-07-10 00:45:36 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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) {
|
|
|
|
|
LOG_ERRNO(LS_ERROR) << debug_name_ << "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))) {
|
|
|
|
|
LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SO_LINGER.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-03 22:08:47 +00:00
|
|
|
// 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))) {
|
|
|
|
|
LOG_ERRNO(LS_ERROR) << debug_name_
|
|
|
|
|
<< "Failed to set SCTP_ENABLE_STREAM_RESET.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Nagle.
|
2013-09-13 23:48:58 +00:00
|
|
|
uint32_t nodelay = 1;
|
|
|
|
|
if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_NODELAY, &nodelay,
|
|
|
|
|
sizeof(nodelay))) {
|
|
|
|
|
LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP_NODELAY.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-10 00:45:36 +00:00
|
|
|
// Subscribe to SCTP event notifications.
|
|
|
|
|
int event_types[] = {SCTP_ASSOC_CHANGE,
|
|
|
|
|
SCTP_PEER_ADDR_CHANGE,
|
2013-08-01 00:00:07 +00:00
|
|
|
SCTP_SEND_FAILED_EVENT,
|
2014-01-03 22:08:47 +00:00
|
|
|
SCTP_SENDER_DRY_EVENT,
|
|
|
|
|
SCTP_STREAM_RESET_EVENT};
|
2013-07-10 00:45:36 +00:00
|
|
|
struct sctp_event event = {0};
|
|
|
|
|
event.se_assoc_id = SCTP_ALL_ASSOC;
|
|
|
|
|
event.se_on = 1;
|
2015-11-10 23:44:30 -08:00
|
|
|
for (size_t i = 0; i < arraysize(event_types); i++) {
|
2013-07-10 00:45:36 +00:00
|
|
|
event.se_type = event_types[i];
|
|
|
|
|
if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_EVENT, &event,
|
|
|
|
|
sizeof(event)) < 0) {
|
|
|
|
|
LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP_EVENT type: "
|
|
|
|
|
<< event.se_type;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Register this class 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(this);
|
|
|
|
|
sending_ = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SctpDataMediaChannel::CloseSctpSocket() {
|
|
|
|
|
sending_ = false;
|
|
|
|
|
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_ = NULL;
|
|
|
|
|
usrsctp_deregister_address(this);
|
2016-05-19 19:58:38 +02:00
|
|
|
|
|
|
|
|
DecrementUsrSctpUsageCount();
|
2013-07-10 00:45:36 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SctpDataMediaChannel::Connect() {
|
|
|
|
|
LOG(LS_VERBOSE) << debug_name_ << "->Connect().";
|
|
|
|
|
|
|
|
|
|
// If we already have a socket connection, just return.
|
|
|
|
|
if (sock_) {
|
|
|
|
|
LOG(LS_WARNING) << 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 (!sock_ && !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) {
|
|
|
|
|
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));
|
2013-07-22 21:07:49 +00:00
|
|
|
if (connect_result < 0 && errno != SCTP_EINPROGRESS) {
|
|
|
|
|
LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed usrsctp_connect. got errno="
|
|
|
|
|
<< errno << ", but wanted " << SCTP_EINPROGRESS;
|
2013-07-10 00:45:36 +00:00
|
|
|
CloseSctpSocket();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2016-08-24 16:26:55 -07:00
|
|
|
// Set the MTU and disable MTU discovery.
|
|
|
|
|
// We can only do this after usrsctp_connect or it has no effect.
|
|
|
|
|
sctp_paddrparams params = {{0}};
|
2016-09-07 10:05:21 -07:00
|
|
|
memcpy(¶ms.spp_address, &remote_sconn, sizeof(remote_sconn));
|
2016-08-24 16:26:55 -07:00
|
|
|
params.spp_flags = SPP_PMTUD_DISABLE;
|
|
|
|
|
params.spp_pathmtu = kSctpMtu;
|
|
|
|
|
if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, ¶ms,
|
|
|
|
|
sizeof(params))) {
|
|
|
|
|
LOG_ERRNO(LS_ERROR) << debug_name_
|
|
|
|
|
<< "Failed to set SCTP_PEER_ADDR_PARAMS.";
|
|
|
|
|
}
|
2013-07-10 00:45:36 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SctpDataMediaChannel::Disconnect() {
|
|
|
|
|
// TODO(ldixon): Consider calling |usrsctp_shutdown(sock_, ...)| to do a
|
|
|
|
|
// shutdown handshake and remove the association.
|
|
|
|
|
CloseSctpSocket();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SctpDataMediaChannel::SetSend(bool send) {
|
|
|
|
|
if (!sending_ && send) {
|
|
|
|
|
return Connect();
|
|
|
|
|
}
|
|
|
|
|
if (sending_ && !send) {
|
|
|
|
|
Disconnect();
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SctpDataMediaChannel::SetReceive(bool receive) {
|
|
|
|
|
receiving_ = receive;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-17 16:42:56 +02:00
|
|
|
bool SctpDataMediaChannel::SetSendParameters(const DataSendParameters& params) {
|
|
|
|
|
return SetSendCodecs(params.codecs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SctpDataMediaChannel::SetRecvParameters(const DataRecvParameters& params) {
|
|
|
|
|
return SetRecvCodecs(params.codecs);
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-10 00:45:36 +00:00
|
|
|
bool SctpDataMediaChannel::AddSendStream(const StreamParams& stream) {
|
2014-01-03 22:08:47 +00:00
|
|
|
return AddStream(stream);
|
2013-07-10 00:45:36 +00:00
|
|
|
}
|
|
|
|
|
|
Use suffixed {uint,int}{8,16,32,64}_t types.
Removes the use of uint8, etc. in favor of uint8_t.
BUG=webrtc:5024
R=henrik.lundin@webrtc.org, henrikg@webrtc.org, perkj@webrtc.org, solenberg@webrtc.org, stefan@webrtc.org, tina.legrand@webrtc.org
Review URL: https://codereview.webrtc.org/1362503003 .
Cr-Commit-Position: refs/heads/master@{#10196}
2015-10-07 12:23:21 +02:00
|
|
|
bool SctpDataMediaChannel::RemoveSendStream(uint32_t ssrc) {
|
2014-01-03 22:08:47 +00:00
|
|
|
return ResetStream(ssrc);
|
2013-07-10 00:45:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SctpDataMediaChannel::AddRecvStream(const StreamParams& stream) {
|
2014-01-14 10:00:58 +00:00
|
|
|
// SCTP DataChannels are always bi-directional and calling AddSendStream will
|
|
|
|
|
// enable both sending and receiving on the stream. So AddRecvStream is a
|
|
|
|
|
// no-op.
|
|
|
|
|
return true;
|
2013-07-10 00:45:36 +00:00
|
|
|
}
|
|
|
|
|
|
Use suffixed {uint,int}{8,16,32,64}_t types.
Removes the use of uint8, etc. in favor of uint8_t.
BUG=webrtc:5024
R=henrik.lundin@webrtc.org, henrikg@webrtc.org, perkj@webrtc.org, solenberg@webrtc.org, stefan@webrtc.org, tina.legrand@webrtc.org
Review URL: https://codereview.webrtc.org/1362503003 .
Cr-Commit-Position: refs/heads/master@{#10196}
2015-10-07 12:23:21 +02:00
|
|
|
bool SctpDataMediaChannel::RemoveRecvStream(uint32_t ssrc) {
|
2014-01-14 10:00:58 +00:00
|
|
|
// SCTP DataChannels are always bi-directional and calling RemoveSendStream
|
|
|
|
|
// will disable both sending and receiving on the stream. So RemoveRecvStream
|
|
|
|
|
// is a no-op.
|
|
|
|
|
return true;
|
2013-07-10 00:45:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SctpDataMediaChannel::SendData(
|
|
|
|
|
const SendDataParams& params,
|
2016-03-20 06:15:43 -07:00
|
|
|
const rtc::CopyOnWriteBuffer& payload,
|
2013-07-10 00:45:36 +00:00
|
|
|
SendDataResult* result) {
|
|
|
|
|
if (result) {
|
2013-09-23 20:34:45 +00:00
|
|
|
// Preset |result| to assume an error. If SendData succeeds, we'll
|
|
|
|
|
// overwrite |*result| once more at the end.
|
2013-07-10 00:45:36 +00:00
|
|
|
*result = SDR_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!sending_) {
|
|
|
|
|
LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
|
|
|
|
|
<< "Not sending packet with ssrc=" << params.ssrc
|
2015-03-24 09:19:06 +00:00
|
|
|
<< " len=" << payload.size() << " before SetSend(true).";
|
2013-07-10 00:45:36 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-19 19:58:38 +02:00
|
|
|
if (params.type != DMT_CONTROL &&
|
2014-01-03 22:08:47 +00:00
|
|
|
open_streams_.find(params.ssrc) == open_streams_.end()) {
|
2013-07-10 00:45:36 +00:00
|
|
|
LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
|
|
|
|
|
<< "Not sending data because ssrc is unknown: "
|
|
|
|
|
<< params.ssrc;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Send data using SCTP.
|
2013-09-23 20:34:45 +00:00
|
|
|
ssize_t send_res = 0; // result from usrsctp_sendv.
|
|
|
|
|
struct sctp_sendv_spa spa = {0};
|
|
|
|
|
spa.sendv_flags |= SCTP_SEND_SNDINFO_VALID;
|
|
|
|
|
spa.sendv_sndinfo.snd_sid = params.ssrc;
|
2014-07-29 17:36:52 +00:00
|
|
|
spa.sendv_sndinfo.snd_ppid = rtc::HostToNetwork32(
|
2013-09-23 20:34:45 +00:00
|
|
|
GetPpid(params.type));
|
|
|
|
|
|
|
|
|
|
// Ordered implies reliable.
|
|
|
|
|
if (!params.ordered) {
|
|
|
|
|
spa.sendv_sndinfo.snd_flags |= SCTP_UNORDERED;
|
|
|
|
|
if (params.max_rtx_count >= 0 || params.max_rtx_ms == 0) {
|
|
|
|
|
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;
|
|
|
|
|
} else {
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We don't fragment.
|
2015-03-24 09:19:06 +00:00
|
|
|
send_res = usrsctp_sendv(
|
|
|
|
|
sock_, payload.data(), static_cast<size_t>(payload.size()), NULL, 0, &spa,
|
|
|
|
|
rtc::checked_cast<socklen_t>(sizeof(spa)), SCTP_SENDV_SPA, 0);
|
2013-09-23 20:34:45 +00:00
|
|
|
if (send_res < 0) {
|
2014-05-08 16:02:23 +00:00
|
|
|
if (errno == SCTP_EWOULDBLOCK) {
|
2013-08-01 00:00:07 +00:00
|
|
|
*result = SDR_BLOCK;
|
|
|
|
|
LOG(LS_INFO) << debug_name_ << "->SendData(...): EWOULDBLOCK returned";
|
|
|
|
|
} else {
|
|
|
|
|
LOG_ERRNO(LS_ERROR) << "ERROR:" << debug_name_
|
|
|
|
|
<< "->SendData(...): "
|
|
|
|
|
<< " usrsctp_sendv: ";
|
|
|
|
|
}
|
2013-07-10 00:45:36 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (result) {
|
2013-09-23 20:34:45 +00:00
|
|
|
// Only way out now is success.
|
2013-07-10 00:45:36 +00:00
|
|
|
*result = SDR_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Called by network interface when a packet has been received.
|
2013-12-13 00:21:03 +00:00
|
|
|
void SctpDataMediaChannel::OnPacketReceived(
|
2016-03-20 06:15:43 -07:00
|
|
|
rtc::CopyOnWriteBuffer* packet, const rtc::PacketTime& packet_time) {
|
2015-09-17 00:24:34 -07:00
|
|
|
RTC_DCHECK(rtc::Thread::Current() == worker_thread_);
|
2015-03-24 09:19:06 +00:00
|
|
|
LOG(LS_VERBOSE) << debug_name_ << "->OnPacketReceived(...): "
|
|
|
|
|
<< " length=" << packet->size() << ", sending: " << sending_;
|
2013-07-10 00:45:36 +00:00
|
|
|
// 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 (sending_) {
|
|
|
|
|
// Pass received packet to SCTP stack. Once processed by usrsctp, the data
|
|
|
|
|
// will be will be given to the global OnSctpInboundData, and then,
|
|
|
|
|
// marshalled by a Post and handled with OnMessage.
|
2016-03-20 06:15:43 -07:00
|
|
|
VerboseLogPacket(packet->cdata(), packet->size(), SCTP_DUMP_INBOUND);
|
|
|
|
|
usrsctp_conninput(this, packet->cdata(), packet->size(), 0);
|
2013-07-10 00:45:36 +00:00
|
|
|
} else {
|
|
|
|
|
// TODO(ldixon): Consider caching the packet for very slightly better
|
|
|
|
|
// reliability.
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SctpDataMediaChannel::OnInboundPacketFromSctpToChannel(
|
|
|
|
|
SctpInboundPacket* packet) {
|
|
|
|
|
LOG(LS_VERBOSE) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): "
|
|
|
|
|
<< "Received SCTP data:"
|
|
|
|
|
<< " ssrc=" << packet->params.ssrc
|
|
|
|
|
<< " notification: " << (packet->flags & MSG_NOTIFICATION)
|
2015-03-24 09:19:06 +00:00
|
|
|
<< " length=" << packet->buffer.size();
|
2013-07-10 00:45:36 +00:00
|
|
|
// Sending a packet with data == NULL (no data) is SCTPs "close the
|
|
|
|
|
// connection" message. This sets sock_ = NULL;
|
2015-03-24 09:19:06 +00:00
|
|
|
if (!packet->buffer.size() || !packet->buffer.data()) {
|
2013-07-10 00:45:36 +00:00
|
|
|
LOG(LS_INFO) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): "
|
|
|
|
|
"No data, closing.";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (packet->flags & MSG_NOTIFICATION) {
|
2016-03-20 06:15:43 -07:00
|
|
|
OnNotificationFromSctp(packet->buffer);
|
2013-07-10 00:45:36 +00:00
|
|
|
} else {
|
2016-03-20 06:15:43 -07:00
|
|
|
OnDataFromSctpToChannel(packet->params, packet->buffer);
|
2013-07-10 00:45:36 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SctpDataMediaChannel::OnDataFromSctpToChannel(
|
2016-03-20 06:15:43 -07:00
|
|
|
const ReceiveDataParams& params, const rtc::CopyOnWriteBuffer& buffer) {
|
2013-07-10 00:45:36 +00:00
|
|
|
if (receiving_) {
|
|
|
|
|
LOG(LS_VERBOSE) << debug_name_ << "->OnDataFromSctpToChannel(...): "
|
2016-03-20 06:15:43 -07:00
|
|
|
<< "Posting with length: " << buffer.size()
|
2014-01-14 10:00:58 +00:00
|
|
|
<< " on stream " << params.ssrc;
|
|
|
|
|
// Reports all received messages to upper layers, no matter whether the sid
|
|
|
|
|
// is known.
|
2016-03-20 06:15:43 -07:00
|
|
|
SignalDataReceived(params, buffer.data<char>(), buffer.size());
|
2013-07-10 00:45:36 +00:00
|
|
|
} else {
|
|
|
|
|
LOG(LS_WARNING) << debug_name_ << "->OnDataFromSctpToChannel(...): "
|
|
|
|
|
<< "Not receiving packet with sid=" << params.ssrc
|
2016-03-20 06:15:43 -07:00
|
|
|
<< " len=" << buffer.size() << " before SetReceive(true).";
|
2013-07-10 00:45:36 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-03 22:08:47 +00:00
|
|
|
bool SctpDataMediaChannel::AddStream(const StreamParams& stream) {
|
|
|
|
|
if (!stream.has_ssrcs()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
Use suffixed {uint,int}{8,16,32,64}_t types.
Removes the use of uint8, etc. in favor of uint8_t.
BUG=webrtc:5024
R=henrik.lundin@webrtc.org, henrikg@webrtc.org, perkj@webrtc.org, solenberg@webrtc.org, stefan@webrtc.org, tina.legrand@webrtc.org
Review URL: https://codereview.webrtc.org/1362503003 .
Cr-Commit-Position: refs/heads/master@{#10196}
2015-10-07 12:23:21 +02:00
|
|
|
const uint32_t ssrc = stream.first_ssrc();
|
2016-08-24 13:15:27 -07:00
|
|
|
if (ssrc > kMaxSctpSid) {
|
2016-01-11 10:24:33 -08:00
|
|
|
LOG(LS_WARNING) << debug_name_ << "->Add(Send|Recv)Stream(...): "
|
|
|
|
|
<< "Not adding data stream '" << stream.id
|
2016-08-24 13:15:27 -07:00
|
|
|
<< "' with sid=" << ssrc << " because sid is too high.";
|
2016-01-11 10:24:33 -08:00
|
|
|
return false;
|
|
|
|
|
} else if (open_streams_.find(ssrc) != open_streams_.end()) {
|
2014-01-14 10:00:58 +00:00
|
|
|
LOG(LS_WARNING) << debug_name_ << "->Add(Send|Recv)Stream(...): "
|
2014-01-03 22:08:47 +00:00
|
|
|
<< "Not adding data stream '" << stream.id
|
2016-08-24 13:15:27 -07:00
|
|
|
<< "' with sid=" << ssrc
|
2014-01-03 22:08:47 +00:00
|
|
|
<< " because stream is already open.";
|
|
|
|
|
return false;
|
|
|
|
|
} else if (queued_reset_streams_.find(ssrc) != queued_reset_streams_.end()
|
|
|
|
|
|| sent_reset_streams_.find(ssrc) != sent_reset_streams_.end()) {
|
|
|
|
|
LOG(LS_WARNING) << debug_name_ << "->Add(Send|Recv)Stream(...): "
|
|
|
|
|
<< "Not adding data stream '" << stream.id
|
2016-08-24 13:15:27 -07:00
|
|
|
<< "' with sid=" << ssrc
|
2014-01-03 22:08:47 +00:00
|
|
|
<< " because stream is still closing.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
open_streams_.insert(ssrc);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
Use suffixed {uint,int}{8,16,32,64}_t types.
Removes the use of uint8, etc. in favor of uint8_t.
BUG=webrtc:5024
R=henrik.lundin@webrtc.org, henrikg@webrtc.org, perkj@webrtc.org, solenberg@webrtc.org, stefan@webrtc.org, tina.legrand@webrtc.org
Review URL: https://codereview.webrtc.org/1362503003 .
Cr-Commit-Position: refs/heads/master@{#10196}
2015-10-07 12:23:21 +02:00
|
|
|
bool SctpDataMediaChannel::ResetStream(uint32_t ssrc) {
|
2014-01-03 22:08:47 +00:00
|
|
|
// We typically get this called twice for the same stream, once each for
|
|
|
|
|
// Send and Recv.
|
|
|
|
|
StreamSet::iterator found = open_streams_.find(ssrc);
|
|
|
|
|
|
|
|
|
|
if (found == open_streams_.end()) {
|
|
|
|
|
LOG(LS_VERBOSE) << debug_name_ << "->ResetStream(" << ssrc << "): "
|
|
|
|
|
<< "stream not found.";
|
|
|
|
|
return false;
|
|
|
|
|
} else {
|
|
|
|
|
LOG(LS_VERBOSE) << debug_name_ << "->ResetStream(" << ssrc << "): "
|
|
|
|
|
<< "Removing and queuing RE-CONFIG chunk.";
|
|
|
|
|
open_streams_.erase(found);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SCTP won't let you have more than one stream reset pending at a time, but
|
|
|
|
|
// you can close multiple streams in a single reset. So, we keep an internal
|
|
|
|
|
// queue of streams-to-reset, and send them as one reset message in
|
|
|
|
|
// SendQueuedStreamResets().
|
|
|
|
|
queued_reset_streams_.insert(ssrc);
|
|
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-20 06:15:43 -07:00
|
|
|
void SctpDataMediaChannel::OnNotificationFromSctp(
|
|
|
|
|
const rtc::CopyOnWriteBuffer& buffer) {
|
2013-07-10 00:45:36 +00:00
|
|
|
const sctp_notification& notification =
|
2016-03-20 06:15:43 -07:00
|
|
|
reinterpret_cast<const sctp_notification&>(*buffer.data());
|
|
|
|
|
ASSERT(notification.sn_header.sn_length == buffer.size());
|
2013-07-10 00:45:36 +00:00
|
|
|
|
|
|
|
|
// TODO(ldixon): handle notifications appropriately.
|
|
|
|
|
switch (notification.sn_header.sn_type) {
|
|
|
|
|
case SCTP_ASSOC_CHANGE:
|
|
|
|
|
LOG(LS_VERBOSE) << "SCTP_ASSOC_CHANGE";
|
|
|
|
|
OnNotificationAssocChange(notification.sn_assoc_change);
|
|
|
|
|
break;
|
|
|
|
|
case SCTP_REMOTE_ERROR:
|
|
|
|
|
LOG(LS_INFO) << "SCTP_REMOTE_ERROR";
|
|
|
|
|
break;
|
|
|
|
|
case SCTP_SHUTDOWN_EVENT:
|
|
|
|
|
LOG(LS_INFO) << "SCTP_SHUTDOWN_EVENT";
|
|
|
|
|
break;
|
|
|
|
|
case SCTP_ADAPTATION_INDICATION:
|
2014-01-03 22:08:47 +00:00
|
|
|
LOG(LS_INFO) << "SCTP_ADAPTATION_INDICATION";
|
2013-07-10 00:45:36 +00:00
|
|
|
break;
|
|
|
|
|
case SCTP_PARTIAL_DELIVERY_EVENT:
|
|
|
|
|
LOG(LS_INFO) << "SCTP_PARTIAL_DELIVERY_EVENT";
|
|
|
|
|
break;
|
|
|
|
|
case SCTP_AUTHENTICATION_EVENT:
|
|
|
|
|
LOG(LS_INFO) << "SCTP_AUTHENTICATION_EVENT";
|
|
|
|
|
break;
|
|
|
|
|
case SCTP_SENDER_DRY_EVENT:
|
2014-01-03 22:08:47 +00:00
|
|
|
LOG(LS_VERBOSE) << "SCTP_SENDER_DRY_EVENT";
|
2013-08-01 00:00:07 +00:00
|
|
|
SignalReadyToSend(true);
|
2013-07-10 00:45:36 +00:00
|
|
|
break;
|
|
|
|
|
// TODO(ldixon): Unblock after congestion.
|
|
|
|
|
case SCTP_NOTIFICATIONS_STOPPED_EVENT:
|
|
|
|
|
LOG(LS_INFO) << "SCTP_NOTIFICATIONS_STOPPED_EVENT";
|
|
|
|
|
break;
|
|
|
|
|
case SCTP_SEND_FAILED_EVENT:
|
|
|
|
|
LOG(LS_INFO) << "SCTP_SEND_FAILED_EVENT";
|
|
|
|
|
break;
|
|
|
|
|
case SCTP_STREAM_RESET_EVENT:
|
2014-01-03 22:08:47 +00:00
|
|
|
OnStreamResetEvent(¬ification.sn_strreset_event);
|
2013-07-10 00:45:36 +00:00
|
|
|
break;
|
|
|
|
|
case SCTP_ASSOC_RESET_EVENT:
|
|
|
|
|
LOG(LS_INFO) << "SCTP_ASSOC_RESET_EVENT";
|
|
|
|
|
break;
|
|
|
|
|
case SCTP_STREAM_CHANGE_EVENT:
|
|
|
|
|
LOG(LS_INFO) << "SCTP_STREAM_CHANGE_EVENT";
|
2014-01-03 22:08:47 +00:00
|
|
|
// 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.
|
2013-07-10 00:45:36 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
LOG(LS_WARNING) << "Unknown SCTP event: "
|
|
|
|
|
<< notification.sn_header.sn_type;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SctpDataMediaChannel::OnNotificationAssocChange(
|
|
|
|
|
const sctp_assoc_change& change) {
|
|
|
|
|
switch (change.sac_state) {
|
|
|
|
|
case SCTP_COMM_UP:
|
|
|
|
|
LOG(LS_VERBOSE) << "Association change SCTP_COMM_UP";
|
|
|
|
|
break;
|
|
|
|
|
case SCTP_COMM_LOST:
|
|
|
|
|
LOG(LS_INFO) << "Association change SCTP_COMM_LOST";
|
|
|
|
|
break;
|
|
|
|
|
case SCTP_RESTART:
|
|
|
|
|
LOG(LS_INFO) << "Association change SCTP_RESTART";
|
|
|
|
|
break;
|
|
|
|
|
case SCTP_SHUTDOWN_COMP:
|
|
|
|
|
LOG(LS_INFO) << "Association change SCTP_SHUTDOWN_COMP";
|
|
|
|
|
break;
|
|
|
|
|
case SCTP_CANT_STR_ASSOC:
|
|
|
|
|
LOG(LS_INFO) << "Association change SCTP_CANT_STR_ASSOC";
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
LOG(LS_INFO) << "Association change UNKNOWN";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-03 22:08:47 +00:00
|
|
|
void SctpDataMediaChannel::OnStreamResetEvent(
|
|
|
|
|
const struct sctp_stream_reset_event* evt) {
|
|
|
|
|
// A stream reset always involves two RE-CONFIG chunks for us -- we always
|
|
|
|
|
// simultaneously reset a sid's sequence number in both directions. The
|
|
|
|
|
// requesting side transmits a RE-CONFIG chunk and waits for the peer to send
|
|
|
|
|
// one back. Both sides get this SCTP_STREAM_RESET_EVENT when they receive
|
|
|
|
|
// RE-CONFIGs.
|
|
|
|
|
const int num_ssrcs = (evt->strreset_length - sizeof(*evt)) /
|
|
|
|
|
sizeof(evt->strreset_stream_list[0]);
|
|
|
|
|
LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
|
|
|
|
|
<< "): Flags = 0x"
|
|
|
|
|
<< std::hex << evt->strreset_flags << " ("
|
|
|
|
|
<< ListFlags(evt->strreset_flags) << ")";
|
|
|
|
|
LOG(LS_VERBOSE) << "Assoc = " << evt->strreset_assoc_id << ", Streams = ["
|
|
|
|
|
<< ListArray(evt->strreset_stream_list, num_ssrcs)
|
|
|
|
|
<< "], Open: ["
|
|
|
|
|
<< ListStreams(open_streams_) << "], Q'd: ["
|
|
|
|
|
<< ListStreams(queued_reset_streams_) << "], Sent: ["
|
|
|
|
|
<< ListStreams(sent_reset_streams_) << "]";
|
|
|
|
|
|
|
|
|
|
// If both sides try to reset some streams at the same time (even if they're
|
|
|
|
|
// disjoint sets), we can get reset failures.
|
|
|
|
|
if (evt->strreset_flags & SCTP_STREAM_RESET_FAILED) {
|
|
|
|
|
// OK, just try again. The stream IDs sent over when the RESET_FAILED flag
|
|
|
|
|
// is set seem to be garbage values. Ignore them.
|
|
|
|
|
queued_reset_streams_.insert(
|
|
|
|
|
sent_reset_streams_.begin(),
|
|
|
|
|
sent_reset_streams_.end());
|
|
|
|
|
sent_reset_streams_.clear();
|
|
|
|
|
|
|
|
|
|
} else if (evt->strreset_flags & SCTP_STREAM_RESET_INCOMING_SSN) {
|
|
|
|
|
// Each side gets an event for each direction of a stream. That is,
|
|
|
|
|
// closing sid k will make each side receive INCOMING and OUTGOING reset
|
|
|
|
|
// events for k. As per RFC6525, Section 5, paragraph 2, each side will
|
|
|
|
|
// get an INCOMING event first.
|
|
|
|
|
for (int i = 0; i < num_ssrcs; i++) {
|
|
|
|
|
const int stream_id = evt->strreset_stream_list[i];
|
|
|
|
|
|
|
|
|
|
// See if this stream ID was closed by our peer or ourselves.
|
|
|
|
|
StreamSet::iterator it = sent_reset_streams_.find(stream_id);
|
|
|
|
|
|
|
|
|
|
// The reset was requested locally.
|
|
|
|
|
if (it != sent_reset_streams_.end()) {
|
|
|
|
|
LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
|
|
|
|
|
<< "): local sid " << stream_id << " acknowledged.";
|
|
|
|
|
sent_reset_streams_.erase(it);
|
|
|
|
|
|
|
|
|
|
} else if ((it = open_streams_.find(stream_id))
|
|
|
|
|
!= open_streams_.end()) {
|
|
|
|
|
// The peer requested the reset.
|
|
|
|
|
LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
|
|
|
|
|
<< "): closing sid " << stream_id;
|
|
|
|
|
open_streams_.erase(it);
|
2014-05-29 22:54:24 +00:00
|
|
|
SignalStreamClosedRemotely(stream_id);
|
2014-01-03 22:08:47 +00:00
|
|
|
|
|
|
|
|
} else if ((it = queued_reset_streams_.find(stream_id))
|
|
|
|
|
!= queued_reset_streams_.end()) {
|
|
|
|
|
// The peer requested the reset, but there was a local reset
|
|
|
|
|
// queued.
|
|
|
|
|
LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
|
|
|
|
|
<< "): double-sided close for sid " << stream_id;
|
|
|
|
|
// Both sides want the stream closed, and the peer got to send the
|
|
|
|
|
// RE-CONFIG first. Treat it like the local Remove(Send|Recv)Stream
|
|
|
|
|
// finished quickly.
|
|
|
|
|
queued_reset_streams_.erase(it);
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
// This stream is unknown. Sometimes this can be from an
|
|
|
|
|
// RESET_FAILED-related retransmit.
|
|
|
|
|
LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
|
|
|
|
|
<< "): Unknown sid " << stream_id;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-12 21:59:29 +00:00
|
|
|
// Always try to send the queued RESET because this call indicates that the
|
|
|
|
|
// last local RESET or remote RESET has made some progress.
|
|
|
|
|
SendQueuedStreamResets();
|
2014-01-03 22:08:47 +00:00
|
|
|
}
|
|
|
|
|
|
2013-10-07 23:32:02 +00:00
|
|
|
// Puts the specified |param| from the codec identified by |id| into |dest|
|
|
|
|
|
// and returns true. Or returns false if it wasn't there, leaving |dest|
|
|
|
|
|
// untouched.
|
|
|
|
|
static bool GetCodecIntParameter(const std::vector<DataCodec>& codecs,
|
|
|
|
|
int id, const std::string& name,
|
|
|
|
|
const std::string& param, int* dest) {
|
|
|
|
|
std::string value;
|
|
|
|
|
Codec match_pattern;
|
|
|
|
|
match_pattern.id = id;
|
|
|
|
|
match_pattern.name = name;
|
|
|
|
|
for (size_t i = 0; i < codecs.size(); ++i) {
|
|
|
|
|
if (codecs[i].Matches(match_pattern)) {
|
|
|
|
|
if (codecs[i].GetParam(param, &value)) {
|
2014-07-29 17:36:52 +00:00
|
|
|
*dest = rtc::FromString<int>(value);
|
2013-10-07 23:32:02 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SctpDataMediaChannel::SetSendCodecs(const std::vector<DataCodec>& codecs) {
|
|
|
|
|
return GetCodecIntParameter(
|
2016-10-08 13:02:44 -07:00
|
|
|
codecs, kGoogleSctpDataCodecPlType, kGoogleSctpDataCodecName,
|
|
|
|
|
kCodecParamPort, &remote_port_);
|
2013-10-07 23:32:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SctpDataMediaChannel::SetRecvCodecs(const std::vector<DataCodec>& codecs) {
|
|
|
|
|
return GetCodecIntParameter(
|
2016-10-08 13:02:44 -07:00
|
|
|
codecs, kGoogleSctpDataCodecPlType, kGoogleSctpDataCodecName,
|
|
|
|
|
kCodecParamPort, &local_port_);
|
2013-10-07 23:32:02 +00:00
|
|
|
}
|
2013-07-10 00:45:36 +00:00
|
|
|
|
|
|
|
|
void SctpDataMediaChannel::OnPacketFromSctpToNetwork(
|
2016-03-20 06:15:43 -07:00
|
|
|
rtc::CopyOnWriteBuffer* buffer) {
|
2016-08-24 16:26:55 -07:00
|
|
|
if (buffer->size() > (kSctpMtu)) {
|
2013-07-10 00:45:36 +00:00
|
|
|
LOG(LS_ERROR) << debug_name_ << "->OnPacketFromSctpToNetwork(...): "
|
2014-01-03 22:08:47 +00:00
|
|
|
<< "SCTP seems to have made a packet that is bigger "
|
2015-08-28 14:54:37 -04:00
|
|
|
<< "than its official MTU: " << buffer->size()
|
2016-08-24 16:26:55 -07:00
|
|
|
<< " vs max of " << kSctpMtu;
|
2013-07-10 00:45:36 +00:00
|
|
|
}
|
2015-10-15 07:26:07 -07:00
|
|
|
MediaChannel::SendPacket(buffer, rtc::PacketOptions());
|
2013-07-10 00:45:36 +00:00
|
|
|
}
|
|
|
|
|
|
2014-01-03 22:08:47 +00:00
|
|
|
bool SctpDataMediaChannel::SendQueuedStreamResets() {
|
2016-05-19 19:58:38 +02:00
|
|
|
if (!sent_reset_streams_.empty() || queued_reset_streams_.empty()) {
|
2014-01-03 22:08:47 +00:00
|
|
|
return true;
|
2016-05-19 19:58:38 +02:00
|
|
|
}
|
2014-01-03 22:08:47 +00:00
|
|
|
|
|
|
|
|
LOG(LS_VERBOSE) << "SendQueuedStreamResets[" << debug_name_ << "]: Sending ["
|
|
|
|
|
<< ListStreams(queued_reset_streams_) << "], Open: ["
|
|
|
|
|
<< ListStreams(open_streams_) << "], Sent: ["
|
|
|
|
|
<< ListStreams(sent_reset_streams_) << "]";
|
|
|
|
|
|
|
|
|
|
const size_t num_streams = queued_reset_streams_.size();
|
Use suffixed {uint,int}{8,16,32,64}_t types.
Removes the use of uint8, etc. in favor of uint8_t.
BUG=webrtc:5024
R=henrik.lundin@webrtc.org, henrikg@webrtc.org, perkj@webrtc.org, solenberg@webrtc.org, stefan@webrtc.org, tina.legrand@webrtc.org
Review URL: https://codereview.webrtc.org/1362503003 .
Cr-Commit-Position: refs/heads/master@{#10196}
2015-10-07 12:23:21 +02:00
|
|
|
const size_t num_bytes =
|
|
|
|
|
sizeof(struct sctp_reset_streams) + (num_streams * sizeof(uint16_t));
|
2014-01-03 22:08:47 +00:00
|
|
|
|
Use suffixed {uint,int}{8,16,32,64}_t types.
Removes the use of uint8, etc. in favor of uint8_t.
BUG=webrtc:5024
R=henrik.lundin@webrtc.org, henrikg@webrtc.org, perkj@webrtc.org, solenberg@webrtc.org, stefan@webrtc.org, tina.legrand@webrtc.org
Review URL: https://codereview.webrtc.org/1362503003 .
Cr-Commit-Position: refs/heads/master@{#10196}
2015-10-07 12:23:21 +02:00
|
|
|
std::vector<uint8_t> reset_stream_buf(num_bytes, 0);
|
2014-01-03 22:08:47 +00:00
|
|
|
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_INCOMING | SCTP_STREAM_RESET_OUTGOING;
|
2014-07-29 17:36:52 +00:00
|
|
|
resetp->srs_number_streams = rtc::checked_cast<uint16_t>(num_streams);
|
2014-01-03 22:08:47 +00:00
|
|
|
int result_idx = 0;
|
|
|
|
|
for (StreamSet::iterator it = queued_reset_streams_.begin();
|
|
|
|
|
it != queued_reset_streams_.end(); ++it) {
|
|
|
|
|
resetp->srs_stream_list[result_idx++] = *it;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-29 17:45:53 +00:00
|
|
|
int ret = usrsctp_setsockopt(
|
|
|
|
|
sock_, IPPROTO_SCTP, SCTP_RESET_STREAMS, resetp,
|
2014-07-29 17:36:52 +00:00
|
|
|
rtc::checked_cast<socklen_t>(reset_stream_buf.size()));
|
2014-01-03 22:08:47 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
|
LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to send a stream reset for "
|
|
|
|
|
<< num_streams << " streams";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// sent_reset_streams_ is empty, and all the queued_reset_streams_ go into
|
|
|
|
|
// it now.
|
|
|
|
|
queued_reset_streams_.swap(sent_reset_streams_);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-29 17:36:52 +00:00
|
|
|
void SctpDataMediaChannel::OnMessage(rtc::Message* msg) {
|
2013-07-10 00:45:36 +00:00
|
|
|
switch (msg->message_id) {
|
|
|
|
|
case MSG_SCTPINBOUNDPACKET: {
|
2016-02-26 03:00:35 -08:00
|
|
|
std::unique_ptr<InboundPacketMessage> pdata(
|
2014-01-03 22:08:47 +00:00
|
|
|
static_cast<InboundPacketMessage*>(msg->pdata));
|
|
|
|
|
OnInboundPacketFromSctpToChannel(pdata->data().get());
|
2013-07-10 00:45:36 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case MSG_SCTPOUTBOUNDPACKET: {
|
2016-02-26 03:00:35 -08:00
|
|
|
std::unique_ptr<OutboundPacketMessage> pdata(
|
2014-01-03 22:08:47 +00:00
|
|
|
static_cast<OutboundPacketMessage*>(msg->pdata));
|
|
|
|
|
OnPacketFromSctpToNetwork(pdata->data().get());
|
2013-07-10 00:45:36 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} // namespace cricket
|