blob: ba3b8f2408444e590510a58391b5e3e6f4b9a5ec [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"
buildbot@webrtc.orga09a9992014-08-13 17:26:08 +000023#include "webrtc/base/helpers.h"
24#include "webrtc/base/logging.h"
Tommid44c0772016-03-11 17:12:32 -080025#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;
jbaucheec21bd2016-03-20 06:15:43 -070092typedef rtc::ScopedMessageData<rtc::CopyOnWriteBuffer> 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 {
jbaucheec21bd2016-03-20 06:15:43 -0700106 rtc::CopyOnWriteBuffer 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.
jbaucheec21bd2016-03-20 06:15:43 -0700168static void VerboseLogPacket(const 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;
jbaucheec21bd2016-03-20 06:15:43 -0700171 // Some downstream project uses an older version of usrsctp that expects
172 // a non-const "void*" as first parameter when dumping the packet, so we
173 // need to cast the const away here to avoid a compiler error.
Lally Singh4c277bb2015-05-08 14:39:04 -0400174 if ((dump_buf = usrsctp_dumppacket(
jbaucheec21bd2016-03-20 06:15:43 -0700175 const_cast<void*>(data), length, direction)) != NULL) {
Lally Singh4c277bb2015-05-08 14:39:04 -0400176 LOG(LS_VERBOSE) << dump_buf;
177 usrsctp_freedumpbuffer(dump_buf);
178 }
179 }
180}
181
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000182// This is the callback usrsctp uses when there's data to send on the network
183// that has been wrapped appropriatly for the SCTP protocol.
184static int OnSctpOutboundPacket(void* addr, void* data, size_t length,
185 uint8_t tos, uint8_t set_df) {
186 SctpDataMediaChannel* channel = static_cast<SctpDataMediaChannel*>(addr);
187 LOG(LS_VERBOSE) << "global OnSctpOutboundPacket():"
188 << "addr: " << addr << "; length: " << length
189 << "; tos: " << std::hex << static_cast<int>(tos)
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000190 << "; set_df: " << std::hex << static_cast<int>(set_df);
Lally Singh4c277bb2015-05-08 14:39:04 -0400191
192 VerboseLogPacket(addr, length, SCTP_DUMP_OUTBOUND);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000193 // Note: We have to copy the data; the caller will delete it.
Karl Wiberg94784372015-04-20 14:03:07 +0200194 auto* msg = new OutboundPacketMessage(
jbaucheec21bd2016-03-20 06:15:43 -0700195 new rtc::CopyOnWriteBuffer(reinterpret_cast<uint8_t*>(data), length));
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000196 channel->worker_thread()->Post(channel, MSG_SCTPOUTBOUNDPACKET, msg);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000197 return 0;
198}
199
200// This is the callback called from usrsctp when data has been received, after
201// a packet has been interpreted and parsed by usrsctp and found to contain
202// payload data. It is called by a usrsctp thread. It is assumed this function
203// will free the memory used by 'data'.
204static int OnSctpInboundPacket(struct socket* sock, union sctp_sockstore addr,
205 void* data, size_t length,
206 struct sctp_rcvinfo rcv, int flags,
207 void* ulp_info) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000208 SctpDataMediaChannel* channel = static_cast<SctpDataMediaChannel*>(ulp_info);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000209 // Post data to the channel's receiver thread (copying it).
210 // TODO(ldixon): Unclear if copy is needed as this method is responsible for
211 // memory cleanup. But this does simplify code.
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000212 const SctpDataMediaChannel::PayloadProtocolIdentifier ppid =
213 static_cast<SctpDataMediaChannel::PayloadProtocolIdentifier>(
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000214 rtc::HostToNetwork32(rcv.rcv_ppid));
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000215 cricket::DataMessageType type = cricket::DMT_NONE;
216 if (!GetDataMediaType(ppid, &type) && !(flags & MSG_NOTIFICATION)) {
217 // It's neither a notification nor a recognized data packet. Drop it.
218 LOG(LS_ERROR) << "Received an unknown PPID " << ppid
219 << " on an SCTP packet. Dropping.";
220 } else {
221 SctpInboundPacket* packet = new SctpInboundPacket;
Karl Wiberg94784372015-04-20 14:03:07 +0200222 packet->buffer.SetData(reinterpret_cast<uint8_t*>(data), length);
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000223 packet->params.ssrc = rcv.rcv_sid;
224 packet->params.seq_num = rcv.rcv_ssn;
225 packet->params.timestamp = rcv.rcv_tsn;
226 packet->params.type = type;
227 packet->flags = flags;
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000228 // The ownership of |packet| transfers to |msg|.
229 InboundPacketMessage* msg = new InboundPacketMessage(packet);
230 channel->worker_thread()->Post(channel, MSG_SCTPINBOUNDPACKET, msg);
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000231 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000232 free(data);
233 return 1;
234}
235
236// Set the initial value of the static SCTP Data Engines reference count.
237int SctpDataEngine::usrsctp_engines_count = 0;
238
wu@webrtc.org0de29502014-02-13 19:54:28 +0000239SctpDataEngine::SctpDataEngine() {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000240 if (usrsctp_engines_count == 0) {
241 // First argument is udp_encapsulation_port, which is not releveant for our
242 // AF_CONN use of sctp.
243 usrsctp_init(0, cricket::OnSctpOutboundPacket, debug_sctp_printf);
244
245 // To turn on/off detailed SCTP debugging. You will also need to have the
246 // SCTP_DEBUG cpp defines flag.
247 // usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_ALL);
248
249 // TODO(ldixon): Consider turning this on/off.
250 usrsctp_sysctl_set_sctp_ecn_enable(0);
251
Lally Singhe8386d22015-08-28 14:54:37 -0400252 // This is harmless, but we should find out when the library default
253 // changes.
254 int send_size = usrsctp_sysctl_get_sctp_sendspace();
255 if (send_size != kSendBufferSize) {
256 LOG(LS_ERROR) << "Got different send size than expected: " << send_size;
257 }
258
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000259 // TODO(ldixon): Consider turning this on/off.
260 // This is not needed right now (we don't do dynamic address changes):
261 // If SCTP Auto-ASCONF is enabled, the peer is informed automatically
262 // when a new address is added or removed. This feature is enabled by
263 // default.
264 // usrsctp_sysctl_set_sctp_auto_asconf(0);
265
266 // TODO(ldixon): Consider turning this on/off.
267 // Add a blackhole sysctl. Setting it to 1 results in no ABORTs
268 // being sent in response to INITs, setting it to 2 results
269 // in no ABORTs being sent for received OOTB packets.
270 // This is similar to the TCP sysctl.
271 //
272 // See: http://lakerest.net/pipermail/sctp-coders/2012-January/009438.html
273 // See: http://svnweb.freebsd.org/base?view=revision&revision=229805
274 // usrsctp_sysctl_set_sctp_blackhole(2);
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000275
276 // Set the number of default outgoing streams. This is the number we'll
277 // send in the SCTP INIT message. The 'appropriate default' in the
278 // second paragraph of
279 // http://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-05#section-6.2
280 // is cricket::kMaxSctpSid.
281 usrsctp_sysctl_set_sctp_nr_outgoing_streams_default(
282 cricket::kMaxSctpSid);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000283 }
284 usrsctp_engines_count++;
285
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000286 cricket::DataCodec codec(kGoogleSctpDataCodecId, kGoogleSctpDataCodecName, 0);
287 codec.SetParam(kCodecParamPort, kSctpDefaultPort);
288 codecs_.push_back(codec);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000289}
290
291SctpDataEngine::~SctpDataEngine() {
jiayl@webrtc.orgf8063d32014-06-18 21:30:40 +0000292 usrsctp_engines_count--;
293 LOG(LS_VERBOSE) << "usrsctp_engines_count:" << usrsctp_engines_count;
294
295 if (usrsctp_engines_count == 0) {
296 // usrsctp_finish() may fail if it's called too soon after the channels are
297 // closed. Wait and try again until it succeeds for up to 3 seconds.
298 for (size_t i = 0; i < 300; ++i) {
299 if (usrsctp_finish() == 0)
300 return;
301
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000302 rtc::Thread::SleepMs(10);
jiayl@webrtc.orgf8063d32014-06-18 21:30:40 +0000303 }
304 LOG(LS_ERROR) << "Failed to shutdown usrsctp.";
305 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000306}
307
308DataMediaChannel* SctpDataEngine::CreateChannel(
309 DataChannelType data_channel_type) {
310 if (data_channel_type != DCT_SCTP) {
311 return NULL;
312 }
tommi73918812015-08-27 04:29:58 -0700313 return new SctpDataMediaChannel(rtc::Thread::Current());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000314}
315
Lally Singhe8386d22015-08-28 14:54:37 -0400316// static
317SctpDataMediaChannel* SctpDataEngine::GetChannelFromSocket(
318 struct socket* sock) {
319 struct sockaddr* addrs = nullptr;
320 int naddrs = usrsctp_getladdrs(sock, 0, &addrs);
321 if (naddrs <= 0 || addrs[0].sa_family != AF_CONN) {
322 return nullptr;
323 }
324 // usrsctp_getladdrs() returns the addresses bound to this socket, which
325 // contains the SctpDataMediaChannel* as sconn_addr. Read the pointer,
326 // then free the list of addresses once we have the pointer. We only open
327 // AF_CONN sockets, and they should all have the sconn_addr set to the
328 // pointer that created them, so [0] is as good as any other.
329 struct sockaddr_conn* sconn =
330 reinterpret_cast<struct sockaddr_conn*>(&addrs[0]);
331 SctpDataMediaChannel* channel =
332 reinterpret_cast<SctpDataMediaChannel*>(sconn->sconn_addr);
333 usrsctp_freeladdrs(addrs);
334
335 return channel;
336}
337
338// static
339int SctpDataEngine::SendThresholdCallback(struct socket* sock,
340 uint32_t sb_free) {
341 // Fired on our I/O thread. SctpDataMediaChannel::OnPacketReceived() gets
342 // a packet containing acknowledgments, which goes into usrsctp_conninput,
343 // and then back here.
344 SctpDataMediaChannel* channel = GetChannelFromSocket(sock);
345 if (!channel) {
346 LOG(LS_ERROR) << "SendThresholdCallback: Failed to get channel for socket "
347 << sock;
348 return 0;
349 }
350 channel->OnSendThresholdCallback();
351 return 0;
352}
353
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000354SctpDataMediaChannel::SctpDataMediaChannel(rtc::Thread* thread)
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000355 : worker_thread_(thread),
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000356 local_port_(kSctpDefaultPort),
357 remote_port_(kSctpDefaultPort),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000358 sock_(NULL),
359 sending_(false),
360 receiving_(false),
361 debug_name_("SctpDataMediaChannel") {
362}
363
364SctpDataMediaChannel::~SctpDataMediaChannel() {
365 CloseSctpSocket();
366}
367
Lally Singhe8386d22015-08-28 14:54:37 -0400368void SctpDataMediaChannel::OnSendThresholdCallback() {
henrikg91d6ede2015-09-17 00:24:34 -0700369 RTC_DCHECK(rtc::Thread::Current() == worker_thread_);
Lally Singhe8386d22015-08-28 14:54:37 -0400370 SignalReadyToSend(true);
371}
372
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000373sockaddr_conn SctpDataMediaChannel::GetSctpSockAddr(int port) {
374 sockaddr_conn sconn = {0};
375 sconn.sconn_family = AF_CONN;
376#ifdef HAVE_SCONN_LEN
377 sconn.sconn_len = sizeof(sockaddr_conn);
378#endif
379 // Note: conversion from int to uint16_t happens here.
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000380 sconn.sconn_port = rtc::HostToNetwork16(port);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000381 sconn.sconn_addr = this;
382 return sconn;
383}
384
385bool SctpDataMediaChannel::OpenSctpSocket() {
386 if (sock_) {
387 LOG(LS_VERBOSE) << debug_name_
388 << "->Ignoring attempt to re-create existing socket.";
389 return false;
390 }
Lally Singhe8386d22015-08-28 14:54:37 -0400391
392 // If kSendBufferSize isn't reflective of reality, we log an error, but we
393 // still have to do something reasonable here. Look up what the buffer's
394 // real size is and set our threshold to something reasonable.
395 const static int kSendThreshold = usrsctp_sysctl_get_sctp_sendspace() / 2;
396
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000397 sock_ = usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP,
Lally Singhe8386d22015-08-28 14:54:37 -0400398 cricket::OnSctpInboundPacket,
399 &SctpDataEngine::SendThresholdCallback,
400 kSendThreshold, this);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000401 if (!sock_) {
402 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to create SCTP socket.";
403 return false;
404 }
405
406 // Make the socket non-blocking. Connect, close, shutdown etc will not block
407 // the thread waiting for the socket operation to complete.
408 if (usrsctp_set_non_blocking(sock_, 1) < 0) {
409 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP to non blocking.";
410 return false;
411 }
412
413 // This ensures that the usrsctp close call deletes the association. This
414 // prevents usrsctp from calling OnSctpOutboundPacket with references to
415 // this class as the address.
416 linger linger_opt;
417 linger_opt.l_onoff = 1;
418 linger_opt.l_linger = 0;
419 if (usrsctp_setsockopt(sock_, SOL_SOCKET, SO_LINGER, &linger_opt,
420 sizeof(linger_opt))) {
421 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SO_LINGER.";
422 return false;
423 }
424
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000425 // Enable stream ID resets.
426 struct sctp_assoc_value stream_rst;
427 stream_rst.assoc_id = SCTP_ALL_ASSOC;
428 stream_rst.assoc_value = 1;
429 if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_ENABLE_STREAM_RESET,
430 &stream_rst, sizeof(stream_rst))) {
431 LOG_ERRNO(LS_ERROR) << debug_name_
432 << "Failed to set SCTP_ENABLE_STREAM_RESET.";
433 return false;
434 }
435
436 // Nagle.
sergeyu@chromium.orga59696b2013-09-13 23:48:58 +0000437 uint32_t nodelay = 1;
438 if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_NODELAY, &nodelay,
439 sizeof(nodelay))) {
440 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP_NODELAY.";
441 return false;
442 }
443
buildbot@webrtc.org624a5042014-08-05 22:13:05 +0000444 // Disable MTU discovery
Lally Singhe8386d22015-08-28 14:54:37 -0400445 sctp_paddrparams params = {{0}};
buildbot@webrtc.org624a5042014-08-05 22:13:05 +0000446 params.spp_assoc_id = 0;
447 params.spp_flags = SPP_PMTUD_DISABLE;
448 params.spp_pathmtu = kSctpMtu;
449 if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &params,
450 sizeof(params))) {
451 LOG_ERRNO(LS_ERROR) << debug_name_
452 << "Failed to set SCTP_PEER_ADDR_PARAMS.";
453 return false;
454 }
455
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000456 // Subscribe to SCTP event notifications.
457 int event_types[] = {SCTP_ASSOC_CHANGE,
458 SCTP_PEER_ADDR_CHANGE,
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000459 SCTP_SEND_FAILED_EVENT,
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000460 SCTP_SENDER_DRY_EVENT,
461 SCTP_STREAM_RESET_EVENT};
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000462 struct sctp_event event = {0};
463 event.se_assoc_id = SCTP_ALL_ASSOC;
464 event.se_on = 1;
tfarina5237aaf2015-11-10 23:44:30 -0800465 for (size_t i = 0; i < arraysize(event_types); i++) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000466 event.se_type = event_types[i];
467 if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_EVENT, &event,
468 sizeof(event)) < 0) {
469 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP_EVENT type: "
470 << event.se_type;
471 return false;
472 }
473 }
474
475 // Register this class as an address for usrsctp. This is used by SCTP to
476 // direct the packets received (by the created socket) to this class.
477 usrsctp_register_address(this);
478 sending_ = true;
479 return true;
480}
481
482void SctpDataMediaChannel::CloseSctpSocket() {
483 sending_ = false;
484 if (sock_) {
485 // We assume that SO_LINGER option is set to close the association when
486 // close is called. This means that any pending packets in usrsctp will be
487 // discarded instead of being sent.
488 usrsctp_close(sock_);
489 sock_ = NULL;
490 usrsctp_deregister_address(this);
491 }
492}
493
494bool SctpDataMediaChannel::Connect() {
495 LOG(LS_VERBOSE) << debug_name_ << "->Connect().";
496
497 // If we already have a socket connection, just return.
498 if (sock_) {
499 LOG(LS_WARNING) << debug_name_ << "->Connect(): Ignored as socket "
500 "is already established.";
501 return true;
502 }
503
504 // If no socket (it was closed) try to start it again. This can happen when
505 // the socket we are connecting to closes, does an sctp shutdown handshake,
506 // or behaves unexpectedly causing us to perform a CloseSctpSocket.
507 if (!sock_ && !OpenSctpSocket()) {
508 return false;
509 }
510
511 // Note: conversion from int to uint16_t happens on assignment.
512 sockaddr_conn local_sconn = GetSctpSockAddr(local_port_);
513 if (usrsctp_bind(sock_, reinterpret_cast<sockaddr *>(&local_sconn),
514 sizeof(local_sconn)) < 0) {
515 LOG_ERRNO(LS_ERROR) << debug_name_ << "->Connect(): "
516 << ("Failed usrsctp_bind");
517 CloseSctpSocket();
518 return false;
519 }
520
521 // Note: conversion from int to uint16_t happens on assignment.
522 sockaddr_conn remote_sconn = GetSctpSockAddr(remote_port_);
523 int connect_result = usrsctp_connect(
524 sock_, reinterpret_cast<sockaddr *>(&remote_sconn), sizeof(remote_sconn));
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000525 if (connect_result < 0 && errno != SCTP_EINPROGRESS) {
526 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed usrsctp_connect. got errno="
527 << errno << ", but wanted " << SCTP_EINPROGRESS;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000528 CloseSctpSocket();
529 return false;
530 }
531 return true;
532}
533
534void SctpDataMediaChannel::Disconnect() {
535 // TODO(ldixon): Consider calling |usrsctp_shutdown(sock_, ...)| to do a
536 // shutdown handshake and remove the association.
537 CloseSctpSocket();
538}
539
540bool SctpDataMediaChannel::SetSend(bool send) {
541 if (!sending_ && send) {
542 return Connect();
543 }
544 if (sending_ && !send) {
545 Disconnect();
546 }
547 return true;
548}
549
550bool SctpDataMediaChannel::SetReceive(bool receive) {
551 receiving_ = receive;
552 return true;
553}
554
Fredrik Solenbergb071a192015-09-17 16:42:56 +0200555bool SctpDataMediaChannel::SetSendParameters(const DataSendParameters& params) {
556 return SetSendCodecs(params.codecs);
557}
558
559bool SctpDataMediaChannel::SetRecvParameters(const DataRecvParameters& params) {
560 return SetRecvCodecs(params.codecs);
561}
562
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000563bool SctpDataMediaChannel::AddSendStream(const StreamParams& stream) {
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000564 return AddStream(stream);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000565}
566
Peter Boström0c4e06b2015-10-07 12:23:21 +0200567bool SctpDataMediaChannel::RemoveSendStream(uint32_t ssrc) {
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000568 return ResetStream(ssrc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000569}
570
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000571bool SctpDataMediaChannel::AddRecvStream(const StreamParams& stream) {
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +0000572 // SCTP DataChannels are always bi-directional and calling AddSendStream will
573 // enable both sending and receiving on the stream. So AddRecvStream is a
574 // no-op.
575 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000576}
577
Peter Boström0c4e06b2015-10-07 12:23:21 +0200578bool SctpDataMediaChannel::RemoveRecvStream(uint32_t ssrc) {
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +0000579 // SCTP DataChannels are always bi-directional and calling RemoveSendStream
580 // will disable both sending and receiving on the stream. So RemoveRecvStream
581 // is a no-op.
582 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000583}
584
585bool SctpDataMediaChannel::SendData(
586 const SendDataParams& params,
jbaucheec21bd2016-03-20 06:15:43 -0700587 const rtc::CopyOnWriteBuffer& payload,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000588 SendDataResult* result) {
589 if (result) {
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000590 // Preset |result| to assume an error. If SendData succeeds, we'll
591 // overwrite |*result| once more at the end.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000592 *result = SDR_ERROR;
593 }
594
595 if (!sending_) {
596 LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
597 << "Not sending packet with ssrc=" << params.ssrc
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000598 << " len=" << payload.size() << " before SetSend(true).";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000599 return false;
600 }
601
wu@webrtc.org91053e72013-08-10 07:18:04 +0000602 if (params.type != cricket::DMT_CONTROL &&
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000603 open_streams_.find(params.ssrc) == open_streams_.end()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000604 LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
605 << "Not sending data because ssrc is unknown: "
606 << params.ssrc;
607 return false;
608 }
609
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000610 //
611 // Send data using SCTP.
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000612 ssize_t send_res = 0; // result from usrsctp_sendv.
613 struct sctp_sendv_spa spa = {0};
614 spa.sendv_flags |= SCTP_SEND_SNDINFO_VALID;
615 spa.sendv_sndinfo.snd_sid = params.ssrc;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000616 spa.sendv_sndinfo.snd_ppid = rtc::HostToNetwork32(
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000617 GetPpid(params.type));
618
619 // Ordered implies reliable.
620 if (!params.ordered) {
621 spa.sendv_sndinfo.snd_flags |= SCTP_UNORDERED;
622 if (params.max_rtx_count >= 0 || params.max_rtx_ms == 0) {
623 spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
624 spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX;
625 spa.sendv_prinfo.pr_value = params.max_rtx_count;
626 } else {
627 spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
628 spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_TTL;
629 spa.sendv_prinfo.pr_value = params.max_rtx_ms;
630 }
631 }
632
633 // We don't fragment.
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000634 send_res = usrsctp_sendv(
635 sock_, payload.data(), static_cast<size_t>(payload.size()), NULL, 0, &spa,
636 rtc::checked_cast<socklen_t>(sizeof(spa)), SCTP_SENDV_SPA, 0);
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000637 if (send_res < 0) {
jiayl@webrtc.orgf7026cd2014-05-08 16:02:23 +0000638 if (errno == SCTP_EWOULDBLOCK) {
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000639 *result = SDR_BLOCK;
640 LOG(LS_INFO) << debug_name_ << "->SendData(...): EWOULDBLOCK returned";
641 } else {
642 LOG_ERRNO(LS_ERROR) << "ERROR:" << debug_name_
643 << "->SendData(...): "
644 << " usrsctp_sendv: ";
645 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000646 return false;
647 }
648 if (result) {
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000649 // Only way out now is success.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000650 *result = SDR_SUCCESS;
651 }
652 return true;
653}
654
655// Called by network interface when a packet has been received.
wu@webrtc.orga9890802013-12-13 00:21:03 +0000656void SctpDataMediaChannel::OnPacketReceived(
jbaucheec21bd2016-03-20 06:15:43 -0700657 rtc::CopyOnWriteBuffer* packet, const rtc::PacketTime& packet_time) {
henrikg91d6ede2015-09-17 00:24:34 -0700658 RTC_DCHECK(rtc::Thread::Current() == worker_thread_);
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000659 LOG(LS_VERBOSE) << debug_name_ << "->OnPacketReceived(...): "
660 << " length=" << packet->size() << ", sending: " << sending_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000661 // Only give receiving packets to usrsctp after if connected. This enables two
662 // peers to each make a connect call, but for them not to receive an INIT
663 // packet before they have called connect; least the last receiver of the INIT
664 // packet will have called connect, and a connection will be established.
665 if (sending_) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000666 // Pass received packet to SCTP stack. Once processed by usrsctp, the data
667 // will be will be given to the global OnSctpInboundData, and then,
668 // marshalled by a Post and handled with OnMessage.
jbaucheec21bd2016-03-20 06:15:43 -0700669 VerboseLogPacket(packet->cdata(), packet->size(), SCTP_DUMP_INBOUND);
670 usrsctp_conninput(this, packet->cdata(), packet->size(), 0);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000671 } else {
672 // TODO(ldixon): Consider caching the packet for very slightly better
673 // reliability.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000674 }
675}
676
677void SctpDataMediaChannel::OnInboundPacketFromSctpToChannel(
678 SctpInboundPacket* packet) {
679 LOG(LS_VERBOSE) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): "
680 << "Received SCTP data:"
681 << " ssrc=" << packet->params.ssrc
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000682 << " notification: " << (packet->flags & MSG_NOTIFICATION)
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000683 << " length=" << packet->buffer.size();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000684 // Sending a packet with data == NULL (no data) is SCTPs "close the
685 // connection" message. This sets sock_ = NULL;
kwiberg@webrtc.orgeebcab52015-03-24 09:19:06 +0000686 if (!packet->buffer.size() || !packet->buffer.data()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000687 LOG(LS_INFO) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): "
688 "No data, closing.";
689 return;
690 }
691 if (packet->flags & MSG_NOTIFICATION) {
jbaucheec21bd2016-03-20 06:15:43 -0700692 OnNotificationFromSctp(packet->buffer);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000693 } else {
jbaucheec21bd2016-03-20 06:15:43 -0700694 OnDataFromSctpToChannel(packet->params, packet->buffer);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000695 }
696}
697
698void SctpDataMediaChannel::OnDataFromSctpToChannel(
jbaucheec21bd2016-03-20 06:15:43 -0700699 const ReceiveDataParams& params, const rtc::CopyOnWriteBuffer& buffer) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000700 if (receiving_) {
701 LOG(LS_VERBOSE) << debug_name_ << "->OnDataFromSctpToChannel(...): "
jbaucheec21bd2016-03-20 06:15:43 -0700702 << "Posting with length: " << buffer.size()
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +0000703 << " on stream " << params.ssrc;
704 // Reports all received messages to upper layers, no matter whether the sid
705 // is known.
jbaucheec21bd2016-03-20 06:15:43 -0700706 SignalDataReceived(params, buffer.data<char>(), buffer.size());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000707 } else {
708 LOG(LS_WARNING) << debug_name_ << "->OnDataFromSctpToChannel(...): "
709 << "Not receiving packet with sid=" << params.ssrc
jbaucheec21bd2016-03-20 06:15:43 -0700710 << " len=" << buffer.size() << " before SetReceive(true).";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000711 }
712}
713
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000714bool SctpDataMediaChannel::AddStream(const StreamParams& stream) {
715 if (!stream.has_ssrcs()) {
716 return false;
717 }
718
Peter Boström0c4e06b2015-10-07 12:23:21 +0200719 const uint32_t ssrc = stream.first_ssrc();
lally27ed3cc2016-01-11 10:24:33 -0800720 if (ssrc >= cricket::kMaxSctpSid) {
721 LOG(LS_WARNING) << debug_name_ << "->Add(Send|Recv)Stream(...): "
722 << "Not adding data stream '" << stream.id
723 << "' with ssrc=" << ssrc
724 << " because stream ssrc is too high.";
725 return false;
726 } else if (open_streams_.find(ssrc) != open_streams_.end()) {
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +0000727 LOG(LS_WARNING) << debug_name_ << "->Add(Send|Recv)Stream(...): "
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000728 << "Not adding data stream '" << stream.id
729 << "' with ssrc=" << ssrc
730 << " because stream is already open.";
731 return false;
732 } else if (queued_reset_streams_.find(ssrc) != queued_reset_streams_.end()
733 || sent_reset_streams_.find(ssrc) != sent_reset_streams_.end()) {
734 LOG(LS_WARNING) << debug_name_ << "->Add(Send|Recv)Stream(...): "
735 << "Not adding data stream '" << stream.id
736 << "' with ssrc=" << ssrc
737 << " because stream is still closing.";
738 return false;
739 }
740
741 open_streams_.insert(ssrc);
742 return true;
743}
744
Peter Boström0c4e06b2015-10-07 12:23:21 +0200745bool SctpDataMediaChannel::ResetStream(uint32_t ssrc) {
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000746 // We typically get this called twice for the same stream, once each for
747 // Send and Recv.
748 StreamSet::iterator found = open_streams_.find(ssrc);
749
750 if (found == open_streams_.end()) {
751 LOG(LS_VERBOSE) << debug_name_ << "->ResetStream(" << ssrc << "): "
752 << "stream not found.";
753 return false;
754 } else {
755 LOG(LS_VERBOSE) << debug_name_ << "->ResetStream(" << ssrc << "): "
756 << "Removing and queuing RE-CONFIG chunk.";
757 open_streams_.erase(found);
758 }
759
760 // SCTP won't let you have more than one stream reset pending at a time, but
761 // you can close multiple streams in a single reset. So, we keep an internal
762 // queue of streams-to-reset, and send them as one reset message in
763 // SendQueuedStreamResets().
764 queued_reset_streams_.insert(ssrc);
765
766 // Signal our stream-reset logic that it should try to send now, if it can.
767 SendQueuedStreamResets();
768
769 // The stream will actually get removed when we get the acknowledgment.
770 return true;
771}
772
jbaucheec21bd2016-03-20 06:15:43 -0700773void SctpDataMediaChannel::OnNotificationFromSctp(
774 const rtc::CopyOnWriteBuffer& buffer) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000775 const sctp_notification& notification =
jbaucheec21bd2016-03-20 06:15:43 -0700776 reinterpret_cast<const sctp_notification&>(*buffer.data());
777 ASSERT(notification.sn_header.sn_length == buffer.size());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000778
779 // TODO(ldixon): handle notifications appropriately.
780 switch (notification.sn_header.sn_type) {
781 case SCTP_ASSOC_CHANGE:
782 LOG(LS_VERBOSE) << "SCTP_ASSOC_CHANGE";
783 OnNotificationAssocChange(notification.sn_assoc_change);
784 break;
785 case SCTP_REMOTE_ERROR:
786 LOG(LS_INFO) << "SCTP_REMOTE_ERROR";
787 break;
788 case SCTP_SHUTDOWN_EVENT:
789 LOG(LS_INFO) << "SCTP_SHUTDOWN_EVENT";
790 break;
791 case SCTP_ADAPTATION_INDICATION:
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000792 LOG(LS_INFO) << "SCTP_ADAPTATION_INDICATION";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000793 break;
794 case SCTP_PARTIAL_DELIVERY_EVENT:
795 LOG(LS_INFO) << "SCTP_PARTIAL_DELIVERY_EVENT";
796 break;
797 case SCTP_AUTHENTICATION_EVENT:
798 LOG(LS_INFO) << "SCTP_AUTHENTICATION_EVENT";
799 break;
800 case SCTP_SENDER_DRY_EVENT:
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000801 LOG(LS_VERBOSE) << "SCTP_SENDER_DRY_EVENT";
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000802 SignalReadyToSend(true);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000803 break;
804 // TODO(ldixon): Unblock after congestion.
805 case SCTP_NOTIFICATIONS_STOPPED_EVENT:
806 LOG(LS_INFO) << "SCTP_NOTIFICATIONS_STOPPED_EVENT";
807 break;
808 case SCTP_SEND_FAILED_EVENT:
809 LOG(LS_INFO) << "SCTP_SEND_FAILED_EVENT";
810 break;
811 case SCTP_STREAM_RESET_EVENT:
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000812 OnStreamResetEvent(&notification.sn_strreset_event);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000813 break;
814 case SCTP_ASSOC_RESET_EVENT:
815 LOG(LS_INFO) << "SCTP_ASSOC_RESET_EVENT";
816 break;
817 case SCTP_STREAM_CHANGE_EVENT:
818 LOG(LS_INFO) << "SCTP_STREAM_CHANGE_EVENT";
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000819 // An acknowledgment we get after our stream resets have gone through,
820 // if they've failed. We log the message, but don't react -- we don't
821 // keep around the last-transmitted set of SSIDs we wanted to close for
822 // error recovery. It doesn't seem likely to occur, and if so, likely
823 // harmless within the lifetime of a single SCTP association.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000824 break;
825 default:
826 LOG(LS_WARNING) << "Unknown SCTP event: "
827 << notification.sn_header.sn_type;
828 break;
829 }
830}
831
832void SctpDataMediaChannel::OnNotificationAssocChange(
833 const sctp_assoc_change& change) {
834 switch (change.sac_state) {
835 case SCTP_COMM_UP:
836 LOG(LS_VERBOSE) << "Association change SCTP_COMM_UP";
837 break;
838 case SCTP_COMM_LOST:
839 LOG(LS_INFO) << "Association change SCTP_COMM_LOST";
840 break;
841 case SCTP_RESTART:
842 LOG(LS_INFO) << "Association change SCTP_RESTART";
843 break;
844 case SCTP_SHUTDOWN_COMP:
845 LOG(LS_INFO) << "Association change SCTP_SHUTDOWN_COMP";
846 break;
847 case SCTP_CANT_STR_ASSOC:
848 LOG(LS_INFO) << "Association change SCTP_CANT_STR_ASSOC";
849 break;
850 default:
851 LOG(LS_INFO) << "Association change UNKNOWN";
852 break;
853 }
854}
855
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000856void SctpDataMediaChannel::OnStreamResetEvent(
857 const struct sctp_stream_reset_event* evt) {
858 // A stream reset always involves two RE-CONFIG chunks for us -- we always
859 // simultaneously reset a sid's sequence number in both directions. The
860 // requesting side transmits a RE-CONFIG chunk and waits for the peer to send
861 // one back. Both sides get this SCTP_STREAM_RESET_EVENT when they receive
862 // RE-CONFIGs.
863 const int num_ssrcs = (evt->strreset_length - sizeof(*evt)) /
864 sizeof(evt->strreset_stream_list[0]);
865 LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
866 << "): Flags = 0x"
867 << std::hex << evt->strreset_flags << " ("
868 << ListFlags(evt->strreset_flags) << ")";
869 LOG(LS_VERBOSE) << "Assoc = " << evt->strreset_assoc_id << ", Streams = ["
870 << ListArray(evt->strreset_stream_list, num_ssrcs)
871 << "], Open: ["
872 << ListStreams(open_streams_) << "], Q'd: ["
873 << ListStreams(queued_reset_streams_) << "], Sent: ["
874 << ListStreams(sent_reset_streams_) << "]";
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000875
876 // If both sides try to reset some streams at the same time (even if they're
877 // disjoint sets), we can get reset failures.
878 if (evt->strreset_flags & SCTP_STREAM_RESET_FAILED) {
879 // OK, just try again. The stream IDs sent over when the RESET_FAILED flag
880 // is set seem to be garbage values. Ignore them.
881 queued_reset_streams_.insert(
882 sent_reset_streams_.begin(),
883 sent_reset_streams_.end());
884 sent_reset_streams_.clear();
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000885
886 } else if (evt->strreset_flags & SCTP_STREAM_RESET_INCOMING_SSN) {
887 // Each side gets an event for each direction of a stream. That is,
888 // closing sid k will make each side receive INCOMING and OUTGOING reset
889 // events for k. As per RFC6525, Section 5, paragraph 2, each side will
890 // get an INCOMING event first.
891 for (int i = 0; i < num_ssrcs; i++) {
892 const int stream_id = evt->strreset_stream_list[i];
893
894 // See if this stream ID was closed by our peer or ourselves.
895 StreamSet::iterator it = sent_reset_streams_.find(stream_id);
896
897 // The reset was requested locally.
898 if (it != sent_reset_streams_.end()) {
899 LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
900 << "): local sid " << stream_id << " acknowledged.";
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000901 sent_reset_streams_.erase(it);
902
903 } else if ((it = open_streams_.find(stream_id))
904 != open_streams_.end()) {
905 // The peer requested the reset.
906 LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
907 << "): closing sid " << stream_id;
908 open_streams_.erase(it);
buildbot@webrtc.org1d66be22014-05-29 22:54:24 +0000909 SignalStreamClosedRemotely(stream_id);
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000910
911 } else if ((it = queued_reset_streams_.find(stream_id))
912 != queued_reset_streams_.end()) {
913 // The peer requested the reset, but there was a local reset
914 // queued.
915 LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
916 << "): double-sided close for sid " << stream_id;
917 // Both sides want the stream closed, and the peer got to send the
918 // RE-CONFIG first. Treat it like the local Remove(Send|Recv)Stream
919 // finished quickly.
920 queued_reset_streams_.erase(it);
921
922 } else {
923 // This stream is unknown. Sometimes this can be from an
924 // RESET_FAILED-related retransmit.
925 LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
926 << "): Unknown sid " << stream_id;
927 }
928 }
929 }
930
jiayl@webrtc.org1a6c6282014-06-12 21:59:29 +0000931 // Always try to send the queued RESET because this call indicates that the
932 // last local RESET or remote RESET has made some progress.
933 SendQueuedStreamResets();
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000934}
935
wu@webrtc.org78187522013-10-07 23:32:02 +0000936// Puts the specified |param| from the codec identified by |id| into |dest|
937// and returns true. Or returns false if it wasn't there, leaving |dest|
938// untouched.
939static bool GetCodecIntParameter(const std::vector<DataCodec>& codecs,
940 int id, const std::string& name,
941 const std::string& param, int* dest) {
942 std::string value;
943 Codec match_pattern;
944 match_pattern.id = id;
945 match_pattern.name = name;
946 for (size_t i = 0; i < codecs.size(); ++i) {
947 if (codecs[i].Matches(match_pattern)) {
948 if (codecs[i].GetParam(param, &value)) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000949 *dest = rtc::FromString<int>(value);
wu@webrtc.org78187522013-10-07 23:32:02 +0000950 return true;
951 }
952 }
953 }
954 return false;
955}
956
957bool SctpDataMediaChannel::SetSendCodecs(const std::vector<DataCodec>& codecs) {
958 return GetCodecIntParameter(
959 codecs, kGoogleSctpDataCodecId, kGoogleSctpDataCodecName, kCodecParamPort,
960 &remote_port_);
961}
962
963bool SctpDataMediaChannel::SetRecvCodecs(const std::vector<DataCodec>& codecs) {
964 return GetCodecIntParameter(
965 codecs, kGoogleSctpDataCodecId, kGoogleSctpDataCodecName, kCodecParamPort,
966 &local_port_);
967}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000968
969void SctpDataMediaChannel::OnPacketFromSctpToNetwork(
jbaucheec21bd2016-03-20 06:15:43 -0700970 rtc::CopyOnWriteBuffer* buffer) {
Lally Singhe8386d22015-08-28 14:54:37 -0400971 // usrsctp seems to interpret the MTU we give it strangely -- it seems to
972 // give us back packets bigger than that MTU, if only by a fixed amount.
973 // This is that amount that we've observed.
974 const int kSctpOverhead = 76;
975 if (buffer->size() > (kSctpOverhead + kSctpMtu)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000976 LOG(LS_ERROR) << debug_name_ << "->OnPacketFromSctpToNetwork(...): "
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000977 << "SCTP seems to have made a packet that is bigger "
Lally Singhe8386d22015-08-28 14:54:37 -0400978 << "than its official MTU: " << buffer->size()
979 << " vs max of " << kSctpMtu
980 << " even after adding " << kSctpOverhead
981 << " extra SCTP overhead";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000982 }
stefanc1aeaf02015-10-15 07:26:07 -0700983 MediaChannel::SendPacket(buffer, rtc::PacketOptions());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000984}
985
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000986bool SctpDataMediaChannel::SendQueuedStreamResets() {
987 if (!sent_reset_streams_.empty() || queued_reset_streams_.empty())
988 return true;
989
990 LOG(LS_VERBOSE) << "SendQueuedStreamResets[" << debug_name_ << "]: Sending ["
991 << ListStreams(queued_reset_streams_) << "], Open: ["
992 << ListStreams(open_streams_) << "], Sent: ["
993 << ListStreams(sent_reset_streams_) << "]";
994
995 const size_t num_streams = queued_reset_streams_.size();
Peter Boström0c4e06b2015-10-07 12:23:21 +0200996 const size_t num_bytes =
997 sizeof(struct sctp_reset_streams) + (num_streams * sizeof(uint16_t));
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000998
Peter Boström0c4e06b2015-10-07 12:23:21 +0200999 std::vector<uint8_t> reset_stream_buf(num_bytes, 0);
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +00001000 struct sctp_reset_streams* resetp = reinterpret_cast<sctp_reset_streams*>(
1001 &reset_stream_buf[0]);
1002 resetp->srs_assoc_id = SCTP_ALL_ASSOC;
1003 resetp->srs_flags = SCTP_STREAM_RESET_INCOMING | SCTP_STREAM_RESET_OUTGOING;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001004 resetp->srs_number_streams = rtc::checked_cast<uint16_t>(num_streams);
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +00001005 int result_idx = 0;
1006 for (StreamSet::iterator it = queued_reset_streams_.begin();
1007 it != queued_reset_streams_.end(); ++it) {
1008 resetp->srs_stream_list[result_idx++] = *it;
1009 }
1010
jiayl@webrtc.orga576faf2014-01-29 17:45:53 +00001011 int ret = usrsctp_setsockopt(
1012 sock_, IPPROTO_SCTP, SCTP_RESET_STREAMS, resetp,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001013 rtc::checked_cast<socklen_t>(reset_stream_buf.size()));
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +00001014 if (ret < 0) {
1015 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to send a stream reset for "
1016 << num_streams << " streams";
1017 return false;
1018 }
1019
1020 // sent_reset_streams_ is empty, and all the queued_reset_streams_ go into
1021 // it now.
1022 queued_reset_streams_.swap(sent_reset_streams_);
1023 return true;
1024}
1025
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001026void SctpDataMediaChannel::OnMessage(rtc::Message* msg) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001027 switch (msg->message_id) {
1028 case MSG_SCTPINBOUNDPACKET: {
kwiberg686a8ef2016-02-26 03:00:35 -08001029 std::unique_ptr<InboundPacketMessage> pdata(
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +00001030 static_cast<InboundPacketMessage*>(msg->pdata));
1031 OnInboundPacketFromSctpToChannel(pdata->data().get());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001032 break;
1033 }
1034 case MSG_SCTPOUTBOUNDPACKET: {
kwiberg686a8ef2016-02-26 03:00:35 -08001035 std::unique_ptr<OutboundPacketMessage> pdata(
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +00001036 static_cast<OutboundPacketMessage*>(msg->pdata));
1037 OnPacketFromSctpToNetwork(pdata->data().get());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001038 break;
1039 }
1040 }
1041}
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001042} // namespace cricket