blob: 2b86001f166bab35119a22c445abec7215ae9b0b [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
2 * libjingle SCTP
3 * Copyright 2012 Google Inc, and Robin Seggelmann
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "talk/media/sctp/sctpdataengine.h"
29
henrike@webrtc.org28e20752013-07-10 00:45:36 +000030#include <stdarg.h>
31#include <stdio.h>
32#include <vector>
33
wu@webrtc.org1d1ffc92013-10-16 18:12:02 +000034#include "talk/app/webrtc/datachannelinterface.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000035#include "talk/base/buffer.h"
36#include "talk/base/helpers.h"
37#include "talk/base/logging.h"
38#include "talk/media/base/codec.h"
39#include "talk/media/base/constants.h"
40#include "talk/media/base/streamparams.h"
wu@webrtc.org1d1ffc92013-10-16 18:12:02 +000041#include "talk/media/sctp/sctputils.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000042#include "usrsctplib/usrsctp.h"
43
henrike@webrtc.org28e20752013-07-10 00:45:36 +000044namespace cricket {
45
46// This is the SCTP port to use. It is passed along the wire and the listener
47// and connector must be using the same port. It is not related to the ports at
48// the IP level. (Corresponds to: sockaddr_conn.sconn_port in usrsctp.h)
49//
50// TODO(ldixon): Allow port to be set from higher level code.
51static const int kSctpDefaultPort = 5001;
52// TODO(ldixon): Find where this is defined, and also check is Sctp really
53// respects this.
54static const size_t kSctpMtu = 1280;
55
56enum {
57 MSG_SCTPINBOUNDPACKET = 1, // MessageData is SctpInboundPacket
58 MSG_SCTPOUTBOUNDPACKET = 2, // MessageData is talk_base:Buffer
59};
60
61struct SctpInboundPacket {
62 talk_base::Buffer buffer;
63 ReceiveDataParams params;
64 // The |flags| parameter is used by SCTP to distinguish notification packets
65 // from other types of packets.
66 int flags;
67};
68
mallinath@webrtc.org1112c302013-09-23 20:34:45 +000069// Helper for logging SCTP messages.
henrike@webrtc.org28e20752013-07-10 00:45:36 +000070static void debug_sctp_printf(const char *format, ...) {
71 char s[255];
72 va_list ap;
73 va_start(ap, format);
74 vsnprintf(s, sizeof(s), format, ap);
mallinath@webrtc.org1112c302013-09-23 20:34:45 +000075 LOG(LS_INFO) << "SCTP: " << s;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000076 va_end(ap);
77}
78
mallinath@webrtc.org1112c302013-09-23 20:34:45 +000079// Get the PPID to use for the terminating fragment of this type.
80static SctpDataMediaChannel::PayloadProtocolIdentifier GetPpid(
81 cricket::DataMessageType type) {
82 switch (type) {
83 default:
84 case cricket::DMT_NONE:
85 return SctpDataMediaChannel::PPID_NONE;
86 case cricket::DMT_CONTROL:
87 return SctpDataMediaChannel::PPID_CONTROL;
88 case cricket::DMT_BINARY:
89 return SctpDataMediaChannel::PPID_BINARY_LAST;
90 case cricket::DMT_TEXT:
91 return SctpDataMediaChannel::PPID_TEXT_LAST;
92 };
93}
94
95static bool GetDataMediaType(
96 SctpDataMediaChannel::PayloadProtocolIdentifier ppid,
97 cricket::DataMessageType *dest) {
98 ASSERT(dest != NULL);
99 switch (ppid) {
100 case SctpDataMediaChannel::PPID_BINARY_PARTIAL:
101 case SctpDataMediaChannel::PPID_BINARY_LAST:
102 *dest = cricket::DMT_BINARY;
103 return true;
104
105 case SctpDataMediaChannel::PPID_TEXT_PARTIAL:
106 case SctpDataMediaChannel::PPID_TEXT_LAST:
107 *dest = cricket::DMT_TEXT;
108 return true;
109
110 case SctpDataMediaChannel::PPID_CONTROL:
111 *dest = cricket::DMT_CONTROL;
112 return true;
113
114 case SctpDataMediaChannel::PPID_NONE:
115 *dest = cricket::DMT_NONE;
116 return true;
117
118 default:
119 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000120 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000121}
122
123// This is the callback usrsctp uses when there's data to send on the network
124// that has been wrapped appropriatly for the SCTP protocol.
125static int OnSctpOutboundPacket(void* addr, void* data, size_t length,
126 uint8_t tos, uint8_t set_df) {
127 SctpDataMediaChannel* channel = static_cast<SctpDataMediaChannel*>(addr);
128 LOG(LS_VERBOSE) << "global OnSctpOutboundPacket():"
129 << "addr: " << addr << "; length: " << length
130 << "; tos: " << std::hex << static_cast<int>(tos)
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000131 << "; set_df: " << std::hex << static_cast<int>(set_df);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000132 // Note: We have to copy the data; the caller will delete it.
133 talk_base::Buffer* buffer = new talk_base::Buffer(data, length);
134 channel->worker_thread()->Post(channel, MSG_SCTPOUTBOUNDPACKET,
135 talk_base::WrapMessageData(buffer));
136 return 0;
137}
138
139// This is the callback called from usrsctp when data has been received, after
140// a packet has been interpreted and parsed by usrsctp and found to contain
141// payload data. It is called by a usrsctp thread. It is assumed this function
142// will free the memory used by 'data'.
143static int OnSctpInboundPacket(struct socket* sock, union sctp_sockstore addr,
144 void* data, size_t length,
145 struct sctp_rcvinfo rcv, int flags,
146 void* ulp_info) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000147 SctpDataMediaChannel* channel = static_cast<SctpDataMediaChannel*>(ulp_info);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000148 // Post data to the channel's receiver thread (copying it).
149 // TODO(ldixon): Unclear if copy is needed as this method is responsible for
150 // memory cleanup. But this does simplify code.
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000151 const SctpDataMediaChannel::PayloadProtocolIdentifier ppid =
152 static_cast<SctpDataMediaChannel::PayloadProtocolIdentifier>(
153 talk_base::HostToNetwork32(rcv.rcv_ppid));
154 cricket::DataMessageType type = cricket::DMT_NONE;
155 if (!GetDataMediaType(ppid, &type) && !(flags & MSG_NOTIFICATION)) {
156 // It's neither a notification nor a recognized data packet. Drop it.
157 LOG(LS_ERROR) << "Received an unknown PPID " << ppid
158 << " on an SCTP packet. Dropping.";
159 } else {
160 SctpInboundPacket* packet = new SctpInboundPacket;
161 packet->buffer.SetData(data, length);
162 packet->params.ssrc = rcv.rcv_sid;
163 packet->params.seq_num = rcv.rcv_ssn;
164 packet->params.timestamp = rcv.rcv_tsn;
165 packet->params.type = type;
166 packet->flags = flags;
167 channel->worker_thread()->Post(channel, MSG_SCTPINBOUNDPACKET,
168 talk_base::WrapMessageData(packet));
169 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000170 free(data);
171 return 1;
172}
173
174// Set the initial value of the static SCTP Data Engines reference count.
175int SctpDataEngine::usrsctp_engines_count = 0;
176
177SctpDataEngine::SctpDataEngine() {
178 if (usrsctp_engines_count == 0) {
179 // First argument is udp_encapsulation_port, which is not releveant for our
180 // AF_CONN use of sctp.
181 usrsctp_init(0, cricket::OnSctpOutboundPacket, debug_sctp_printf);
182
183 // To turn on/off detailed SCTP debugging. You will also need to have the
184 // SCTP_DEBUG cpp defines flag.
185 // usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_ALL);
186
187 // TODO(ldixon): Consider turning this on/off.
188 usrsctp_sysctl_set_sctp_ecn_enable(0);
189
190 // TODO(ldixon): Consider turning this on/off.
191 // This is not needed right now (we don't do dynamic address changes):
192 // If SCTP Auto-ASCONF is enabled, the peer is informed automatically
193 // when a new address is added or removed. This feature is enabled by
194 // default.
195 // usrsctp_sysctl_set_sctp_auto_asconf(0);
196
197 // TODO(ldixon): Consider turning this on/off.
198 // Add a blackhole sysctl. Setting it to 1 results in no ABORTs
199 // being sent in response to INITs, setting it to 2 results
200 // in no ABORTs being sent for received OOTB packets.
201 // This is similar to the TCP sysctl.
202 //
203 // See: http://lakerest.net/pipermail/sctp-coders/2012-January/009438.html
204 // See: http://svnweb.freebsd.org/base?view=revision&revision=229805
205 // usrsctp_sysctl_set_sctp_blackhole(2);
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000206
207 // Set the number of default outgoing streams. This is the number we'll
208 // send in the SCTP INIT message. The 'appropriate default' in the
209 // second paragraph of
210 // http://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-05#section-6.2
211 // is cricket::kMaxSctpSid.
212 usrsctp_sysctl_set_sctp_nr_outgoing_streams_default(
213 cricket::kMaxSctpSid);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000214 }
215 usrsctp_engines_count++;
216
217 // We don't put in a codec because we don't want one offered when we
218 // use the hybrid data engine.
219 // codecs_.push_back(cricket::DataCodec( kGoogleSctpDataCodecId,
220 // kGoogleSctpDataCodecName, 0));
221}
222
223SctpDataEngine::~SctpDataEngine() {
224 // TODO(ldixon): There is currently a bug in teardown of usrsctp that blocks
225 // indefintely if a finish call made too soon after close calls. So teardown
226 // has been skipped. Once the bug is fixed, retest and enable teardown.
227 //
228 // usrsctp_engines_count--;
229 // LOG(LS_VERBOSE) << "usrsctp_engines_count:" << usrsctp_engines_count;
230 // if (usrsctp_engines_count == 0) {
231 // if (usrsctp_finish() != 0) {
232 // LOG(LS_WARNING) << "usrsctp_finish.";
233 // }
234 // }
235}
236
237DataMediaChannel* SctpDataEngine::CreateChannel(
238 DataChannelType data_channel_type) {
239 if (data_channel_type != DCT_SCTP) {
240 return NULL;
241 }
242 return new SctpDataMediaChannel(talk_base::Thread::Current());
243}
244
245SctpDataMediaChannel::SctpDataMediaChannel(talk_base::Thread* thread)
246 : worker_thread_(thread),
wu@webrtc.org78187522013-10-07 23:32:02 +0000247 local_port_(-1),
248 remote_port_(-1),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000249 sock_(NULL),
250 sending_(false),
251 receiving_(false),
252 debug_name_("SctpDataMediaChannel") {
253}
254
255SctpDataMediaChannel::~SctpDataMediaChannel() {
256 CloseSctpSocket();
257}
258
259sockaddr_conn SctpDataMediaChannel::GetSctpSockAddr(int port) {
260 sockaddr_conn sconn = {0};
261 sconn.sconn_family = AF_CONN;
262#ifdef HAVE_SCONN_LEN
263 sconn.sconn_len = sizeof(sockaddr_conn);
264#endif
265 // Note: conversion from int to uint16_t happens here.
266 sconn.sconn_port = talk_base::HostToNetwork16(port);
267 sconn.sconn_addr = this;
268 return sconn;
269}
270
271bool SctpDataMediaChannel::OpenSctpSocket() {
272 if (sock_) {
273 LOG(LS_VERBOSE) << debug_name_
274 << "->Ignoring attempt to re-create existing socket.";
275 return false;
276 }
277 sock_ = usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP,
278 cricket::OnSctpInboundPacket, NULL, 0, this);
279 if (!sock_) {
280 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to create SCTP socket.";
281 return false;
282 }
283
284 // Make the socket non-blocking. Connect, close, shutdown etc will not block
285 // the thread waiting for the socket operation to complete.
286 if (usrsctp_set_non_blocking(sock_, 1) < 0) {
287 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP to non blocking.";
288 return false;
289 }
290
291 // This ensures that the usrsctp close call deletes the association. This
292 // prevents usrsctp from calling OnSctpOutboundPacket with references to
293 // this class as the address.
294 linger linger_opt;
295 linger_opt.l_onoff = 1;
296 linger_opt.l_linger = 0;
297 if (usrsctp_setsockopt(sock_, SOL_SOCKET, SO_LINGER, &linger_opt,
298 sizeof(linger_opt))) {
299 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SO_LINGER.";
300 return false;
301 }
302
sergeyu@chromium.orga59696b2013-09-13 23:48:58 +0000303 uint32_t nodelay = 1;
304 if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_NODELAY, &nodelay,
305 sizeof(nodelay))) {
306 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP_NODELAY.";
307 return false;
308 }
309
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000310 // Subscribe to SCTP event notifications.
311 int event_types[] = {SCTP_ASSOC_CHANGE,
312 SCTP_PEER_ADDR_CHANGE,
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000313 SCTP_SEND_FAILED_EVENT,
314 SCTP_SENDER_DRY_EVENT};
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000315 struct sctp_event event = {0};
316 event.se_assoc_id = SCTP_ALL_ASSOC;
317 event.se_on = 1;
318 for (size_t i = 0; i < ARRAY_SIZE(event_types); i++) {
319 event.se_type = event_types[i];
320 if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_EVENT, &event,
321 sizeof(event)) < 0) {
322 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP_EVENT type: "
323 << event.se_type;
324 return false;
325 }
326 }
327
328 // Register this class as an address for usrsctp. This is used by SCTP to
329 // direct the packets received (by the created socket) to this class.
330 usrsctp_register_address(this);
331 sending_ = true;
332 return true;
333}
334
335void SctpDataMediaChannel::CloseSctpSocket() {
336 sending_ = false;
337 if (sock_) {
338 // We assume that SO_LINGER option is set to close the association when
339 // close is called. This means that any pending packets in usrsctp will be
340 // discarded instead of being sent.
341 usrsctp_close(sock_);
342 sock_ = NULL;
343 usrsctp_deregister_address(this);
344 }
345}
346
347bool SctpDataMediaChannel::Connect() {
348 LOG(LS_VERBOSE) << debug_name_ << "->Connect().";
wu@webrtc.org78187522013-10-07 23:32:02 +0000349 if (remote_port_ < 0) {
350 remote_port_ = kSctpDefaultPort;
351 }
352 if (local_port_ < 0) {
353 local_port_ = kSctpDefaultPort;
354 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000355
356 // If we already have a socket connection, just return.
357 if (sock_) {
358 LOG(LS_WARNING) << debug_name_ << "->Connect(): Ignored as socket "
359 "is already established.";
360 return true;
361 }
362
363 // If no socket (it was closed) try to start it again. This can happen when
364 // the socket we are connecting to closes, does an sctp shutdown handshake,
365 // or behaves unexpectedly causing us to perform a CloseSctpSocket.
366 if (!sock_ && !OpenSctpSocket()) {
367 return false;
368 }
369
370 // Note: conversion from int to uint16_t happens on assignment.
371 sockaddr_conn local_sconn = GetSctpSockAddr(local_port_);
372 if (usrsctp_bind(sock_, reinterpret_cast<sockaddr *>(&local_sconn),
373 sizeof(local_sconn)) < 0) {
374 LOG_ERRNO(LS_ERROR) << debug_name_ << "->Connect(): "
375 << ("Failed usrsctp_bind");
376 CloseSctpSocket();
377 return false;
378 }
379
380 // Note: conversion from int to uint16_t happens on assignment.
381 sockaddr_conn remote_sconn = GetSctpSockAddr(remote_port_);
382 int connect_result = usrsctp_connect(
383 sock_, reinterpret_cast<sockaddr *>(&remote_sconn), sizeof(remote_sconn));
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000384 if (connect_result < 0 && errno != SCTP_EINPROGRESS) {
385 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed usrsctp_connect. got errno="
386 << errno << ", but wanted " << SCTP_EINPROGRESS;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000387 CloseSctpSocket();
388 return false;
389 }
390 return true;
391}
392
393void SctpDataMediaChannel::Disconnect() {
394 // TODO(ldixon): Consider calling |usrsctp_shutdown(sock_, ...)| to do a
395 // shutdown handshake and remove the association.
396 CloseSctpSocket();
397}
398
399bool SctpDataMediaChannel::SetSend(bool send) {
400 if (!sending_ && send) {
401 return Connect();
402 }
403 if (sending_ && !send) {
404 Disconnect();
405 }
406 return true;
407}
408
409bool SctpDataMediaChannel::SetReceive(bool receive) {
410 receiving_ = receive;
411 return true;
412}
413
414bool SctpDataMediaChannel::AddSendStream(const StreamParams& stream) {
415 if (!stream.has_ssrcs()) {
416 return false;
417 }
418
419 StreamParams found_stream;
wu@webrtc.org91053e72013-08-10 07:18:04 +0000420 // TODO(lally): Consider keeping this sorted.
421 if (GetStreamBySsrc(streams_, stream.first_ssrc(), &found_stream)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000422 LOG(LS_WARNING) << debug_name_ << "->AddSendStream(...): "
423 << "Not adding data send stream '" << stream.id
424 << "' with ssrc=" << stream.first_ssrc()
425 << " because stream already exists.";
426 return false;
427 }
428
wu@webrtc.org91053e72013-08-10 07:18:04 +0000429 streams_.push_back(stream);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000430 return true;
431}
432
433bool SctpDataMediaChannel::RemoveSendStream(uint32 ssrc) {
434 StreamParams found_stream;
wu@webrtc.org91053e72013-08-10 07:18:04 +0000435 if (!GetStreamBySsrc(streams_, ssrc, &found_stream)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000436 return false;
437 }
438
wu@webrtc.org91053e72013-08-10 07:18:04 +0000439 RemoveStreamBySsrc(&streams_, ssrc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000440 return true;
441}
442
443// Note: expects exactly one ssrc. If none are given, it will fail. If more
444// than one are given, it will use the first.
445bool SctpDataMediaChannel::AddRecvStream(const StreamParams& stream) {
446 if (!stream.has_ssrcs()) {
447 return false;
448 }
449
450 StreamParams found_stream;
wu@webrtc.org91053e72013-08-10 07:18:04 +0000451 if (GetStreamBySsrc(streams_, stream.first_ssrc(), &found_stream)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000452 LOG(LS_WARNING) << debug_name_ << "->AddRecvStream(...): "
453 << "Not adding data recv stream '" << stream.id
454 << "' with ssrc=" << stream.first_ssrc()
455 << " because stream already exists.";
456 return false;
457 }
458
wu@webrtc.org91053e72013-08-10 07:18:04 +0000459 streams_.push_back(stream);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000460 LOG(LS_VERBOSE) << debug_name_ << "->AddRecvStream(...): "
461 << "Added data recv stream '" << stream.id
462 << "' with ssrc=" << stream.first_ssrc();
463 return true;
464}
465
466bool SctpDataMediaChannel::RemoveRecvStream(uint32 ssrc) {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000467 RemoveStreamBySsrc(&streams_, ssrc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000468 return true;
469}
470
471bool SctpDataMediaChannel::SendData(
472 const SendDataParams& params,
473 const talk_base::Buffer& payload,
474 SendDataResult* result) {
475 if (result) {
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000476 // Preset |result| to assume an error. If SendData succeeds, we'll
477 // overwrite |*result| once more at the end.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000478 *result = SDR_ERROR;
479 }
480
481 if (!sending_) {
482 LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
483 << "Not sending packet with ssrc=" << params.ssrc
484 << " len=" << payload.length() << " before SetSend(true).";
485 return false;
486 }
487
488 StreamParams found_stream;
wu@webrtc.org91053e72013-08-10 07:18:04 +0000489 if (params.type != cricket::DMT_CONTROL &&
490 !GetStreamBySsrc(streams_, params.ssrc, &found_stream)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000491 LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
492 << "Not sending data because ssrc is unknown: "
493 << params.ssrc;
494 return false;
495 }
496
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000497 //
498 // Send data using SCTP.
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000499 ssize_t send_res = 0; // result from usrsctp_sendv.
500 struct sctp_sendv_spa spa = {0};
501 spa.sendv_flags |= SCTP_SEND_SNDINFO_VALID;
502 spa.sendv_sndinfo.snd_sid = params.ssrc;
503 spa.sendv_sndinfo.snd_ppid = talk_base::HostToNetwork32(
504 GetPpid(params.type));
505
506 // Ordered implies reliable.
507 if (!params.ordered) {
508 spa.sendv_sndinfo.snd_flags |= SCTP_UNORDERED;
509 if (params.max_rtx_count >= 0 || params.max_rtx_ms == 0) {
510 spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
511 spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX;
512 spa.sendv_prinfo.pr_value = params.max_rtx_count;
513 } else {
514 spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
515 spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_TTL;
516 spa.sendv_prinfo.pr_value = params.max_rtx_ms;
517 }
518 }
519
520 // We don't fragment.
521 send_res = usrsctp_sendv(sock_, payload.data(),
522 static_cast<size_t>(payload.length()),
523 NULL, 0, &spa,
524 static_cast<socklen_t>(sizeof(spa)),
525 SCTP_SENDV_SPA, 0);
526 if (send_res < 0) {
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000527 if (errno == EWOULDBLOCK) {
528 *result = SDR_BLOCK;
529 LOG(LS_INFO) << debug_name_ << "->SendData(...): EWOULDBLOCK returned";
530 } else {
531 LOG_ERRNO(LS_ERROR) << "ERROR:" << debug_name_
532 << "->SendData(...): "
533 << " usrsctp_sendv: ";
534 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000535 return false;
536 }
537 if (result) {
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000538 // Only way out now is success.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000539 *result = SDR_SUCCESS;
540 }
541 return true;
542}
543
544// Called by network interface when a packet has been received.
545void SctpDataMediaChannel::OnPacketReceived(talk_base::Buffer* packet) {
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000546 LOG(LS_VERBOSE) << debug_name_ << "->OnPacketReceived(...): " << " length="
547 << packet->length() << ", sending: " << sending_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000548 // Only give receiving packets to usrsctp after if connected. This enables two
549 // peers to each make a connect call, but for them not to receive an INIT
550 // packet before they have called connect; least the last receiver of the INIT
551 // packet will have called connect, and a connection will be established.
552 if (sending_) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000553 // Pass received packet to SCTP stack. Once processed by usrsctp, the data
554 // will be will be given to the global OnSctpInboundData, and then,
555 // marshalled by a Post and handled with OnMessage.
556 usrsctp_conninput(this, packet->data(), packet->length(), 0);
557 } else {
558 // TODO(ldixon): Consider caching the packet for very slightly better
559 // reliability.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000560 }
561}
562
563void SctpDataMediaChannel::OnInboundPacketFromSctpToChannel(
564 SctpInboundPacket* packet) {
565 LOG(LS_VERBOSE) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): "
566 << "Received SCTP data:"
567 << " ssrc=" << packet->params.ssrc
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000568 << " notification: " << (packet->flags & MSG_NOTIFICATION)
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000569 << " length=" << packet->buffer.length();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000570 // Sending a packet with data == NULL (no data) is SCTPs "close the
571 // connection" message. This sets sock_ = NULL;
572 if (!packet->buffer.length() || !packet->buffer.data()) {
573 LOG(LS_INFO) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): "
574 "No data, closing.";
575 return;
576 }
577 if (packet->flags & MSG_NOTIFICATION) {
578 OnNotificationFromSctp(&packet->buffer);
579 } else {
580 OnDataFromSctpToChannel(packet->params, &packet->buffer);
581 }
582}
583
584void SctpDataMediaChannel::OnDataFromSctpToChannel(
585 const ReceiveDataParams& params, talk_base::Buffer* buffer) {
586 StreamParams found_stream;
wu@webrtc.org91053e72013-08-10 07:18:04 +0000587 if (!GetStreamBySsrc(streams_, params.ssrc, &found_stream)) {
588 if (params.type == DMT_CONTROL) {
wu@webrtc.org1d1ffc92013-10-16 18:12:02 +0000589 std::string label;
590 webrtc::DataChannelInit config;
591 if (ParseDataChannelOpenMessage(*buffer, &label, &config)) {
592 config.id = params.ssrc;
593 // Do not send the OPEN message for this data channel.
594 config.negotiated = true;
595 SignalNewStreamReceived(label, config);
596
597 // Add the stream immediately.
598 cricket::StreamParams sparams =
599 cricket::StreamParams::CreateLegacy(params.ssrc);
600 AddSendStream(sparams);
601 AddRecvStream(sparams);
602 } else {
603 LOG(LS_ERROR) << debug_name_ << "->OnDataFromSctpToChannel(...): "
604 << "Received malformed control message";
605 }
wu@webrtc.org91053e72013-08-10 07:18:04 +0000606 } else {
607 LOG(LS_WARNING) << debug_name_ << "->OnDataFromSctpToChannel(...): "
608 << "Received packet for unknown ssrc: " << params.ssrc;
609 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000610 return;
611 }
612
613 if (receiving_) {
614 LOG(LS_VERBOSE) << debug_name_ << "->OnDataFromSctpToChannel(...): "
615 << "Posting with length: " << buffer->length();
616 SignalDataReceived(params, buffer->data(), buffer->length());
617 } else {
618 LOG(LS_WARNING) << debug_name_ << "->OnDataFromSctpToChannel(...): "
619 << "Not receiving packet with sid=" << params.ssrc
620 << " len=" << buffer->length()
621 << " before SetReceive(true).";
622 }
623}
624
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000625void SctpDataMediaChannel::OnNotificationFromSctp(talk_base::Buffer* buffer) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000626 const sctp_notification& notification =
627 reinterpret_cast<const sctp_notification&>(*buffer->data());
628 ASSERT(notification.sn_header.sn_length == buffer->length());
629
630 // TODO(ldixon): handle notifications appropriately.
631 switch (notification.sn_header.sn_type) {
632 case SCTP_ASSOC_CHANGE:
633 LOG(LS_VERBOSE) << "SCTP_ASSOC_CHANGE";
634 OnNotificationAssocChange(notification.sn_assoc_change);
635 break;
636 case SCTP_REMOTE_ERROR:
637 LOG(LS_INFO) << "SCTP_REMOTE_ERROR";
638 break;
639 case SCTP_SHUTDOWN_EVENT:
640 LOG(LS_INFO) << "SCTP_SHUTDOWN_EVENT";
641 break;
642 case SCTP_ADAPTATION_INDICATION:
643 LOG(LS_INFO) << "SCTP_ADAPTATION_INIDICATION";
644 break;
645 case SCTP_PARTIAL_DELIVERY_EVENT:
646 LOG(LS_INFO) << "SCTP_PARTIAL_DELIVERY_EVENT";
647 break;
648 case SCTP_AUTHENTICATION_EVENT:
649 LOG(LS_INFO) << "SCTP_AUTHENTICATION_EVENT";
650 break;
651 case SCTP_SENDER_DRY_EVENT:
652 LOG(LS_INFO) << "SCTP_SENDER_DRY_EVENT";
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000653 SignalReadyToSend(true);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000654 break;
655 // TODO(ldixon): Unblock after congestion.
656 case SCTP_NOTIFICATIONS_STOPPED_EVENT:
657 LOG(LS_INFO) << "SCTP_NOTIFICATIONS_STOPPED_EVENT";
658 break;
659 case SCTP_SEND_FAILED_EVENT:
660 LOG(LS_INFO) << "SCTP_SEND_FAILED_EVENT";
661 break;
662 case SCTP_STREAM_RESET_EVENT:
663 LOG(LS_INFO) << "SCTP_STREAM_RESET_EVENT";
664 // TODO(ldixon): Notify up to channel that stream resent has happened,
665 // and write unit test for this case.
666 break;
667 case SCTP_ASSOC_RESET_EVENT:
668 LOG(LS_INFO) << "SCTP_ASSOC_RESET_EVENT";
669 break;
670 case SCTP_STREAM_CHANGE_EVENT:
671 LOG(LS_INFO) << "SCTP_STREAM_CHANGE_EVENT";
672 break;
673 default:
674 LOG(LS_WARNING) << "Unknown SCTP event: "
675 << notification.sn_header.sn_type;
676 break;
677 }
678}
679
680void SctpDataMediaChannel::OnNotificationAssocChange(
681 const sctp_assoc_change& change) {
682 switch (change.sac_state) {
683 case SCTP_COMM_UP:
684 LOG(LS_VERBOSE) << "Association change SCTP_COMM_UP";
685 break;
686 case SCTP_COMM_LOST:
687 LOG(LS_INFO) << "Association change SCTP_COMM_LOST";
688 break;
689 case SCTP_RESTART:
690 LOG(LS_INFO) << "Association change SCTP_RESTART";
691 break;
692 case SCTP_SHUTDOWN_COMP:
693 LOG(LS_INFO) << "Association change SCTP_SHUTDOWN_COMP";
694 break;
695 case SCTP_CANT_STR_ASSOC:
696 LOG(LS_INFO) << "Association change SCTP_CANT_STR_ASSOC";
697 break;
698 default:
699 LOG(LS_INFO) << "Association change UNKNOWN";
700 break;
701 }
702}
703
wu@webrtc.org78187522013-10-07 23:32:02 +0000704// Puts the specified |param| from the codec identified by |id| into |dest|
705// and returns true. Or returns false if it wasn't there, leaving |dest|
706// untouched.
707static bool GetCodecIntParameter(const std::vector<DataCodec>& codecs,
708 int id, const std::string& name,
709 const std::string& param, int* dest) {
710 std::string value;
711 Codec match_pattern;
712 match_pattern.id = id;
713 match_pattern.name = name;
714 for (size_t i = 0; i < codecs.size(); ++i) {
715 if (codecs[i].Matches(match_pattern)) {
716 if (codecs[i].GetParam(param, &value)) {
717 *dest = talk_base::FromString<int>(value);
718 return true;
719 }
720 }
721 }
722 return false;
723}
724
725bool SctpDataMediaChannel::SetSendCodecs(const std::vector<DataCodec>& codecs) {
726 return GetCodecIntParameter(
727 codecs, kGoogleSctpDataCodecId, kGoogleSctpDataCodecName, kCodecParamPort,
728 &remote_port_);
729}
730
731bool SctpDataMediaChannel::SetRecvCodecs(const std::vector<DataCodec>& codecs) {
732 return GetCodecIntParameter(
733 codecs, kGoogleSctpDataCodecId, kGoogleSctpDataCodecName, kCodecParamPort,
734 &local_port_);
735}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000736
737void SctpDataMediaChannel::OnPacketFromSctpToNetwork(
738 talk_base::Buffer* buffer) {
739 if (buffer->length() > kSctpMtu) {
740 LOG(LS_ERROR) << debug_name_ << "->OnPacketFromSctpToNetwork(...): "
741 << "SCTP seems to have made a poacket that is bigger "
742 "than its official MTU.";
743 }
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000744 MediaChannel::SendPacket(buffer);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000745}
746
747void SctpDataMediaChannel::OnMessage(talk_base::Message* msg) {
748 switch (msg->message_id) {
749 case MSG_SCTPINBOUNDPACKET: {
750 SctpInboundPacket* packet =
751 static_cast<talk_base::TypedMessageData<SctpInboundPacket*>*>(
752 msg->pdata)->data();
753 OnInboundPacketFromSctpToChannel(packet);
754 delete packet;
755 break;
756 }
757 case MSG_SCTPOUTBOUNDPACKET: {
758 talk_base::Buffer* buffer =
759 static_cast<talk_base::TypedMessageData<talk_base::Buffer*>*>(
760 msg->pdata)->data();
761 OnPacketFromSctpToNetwork(buffer);
762 delete buffer;
763 break;
764 }
765 }
766}
767
768} // namespace cricket