blob: ce0f91ae3919c0d50059c438154f0374ed75c02d [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"
buildbot@webrtc.orga09a9992014-08-13 17:26:08 +000022#include "webrtc/base/buffer.h"
23#include "webrtc/base/helpers.h"
24#include "webrtc/base/logging.h"
25#include "webrtc/base/safe_conversions.h"
kjellandera96e2d72016-02-04 23:52:28 -080026#include "webrtc/media/base/codec.h"
kjellanderf4752772016-03-02 05:42:30 -080027#include "webrtc/media/base/mediaconstants.h"
kjellandera96e2d72016-02-04 23:52:28 -080028#include "webrtc/media/base/streamparams.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000029
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +000030namespace {
31typedef cricket::SctpDataMediaChannel::StreamSet StreamSet;
32// Returns a comma-separated, human-readable list of the stream IDs in 's'
33std::string ListStreams(const StreamSet& s) {
34 std::stringstream result;
35 bool first = true;
wu@webrtc.orge00265e2014-01-07 19:32:40 +000036 for (StreamSet::const_iterator it = s.begin(); it != s.end(); ++it) {
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +000037 if (!first) {
38 result << ", " << *it;
39 } else {
40 result << *it;
41 first = false;
42 }
43 }
44 return result.str();
45}
46
47// Returns a pipe-separated, human-readable list of the SCTP_STREAM_RESET
48// flags in 'flags'
49std::string ListFlags(int flags) {
50 std::stringstream result;
51 bool first = true;
52 // Skip past the first 12 chars (strlen("SCTP_STREAM_"))
53#define MAKEFLAG(X) { X, #X + 12}
54 struct flaginfo_t {
55 int value;
56 const char* name;
57 } flaginfo[] = {
58 MAKEFLAG(SCTP_STREAM_RESET_INCOMING_SSN),
59 MAKEFLAG(SCTP_STREAM_RESET_OUTGOING_SSN),
60 MAKEFLAG(SCTP_STREAM_RESET_DENIED),
61 MAKEFLAG(SCTP_STREAM_RESET_FAILED),
62 MAKEFLAG(SCTP_STREAM_CHANGE_DENIED)
63 };
64#undef MAKEFLAG
kjellandera96e2d72016-02-04 23:52:28 -080065 for (uint32_t i = 0; i < arraysize(flaginfo); ++i) {
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +000066 if (flags & flaginfo[i].value) {
67 if (!first) result << " | ";
68 result << flaginfo[i].name;
69 first = false;
70 }
71 }
72 return result.str();
73}
74
75// Returns a comma-separated, human-readable list of the integers in 'array'.
76// All 'num_elems' of them.
Peter Boström0c4e06b2015-10-07 12:23:21 +020077std::string ListArray(const uint16_t* array, int num_elems) {
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +000078 std::stringstream result;
79 for (int i = 0; i < num_elems; ++i) {
80 if (i) {
81 result << ", " << array[i];
82 } else {
83 result << array[i];
84 }
85 }
86 return result.str();
87}
88} // namespace
89
henrike@webrtc.org28e20752013-07-10 00:45:36 +000090namespace cricket {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000091typedef rtc::ScopedMessageData<SctpInboundPacket> InboundPacketMessage;
92typedef rtc::ScopedMessageData<rtc::Buffer> OutboundPacketMessage;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000093
buildbot@webrtc.org624a5042014-08-05 22:13:05 +000094// The biggest SCTP packet. Starting from a 'safe' wire MTU value of 1280,
95// take off 80 bytes for DTLS/TURN/TCP/IP overhead.
96static const size_t kSctpMtu = 1200;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000097
Lally Singhe8386d22015-08-28 14:54:37 -040098// The size of the SCTP association send buffer. 256kB, the usrsctp default.
99static const int kSendBufferSize = 262144;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000100enum {
101 MSG_SCTPINBOUNDPACKET = 1, // MessageData is SctpInboundPacket
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000102 MSG_SCTPOUTBOUNDPACKET = 2, // MessageData is rtc:Buffer
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000103};
104
105struct SctpInboundPacket {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000106 rtc::Buffer buffer;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000107 ReceiveDataParams params;
108 // The |flags| parameter is used by SCTP to distinguish notification packets
109 // from other types of packets.
110 int flags;
111};
112
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000113// Helper for logging SCTP messages.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000114static void debug_sctp_printf(const char *format, ...) {
115 char s[255];
116 va_list ap;
117 va_start(ap, format);
118 vsnprintf(s, sizeof(s), format, ap);
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000119 LOG(LS_INFO) << "SCTP: " << s;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000120 va_end(ap);
121}
122
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000123// Get the PPID to use for the terminating fragment of this type.
124static SctpDataMediaChannel::PayloadProtocolIdentifier GetPpid(
125 cricket::DataMessageType type) {
126 switch (type) {
127 default:
128 case cricket::DMT_NONE:
129 return SctpDataMediaChannel::PPID_NONE;
130 case cricket::DMT_CONTROL:
131 return SctpDataMediaChannel::PPID_CONTROL;
132 case cricket::DMT_BINARY:
133 return SctpDataMediaChannel::PPID_BINARY_LAST;
134 case cricket::DMT_TEXT:
135 return SctpDataMediaChannel::PPID_TEXT_LAST;
136 };
137}
138
139static bool GetDataMediaType(
140 SctpDataMediaChannel::PayloadProtocolIdentifier ppid,
141 cricket::DataMessageType *dest) {
142 ASSERT(dest != NULL);
143 switch (ppid) {
144 case SctpDataMediaChannel::PPID_BINARY_PARTIAL:
145 case SctpDataMediaChannel::PPID_BINARY_LAST:
146 *dest = cricket::DMT_BINARY;
147 return true;
148
149 case SctpDataMediaChannel::PPID_TEXT_PARTIAL:
150 case SctpDataMediaChannel::PPID_TEXT_LAST:
151 *dest = cricket::DMT_TEXT;
152 return true;
153
154 case SctpDataMediaChannel::PPID_CONTROL:
155 *dest = cricket::DMT_CONTROL;
156 return true;
157
158 case SctpDataMediaChannel::PPID_NONE:
159 *dest = cricket::DMT_NONE;
160 return true;
161
162 default:
163 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000164 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000165}
166
Lally Singh4c277bb2015-05-08 14:39:04 -0400167// Log the packet in text2pcap format, if log level is at LS_VERBOSE.
Lally Singhe8386d22015-08-28 14:54:37 -0400168static void VerboseLogPacket(void *data, size_t length, int direction) {
Lally Singh4c277bb2015-05-08 14:39:04 -0400169 if (LOG_CHECK_LEVEL(LS_VERBOSE) && length > 0) {
170 char *dump_buf;
171 if ((dump_buf = usrsctp_dumppacket(
Lally Singhe8386d22015-08-28 14:54:37 -0400172 data, length, direction)) != NULL) {
Lally Singh4c277bb2015-05-08 14:39:04 -0400173 LOG(LS_VERBOSE) << dump_buf;
174 usrsctp_freedumpbuffer(dump_buf);
175 }
176 }
177}
178
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000179// This is the callback usrsctp uses when there's data to send on the network
180// that has been wrapped appropriatly for the SCTP protocol.
181static int OnSctpOutboundPacket(void* addr, void* data, size_t length,
182 uint8_t tos, uint8_t set_df) {
183 SctpDataMediaChannel* channel = static_cast<SctpDataMediaChannel*>(addr);
184 LOG(LS_VERBOSE) << "global OnSctpOutboundPacket():"
185 << "addr: " << addr << "; length: " << length
186 << "; tos: " << std::hex << static_cast<int>(tos)
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000187 << "; set_df: " << std::hex << static_cast<int>(set_df);
Lally Singh4c277bb2015-05-08 14:39:04 -0400188
189 VerboseLogPacket(addr, length, SCTP_DUMP_OUTBOUND);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000190 // Note: We have to copy the data; the caller will delete it.
Karl Wiberg94784372015-04-20 14:03:07 +0200191 auto* msg = new OutboundPacketMessage(
192 new rtc::Buffer(reinterpret_cast<uint8_t*>(data), length));
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000193 channel->worker_thread()->Post(channel, MSG_SCTPOUTBOUNDPACKET, msg);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000194 return 0;
195}
196
197// This is the callback called from usrsctp when data has been received, after
198// a packet has been interpreted and parsed by usrsctp and found to contain
199// payload data. It is called by a usrsctp thread. It is assumed this function
200// will free the memory used by 'data'.
201static int OnSctpInboundPacket(struct socket* sock, union sctp_sockstore addr,
202 void* data, size_t length,
203 struct sctp_rcvinfo rcv, int flags,
204 void* ulp_info) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000205 SctpDataMediaChannel* channel = static_cast<SctpDataMediaChannel*>(ulp_info);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000206 // Post data to the channel's receiver thread (copying it).
207 // TODO(ldixon): Unclear if copy is needed as this method is responsible for
208 // memory cleanup. But this does simplify code.
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000209 const SctpDataMediaChannel::PayloadProtocolIdentifier ppid =
210 static_cast<SctpDataMediaChannel::PayloadProtocolIdentifier>(
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000211 rtc::HostToNetwork32(rcv.rcv_ppid));
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000212 cricket::DataMessageType type = cricket::DMT_NONE;
213 if (!GetDataMediaType(ppid, &type) && !(flags & MSG_NOTIFICATION)) {
214 // It's neither a notification nor a recognized data packet. Drop it.
215 LOG(LS_ERROR) << "Received an unknown PPID " << ppid
216 << " on an SCTP packet. Dropping.";
217 } else {
218 SctpInboundPacket* packet = new SctpInboundPacket;
Karl Wiberg94784372015-04-20 14:03:07 +0200219 packet->buffer.SetData(reinterpret_cast<uint8_t*>(data), length);
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000220 packet->params.ssrc = rcv.rcv_sid;
221 packet->params.seq_num = rcv.rcv_ssn;
222 packet->params.timestamp = rcv.rcv_tsn;
223 packet->params.type = type;
224 packet->flags = flags;
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000225 // The ownership of |packet| transfers to |msg|.
226 InboundPacketMessage* msg = new InboundPacketMessage(packet);
227 channel->worker_thread()->Post(channel, MSG_SCTPINBOUNDPACKET, msg);
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000228 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000229 free(data);
230 return 1;
231}
232
233// Set the initial value of the static SCTP Data Engines reference count.
234int SctpDataEngine::usrsctp_engines_count = 0;
235
wu@webrtc.org0de29502014-02-13 19:54:28 +0000236SctpDataEngine::SctpDataEngine() {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000237 if (usrsctp_engines_count == 0) {
238 // First argument is udp_encapsulation_port, which is not releveant for our
239 // AF_CONN use of sctp.
240 usrsctp_init(0, cricket::OnSctpOutboundPacket, debug_sctp_printf);
241
242 // To turn on/off detailed SCTP debugging. You will also need to have the
243 // SCTP_DEBUG cpp defines flag.
244 // usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_ALL);
245
246 // TODO(ldixon): Consider turning this on/off.
247 usrsctp_sysctl_set_sctp_ecn_enable(0);
248
Lally Singhe8386d22015-08-28 14:54:37 -0400249 // This is harmless, but we should find out when the library default
250 // changes.
251 int send_size = usrsctp_sysctl_get_sctp_sendspace();
252 if (send_size != kSendBufferSize) {
253 LOG(LS_ERROR) << "Got different send size than expected: " << send_size;
254 }
255
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000256 // TODO(ldixon): Consider turning this on/off.
257 // This is not needed right now (we don't do dynamic address changes):
258 // If SCTP Auto-ASCONF is enabled, the peer is informed automatically
259 // when a new address is added or removed. This feature is enabled by
260 // default.
261 // usrsctp_sysctl_set_sctp_auto_asconf(0);
262
263 // TODO(ldixon): Consider turning this on/off.
264 // Add a blackhole sysctl. Setting it to 1 results in no ABORTs
265 // being sent in response to INITs, setting it to 2 results
266 // in no ABORTs being sent for received OOTB packets.
267 // This is similar to the TCP sysctl.
268 //
269 // See: http://lakerest.net/pipermail/sctp-coders/2012-January/009438.html
270 // See: http://svnweb.freebsd.org/base?view=revision&revision=229805
271 // usrsctp_sysctl_set_sctp_blackhole(2);
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000272
273 // Set the number of default outgoing streams. This is the number we'll
274 // send in the SCTP INIT message. The 'appropriate default' in the
275 // second paragraph of
276 // http://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-05#section-6.2
277 // is cricket::kMaxSctpSid.
278 usrsctp_sysctl_set_sctp_nr_outgoing_streams_default(
279 cricket::kMaxSctpSid);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000280 }
281 usrsctp_engines_count++;
282
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000283 cricket::DataCodec codec(kGoogleSctpDataCodecId, kGoogleSctpDataCodecName, 0);
284 codec.SetParam(kCodecParamPort, kSctpDefaultPort);
285 codecs_.push_back(codec);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000286}
287
288SctpDataEngine::~SctpDataEngine() {
jiayl@webrtc.orgf8063d32014-06-18 21:30:40 +0000289 usrsctp_engines_count--;
290 LOG(LS_VERBOSE) << "usrsctp_engines_count:" << usrsctp_engines_count;
291
292 if (usrsctp_engines_count == 0) {
293 // usrsctp_finish() may fail if it's called too soon after the channels are
294 // closed. Wait and try again until it succeeds for up to 3 seconds.
295 for (size_t i = 0; i < 300; ++i) {
296 if (usrsctp_finish() == 0)
297 return;
298
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000299 rtc::Thread::SleepMs(10);
jiayl@webrtc.orgf8063d32014-06-18 21:30:40 +0000300 }
301 LOG(LS_ERROR) << "Failed to shutdown usrsctp.";
302 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000303}
304
305DataMediaChannel* SctpDataEngine::CreateChannel(
306 DataChannelType data_channel_type) {
307 if (data_channel_type != DCT_SCTP) {
308 return NULL;
309 }
tommi73918812015-08-27 04:29:58 -0700310 return new SctpDataMediaChannel(rtc::Thread::Current());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000311}
312
Lally Singhe8386d22015-08-28 14:54:37 -0400313// static
314SctpDataMediaChannel* SctpDataEngine::GetChannelFromSocket(
315 struct socket* sock) {
316 struct sockaddr* addrs = nullptr;
317 int naddrs = usrsctp_getladdrs(sock, 0, &addrs);
318 if (naddrs <= 0 || addrs[0].sa_family != AF_CONN) {
319 return nullptr;
320 }
321 // usrsctp_getladdrs() returns the addresses bound to this socket, which
322 // contains the SctpDataMediaChannel* as sconn_addr. Read the pointer,
323 // then free the list of addresses once we have the pointer. We only open
324 // AF_CONN sockets, and they should all have the sconn_addr set to the
325 // pointer that created them, so [0] is as good as any other.
326 struct sockaddr_conn* sconn =
327 reinterpret_cast<struct sockaddr_conn*>(&addrs[0]);
328 SctpDataMediaChannel* channel =
329 reinterpret_cast<SctpDataMediaChannel*>(sconn->sconn_addr);
330 usrsctp_freeladdrs(addrs);
331
332 return channel;
333}
334
335// static
336int SctpDataEngine::SendThresholdCallback(struct socket* sock,
337 uint32_t sb_free) {
338 // Fired on our I/O thread. SctpDataMediaChannel::OnPacketReceived() gets
339 // a packet containing acknowledgments, which goes into usrsctp_conninput,
340 // and then back here.
341 SctpDataMediaChannel* channel = GetChannelFromSocket(sock);
342 if (!channel) {
343 LOG(LS_ERROR) << "SendThresholdCallback: Failed to get channel for socket "
344 << sock;
345 return 0;
346 }
347 channel->OnSendThresholdCallback();
348 return 0;
349}
350
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000351SctpDataMediaChannel::SctpDataMediaChannel(rtc::Thread* thread)
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000352 : worker_thread_(thread),
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000353 local_port_(kSctpDefaultPort),
354 remote_port_(kSctpDefaultPort),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000355 sock_(NULL),
356 sending_(false),
357 receiving_(false),
358 debug_name_("SctpDataMediaChannel") {
359}
360
361SctpDataMediaChannel::~SctpDataMediaChannel() {
362 CloseSctpSocket();
363}
364
Lally Singhe8386d22015-08-28 14:54:37 -0400365void SctpDataMediaChannel::OnSendThresholdCallback() {
henrikg91d6ede2015-09-17 00:24:34 -0700366 RTC_DCHECK(rtc::Thread::Current() == worker_thread_);
Lally Singhe8386d22015-08-28 14:54:37 -0400367 SignalReadyToSend(true);
368}
369
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000370sockaddr_conn SctpDataMediaChannel::GetSctpSockAddr(int port) {
371 sockaddr_conn sconn = {0};
372 sconn.sconn_family = AF_CONN;
373#ifdef HAVE_SCONN_LEN
374 sconn.sconn_len = sizeof(sockaddr_conn);
375#endif
376 // Note: conversion from int to uint16_t happens here.
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000377 sconn.sconn_port = rtc::HostToNetwork16(port);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000378 sconn.sconn_addr = this;
379 return sconn;
380}
381
382bool SctpDataMediaChannel::OpenSctpSocket() {
383 if (sock_) {
384 LOG(LS_VERBOSE) << debug_name_
385 << "->Ignoring attempt to re-create existing socket.";
386 return false;
387 }
Lally Singhe8386d22015-08-28 14:54:37 -0400388
389 // If kSendBufferSize isn't reflective of reality, we log an error, but we
390 // still have to do something reasonable here. Look up what the buffer's
391 // real size is and set our threshold to something reasonable.
392 const static int kSendThreshold = usrsctp_sysctl_get_sctp_sendspace() / 2;
393
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000394 sock_ = usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP,
Lally Singhe8386d22015-08-28 14:54:37 -0400395 cricket::OnSctpInboundPacket,
396 &SctpDataEngine::SendThresholdCallback,
397 kSendThreshold, this);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000398 if (!sock_) {
399 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to create SCTP socket.";
400 return false;
401 }
402
403 // Make the socket non-blocking. Connect, close, shutdown etc will not block
404 // the thread waiting for the socket operation to complete.
405 if (usrsctp_set_non_blocking(sock_, 1) < 0) {
406 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP to non blocking.";
407 return false;
408 }
409
410 // This ensures that the usrsctp close call deletes the association. This
411 // prevents usrsctp from calling OnSctpOutboundPacket with references to
412 // this class as the address.
413 linger linger_opt;
414 linger_opt.l_onoff = 1;
415 linger_opt.l_linger = 0;
416 if (usrsctp_setsockopt(sock_, SOL_SOCKET, SO_LINGER, &linger_opt,
417 sizeof(linger_opt))) {
418 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SO_LINGER.";
419 return false;
420 }
421
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000422 // Enable stream ID resets.
423 struct sctp_assoc_value stream_rst;
424 stream_rst.assoc_id = SCTP_ALL_ASSOC;
425 stream_rst.assoc_value = 1;
426 if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_ENABLE_STREAM_RESET,
427 &stream_rst, sizeof(stream_rst))) {
428 LOG_ERRNO(LS_ERROR) << debug_name_
429 << "Failed to set SCTP_ENABLE_STREAM_RESET.";
430 return false;
431 }
432
433 // Nagle.
sergeyu@chromium.orga59696b2013-09-13 23:48:58 +0000434 uint32_t nodelay = 1;
435 if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_NODELAY, &nodelay,
436 sizeof(nodelay))) {
437 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP_NODELAY.";
438 return false;
439 }
440
buildbot@webrtc.org624a5042014-08-05 22:13:05 +0000441 // Disable MTU discovery
Lally Singhe8386d22015-08-28 14:54:37 -0400442 sctp_paddrparams params = {{0}};
buildbot@webrtc.org624a5042014-08-05 22:13:05 +0000443 params.spp_assoc_id = 0;
444 params.spp_flags = SPP_PMTUD_DISABLE;
445 params.spp_pathmtu = kSctpMtu;
446 if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &params,
447 sizeof(params))) {
448 LOG_ERRNO(LS_ERROR) << debug_name_
449 << "Failed to set SCTP_PEER_ADDR_PARAMS.";
450 return false;
451 }
452
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000453 // Subscribe to SCTP event notifications.
454 int event_types[] = {SCTP_ASSOC_CHANGE,
455 SCTP_PEER_ADDR_CHANGE,
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000456 SCTP_SEND_FAILED_EVENT,
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000457 SCTP_SENDER_DRY_EVENT,
458 SCTP_STREAM_RESET_EVENT};
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000459 struct sctp_event event = {0};
460 event.se_assoc_id = SCTP_ALL_ASSOC;
461 event.se_on = 1;
tfarina5237aaf2015-11-10 23:44:30 -0800462 for (size_t i = 0; i < arraysize(event_types); i++) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000463 event.se_type = event_types[i];
464 if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_EVENT, &event,
465 sizeof(event)) < 0) {
466 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP_EVENT type: "
467 << event.se_type;
468 return false;
469 }
470 }
471
472 // Register this class as an address for usrsctp. This is used by SCTP to
473 // direct the packets received (by the created socket) to this class.
474 usrsctp_register_address(this);
475 sending_ = true;
476 return true;
477}
478
479void SctpDataMediaChannel::CloseSctpSocket() {
480 sending_ = false;
481 if (sock_) {
482 // We assume that SO_LINGER option is set to close the association when
483 // close is called. This means that any pending packets in usrsctp will be
484 // discarded instead of being sent.
485 usrsctp_close(sock_);
486 sock_ = NULL;
487 usrsctp_deregister_address(this);
488 }
489}
490
491bool SctpDataMediaChannel::Connect() {
492 LOG(LS_VERBOSE) << debug_name_ << "->Connect().";
493
494 // If we already have a socket connection, just return.
495 if (sock_) {
496 LOG(LS_WARNING) << debug_name_ << "->Connect(): Ignored as socket "
497 "is already established.";
498 return true;
499 }
500
501 // If no socket (it was closed) try to start it again. This can happen when
502 // the socket we are connecting to closes, does an sctp shutdown handshake,
503 // or behaves unexpectedly causing us to perform a CloseSctpSocket.
504 if (!sock_ && !OpenSctpSocket()) {
505 return false;
506 }
507
508 // Note: conversion from int to uint16_t happens on assignment.
509 sockaddr_conn local_sconn = GetSctpSockAddr(local_port_);
510 if (usrsctp_bind(sock_, reinterpret_cast<sockaddr *>(&local_sconn),
511 sizeof(local_sconn)) < 0) {
512 LOG_ERRNO(LS_ERROR) << debug_name_ << "->Connect(): "
513 << ("Failed usrsctp_bind");
514 CloseSctpSocket();
515 return false;
516 }
517
518 // Note: conversion from int to uint16_t happens on assignment.
519 sockaddr_conn remote_sconn = GetSctpSockAddr(remote_port_);
520 int connect_result = usrsctp_connect(
521 sock_, reinterpret_cast<sockaddr *>(&remote_sconn), sizeof(remote_sconn));
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000522 if (connect_result < 0 && errno != SCTP_EINPROGRESS) {
523 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed usrsctp_connect. got errno="
524 << errno << ", but wanted " << SCTP_EINPROGRESS;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000525 CloseSctpSocket();
526 return false;
527 }
528 return true;
529}
530
531void SctpDataMediaChannel::Disconnect() {
532 // TODO(ldixon): Consider calling |usrsctp_shutdown(sock_, ...)| to do a
533 // shutdown handshake and remove the association.
534 CloseSctpSocket();
535}
536
537bool SctpDataMediaChannel::SetSend(bool send) {
538 if (!sending_ && send) {
539 return Connect();
540 }
541 if (sending_ && !send) {
542 Disconnect();
543 }
544 return true;
545}
546
547bool SctpDataMediaChannel::SetReceive(bool receive) {
548 receiving_ = receive;
549 return true;
550}
551
Fredrik Solenbergb071a192015-09-17 16:42:56 +0200552bool SctpDataMediaChannel::SetSendParameters(const DataSendParameters& params) {
553 return SetSendCodecs(params.codecs);
554}
555
556bool SctpDataMediaChannel::SetRecvParameters(const DataRecvParameters& params) {
557 return SetRecvCodecs(params.codecs);
558}
559
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000560bool SctpDataMediaChannel::AddSendStream(const StreamParams& stream) {
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000561 return AddStream(stream);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000562}
563
Peter Boström0c4e06b2015-10-07 12:23:21 +0200564bool SctpDataMediaChannel::RemoveSendStream(uint32_t ssrc) {
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000565 return ResetStream(ssrc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000566}
567
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000568bool SctpDataMediaChannel::AddRecvStream(const StreamParams& stream) {
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +0000569 // SCTP DataChannels are always bi-directional and calling AddSendStream will
570 // enable both sending and receiving on the stream. So AddRecvStream is a
571 // no-op.
572 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000573}
574
Peter Boström0c4e06b2015-10-07 12:23:21 +0200575bool SctpDataMediaChannel::RemoveRecvStream(uint32_t ssrc) {
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +0000576 // SCTP DataChannels are always bi-directional and calling RemoveSendStream
577 // will disable both sending and receiving on the stream. So RemoveRecvStream
578 // is a no-op.
579 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000580}
581
582bool SctpDataMediaChannel::SendData(
583 const SendDataParams& params,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000584 const rtc::Buffer& payload,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000585 SendDataResult* result) {
586 if (result) {
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000587 // Preset |result| to assume an error. If SendData succeeds, we'll
588 // overwrite |*result| once more at the end.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000589 *result = SDR_ERROR;
590 }
591
592 if (!sending_) {
593 LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
594 << "Not sending packet with ssrc=" << params.ssrc
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000595 << " len=" << payload.size() << " before SetSend(true).";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000596 return false;
597 }
598
wu@webrtc.org91053e72013-08-10 07:18:04 +0000599 if (params.type != cricket::DMT_CONTROL &&
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000600 open_streams_.find(params.ssrc) == open_streams_.end()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000601 LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
602 << "Not sending data because ssrc is unknown: "
603 << params.ssrc;
604 return false;
605 }
606
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000607 //
608 // Send data using SCTP.
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000609 ssize_t send_res = 0; // result from usrsctp_sendv.
610 struct sctp_sendv_spa spa = {0};
611 spa.sendv_flags |= SCTP_SEND_SNDINFO_VALID;
612 spa.sendv_sndinfo.snd_sid = params.ssrc;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000613 spa.sendv_sndinfo.snd_ppid = rtc::HostToNetwork32(
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000614 GetPpid(params.type));
615
616 // Ordered implies reliable.
617 if (!params.ordered) {
618 spa.sendv_sndinfo.snd_flags |= SCTP_UNORDERED;
619 if (params.max_rtx_count >= 0 || params.max_rtx_ms == 0) {
620 spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
621 spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX;
622 spa.sendv_prinfo.pr_value = params.max_rtx_count;
623 } else {
624 spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
625 spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_TTL;
626 spa.sendv_prinfo.pr_value = params.max_rtx_ms;
627 }
628 }
629
630 // We don't fragment.
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000631 send_res = usrsctp_sendv(
632 sock_, payload.data(), static_cast<size_t>(payload.size()), NULL, 0, &spa,
633 rtc::checked_cast<socklen_t>(sizeof(spa)), SCTP_SENDV_SPA, 0);
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000634 if (send_res < 0) {
jiayl@webrtc.orgf7026cd2014-05-08 16:02:23 +0000635 if (errno == SCTP_EWOULDBLOCK) {
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000636 *result = SDR_BLOCK;
637 LOG(LS_INFO) << debug_name_ << "->SendData(...): EWOULDBLOCK returned";
638 } else {
639 LOG_ERRNO(LS_ERROR) << "ERROR:" << debug_name_
640 << "->SendData(...): "
641 << " usrsctp_sendv: ";
642 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000643 return false;
644 }
645 if (result) {
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000646 // Only way out now is success.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000647 *result = SDR_SUCCESS;
648 }
649 return true;
650}
651
652// Called by network interface when a packet has been received.
wu@webrtc.orga9890802013-12-13 00:21:03 +0000653void SctpDataMediaChannel::OnPacketReceived(
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000654 rtc::Buffer* packet, const rtc::PacketTime& packet_time) {
henrikg91d6ede2015-09-17 00:24:34 -0700655 RTC_DCHECK(rtc::Thread::Current() == worker_thread_);
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000656 LOG(LS_VERBOSE) << debug_name_ << "->OnPacketReceived(...): "
657 << " length=" << packet->size() << ", sending: " << sending_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000658 // Only give receiving packets to usrsctp after if connected. This enables two
659 // peers to each make a connect call, but for them not to receive an INIT
660 // packet before they have called connect; least the last receiver of the INIT
661 // packet will have called connect, and a connection will be established.
662 if (sending_) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000663 // Pass received packet to SCTP stack. Once processed by usrsctp, the data
664 // will be will be given to the global OnSctpInboundData, and then,
665 // marshalled by a Post and handled with OnMessage.
Lally Singh4c277bb2015-05-08 14:39:04 -0400666 VerboseLogPacket(packet->data(), packet->size(), SCTP_DUMP_INBOUND);
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000667 usrsctp_conninput(this, packet->data(), packet->size(), 0);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000668 } else {
669 // TODO(ldixon): Consider caching the packet for very slightly better
670 // reliability.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000671 }
672}
673
674void SctpDataMediaChannel::OnInboundPacketFromSctpToChannel(
675 SctpInboundPacket* packet) {
676 LOG(LS_VERBOSE) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): "
677 << "Received SCTP data:"
678 << " ssrc=" << packet->params.ssrc
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000679 << " notification: " << (packet->flags & MSG_NOTIFICATION)
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000680 << " length=" << packet->buffer.size();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000681 // Sending a packet with data == NULL (no data) is SCTPs "close the
682 // connection" message. This sets sock_ = NULL;
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000683 if (!packet->buffer.size() || !packet->buffer.data()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000684 LOG(LS_INFO) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): "
685 "No data, closing.";
686 return;
687 }
688 if (packet->flags & MSG_NOTIFICATION) {
689 OnNotificationFromSctp(&packet->buffer);
690 } else {
691 OnDataFromSctpToChannel(packet->params, &packet->buffer);
692 }
693}
694
695void SctpDataMediaChannel::OnDataFromSctpToChannel(
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000696 const ReceiveDataParams& params, rtc::Buffer* buffer) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000697 if (receiving_) {
698 LOG(LS_VERBOSE) << debug_name_ << "->OnDataFromSctpToChannel(...): "
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000699 << "Posting with length: " << buffer->size()
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +0000700 << " on stream " << params.ssrc;
701 // Reports all received messages to upper layers, no matter whether the sid
702 // is known.
Karl Wiberg94784372015-04-20 14:03:07 +0200703 SignalDataReceived(params, buffer->data<char>(), buffer->size());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000704 } else {
705 LOG(LS_WARNING) << debug_name_ << "->OnDataFromSctpToChannel(...): "
706 << "Not receiving packet with sid=" << params.ssrc
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000707 << " len=" << buffer->size() << " before SetReceive(true).";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000708 }
709}
710
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000711bool SctpDataMediaChannel::AddStream(const StreamParams& stream) {
712 if (!stream.has_ssrcs()) {
713 return false;
714 }
715
Peter Boström0c4e06b2015-10-07 12:23:21 +0200716 const uint32_t ssrc = stream.first_ssrc();
lally27ed3cc2016-01-11 10:24:33 -0800717 if (ssrc >= cricket::kMaxSctpSid) {
718 LOG(LS_WARNING) << debug_name_ << "->Add(Send|Recv)Stream(...): "
719 << "Not adding data stream '" << stream.id
720 << "' with ssrc=" << ssrc
721 << " because stream ssrc is too high.";
722 return false;
723 } else if (open_streams_.find(ssrc) != open_streams_.end()) {
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +0000724 LOG(LS_WARNING) << debug_name_ << "->Add(Send|Recv)Stream(...): "
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000725 << "Not adding data stream '" << stream.id
726 << "' with ssrc=" << ssrc
727 << " because stream is already open.";
728 return false;
729 } else if (queued_reset_streams_.find(ssrc) != queued_reset_streams_.end()
730 || sent_reset_streams_.find(ssrc) != sent_reset_streams_.end()) {
731 LOG(LS_WARNING) << debug_name_ << "->Add(Send|Recv)Stream(...): "
732 << "Not adding data stream '" << stream.id
733 << "' with ssrc=" << ssrc
734 << " because stream is still closing.";
735 return false;
736 }
737
738 open_streams_.insert(ssrc);
739 return true;
740}
741
Peter Boström0c4e06b2015-10-07 12:23:21 +0200742bool SctpDataMediaChannel::ResetStream(uint32_t ssrc) {
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000743 // We typically get this called twice for the same stream, once each for
744 // Send and Recv.
745 StreamSet::iterator found = open_streams_.find(ssrc);
746
747 if (found == open_streams_.end()) {
748 LOG(LS_VERBOSE) << debug_name_ << "->ResetStream(" << ssrc << "): "
749 << "stream not found.";
750 return false;
751 } else {
752 LOG(LS_VERBOSE) << debug_name_ << "->ResetStream(" << ssrc << "): "
753 << "Removing and queuing RE-CONFIG chunk.";
754 open_streams_.erase(found);
755 }
756
757 // SCTP won't let you have more than one stream reset pending at a time, but
758 // you can close multiple streams in a single reset. So, we keep an internal
759 // queue of streams-to-reset, and send them as one reset message in
760 // SendQueuedStreamResets().
761 queued_reset_streams_.insert(ssrc);
762
763 // Signal our stream-reset logic that it should try to send now, if it can.
764 SendQueuedStreamResets();
765
766 // The stream will actually get removed when we get the acknowledgment.
767 return true;
768}
769
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000770void SctpDataMediaChannel::OnNotificationFromSctp(rtc::Buffer* buffer) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000771 const sctp_notification& notification =
772 reinterpret_cast<const sctp_notification&>(*buffer->data());
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000773 ASSERT(notification.sn_header.sn_length == buffer->size());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000774
775 // TODO(ldixon): handle notifications appropriately.
776 switch (notification.sn_header.sn_type) {
777 case SCTP_ASSOC_CHANGE:
778 LOG(LS_VERBOSE) << "SCTP_ASSOC_CHANGE";
779 OnNotificationAssocChange(notification.sn_assoc_change);
780 break;
781 case SCTP_REMOTE_ERROR:
782 LOG(LS_INFO) << "SCTP_REMOTE_ERROR";
783 break;
784 case SCTP_SHUTDOWN_EVENT:
785 LOG(LS_INFO) << "SCTP_SHUTDOWN_EVENT";
786 break;
787 case SCTP_ADAPTATION_INDICATION:
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000788 LOG(LS_INFO) << "SCTP_ADAPTATION_INDICATION";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000789 break;
790 case SCTP_PARTIAL_DELIVERY_EVENT:
791 LOG(LS_INFO) << "SCTP_PARTIAL_DELIVERY_EVENT";
792 break;
793 case SCTP_AUTHENTICATION_EVENT:
794 LOG(LS_INFO) << "SCTP_AUTHENTICATION_EVENT";
795 break;
796 case SCTP_SENDER_DRY_EVENT:
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000797 LOG(LS_VERBOSE) << "SCTP_SENDER_DRY_EVENT";
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000798 SignalReadyToSend(true);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000799 break;
800 // TODO(ldixon): Unblock after congestion.
801 case SCTP_NOTIFICATIONS_STOPPED_EVENT:
802 LOG(LS_INFO) << "SCTP_NOTIFICATIONS_STOPPED_EVENT";
803 break;
804 case SCTP_SEND_FAILED_EVENT:
805 LOG(LS_INFO) << "SCTP_SEND_FAILED_EVENT";
806 break;
807 case SCTP_STREAM_RESET_EVENT:
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000808 OnStreamResetEvent(&notification.sn_strreset_event);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000809 break;
810 case SCTP_ASSOC_RESET_EVENT:
811 LOG(LS_INFO) << "SCTP_ASSOC_RESET_EVENT";
812 break;
813 case SCTP_STREAM_CHANGE_EVENT:
814 LOG(LS_INFO) << "SCTP_STREAM_CHANGE_EVENT";
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000815 // An acknowledgment we get after our stream resets have gone through,
816 // if they've failed. We log the message, but don't react -- we don't
817 // keep around the last-transmitted set of SSIDs we wanted to close for
818 // error recovery. It doesn't seem likely to occur, and if so, likely
819 // harmless within the lifetime of a single SCTP association.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000820 break;
821 default:
822 LOG(LS_WARNING) << "Unknown SCTP event: "
823 << notification.sn_header.sn_type;
824 break;
825 }
826}
827
828void SctpDataMediaChannel::OnNotificationAssocChange(
829 const sctp_assoc_change& change) {
830 switch (change.sac_state) {
831 case SCTP_COMM_UP:
832 LOG(LS_VERBOSE) << "Association change SCTP_COMM_UP";
833 break;
834 case SCTP_COMM_LOST:
835 LOG(LS_INFO) << "Association change SCTP_COMM_LOST";
836 break;
837 case SCTP_RESTART:
838 LOG(LS_INFO) << "Association change SCTP_RESTART";
839 break;
840 case SCTP_SHUTDOWN_COMP:
841 LOG(LS_INFO) << "Association change SCTP_SHUTDOWN_COMP";
842 break;
843 case SCTP_CANT_STR_ASSOC:
844 LOG(LS_INFO) << "Association change SCTP_CANT_STR_ASSOC";
845 break;
846 default:
847 LOG(LS_INFO) << "Association change UNKNOWN";
848 break;
849 }
850}
851
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000852void SctpDataMediaChannel::OnStreamResetEvent(
853 const struct sctp_stream_reset_event* evt) {
854 // A stream reset always involves two RE-CONFIG chunks for us -- we always
855 // simultaneously reset a sid's sequence number in both directions. The
856 // requesting side transmits a RE-CONFIG chunk and waits for the peer to send
857 // one back. Both sides get this SCTP_STREAM_RESET_EVENT when they receive
858 // RE-CONFIGs.
859 const int num_ssrcs = (evt->strreset_length - sizeof(*evt)) /
860 sizeof(evt->strreset_stream_list[0]);
861 LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
862 << "): Flags = 0x"
863 << std::hex << evt->strreset_flags << " ("
864 << ListFlags(evt->strreset_flags) << ")";
865 LOG(LS_VERBOSE) << "Assoc = " << evt->strreset_assoc_id << ", Streams = ["
866 << ListArray(evt->strreset_stream_list, num_ssrcs)
867 << "], Open: ["
868 << ListStreams(open_streams_) << "], Q'd: ["
869 << ListStreams(queued_reset_streams_) << "], Sent: ["
870 << ListStreams(sent_reset_streams_) << "]";
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000871
872 // If both sides try to reset some streams at the same time (even if they're
873 // disjoint sets), we can get reset failures.
874 if (evt->strreset_flags & SCTP_STREAM_RESET_FAILED) {
875 // OK, just try again. The stream IDs sent over when the RESET_FAILED flag
876 // is set seem to be garbage values. Ignore them.
877 queued_reset_streams_.insert(
878 sent_reset_streams_.begin(),
879 sent_reset_streams_.end());
880 sent_reset_streams_.clear();
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000881
882 } else if (evt->strreset_flags & SCTP_STREAM_RESET_INCOMING_SSN) {
883 // Each side gets an event for each direction of a stream. That is,
884 // closing sid k will make each side receive INCOMING and OUTGOING reset
885 // events for k. As per RFC6525, Section 5, paragraph 2, each side will
886 // get an INCOMING event first.
887 for (int i = 0; i < num_ssrcs; i++) {
888 const int stream_id = evt->strreset_stream_list[i];
889
890 // See if this stream ID was closed by our peer or ourselves.
891 StreamSet::iterator it = sent_reset_streams_.find(stream_id);
892
893 // The reset was requested locally.
894 if (it != sent_reset_streams_.end()) {
895 LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
896 << "): local sid " << stream_id << " acknowledged.";
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000897 sent_reset_streams_.erase(it);
898
899 } else if ((it = open_streams_.find(stream_id))
900 != open_streams_.end()) {
901 // The peer requested the reset.
902 LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
903 << "): closing sid " << stream_id;
904 open_streams_.erase(it);
buildbot@webrtc.org1d66be22014-05-29 22:54:24 +0000905 SignalStreamClosedRemotely(stream_id);
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000906
907 } else if ((it = queued_reset_streams_.find(stream_id))
908 != queued_reset_streams_.end()) {
909 // The peer requested the reset, but there was a local reset
910 // queued.
911 LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
912 << "): double-sided close for sid " << stream_id;
913 // Both sides want the stream closed, and the peer got to send the
914 // RE-CONFIG first. Treat it like the local Remove(Send|Recv)Stream
915 // finished quickly.
916 queued_reset_streams_.erase(it);
917
918 } else {
919 // This stream is unknown. Sometimes this can be from an
920 // RESET_FAILED-related retransmit.
921 LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
922 << "): Unknown sid " << stream_id;
923 }
924 }
925 }
926
jiayl@webrtc.org1a6c6282014-06-12 21:59:29 +0000927 // Always try to send the queued RESET because this call indicates that the
928 // last local RESET or remote RESET has made some progress.
929 SendQueuedStreamResets();
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000930}
931
wu@webrtc.org78187522013-10-07 23:32:02 +0000932// Puts the specified |param| from the codec identified by |id| into |dest|
933// and returns true. Or returns false if it wasn't there, leaving |dest|
934// untouched.
935static bool GetCodecIntParameter(const std::vector<DataCodec>& codecs,
936 int id, const std::string& name,
937 const std::string& param, int* dest) {
938 std::string value;
939 Codec match_pattern;
940 match_pattern.id = id;
941 match_pattern.name = name;
942 for (size_t i = 0; i < codecs.size(); ++i) {
943 if (codecs[i].Matches(match_pattern)) {
944 if (codecs[i].GetParam(param, &value)) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000945 *dest = rtc::FromString<int>(value);
wu@webrtc.org78187522013-10-07 23:32:02 +0000946 return true;
947 }
948 }
949 }
950 return false;
951}
952
953bool SctpDataMediaChannel::SetSendCodecs(const std::vector<DataCodec>& codecs) {
954 return GetCodecIntParameter(
955 codecs, kGoogleSctpDataCodecId, kGoogleSctpDataCodecName, kCodecParamPort,
956 &remote_port_);
957}
958
959bool SctpDataMediaChannel::SetRecvCodecs(const std::vector<DataCodec>& codecs) {
960 return GetCodecIntParameter(
961 codecs, kGoogleSctpDataCodecId, kGoogleSctpDataCodecName, kCodecParamPort,
962 &local_port_);
963}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000964
965void SctpDataMediaChannel::OnPacketFromSctpToNetwork(
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000966 rtc::Buffer* buffer) {
Lally Singhe8386d22015-08-28 14:54:37 -0400967 // usrsctp seems to interpret the MTU we give it strangely -- it seems to
968 // give us back packets bigger than that MTU, if only by a fixed amount.
969 // This is that amount that we've observed.
970 const int kSctpOverhead = 76;
971 if (buffer->size() > (kSctpOverhead + kSctpMtu)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000972 LOG(LS_ERROR) << debug_name_ << "->OnPacketFromSctpToNetwork(...): "
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000973 << "SCTP seems to have made a packet that is bigger "
Lally Singhe8386d22015-08-28 14:54:37 -0400974 << "than its official MTU: " << buffer->size()
975 << " vs max of " << kSctpMtu
976 << " even after adding " << kSctpOverhead
977 << " extra SCTP overhead";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000978 }
stefanc1aeaf02015-10-15 07:26:07 -0700979 MediaChannel::SendPacket(buffer, rtc::PacketOptions());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000980}
981
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000982bool SctpDataMediaChannel::SendQueuedStreamResets() {
983 if (!sent_reset_streams_.empty() || queued_reset_streams_.empty())
984 return true;
985
986 LOG(LS_VERBOSE) << "SendQueuedStreamResets[" << debug_name_ << "]: Sending ["
987 << ListStreams(queued_reset_streams_) << "], Open: ["
988 << ListStreams(open_streams_) << "], Sent: ["
989 << ListStreams(sent_reset_streams_) << "]";
990
991 const size_t num_streams = queued_reset_streams_.size();
Peter Boström0c4e06b2015-10-07 12:23:21 +0200992 const size_t num_bytes =
993 sizeof(struct sctp_reset_streams) + (num_streams * sizeof(uint16_t));
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000994
Peter Boström0c4e06b2015-10-07 12:23:21 +0200995 std::vector<uint8_t> reset_stream_buf(num_bytes, 0);
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000996 struct sctp_reset_streams* resetp = reinterpret_cast<sctp_reset_streams*>(
997 &reset_stream_buf[0]);
998 resetp->srs_assoc_id = SCTP_ALL_ASSOC;
999 resetp->srs_flags = SCTP_STREAM_RESET_INCOMING | SCTP_STREAM_RESET_OUTGOING;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001000 resetp->srs_number_streams = rtc::checked_cast<uint16_t>(num_streams);
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +00001001 int result_idx = 0;
1002 for (StreamSet::iterator it = queued_reset_streams_.begin();
1003 it != queued_reset_streams_.end(); ++it) {
1004 resetp->srs_stream_list[result_idx++] = *it;
1005 }
1006
jiayl@webrtc.orga576faf2014-01-29 17:45:53 +00001007 int ret = usrsctp_setsockopt(
1008 sock_, IPPROTO_SCTP, SCTP_RESET_STREAMS, resetp,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001009 rtc::checked_cast<socklen_t>(reset_stream_buf.size()));
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +00001010 if (ret < 0) {
1011 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to send a stream reset for "
1012 << num_streams << " streams";
1013 return false;
1014 }
1015
1016 // sent_reset_streams_ is empty, and all the queued_reset_streams_ go into
1017 // it now.
1018 queued_reset_streams_.swap(sent_reset_streams_);
1019 return true;
1020}
1021
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001022void SctpDataMediaChannel::OnMessage(rtc::Message* msg) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001023 switch (msg->message_id) {
1024 case MSG_SCTPINBOUNDPACKET: {
kwiberg686a8ef2016-02-26 03:00:35 -08001025 std::unique_ptr<InboundPacketMessage> pdata(
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +00001026 static_cast<InboundPacketMessage*>(msg->pdata));
1027 OnInboundPacketFromSctpToChannel(pdata->data().get());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001028 break;
1029 }
1030 case MSG_SCTPOUTBOUNDPACKET: {
kwiberg686a8ef2016-02-26 03:00:35 -08001031 std::unique_ptr<OutboundPacketMessage> pdata(
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +00001032 static_cast<OutboundPacketMessage*>(msg->pdata));
1033 OnPacketFromSctpToNetwork(pdata->data().get());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001034 break;
1035 }
1036 }
1037}
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001038} // namespace cricket