blob: 958cd9a0f63fce08c67f362e8db1739481f38df5 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
kjellander1afca732016-02-07 20:46:45 -08002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003 *
kjellander1afca732016-02-07 20:46:45 -08004 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00009 */
10
kjellandera96e2d72016-02-04 23:52:28 -080011#include "webrtc/media/sctp/sctpdataengine.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000012
henrike@webrtc.org28e20752013-07-10 00:45:36 +000013#include <stdarg.h>
14#include <stdio.h>
kwiberg686a8ef2016-02-26 03:00:35 -080015
16#include <memory>
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +000017#include <sstream>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000018#include <vector>
19
henrike@webrtc.org28e20752013-07-10 00:45:36 +000020#include "usrsctplib/usrsctp.h"
tfarina5237aaf2015-11-10 23:44:30 -080021#include "webrtc/base/arraysize.h"
jbaucheec21bd2016-03-20 06:15:43 -070022#include "webrtc/base/copyonwritebuffer.h"
Tommi7d013312016-05-19 19:58:38 +020023#include "webrtc/base/criticalsection.h"
buildbot@webrtc.orga09a9992014-08-13 17:26:08 +000024#include "webrtc/base/helpers.h"
25#include "webrtc/base/logging.h"
Tommid44c0772016-03-11 17:12:32 -080026#include "webrtc/base/safe_conversions.h"
kjellandera96e2d72016-02-04 23:52:28 -080027#include "webrtc/media/base/codec.h"
kjellanderf4752772016-03-02 05:42:30 -080028#include "webrtc/media/base/mediaconstants.h"
kjellandera96e2d72016-02-04 23:52:28 -080029#include "webrtc/media/base/streamparams.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000030
Tommi7d013312016-05-19 19:58:38 +020031namespace cricket {
32// The biggest SCTP packet. Starting from a 'safe' wire MTU value of 1280,
33// take off 80 bytes for DTLS/TURN/TCP/IP overhead.
34static const size_t kSctpMtu = 1200;
35
36// The size of the SCTP association send buffer. 256kB, the usrsctp default.
37static const int kSendBufferSize = 262144;
38
39struct SctpInboundPacket {
40 rtc::CopyOnWriteBuffer buffer;
41 ReceiveDataParams params;
42 // The |flags| parameter is used by SCTP to distinguish notification packets
43 // from other types of packets.
44 int flags;
45};
46
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +000047namespace {
Tommi7d013312016-05-19 19:58:38 +020048// Set the initial value of the static SCTP Data Engines reference count.
49int g_usrsctp_usage_count = 0;
50rtc::GlobalLockPod g_usrsctp_lock_;
51
52typedef SctpDataMediaChannel::StreamSet StreamSet;
53
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +000054// Returns a comma-separated, human-readable list of the stream IDs in 's'
55std::string ListStreams(const StreamSet& s) {
56 std::stringstream result;
57 bool first = true;
wu@webrtc.orge00265e2014-01-07 19:32:40 +000058 for (StreamSet::const_iterator it = s.begin(); it != s.end(); ++it) {
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +000059 if (!first) {
60 result << ", " << *it;
61 } else {
62 result << *it;
63 first = false;
64 }
65 }
66 return result.str();
67}
68
69// Returns a pipe-separated, human-readable list of the SCTP_STREAM_RESET
70// flags in 'flags'
71std::string ListFlags(int flags) {
72 std::stringstream result;
73 bool first = true;
74 // Skip past the first 12 chars (strlen("SCTP_STREAM_"))
75#define MAKEFLAG(X) { X, #X + 12}
76 struct flaginfo_t {
77 int value;
78 const char* name;
79 } flaginfo[] = {
80 MAKEFLAG(SCTP_STREAM_RESET_INCOMING_SSN),
81 MAKEFLAG(SCTP_STREAM_RESET_OUTGOING_SSN),
82 MAKEFLAG(SCTP_STREAM_RESET_DENIED),
83 MAKEFLAG(SCTP_STREAM_RESET_FAILED),
84 MAKEFLAG(SCTP_STREAM_CHANGE_DENIED)
85 };
86#undef MAKEFLAG
kjellandera96e2d72016-02-04 23:52:28 -080087 for (uint32_t i = 0; i < arraysize(flaginfo); ++i) {
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +000088 if (flags & flaginfo[i].value) {
89 if (!first) result << " | ";
90 result << flaginfo[i].name;
91 first = false;
92 }
93 }
94 return result.str();
95}
96
97// Returns a comma-separated, human-readable list of the integers in 'array'.
98// All 'num_elems' of them.
Peter Boström0c4e06b2015-10-07 12:23:21 +020099std::string ListArray(const uint16_t* array, int num_elems) {
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000100 std::stringstream result;
101 for (int i = 0; i < num_elems; ++i) {
102 if (i) {
103 result << ", " << array[i];
104 } else {
105 result << array[i];
106 }
107 }
108 return result.str();
109}
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000110
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000111typedef rtc::ScopedMessageData<SctpInboundPacket> InboundPacketMessage;
jbaucheec21bd2016-03-20 06:15:43 -0700112typedef rtc::ScopedMessageData<rtc::CopyOnWriteBuffer> OutboundPacketMessage;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000113
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000114enum {
115 MSG_SCTPINBOUNDPACKET = 1, // MessageData is SctpInboundPacket
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000116 MSG_SCTPOUTBOUNDPACKET = 2, // MessageData is rtc:Buffer
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000117};
118
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000119// Helper for logging SCTP messages.
Tommi7d013312016-05-19 19:58:38 +0200120void DebugSctpPrintf(const char* format, ...) {
121#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000122 char s[255];
123 va_list ap;
124 va_start(ap, format);
125 vsnprintf(s, sizeof(s), format, ap);
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000126 LOG(LS_INFO) << "SCTP: " << s;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000127 va_end(ap);
Tommi7d013312016-05-19 19:58:38 +0200128#endif
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000129}
130
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000131// Get the PPID to use for the terminating fragment of this type.
Tommi7d013312016-05-19 19:58:38 +0200132SctpDataMediaChannel::PayloadProtocolIdentifier GetPpid(DataMessageType type) {
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000133 switch (type) {
134 default:
Tommi7d013312016-05-19 19:58:38 +0200135 case DMT_NONE:
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000136 return SctpDataMediaChannel::PPID_NONE;
Tommi7d013312016-05-19 19:58:38 +0200137 case DMT_CONTROL:
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000138 return SctpDataMediaChannel::PPID_CONTROL;
Tommi7d013312016-05-19 19:58:38 +0200139 case DMT_BINARY:
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000140 return SctpDataMediaChannel::PPID_BINARY_LAST;
Tommi7d013312016-05-19 19:58:38 +0200141 case DMT_TEXT:
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000142 return SctpDataMediaChannel::PPID_TEXT_LAST;
143 };
144}
145
Tommi7d013312016-05-19 19:58:38 +0200146bool GetDataMediaType(SctpDataMediaChannel::PayloadProtocolIdentifier ppid,
147 DataMessageType* dest) {
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000148 ASSERT(dest != NULL);
149 switch (ppid) {
150 case SctpDataMediaChannel::PPID_BINARY_PARTIAL:
151 case SctpDataMediaChannel::PPID_BINARY_LAST:
Tommi7d013312016-05-19 19:58:38 +0200152 *dest = DMT_BINARY;
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000153 return true;
154
155 case SctpDataMediaChannel::PPID_TEXT_PARTIAL:
156 case SctpDataMediaChannel::PPID_TEXT_LAST:
Tommi7d013312016-05-19 19:58:38 +0200157 *dest = DMT_TEXT;
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000158 return true;
159
160 case SctpDataMediaChannel::PPID_CONTROL:
Tommi7d013312016-05-19 19:58:38 +0200161 *dest = DMT_CONTROL;
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000162 return true;
163
164 case SctpDataMediaChannel::PPID_NONE:
Tommi7d013312016-05-19 19:58:38 +0200165 *dest = DMT_NONE;
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000166 return true;
167
168 default:
169 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000170 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000171}
172
Lally Singh4c277bb2015-05-08 14:39:04 -0400173// Log the packet in text2pcap format, if log level is at LS_VERBOSE.
Tommi7d013312016-05-19 19:58:38 +0200174void VerboseLogPacket(const void* data, size_t length, int direction) {
Lally Singh4c277bb2015-05-08 14:39:04 -0400175 if (LOG_CHECK_LEVEL(LS_VERBOSE) && length > 0) {
176 char *dump_buf;
jbaucheec21bd2016-03-20 06:15:43 -0700177 // Some downstream project uses an older version of usrsctp that expects
178 // a non-const "void*" as first parameter when dumping the packet, so we
179 // need to cast the const away here to avoid a compiler error.
Lally Singh4c277bb2015-05-08 14:39:04 -0400180 if ((dump_buf = usrsctp_dumppacket(
jbaucheec21bd2016-03-20 06:15:43 -0700181 const_cast<void*>(data), length, direction)) != NULL) {
Lally Singh4c277bb2015-05-08 14:39:04 -0400182 LOG(LS_VERBOSE) << dump_buf;
183 usrsctp_freedumpbuffer(dump_buf);
184 }
185 }
186}
187
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000188// This is the callback usrsctp uses when there's data to send on the network
189// that has been wrapped appropriatly for the SCTP protocol.
Tommi7d013312016-05-19 19:58:38 +0200190int OnSctpOutboundPacket(void* addr,
191 void* data,
192 size_t length,
193 uint8_t tos,
194 uint8_t set_df) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000195 SctpDataMediaChannel* channel = static_cast<SctpDataMediaChannel*>(addr);
196 LOG(LS_VERBOSE) << "global OnSctpOutboundPacket():"
197 << "addr: " << addr << "; length: " << length
198 << "; tos: " << std::hex << static_cast<int>(tos)
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000199 << "; set_df: " << std::hex << static_cast<int>(set_df);
Lally Singh4c277bb2015-05-08 14:39:04 -0400200
201 VerboseLogPacket(addr, length, SCTP_DUMP_OUTBOUND);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000202 // Note: We have to copy the data; the caller will delete it.
Karl Wiberg94784372015-04-20 14:03:07 +0200203 auto* msg = new OutboundPacketMessage(
jbaucheec21bd2016-03-20 06:15:43 -0700204 new rtc::CopyOnWriteBuffer(reinterpret_cast<uint8_t*>(data), length));
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000205 channel->worker_thread()->Post(channel, MSG_SCTPOUTBOUNDPACKET, msg);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000206 return 0;
207}
208
209// This is the callback called from usrsctp when data has been received, after
210// a packet has been interpreted and parsed by usrsctp and found to contain
211// payload data. It is called by a usrsctp thread. It is assumed this function
212// will free the memory used by 'data'.
Tommi7d013312016-05-19 19:58:38 +0200213int OnSctpInboundPacket(struct socket* sock,
214 union sctp_sockstore addr,
215 void* data,
216 size_t length,
217 struct sctp_rcvinfo rcv,
218 int flags,
219 void* ulp_info) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000220 SctpDataMediaChannel* channel = static_cast<SctpDataMediaChannel*>(ulp_info);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000221 // Post data to the channel's receiver thread (copying it).
222 // TODO(ldixon): Unclear if copy is needed as this method is responsible for
223 // memory cleanup. But this does simplify code.
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000224 const SctpDataMediaChannel::PayloadProtocolIdentifier ppid =
225 static_cast<SctpDataMediaChannel::PayloadProtocolIdentifier>(
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000226 rtc::HostToNetwork32(rcv.rcv_ppid));
Tommi7d013312016-05-19 19:58:38 +0200227 DataMessageType type = DMT_NONE;
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000228 if (!GetDataMediaType(ppid, &type) && !(flags & MSG_NOTIFICATION)) {
229 // It's neither a notification nor a recognized data packet. Drop it.
230 LOG(LS_ERROR) << "Received an unknown PPID " << ppid
231 << " on an SCTP packet. Dropping.";
232 } else {
233 SctpInboundPacket* packet = new SctpInboundPacket;
Karl Wiberg94784372015-04-20 14:03:07 +0200234 packet->buffer.SetData(reinterpret_cast<uint8_t*>(data), length);
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000235 packet->params.ssrc = rcv.rcv_sid;
236 packet->params.seq_num = rcv.rcv_ssn;
237 packet->params.timestamp = rcv.rcv_tsn;
238 packet->params.type = type;
239 packet->flags = flags;
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000240 // The ownership of |packet| transfers to |msg|.
241 InboundPacketMessage* msg = new InboundPacketMessage(packet);
242 channel->worker_thread()->Post(channel, MSG_SCTPINBOUNDPACKET, msg);
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000243 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000244 free(data);
245 return 1;
246}
247
Tommi7d013312016-05-19 19:58:38 +0200248void InitializeUsrSctp() {
249 LOG(LS_INFO) << __FUNCTION__;
250 // First argument is udp_encapsulation_port, which is not releveant for our
251 // AF_CONN use of sctp.
252 usrsctp_init(0, &OnSctpOutboundPacket, &DebugSctpPrintf);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000253
Tommi7d013312016-05-19 19:58:38 +0200254 // To turn on/off detailed SCTP debugging. You will also need to have the
255 // SCTP_DEBUG cpp defines flag.
256 // usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_ALL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000257
Tommi7d013312016-05-19 19:58:38 +0200258 // TODO(ldixon): Consider turning this on/off.
259 usrsctp_sysctl_set_sctp_ecn_enable(0);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000260
Tommi7d013312016-05-19 19:58:38 +0200261 // This is harmless, but we should find out when the library default
262 // changes.
263 int send_size = usrsctp_sysctl_get_sctp_sendspace();
264 if (send_size != kSendBufferSize) {
265 LOG(LS_ERROR) << "Got different send size than expected: " << send_size;
266 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000267
Tommi7d013312016-05-19 19:58:38 +0200268 // TODO(ldixon): Consider turning this on/off.
269 // This is not needed right now (we don't do dynamic address changes):
270 // If SCTP Auto-ASCONF is enabled, the peer is informed automatically
271 // when a new address is added or removed. This feature is enabled by
272 // default.
273 // usrsctp_sysctl_set_sctp_auto_asconf(0);
274
275 // TODO(ldixon): Consider turning this on/off.
276 // Add a blackhole sysctl. Setting it to 1 results in no ABORTs
277 // being sent in response to INITs, setting it to 2 results
278 // in no ABORTs being sent for received OOTB packets.
279 // This is similar to the TCP sysctl.
280 //
281 // See: http://lakerest.net/pipermail/sctp-coders/2012-January/009438.html
282 // See: http://svnweb.freebsd.org/base?view=revision&revision=229805
283 // usrsctp_sysctl_set_sctp_blackhole(2);
284
285 // Set the number of default outgoing streams. This is the number we'll
286 // send in the SCTP INIT message. The 'appropriate default' in the
287 // second paragraph of
288 // http://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-05#section-6.2
289 // is kMaxSctpSid.
290 usrsctp_sysctl_set_sctp_nr_outgoing_streams_default(kMaxSctpSid);
291}
292
293void UninitializeUsrSctp() {
294 LOG(LS_INFO) << __FUNCTION__;
295 // usrsctp_finish() may fail if it's called too soon after the channels are
296 // closed. Wait and try again until it succeeds for up to 3 seconds.
297 for (size_t i = 0; i < 300; ++i) {
298 if (usrsctp_finish() == 0) {
299 return;
Lally Singhe8386d22015-08-28 14:54:37 -0400300 }
301
Tommi7d013312016-05-19 19:58:38 +0200302 rtc::Thread::SleepMs(10);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000303 }
Tommi7d013312016-05-19 19:58:38 +0200304 LOG(LS_ERROR) << "Failed to shutdown usrsctp.";
305}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000306
Tommi7d013312016-05-19 19:58:38 +0200307void IncrementUsrSctpUsageCount() {
308 rtc::GlobalLockScope lock(&g_usrsctp_lock_);
309 if (!g_usrsctp_usage_count) {
310 InitializeUsrSctp();
311 }
312 ++g_usrsctp_usage_count;
313}
314
315void DecrementUsrSctpUsageCount() {
316 rtc::GlobalLockScope lock(&g_usrsctp_lock_);
317 --g_usrsctp_usage_count;
318 if (!g_usrsctp_usage_count) {
319 UninitializeUsrSctp();
320 }
321}
322
323DataCodec GetSctpDataCodec() {
324 DataCodec codec(kGoogleSctpDataCodecId, kGoogleSctpDataCodecName);
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000325 codec.SetParam(kCodecParamPort, kSctpDefaultPort);
Tommi7d013312016-05-19 19:58:38 +0200326 return codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000327}
328
Tommi7d013312016-05-19 19:58:38 +0200329} // namespace
jiayl@webrtc.orgf8063d32014-06-18 21:30:40 +0000330
Tommi7d013312016-05-19 19:58:38 +0200331SctpDataEngine::SctpDataEngine() : codecs_(1, GetSctpDataCodec()) {}
jiayl@webrtc.orgf8063d32014-06-18 21:30:40 +0000332
Tommi7d013312016-05-19 19:58:38 +0200333SctpDataEngine::~SctpDataEngine() {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000334
Tommi7d013312016-05-19 19:58:38 +0200335// Called on the worker thread.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000336DataMediaChannel* SctpDataEngine::CreateChannel(
337 DataChannelType data_channel_type) {
338 if (data_channel_type != DCT_SCTP) {
339 return NULL;
340 }
tommi73918812015-08-27 04:29:58 -0700341 return new SctpDataMediaChannel(rtc::Thread::Current());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000342}
343
Lally Singhe8386d22015-08-28 14:54:37 -0400344// static
Tommi7d013312016-05-19 19:58:38 +0200345SctpDataMediaChannel* SctpDataMediaChannel::GetChannelFromSocket(
Lally Singhe8386d22015-08-28 14:54:37 -0400346 struct socket* sock) {
347 struct sockaddr* addrs = nullptr;
348 int naddrs = usrsctp_getladdrs(sock, 0, &addrs);
349 if (naddrs <= 0 || addrs[0].sa_family != AF_CONN) {
350 return nullptr;
351 }
352 // usrsctp_getladdrs() returns the addresses bound to this socket, which
353 // contains the SctpDataMediaChannel* as sconn_addr. Read the pointer,
354 // then free the list of addresses once we have the pointer. We only open
355 // AF_CONN sockets, and they should all have the sconn_addr set to the
356 // pointer that created them, so [0] is as good as any other.
357 struct sockaddr_conn* sconn =
358 reinterpret_cast<struct sockaddr_conn*>(&addrs[0]);
359 SctpDataMediaChannel* channel =
360 reinterpret_cast<SctpDataMediaChannel*>(sconn->sconn_addr);
361 usrsctp_freeladdrs(addrs);
362
363 return channel;
364}
365
366// static
Tommi7d013312016-05-19 19:58:38 +0200367int SctpDataMediaChannel::SendThresholdCallback(struct socket* sock,
368 uint32_t sb_free) {
Lally Singhe8386d22015-08-28 14:54:37 -0400369 // Fired on our I/O thread. SctpDataMediaChannel::OnPacketReceived() gets
370 // a packet containing acknowledgments, which goes into usrsctp_conninput,
371 // and then back here.
372 SctpDataMediaChannel* channel = GetChannelFromSocket(sock);
373 if (!channel) {
374 LOG(LS_ERROR) << "SendThresholdCallback: Failed to get channel for socket "
375 << sock;
376 return 0;
377 }
378 channel->OnSendThresholdCallback();
379 return 0;
380}
381
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000382SctpDataMediaChannel::SctpDataMediaChannel(rtc::Thread* thread)
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000383 : worker_thread_(thread),
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000384 local_port_(kSctpDefaultPort),
385 remote_port_(kSctpDefaultPort),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000386 sock_(NULL),
387 sending_(false),
388 receiving_(false),
389 debug_name_("SctpDataMediaChannel") {
390}
391
392SctpDataMediaChannel::~SctpDataMediaChannel() {
393 CloseSctpSocket();
394}
395
Lally Singhe8386d22015-08-28 14:54:37 -0400396void SctpDataMediaChannel::OnSendThresholdCallback() {
henrikg91d6ede2015-09-17 00:24:34 -0700397 RTC_DCHECK(rtc::Thread::Current() == worker_thread_);
Lally Singhe8386d22015-08-28 14:54:37 -0400398 SignalReadyToSend(true);
399}
400
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000401sockaddr_conn SctpDataMediaChannel::GetSctpSockAddr(int port) {
402 sockaddr_conn sconn = {0};
403 sconn.sconn_family = AF_CONN;
404#ifdef HAVE_SCONN_LEN
405 sconn.sconn_len = sizeof(sockaddr_conn);
406#endif
407 // Note: conversion from int to uint16_t happens here.
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000408 sconn.sconn_port = rtc::HostToNetwork16(port);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000409 sconn.sconn_addr = this;
410 return sconn;
411}
412
413bool SctpDataMediaChannel::OpenSctpSocket() {
414 if (sock_) {
415 LOG(LS_VERBOSE) << debug_name_
416 << "->Ignoring attempt to re-create existing socket.";
417 return false;
418 }
Lally Singhe8386d22015-08-28 14:54:37 -0400419
Tommi7d013312016-05-19 19:58:38 +0200420 IncrementUsrSctpUsageCount();
421
Lally Singhe8386d22015-08-28 14:54:37 -0400422 // If kSendBufferSize isn't reflective of reality, we log an error, but we
423 // still have to do something reasonable here. Look up what the buffer's
424 // real size is and set our threshold to something reasonable.
425 const static int kSendThreshold = usrsctp_sysctl_get_sctp_sendspace() / 2;
426
Tommi7d013312016-05-19 19:58:38 +0200427 sock_ = usrsctp_socket(
428 AF_CONN, SOCK_STREAM, IPPROTO_SCTP, OnSctpInboundPacket,
429 &SctpDataMediaChannel::SendThresholdCallback, kSendThreshold, this);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000430 if (!sock_) {
431 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to create SCTP socket.";
Tommi7d013312016-05-19 19:58:38 +0200432 DecrementUsrSctpUsageCount();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000433 return false;
434 }
435
436 // Make the socket non-blocking. Connect, close, shutdown etc will not block
437 // the thread waiting for the socket operation to complete.
438 if (usrsctp_set_non_blocking(sock_, 1) < 0) {
439 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP to non blocking.";
440 return false;
441 }
442
443 // This ensures that the usrsctp close call deletes the association. This
444 // prevents usrsctp from calling OnSctpOutboundPacket with references to
445 // this class as the address.
446 linger linger_opt;
447 linger_opt.l_onoff = 1;
448 linger_opt.l_linger = 0;
449 if (usrsctp_setsockopt(sock_, SOL_SOCKET, SO_LINGER, &linger_opt,
450 sizeof(linger_opt))) {
451 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SO_LINGER.";
452 return false;
453 }
454
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000455 // Enable stream ID resets.
456 struct sctp_assoc_value stream_rst;
457 stream_rst.assoc_id = SCTP_ALL_ASSOC;
458 stream_rst.assoc_value = 1;
459 if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_ENABLE_STREAM_RESET,
460 &stream_rst, sizeof(stream_rst))) {
461 LOG_ERRNO(LS_ERROR) << debug_name_
462 << "Failed to set SCTP_ENABLE_STREAM_RESET.";
463 return false;
464 }
465
466 // Nagle.
sergeyu@chromium.orga59696b2013-09-13 23:48:58 +0000467 uint32_t nodelay = 1;
468 if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_NODELAY, &nodelay,
469 sizeof(nodelay))) {
470 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP_NODELAY.";
471 return false;
472 }
473
buildbot@webrtc.org624a5042014-08-05 22:13:05 +0000474 // Disable MTU discovery
Lally Singhe8386d22015-08-28 14:54:37 -0400475 sctp_paddrparams params = {{0}};
buildbot@webrtc.org624a5042014-08-05 22:13:05 +0000476 params.spp_assoc_id = 0;
477 params.spp_flags = SPP_PMTUD_DISABLE;
478 params.spp_pathmtu = kSctpMtu;
479 if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &params,
480 sizeof(params))) {
481 LOG_ERRNO(LS_ERROR) << debug_name_
482 << "Failed to set SCTP_PEER_ADDR_PARAMS.";
483 return false;
484 }
485
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000486 // Subscribe to SCTP event notifications.
487 int event_types[] = {SCTP_ASSOC_CHANGE,
488 SCTP_PEER_ADDR_CHANGE,
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000489 SCTP_SEND_FAILED_EVENT,
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000490 SCTP_SENDER_DRY_EVENT,
491 SCTP_STREAM_RESET_EVENT};
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000492 struct sctp_event event = {0};
493 event.se_assoc_id = SCTP_ALL_ASSOC;
494 event.se_on = 1;
tfarina5237aaf2015-11-10 23:44:30 -0800495 for (size_t i = 0; i < arraysize(event_types); i++) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000496 event.se_type = event_types[i];
497 if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_EVENT, &event,
498 sizeof(event)) < 0) {
499 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP_EVENT type: "
500 << event.se_type;
501 return false;
502 }
503 }
504
505 // Register this class as an address for usrsctp. This is used by SCTP to
506 // direct the packets received (by the created socket) to this class.
507 usrsctp_register_address(this);
508 sending_ = true;
509 return true;
510}
511
512void SctpDataMediaChannel::CloseSctpSocket() {
513 sending_ = false;
514 if (sock_) {
515 // We assume that SO_LINGER option is set to close the association when
516 // close is called. This means that any pending packets in usrsctp will be
517 // discarded instead of being sent.
518 usrsctp_close(sock_);
519 sock_ = NULL;
520 usrsctp_deregister_address(this);
Tommi7d013312016-05-19 19:58:38 +0200521
522 DecrementUsrSctpUsageCount();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000523 }
524}
525
526bool SctpDataMediaChannel::Connect() {
527 LOG(LS_VERBOSE) << debug_name_ << "->Connect().";
528
529 // If we already have a socket connection, just return.
530 if (sock_) {
531 LOG(LS_WARNING) << debug_name_ << "->Connect(): Ignored as socket "
532 "is already established.";
533 return true;
534 }
535
536 // If no socket (it was closed) try to start it again. This can happen when
537 // the socket we are connecting to closes, does an sctp shutdown handshake,
538 // or behaves unexpectedly causing us to perform a CloseSctpSocket.
539 if (!sock_ && !OpenSctpSocket()) {
540 return false;
541 }
542
543 // Note: conversion from int to uint16_t happens on assignment.
544 sockaddr_conn local_sconn = GetSctpSockAddr(local_port_);
545 if (usrsctp_bind(sock_, reinterpret_cast<sockaddr *>(&local_sconn),
546 sizeof(local_sconn)) < 0) {
547 LOG_ERRNO(LS_ERROR) << debug_name_ << "->Connect(): "
548 << ("Failed usrsctp_bind");
549 CloseSctpSocket();
550 return false;
551 }
552
553 // Note: conversion from int to uint16_t happens on assignment.
554 sockaddr_conn remote_sconn = GetSctpSockAddr(remote_port_);
555 int connect_result = usrsctp_connect(
556 sock_, reinterpret_cast<sockaddr *>(&remote_sconn), sizeof(remote_sconn));
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000557 if (connect_result < 0 && errno != SCTP_EINPROGRESS) {
558 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed usrsctp_connect. got errno="
559 << errno << ", but wanted " << SCTP_EINPROGRESS;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000560 CloseSctpSocket();
561 return false;
562 }
563 return true;
564}
565
566void SctpDataMediaChannel::Disconnect() {
567 // TODO(ldixon): Consider calling |usrsctp_shutdown(sock_, ...)| to do a
568 // shutdown handshake and remove the association.
569 CloseSctpSocket();
570}
571
572bool SctpDataMediaChannel::SetSend(bool send) {
573 if (!sending_ && send) {
574 return Connect();
575 }
576 if (sending_ && !send) {
577 Disconnect();
578 }
579 return true;
580}
581
582bool SctpDataMediaChannel::SetReceive(bool receive) {
583 receiving_ = receive;
584 return true;
585}
586
Fredrik Solenbergb071a192015-09-17 16:42:56 +0200587bool SctpDataMediaChannel::SetSendParameters(const DataSendParameters& params) {
588 return SetSendCodecs(params.codecs);
589}
590
591bool SctpDataMediaChannel::SetRecvParameters(const DataRecvParameters& params) {
592 return SetRecvCodecs(params.codecs);
593}
594
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000595bool SctpDataMediaChannel::AddSendStream(const StreamParams& stream) {
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000596 return AddStream(stream);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000597}
598
Peter Boström0c4e06b2015-10-07 12:23:21 +0200599bool SctpDataMediaChannel::RemoveSendStream(uint32_t ssrc) {
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000600 return ResetStream(ssrc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000601}
602
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000603bool SctpDataMediaChannel::AddRecvStream(const StreamParams& stream) {
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +0000604 // SCTP DataChannels are always bi-directional and calling AddSendStream will
605 // enable both sending and receiving on the stream. So AddRecvStream is a
606 // no-op.
607 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000608}
609
Peter Boström0c4e06b2015-10-07 12:23:21 +0200610bool SctpDataMediaChannel::RemoveRecvStream(uint32_t ssrc) {
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +0000611 // SCTP DataChannels are always bi-directional and calling RemoveSendStream
612 // will disable both sending and receiving on the stream. So RemoveRecvStream
613 // is a no-op.
614 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000615}
616
617bool SctpDataMediaChannel::SendData(
618 const SendDataParams& params,
jbaucheec21bd2016-03-20 06:15:43 -0700619 const rtc::CopyOnWriteBuffer& payload,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000620 SendDataResult* result) {
621 if (result) {
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000622 // Preset |result| to assume an error. If SendData succeeds, we'll
623 // overwrite |*result| once more at the end.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000624 *result = SDR_ERROR;
625 }
626
627 if (!sending_) {
628 LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
629 << "Not sending packet with ssrc=" << params.ssrc
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000630 << " len=" << payload.size() << " before SetSend(true).";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000631 return false;
632 }
633
Tommi7d013312016-05-19 19:58:38 +0200634 if (params.type != DMT_CONTROL &&
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000635 open_streams_.find(params.ssrc) == open_streams_.end()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000636 LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
637 << "Not sending data because ssrc is unknown: "
638 << params.ssrc;
639 return false;
640 }
641
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000642 //
643 // Send data using SCTP.
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000644 ssize_t send_res = 0; // result from usrsctp_sendv.
645 struct sctp_sendv_spa spa = {0};
646 spa.sendv_flags |= SCTP_SEND_SNDINFO_VALID;
647 spa.sendv_sndinfo.snd_sid = params.ssrc;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000648 spa.sendv_sndinfo.snd_ppid = rtc::HostToNetwork32(
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000649 GetPpid(params.type));
650
651 // Ordered implies reliable.
652 if (!params.ordered) {
653 spa.sendv_sndinfo.snd_flags |= SCTP_UNORDERED;
654 if (params.max_rtx_count >= 0 || params.max_rtx_ms == 0) {
655 spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
656 spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX;
657 spa.sendv_prinfo.pr_value = params.max_rtx_count;
658 } else {
659 spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
660 spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_TTL;
661 spa.sendv_prinfo.pr_value = params.max_rtx_ms;
662 }
663 }
664
665 // We don't fragment.
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000666 send_res = usrsctp_sendv(
667 sock_, payload.data(), static_cast<size_t>(payload.size()), NULL, 0, &spa,
668 rtc::checked_cast<socklen_t>(sizeof(spa)), SCTP_SENDV_SPA, 0);
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000669 if (send_res < 0) {
jiayl@webrtc.orgf7026cd2014-05-08 16:02:23 +0000670 if (errno == SCTP_EWOULDBLOCK) {
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000671 *result = SDR_BLOCK;
672 LOG(LS_INFO) << debug_name_ << "->SendData(...): EWOULDBLOCK returned";
673 } else {
674 LOG_ERRNO(LS_ERROR) << "ERROR:" << debug_name_
675 << "->SendData(...): "
676 << " usrsctp_sendv: ";
677 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000678 return false;
679 }
680 if (result) {
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000681 // Only way out now is success.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000682 *result = SDR_SUCCESS;
683 }
684 return true;
685}
686
687// Called by network interface when a packet has been received.
wu@webrtc.orga9890802013-12-13 00:21:03 +0000688void SctpDataMediaChannel::OnPacketReceived(
jbaucheec21bd2016-03-20 06:15:43 -0700689 rtc::CopyOnWriteBuffer* packet, const rtc::PacketTime& packet_time) {
henrikg91d6ede2015-09-17 00:24:34 -0700690 RTC_DCHECK(rtc::Thread::Current() == worker_thread_);
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000691 LOG(LS_VERBOSE) << debug_name_ << "->OnPacketReceived(...): "
692 << " length=" << packet->size() << ", sending: " << sending_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000693 // Only give receiving packets to usrsctp after if connected. This enables two
694 // peers to each make a connect call, but for them not to receive an INIT
695 // packet before they have called connect; least the last receiver of the INIT
696 // packet will have called connect, and a connection will be established.
697 if (sending_) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000698 // Pass received packet to SCTP stack. Once processed by usrsctp, the data
699 // will be will be given to the global OnSctpInboundData, and then,
700 // marshalled by a Post and handled with OnMessage.
jbaucheec21bd2016-03-20 06:15:43 -0700701 VerboseLogPacket(packet->cdata(), packet->size(), SCTP_DUMP_INBOUND);
702 usrsctp_conninput(this, packet->cdata(), packet->size(), 0);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000703 } else {
704 // TODO(ldixon): Consider caching the packet for very slightly better
705 // reliability.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000706 }
707}
708
709void SctpDataMediaChannel::OnInboundPacketFromSctpToChannel(
710 SctpInboundPacket* packet) {
711 LOG(LS_VERBOSE) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): "
712 << "Received SCTP data:"
713 << " ssrc=" << packet->params.ssrc
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000714 << " notification: " << (packet->flags & MSG_NOTIFICATION)
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000715 << " length=" << packet->buffer.size();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000716 // Sending a packet with data == NULL (no data) is SCTPs "close the
717 // connection" message. This sets sock_ = NULL;
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000718 if (!packet->buffer.size() || !packet->buffer.data()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000719 LOG(LS_INFO) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): "
720 "No data, closing.";
721 return;
722 }
723 if (packet->flags & MSG_NOTIFICATION) {
jbaucheec21bd2016-03-20 06:15:43 -0700724 OnNotificationFromSctp(packet->buffer);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000725 } else {
jbaucheec21bd2016-03-20 06:15:43 -0700726 OnDataFromSctpToChannel(packet->params, packet->buffer);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000727 }
728}
729
730void SctpDataMediaChannel::OnDataFromSctpToChannel(
jbaucheec21bd2016-03-20 06:15:43 -0700731 const ReceiveDataParams& params, const rtc::CopyOnWriteBuffer& buffer) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000732 if (receiving_) {
733 LOG(LS_VERBOSE) << debug_name_ << "->OnDataFromSctpToChannel(...): "
jbaucheec21bd2016-03-20 06:15:43 -0700734 << "Posting with length: " << buffer.size()
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +0000735 << " on stream " << params.ssrc;
736 // Reports all received messages to upper layers, no matter whether the sid
737 // is known.
jbaucheec21bd2016-03-20 06:15:43 -0700738 SignalDataReceived(params, buffer.data<char>(), buffer.size());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000739 } else {
740 LOG(LS_WARNING) << debug_name_ << "->OnDataFromSctpToChannel(...): "
741 << "Not receiving packet with sid=" << params.ssrc
jbaucheec21bd2016-03-20 06:15:43 -0700742 << " len=" << buffer.size() << " before SetReceive(true).";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000743 }
744}
745
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000746bool SctpDataMediaChannel::AddStream(const StreamParams& stream) {
747 if (!stream.has_ssrcs()) {
748 return false;
749 }
750
Peter Boström0c4e06b2015-10-07 12:23:21 +0200751 const uint32_t ssrc = stream.first_ssrc();
Tommi7d013312016-05-19 19:58:38 +0200752 if (ssrc >= kMaxSctpSid) {
lally27ed3cc2016-01-11 10:24:33 -0800753 LOG(LS_WARNING) << debug_name_ << "->Add(Send|Recv)Stream(...): "
754 << "Not adding data stream '" << stream.id
755 << "' with ssrc=" << ssrc
756 << " because stream ssrc is too high.";
757 return false;
758 } else if (open_streams_.find(ssrc) != open_streams_.end()) {
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +0000759 LOG(LS_WARNING) << debug_name_ << "->Add(Send|Recv)Stream(...): "
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000760 << "Not adding data stream '" << stream.id
761 << "' with ssrc=" << ssrc
762 << " because stream is already open.";
763 return false;
764 } else if (queued_reset_streams_.find(ssrc) != queued_reset_streams_.end()
765 || sent_reset_streams_.find(ssrc) != sent_reset_streams_.end()) {
766 LOG(LS_WARNING) << debug_name_ << "->Add(Send|Recv)Stream(...): "
767 << "Not adding data stream '" << stream.id
768 << "' with ssrc=" << ssrc
769 << " because stream is still closing.";
770 return false;
771 }
772
773 open_streams_.insert(ssrc);
774 return true;
775}
776
Peter Boström0c4e06b2015-10-07 12:23:21 +0200777bool SctpDataMediaChannel::ResetStream(uint32_t ssrc) {
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000778 // We typically get this called twice for the same stream, once each for
779 // Send and Recv.
780 StreamSet::iterator found = open_streams_.find(ssrc);
781
782 if (found == open_streams_.end()) {
783 LOG(LS_VERBOSE) << debug_name_ << "->ResetStream(" << ssrc << "): "
784 << "stream not found.";
785 return false;
786 } else {
787 LOG(LS_VERBOSE) << debug_name_ << "->ResetStream(" << ssrc << "): "
788 << "Removing and queuing RE-CONFIG chunk.";
789 open_streams_.erase(found);
790 }
791
792 // SCTP won't let you have more than one stream reset pending at a time, but
793 // you can close multiple streams in a single reset. So, we keep an internal
794 // queue of streams-to-reset, and send them as one reset message in
795 // SendQueuedStreamResets().
796 queued_reset_streams_.insert(ssrc);
797
798 // Signal our stream-reset logic that it should try to send now, if it can.
799 SendQueuedStreamResets();
800
801 // The stream will actually get removed when we get the acknowledgment.
802 return true;
803}
804
jbaucheec21bd2016-03-20 06:15:43 -0700805void SctpDataMediaChannel::OnNotificationFromSctp(
806 const rtc::CopyOnWriteBuffer& buffer) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000807 const sctp_notification& notification =
jbaucheec21bd2016-03-20 06:15:43 -0700808 reinterpret_cast<const sctp_notification&>(*buffer.data());
809 ASSERT(notification.sn_header.sn_length == buffer.size());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000810
811 // TODO(ldixon): handle notifications appropriately.
812 switch (notification.sn_header.sn_type) {
813 case SCTP_ASSOC_CHANGE:
814 LOG(LS_VERBOSE) << "SCTP_ASSOC_CHANGE";
815 OnNotificationAssocChange(notification.sn_assoc_change);
816 break;
817 case SCTP_REMOTE_ERROR:
818 LOG(LS_INFO) << "SCTP_REMOTE_ERROR";
819 break;
820 case SCTP_SHUTDOWN_EVENT:
821 LOG(LS_INFO) << "SCTP_SHUTDOWN_EVENT";
822 break;
823 case SCTP_ADAPTATION_INDICATION:
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000824 LOG(LS_INFO) << "SCTP_ADAPTATION_INDICATION";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000825 break;
826 case SCTP_PARTIAL_DELIVERY_EVENT:
827 LOG(LS_INFO) << "SCTP_PARTIAL_DELIVERY_EVENT";
828 break;
829 case SCTP_AUTHENTICATION_EVENT:
830 LOG(LS_INFO) << "SCTP_AUTHENTICATION_EVENT";
831 break;
832 case SCTP_SENDER_DRY_EVENT:
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000833 LOG(LS_VERBOSE) << "SCTP_SENDER_DRY_EVENT";
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000834 SignalReadyToSend(true);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000835 break;
836 // TODO(ldixon): Unblock after congestion.
837 case SCTP_NOTIFICATIONS_STOPPED_EVENT:
838 LOG(LS_INFO) << "SCTP_NOTIFICATIONS_STOPPED_EVENT";
839 break;
840 case SCTP_SEND_FAILED_EVENT:
841 LOG(LS_INFO) << "SCTP_SEND_FAILED_EVENT";
842 break;
843 case SCTP_STREAM_RESET_EVENT:
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000844 OnStreamResetEvent(&notification.sn_strreset_event);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000845 break;
846 case SCTP_ASSOC_RESET_EVENT:
847 LOG(LS_INFO) << "SCTP_ASSOC_RESET_EVENT";
848 break;
849 case SCTP_STREAM_CHANGE_EVENT:
850 LOG(LS_INFO) << "SCTP_STREAM_CHANGE_EVENT";
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000851 // An acknowledgment we get after our stream resets have gone through,
852 // if they've failed. We log the message, but don't react -- we don't
853 // keep around the last-transmitted set of SSIDs we wanted to close for
854 // error recovery. It doesn't seem likely to occur, and if so, likely
855 // harmless within the lifetime of a single SCTP association.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000856 break;
857 default:
858 LOG(LS_WARNING) << "Unknown SCTP event: "
859 << notification.sn_header.sn_type;
860 break;
861 }
862}
863
864void SctpDataMediaChannel::OnNotificationAssocChange(
865 const sctp_assoc_change& change) {
866 switch (change.sac_state) {
867 case SCTP_COMM_UP:
868 LOG(LS_VERBOSE) << "Association change SCTP_COMM_UP";
869 break;
870 case SCTP_COMM_LOST:
871 LOG(LS_INFO) << "Association change SCTP_COMM_LOST";
872 break;
873 case SCTP_RESTART:
874 LOG(LS_INFO) << "Association change SCTP_RESTART";
875 break;
876 case SCTP_SHUTDOWN_COMP:
877 LOG(LS_INFO) << "Association change SCTP_SHUTDOWN_COMP";
878 break;
879 case SCTP_CANT_STR_ASSOC:
880 LOG(LS_INFO) << "Association change SCTP_CANT_STR_ASSOC";
881 break;
882 default:
883 LOG(LS_INFO) << "Association change UNKNOWN";
884 break;
885 }
886}
887
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000888void SctpDataMediaChannel::OnStreamResetEvent(
889 const struct sctp_stream_reset_event* evt) {
890 // A stream reset always involves two RE-CONFIG chunks for us -- we always
891 // simultaneously reset a sid's sequence number in both directions. The
892 // requesting side transmits a RE-CONFIG chunk and waits for the peer to send
893 // one back. Both sides get this SCTP_STREAM_RESET_EVENT when they receive
894 // RE-CONFIGs.
895 const int num_ssrcs = (evt->strreset_length - sizeof(*evt)) /
896 sizeof(evt->strreset_stream_list[0]);
897 LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
898 << "): Flags = 0x"
899 << std::hex << evt->strreset_flags << " ("
900 << ListFlags(evt->strreset_flags) << ")";
901 LOG(LS_VERBOSE) << "Assoc = " << evt->strreset_assoc_id << ", Streams = ["
902 << ListArray(evt->strreset_stream_list, num_ssrcs)
903 << "], Open: ["
904 << ListStreams(open_streams_) << "], Q'd: ["
905 << ListStreams(queued_reset_streams_) << "], Sent: ["
906 << ListStreams(sent_reset_streams_) << "]";
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000907
908 // If both sides try to reset some streams at the same time (even if they're
909 // disjoint sets), we can get reset failures.
910 if (evt->strreset_flags & SCTP_STREAM_RESET_FAILED) {
911 // OK, just try again. The stream IDs sent over when the RESET_FAILED flag
912 // is set seem to be garbage values. Ignore them.
913 queued_reset_streams_.insert(
914 sent_reset_streams_.begin(),
915 sent_reset_streams_.end());
916 sent_reset_streams_.clear();
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000917
918 } else if (evt->strreset_flags & SCTP_STREAM_RESET_INCOMING_SSN) {
919 // Each side gets an event for each direction of a stream. That is,
920 // closing sid k will make each side receive INCOMING and OUTGOING reset
921 // events for k. As per RFC6525, Section 5, paragraph 2, each side will
922 // get an INCOMING event first.
923 for (int i = 0; i < num_ssrcs; i++) {
924 const int stream_id = evt->strreset_stream_list[i];
925
926 // See if this stream ID was closed by our peer or ourselves.
927 StreamSet::iterator it = sent_reset_streams_.find(stream_id);
928
929 // The reset was requested locally.
930 if (it != sent_reset_streams_.end()) {
931 LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
932 << "): local sid " << stream_id << " acknowledged.";
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000933 sent_reset_streams_.erase(it);
934
935 } else if ((it = open_streams_.find(stream_id))
936 != open_streams_.end()) {
937 // The peer requested the reset.
938 LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
939 << "): closing sid " << stream_id;
940 open_streams_.erase(it);
buildbot@webrtc.org1d66be22014-05-29 22:54:24 +0000941 SignalStreamClosedRemotely(stream_id);
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000942
943 } else if ((it = queued_reset_streams_.find(stream_id))
944 != queued_reset_streams_.end()) {
945 // The peer requested the reset, but there was a local reset
946 // queued.
947 LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
948 << "): double-sided close for sid " << stream_id;
949 // Both sides want the stream closed, and the peer got to send the
950 // RE-CONFIG first. Treat it like the local Remove(Send|Recv)Stream
951 // finished quickly.
952 queued_reset_streams_.erase(it);
953
954 } else {
955 // This stream is unknown. Sometimes this can be from an
956 // RESET_FAILED-related retransmit.
957 LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
958 << "): Unknown sid " << stream_id;
959 }
960 }
961 }
962
jiayl@webrtc.org1a6c6282014-06-12 21:59:29 +0000963 // Always try to send the queued RESET because this call indicates that the
964 // last local RESET or remote RESET has made some progress.
965 SendQueuedStreamResets();
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000966}
967
wu@webrtc.org78187522013-10-07 23:32:02 +0000968// Puts the specified |param| from the codec identified by |id| into |dest|
969// and returns true. Or returns false if it wasn't there, leaving |dest|
970// untouched.
971static bool GetCodecIntParameter(const std::vector<DataCodec>& codecs,
972 int id, const std::string& name,
973 const std::string& param, int* dest) {
974 std::string value;
975 Codec match_pattern;
976 match_pattern.id = id;
977 match_pattern.name = name;
978 for (size_t i = 0; i < codecs.size(); ++i) {
979 if (codecs[i].Matches(match_pattern)) {
980 if (codecs[i].GetParam(param, &value)) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000981 *dest = rtc::FromString<int>(value);
wu@webrtc.org78187522013-10-07 23:32:02 +0000982 return true;
983 }
984 }
985 }
986 return false;
987}
988
989bool SctpDataMediaChannel::SetSendCodecs(const std::vector<DataCodec>& codecs) {
990 return GetCodecIntParameter(
991 codecs, kGoogleSctpDataCodecId, kGoogleSctpDataCodecName, kCodecParamPort,
992 &remote_port_);
993}
994
995bool SctpDataMediaChannel::SetRecvCodecs(const std::vector<DataCodec>& codecs) {
996 return GetCodecIntParameter(
997 codecs, kGoogleSctpDataCodecId, kGoogleSctpDataCodecName, kCodecParamPort,
998 &local_port_);
999}
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001000
1001void SctpDataMediaChannel::OnPacketFromSctpToNetwork(
jbaucheec21bd2016-03-20 06:15:43 -07001002 rtc::CopyOnWriteBuffer* buffer) {
Lally Singhe8386d22015-08-28 14:54:37 -04001003 // usrsctp seems to interpret the MTU we give it strangely -- it seems to
1004 // give us back packets bigger than that MTU, if only by a fixed amount.
1005 // This is that amount that we've observed.
1006 const int kSctpOverhead = 76;
1007 if (buffer->size() > (kSctpOverhead + kSctpMtu)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001008 LOG(LS_ERROR) << debug_name_ << "->OnPacketFromSctpToNetwork(...): "
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +00001009 << "SCTP seems to have made a packet that is bigger "
Lally Singhe8386d22015-08-28 14:54:37 -04001010 << "than its official MTU: " << buffer->size()
1011 << " vs max of " << kSctpMtu
1012 << " even after adding " << kSctpOverhead
1013 << " extra SCTP overhead";
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001014 }
stefanc1aeaf02015-10-15 07:26:07 -07001015 MediaChannel::SendPacket(buffer, rtc::PacketOptions());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001016}
1017
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +00001018bool SctpDataMediaChannel::SendQueuedStreamResets() {
Tommi7d013312016-05-19 19:58:38 +02001019 if (!sent_reset_streams_.empty() || queued_reset_streams_.empty()) {
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +00001020 return true;
Tommi7d013312016-05-19 19:58:38 +02001021 }
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +00001022
1023 LOG(LS_VERBOSE) << "SendQueuedStreamResets[" << debug_name_ << "]: Sending ["
1024 << ListStreams(queued_reset_streams_) << "], Open: ["
1025 << ListStreams(open_streams_) << "], Sent: ["
1026 << ListStreams(sent_reset_streams_) << "]";
1027
1028 const size_t num_streams = queued_reset_streams_.size();
Peter Boström0c4e06b2015-10-07 12:23:21 +02001029 const size_t num_bytes =
1030 sizeof(struct sctp_reset_streams) + (num_streams * sizeof(uint16_t));
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +00001031
Peter Boström0c4e06b2015-10-07 12:23:21 +02001032 std::vector<uint8_t> reset_stream_buf(num_bytes, 0);
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +00001033 struct sctp_reset_streams* resetp = reinterpret_cast<sctp_reset_streams*>(
1034 &reset_stream_buf[0]);
1035 resetp->srs_assoc_id = SCTP_ALL_ASSOC;
1036 resetp->srs_flags = SCTP_STREAM_RESET_INCOMING | SCTP_STREAM_RESET_OUTGOING;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001037 resetp->srs_number_streams = rtc::checked_cast<uint16_t>(num_streams);
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +00001038 int result_idx = 0;
1039 for (StreamSet::iterator it = queued_reset_streams_.begin();
1040 it != queued_reset_streams_.end(); ++it) {
1041 resetp->srs_stream_list[result_idx++] = *it;
1042 }
1043
jiayl@webrtc.orga576faf2014-01-29 17:45:53 +00001044 int ret = usrsctp_setsockopt(
1045 sock_, IPPROTO_SCTP, SCTP_RESET_STREAMS, resetp,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001046 rtc::checked_cast<socklen_t>(reset_stream_buf.size()));
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +00001047 if (ret < 0) {
1048 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to send a stream reset for "
1049 << num_streams << " streams";
1050 return false;
1051 }
1052
1053 // sent_reset_streams_ is empty, and all the queued_reset_streams_ go into
1054 // it now.
1055 queued_reset_streams_.swap(sent_reset_streams_);
1056 return true;
1057}
1058
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001059void SctpDataMediaChannel::OnMessage(rtc::Message* msg) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001060 switch (msg->message_id) {
1061 case MSG_SCTPINBOUNDPACKET: {
kwiberg686a8ef2016-02-26 03:00:35 -08001062 std::unique_ptr<InboundPacketMessage> pdata(
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +00001063 static_cast<InboundPacketMessage*>(msg->pdata));
1064 OnInboundPacketFromSctpToChannel(pdata->data().get());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001065 break;
1066 }
1067 case MSG_SCTPOUTBOUNDPACKET: {
kwiberg686a8ef2016-02-26 03:00:35 -08001068 std::unique_ptr<OutboundPacketMessage> pdata(
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +00001069 static_cast<OutboundPacketMessage*>(msg->pdata));
1070 OnPacketFromSctpToNetwork(pdata->data().get());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001071 break;
1072 }
1073 }
1074}
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001075} // namespace cricket