1 /*
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
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <vector>
33
34 #include "talk/app/webrtc/datachannelinterface.h"
35 #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"
41 #include "talk/media/sctp/sctputils.h"
42 #include "usrsctplib/usrsctp.h"
43
44 namespace 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.
51 static const int kSctpDefaultPort = 5001;
52 // TODO(ldixon): Find where this is defined, and also check is Sctp really
53 // respects this.
54 static const size_t kSctpMtu = 1280;
55
56 enum {
57 MSG_SCTPINBOUNDPACKET = 1, // MessageData is SctpInboundPacket
58 MSG_SCTPOUTBOUNDPACKET = 2, // MessageData is talk_base:Buffer
59 };
60
61 struct 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
69 // Helper for logging SCTP messages.
debug_sctp_printf(const char * format,...)70 static 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);
75 LOG(LS_INFO) << "SCTP: " << s;
76 va_end(ap);
77 }
78
79 // Get the PPID to use for the terminating fragment of this type.
GetPpid(cricket::DataMessageType type)80 static 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
GetDataMediaType(SctpDataMediaChannel::PayloadProtocolIdentifier ppid,cricket::DataMessageType * dest)95 static 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;
120 }
121 }
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.
OnSctpOutboundPacket(void * addr,void * data,size_t length,uint8_t tos,uint8_t set_df)125 static 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)
131 << "; set_df: " << std::hex << static_cast<int>(set_df);
132 // 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'.
OnSctpInboundPacket(struct socket * sock,union sctp_sockstore addr,void * data,size_t length,struct sctp_rcvinfo rcv,int flags,void * ulp_info)143 static 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) {
147 SctpDataMediaChannel* channel = static_cast<SctpDataMediaChannel*>(ulp_info);
148 // 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.
151 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 }
170 free(data);
171 return 1;
172 }
173
174 // Set the initial value of the static SCTP Data Engines reference count.
175 int SctpDataEngine::usrsctp_engines_count = 0;
176
SctpDataEngine()177 SctpDataEngine::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);
206
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);
214 }
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
~SctpDataEngine()223 SctpDataEngine::~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
CreateChannel(DataChannelType data_channel_type)237 DataMediaChannel* 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
SctpDataMediaChannel(talk_base::Thread * thread)245 SctpDataMediaChannel::SctpDataMediaChannel(talk_base::Thread* thread)
246 : worker_thread_(thread),
247 local_port_(-1),
248 remote_port_(-1),
249 sock_(NULL),
250 sending_(false),
251 receiving_(false),
252 debug_name_("SctpDataMediaChannel") {
253 }
254
~SctpDataMediaChannel()255 SctpDataMediaChannel::~SctpDataMediaChannel() {
256 CloseSctpSocket();
257 }
258
GetSctpSockAddr(int port)259 sockaddr_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
OpenSctpSocket()271 bool 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
303 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
310 // Subscribe to SCTP event notifications.
311 int event_types[] = {SCTP_ASSOC_CHANGE,
312 SCTP_PEER_ADDR_CHANGE,
313 SCTP_SEND_FAILED_EVENT,
314 SCTP_SENDER_DRY_EVENT};
315 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
CloseSctpSocket()335 void 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
Connect()347 bool SctpDataMediaChannel::Connect() {
348 LOG(LS_VERBOSE) << debug_name_ << "->Connect().";
349 if (remote_port_ < 0) {
350 remote_port_ = kSctpDefaultPort;
351 }
352 if (local_port_ < 0) {
353 local_port_ = kSctpDefaultPort;
354 }
355
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));
384 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;
387 CloseSctpSocket();
388 return false;
389 }
390 return true;
391 }
392
Disconnect()393 void SctpDataMediaChannel::Disconnect() {
394 // TODO(ldixon): Consider calling |usrsctp_shutdown(sock_, ...)| to do a
395 // shutdown handshake and remove the association.
396 CloseSctpSocket();
397 }
398
SetSend(bool send)399 bool SctpDataMediaChannel::SetSend(bool send) {
400 if (!sending_ && send) {
401 return Connect();
402 }
403 if (sending_ && !send) {
404 Disconnect();
405 }
406 return true;
407 }
408
SetReceive(bool receive)409 bool SctpDataMediaChannel::SetReceive(bool receive) {
410 receiving_ = receive;
411 return true;
412 }
413
AddSendStream(const StreamParams & stream)414 bool SctpDataMediaChannel::AddSendStream(const StreamParams& stream) {
415 if (!stream.has_ssrcs()) {
416 return false;
417 }
418
419 StreamParams found_stream;
420 // TODO(lally): Consider keeping this sorted.
421 if (GetStreamBySsrc(streams_, stream.first_ssrc(), &found_stream)) {
422 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
429 streams_.push_back(stream);
430 return true;
431 }
432
RemoveSendStream(uint32 ssrc)433 bool SctpDataMediaChannel::RemoveSendStream(uint32 ssrc) {
434 StreamParams found_stream;
435 if (!GetStreamBySsrc(streams_, ssrc, &found_stream)) {
436 return false;
437 }
438
439 RemoveStreamBySsrc(&streams_, ssrc);
440 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.
AddRecvStream(const StreamParams & stream)445 bool SctpDataMediaChannel::AddRecvStream(const StreamParams& stream) {
446 if (!stream.has_ssrcs()) {
447 return false;
448 }
449
450 StreamParams found_stream;
451 if (GetStreamBySsrc(streams_, stream.first_ssrc(), &found_stream)) {
452 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
459 streams_.push_back(stream);
460 LOG(LS_VERBOSE) << debug_name_ << "->AddRecvStream(...): "
461 << "Added data recv stream '" << stream.id
462 << "' with ssrc=" << stream.first_ssrc();
463 return true;
464 }
465
RemoveRecvStream(uint32 ssrc)466 bool SctpDataMediaChannel::RemoveRecvStream(uint32 ssrc) {
467 RemoveStreamBySsrc(&streams_, ssrc);
468 return true;
469 }
470
SendData(const SendDataParams & params,const talk_base::Buffer & payload,SendDataResult * result)471 bool SctpDataMediaChannel::SendData(
472 const SendDataParams& params,
473 const talk_base::Buffer& payload,
474 SendDataResult* result) {
475 if (result) {
476 // Preset |result| to assume an error. If SendData succeeds, we'll
477 // overwrite |*result| once more at the end.
478 *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;
489 if (params.type != cricket::DMT_CONTROL &&
490 !GetStreamBySsrc(streams_, params.ssrc, &found_stream)) {
491 LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
492 << "Not sending data because ssrc is unknown: "
493 << params.ssrc;
494 return false;
495 }
496
497 //
498 // Send data using SCTP.
499 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) {
527 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 }
535 return false;
536 }
537 if (result) {
538 // Only way out now is success.
539 *result = SDR_SUCCESS;
540 }
541 return true;
542 }
543
544 // Called by network interface when a packet has been received.
OnPacketReceived(talk_base::Buffer * packet,const talk_base::PacketTime & packet_time)545 void SctpDataMediaChannel::OnPacketReceived(
546 talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) {
547 LOG(LS_VERBOSE) << debug_name_ << "->OnPacketReceived(...): " << " length="
548 << packet->length() << ", sending: " << sending_;
549 // Only give receiving packets to usrsctp after if connected. This enables two
550 // peers to each make a connect call, but for them not to receive an INIT
551 // packet before they have called connect; least the last receiver of the INIT
552 // packet will have called connect, and a connection will be established.
553 if (sending_) {
554 // Pass received packet to SCTP stack. Once processed by usrsctp, the data
555 // will be will be given to the global OnSctpInboundData, and then,
556 // marshalled by a Post and handled with OnMessage.
557 usrsctp_conninput(this, packet->data(), packet->length(), 0);
558 } else {
559 // TODO(ldixon): Consider caching the packet for very slightly better
560 // reliability.
561 }
562 }
563
OnInboundPacketFromSctpToChannel(SctpInboundPacket * packet)564 void SctpDataMediaChannel::OnInboundPacketFromSctpToChannel(
565 SctpInboundPacket* packet) {
566 LOG(LS_VERBOSE) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): "
567 << "Received SCTP data:"
568 << " ssrc=" << packet->params.ssrc
569 << " notification: " << (packet->flags & MSG_NOTIFICATION)
570 << " length=" << packet->buffer.length();
571 // Sending a packet with data == NULL (no data) is SCTPs "close the
572 // connection" message. This sets sock_ = NULL;
573 if (!packet->buffer.length() || !packet->buffer.data()) {
574 LOG(LS_INFO) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): "
575 "No data, closing.";
576 return;
577 }
578 if (packet->flags & MSG_NOTIFICATION) {
579 OnNotificationFromSctp(&packet->buffer);
580 } else {
581 OnDataFromSctpToChannel(packet->params, &packet->buffer);
582 }
583 }
584
OnDataFromSctpToChannel(const ReceiveDataParams & params,talk_base::Buffer * buffer)585 void SctpDataMediaChannel::OnDataFromSctpToChannel(
586 const ReceiveDataParams& params, talk_base::Buffer* buffer) {
587 StreamParams found_stream;
588 if (!GetStreamBySsrc(streams_, params.ssrc, &found_stream)) {
589 if (params.type == DMT_CONTROL) {
590 std::string label;
591 webrtc::DataChannelInit config;
592 if (ParseDataChannelOpenMessage(*buffer, &label, &config)) {
593 config.id = params.ssrc;
594 // Do not send the OPEN message for this data channel.
595 config.negotiated = true;
596 SignalNewStreamReceived(label, config);
597
598 // Add the stream immediately.
599 cricket::StreamParams sparams =
600 cricket::StreamParams::CreateLegacy(params.ssrc);
601 AddSendStream(sparams);
602 AddRecvStream(sparams);
603 } else {
604 LOG(LS_ERROR) << debug_name_ << "->OnDataFromSctpToChannel(...): "
605 << "Received malformed control message";
606 }
607 } else {
608 LOG(LS_WARNING) << debug_name_ << "->OnDataFromSctpToChannel(...): "
609 << "Received packet for unknown ssrc: " << params.ssrc;
610 }
611 return;
612 }
613
614 if (receiving_) {
615 LOG(LS_VERBOSE) << debug_name_ << "->OnDataFromSctpToChannel(...): "
616 << "Posting with length: " << buffer->length();
617 SignalDataReceived(params, buffer->data(), buffer->length());
618 } else {
619 LOG(LS_WARNING) << debug_name_ << "->OnDataFromSctpToChannel(...): "
620 << "Not receiving packet with sid=" << params.ssrc
621 << " len=" << buffer->length()
622 << " before SetReceive(true).";
623 }
624 }
625
OnNotificationFromSctp(talk_base::Buffer * buffer)626 void SctpDataMediaChannel::OnNotificationFromSctp(talk_base::Buffer* buffer) {
627 const sctp_notification& notification =
628 reinterpret_cast<const sctp_notification&>(*buffer->data());
629 ASSERT(notification.sn_header.sn_length == buffer->length());
630
631 // TODO(ldixon): handle notifications appropriately.
632 switch (notification.sn_header.sn_type) {
633 case SCTP_ASSOC_CHANGE:
634 LOG(LS_VERBOSE) << "SCTP_ASSOC_CHANGE";
635 OnNotificationAssocChange(notification.sn_assoc_change);
636 break;
637 case SCTP_REMOTE_ERROR:
638 LOG(LS_INFO) << "SCTP_REMOTE_ERROR";
639 break;
640 case SCTP_SHUTDOWN_EVENT:
641 LOG(LS_INFO) << "SCTP_SHUTDOWN_EVENT";
642 break;
643 case SCTP_ADAPTATION_INDICATION:
644 LOG(LS_INFO) << "SCTP_ADAPTATION_INIDICATION";
645 break;
646 case SCTP_PARTIAL_DELIVERY_EVENT:
647 LOG(LS_INFO) << "SCTP_PARTIAL_DELIVERY_EVENT";
648 break;
649 case SCTP_AUTHENTICATION_EVENT:
650 LOG(LS_INFO) << "SCTP_AUTHENTICATION_EVENT";
651 break;
652 case SCTP_SENDER_DRY_EVENT:
653 LOG(LS_INFO) << "SCTP_SENDER_DRY_EVENT";
654 SignalReadyToSend(true);
655 break;
656 // TODO(ldixon): Unblock after congestion.
657 case SCTP_NOTIFICATIONS_STOPPED_EVENT:
658 LOG(LS_INFO) << "SCTP_NOTIFICATIONS_STOPPED_EVENT";
659 break;
660 case SCTP_SEND_FAILED_EVENT:
661 LOG(LS_INFO) << "SCTP_SEND_FAILED_EVENT";
662 break;
663 case SCTP_STREAM_RESET_EVENT:
664 LOG(LS_INFO) << "SCTP_STREAM_RESET_EVENT";
665 // TODO(ldixon): Notify up to channel that stream resent has happened,
666 // and write unit test for this case.
667 break;
668 case SCTP_ASSOC_RESET_EVENT:
669 LOG(LS_INFO) << "SCTP_ASSOC_RESET_EVENT";
670 break;
671 case SCTP_STREAM_CHANGE_EVENT:
672 LOG(LS_INFO) << "SCTP_STREAM_CHANGE_EVENT";
673 break;
674 default:
675 LOG(LS_WARNING) << "Unknown SCTP event: "
676 << notification.sn_header.sn_type;
677 break;
678 }
679 }
680
OnNotificationAssocChange(const sctp_assoc_change & change)681 void SctpDataMediaChannel::OnNotificationAssocChange(
682 const sctp_assoc_change& change) {
683 switch (change.sac_state) {
684 case SCTP_COMM_UP:
685 LOG(LS_VERBOSE) << "Association change SCTP_COMM_UP";
686 break;
687 case SCTP_COMM_LOST:
688 LOG(LS_INFO) << "Association change SCTP_COMM_LOST";
689 break;
690 case SCTP_RESTART:
691 LOG(LS_INFO) << "Association change SCTP_RESTART";
692 break;
693 case SCTP_SHUTDOWN_COMP:
694 LOG(LS_INFO) << "Association change SCTP_SHUTDOWN_COMP";
695 break;
696 case SCTP_CANT_STR_ASSOC:
697 LOG(LS_INFO) << "Association change SCTP_CANT_STR_ASSOC";
698 break;
699 default:
700 LOG(LS_INFO) << "Association change UNKNOWN";
701 break;
702 }
703 }
704
705 // Puts the specified |param| from the codec identified by |id| into |dest|
706 // and returns true. Or returns false if it wasn't there, leaving |dest|
707 // untouched.
GetCodecIntParameter(const std::vector<DataCodec> & codecs,int id,const std::string & name,const std::string & param,int * dest)708 static bool GetCodecIntParameter(const std::vector<DataCodec>& codecs,
709 int id, const std::string& name,
710 const std::string& param, int* dest) {
711 std::string value;
712 Codec match_pattern;
713 match_pattern.id = id;
714 match_pattern.name = name;
715 for (size_t i = 0; i < codecs.size(); ++i) {
716 if (codecs[i].Matches(match_pattern)) {
717 if (codecs[i].GetParam(param, &value)) {
718 *dest = talk_base::FromString<int>(value);
719 return true;
720 }
721 }
722 }
723 return false;
724 }
725
SetSendCodecs(const std::vector<DataCodec> & codecs)726 bool SctpDataMediaChannel::SetSendCodecs(const std::vector<DataCodec>& codecs) {
727 return GetCodecIntParameter(
728 codecs, kGoogleSctpDataCodecId, kGoogleSctpDataCodecName, kCodecParamPort,
729 &remote_port_);
730 }
731
SetRecvCodecs(const std::vector<DataCodec> & codecs)732 bool SctpDataMediaChannel::SetRecvCodecs(const std::vector<DataCodec>& codecs) {
733 return GetCodecIntParameter(
734 codecs, kGoogleSctpDataCodecId, kGoogleSctpDataCodecName, kCodecParamPort,
735 &local_port_);
736 }
737
OnPacketFromSctpToNetwork(talk_base::Buffer * buffer)738 void SctpDataMediaChannel::OnPacketFromSctpToNetwork(
739 talk_base::Buffer* buffer) {
740 if (buffer->length() > kSctpMtu) {
741 LOG(LS_ERROR) << debug_name_ << "->OnPacketFromSctpToNetwork(...): "
742 << "SCTP seems to have made a poacket that is bigger "
743 "than its official MTU.";
744 }
745 MediaChannel::SendPacket(buffer);
746 }
747
OnMessage(talk_base::Message * msg)748 void SctpDataMediaChannel::OnMessage(talk_base::Message* msg) {
749 switch (msg->message_id) {
750 case MSG_SCTPINBOUNDPACKET: {
751 SctpInboundPacket* packet =
752 static_cast<talk_base::TypedMessageData<SctpInboundPacket*>*>(
753 msg->pdata)->data();
754 OnInboundPacketFromSctpToChannel(packet);
755 delete packet;
756 break;
757 }
758 case MSG_SCTPOUTBOUNDPACKET: {
759 talk_base::Buffer* buffer =
760 static_cast<talk_base::TypedMessageData<talk_base::Buffer*>*>(
761 msg->pdata)->data();
762 OnPacketFromSctpToNetwork(buffer);
763 delete buffer;
764 break;
765 }
766 }
767 }
768
769 } // namespace cricket
770