blob: 935ccb8ba8b984dfe70ea011ad6a4d7249489956 [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
34#include "talk/base/buffer.h"
35#include "talk/base/helpers.h"
36#include "talk/base/logging.h"
37#include "talk/media/base/codec.h"
38#include "talk/media/base/constants.h"
39#include "talk/media/base/streamparams.h"
40#include "usrsctplib/usrsctp.h"
41
henrike@webrtc.org28e20752013-07-10 00:45:36 +000042namespace cricket {
43
44// This is the SCTP port to use. It is passed along the wire and the listener
45// and connector must be using the same port. It is not related to the ports at
46// the IP level. (Corresponds to: sockaddr_conn.sconn_port in usrsctp.h)
47//
48// TODO(ldixon): Allow port to be set from higher level code.
49static const int kSctpDefaultPort = 5001;
50// TODO(ldixon): Find where this is defined, and also check is Sctp really
51// respects this.
52static const size_t kSctpMtu = 1280;
53
54enum {
55 MSG_SCTPINBOUNDPACKET = 1, // MessageData is SctpInboundPacket
56 MSG_SCTPOUTBOUNDPACKET = 2, // MessageData is talk_base:Buffer
57};
58
59struct SctpInboundPacket {
60 talk_base::Buffer buffer;
61 ReceiveDataParams params;
62 // The |flags| parameter is used by SCTP to distinguish notification packets
63 // from other types of packets.
64 int flags;
65};
66
67// Helper for logging SCTP data. Given a buffer, returns a readable string.
68static void debug_sctp_printf(const char *format, ...) {
69 char s[255];
70 va_list ap;
71 va_start(ap, format);
72 vsnprintf(s, sizeof(s), format, ap);
73 LOG(LS_INFO) << s;
74 // vprintf(format, ap);
75 va_end(ap);
76}
77
78// Helper for make a string dump of some SCTP data. Used for LOG
79// debugging messages.
80static std::string SctpDataToDebugString(void* buffer, size_t length,
81 int dump_type) {
82 char *dump_buf = usrsctp_dumppacket(buffer, length, dump_type);
83 if (!dump_buf) {
84 return "";
85 }
86 std::string s = std::string(dump_buf);
87 usrsctp_freedumpbuffer(dump_buf);
88 return s;
89}
90
91// This is the callback usrsctp uses when there's data to send on the network
92// that has been wrapped appropriatly for the SCTP protocol.
93static int OnSctpOutboundPacket(void* addr, void* data, size_t length,
94 uint8_t tos, uint8_t set_df) {
95 SctpDataMediaChannel* channel = static_cast<SctpDataMediaChannel*>(addr);
96 LOG(LS_VERBOSE) << "global OnSctpOutboundPacket():"
97 << "addr: " << addr << "; length: " << length
98 << "; tos: " << std::hex << static_cast<int>(tos)
99 << "; set_df: " << std::hex << static_cast<int>(set_df)
100 << "; data:" << SctpDataToDebugString(data, length,
101 SCTP_DUMP_OUTBOUND);
102 // Note: We have to copy the data; the caller will delete it.
103 talk_base::Buffer* buffer = new talk_base::Buffer(data, length);
104 channel->worker_thread()->Post(channel, MSG_SCTPOUTBOUNDPACKET,
105 talk_base::WrapMessageData(buffer));
106 return 0;
107}
108
109// This is the callback called from usrsctp when data has been received, after
110// a packet has been interpreted and parsed by usrsctp and found to contain
111// payload data. It is called by a usrsctp thread. It is assumed this function
112// will free the memory used by 'data'.
113static int OnSctpInboundPacket(struct socket* sock, union sctp_sockstore addr,
114 void* data, size_t length,
115 struct sctp_rcvinfo rcv, int flags,
116 void* ulp_info) {
117 LOG(LS_VERBOSE) << "global OnSctpInboundPacket... Msg of length "
118 << length << " received via " << addr.sconn.sconn_addr << ":"
119 << talk_base::NetworkToHost16(addr.sconn.sconn_port)
120 << " on stream " << rcv.rcv_sid
121 << " with SSN " << rcv.rcv_ssn
122 << " and TSN " << rcv.rcv_tsn << ", PPID "
123 << talk_base::NetworkToHost32(rcv.rcv_ppid)
124 << ", context " << rcv.rcv_context
125 << ", data: " << data
126 << ", ulp_info:" << ulp_info
127 << ", flags:" << std::hex << flags;
128 SctpDataMediaChannel* channel = static_cast<SctpDataMediaChannel*>(ulp_info);
129 // The second log call is useful when the defines flags are incorrect. In
130 // this case, ulp_info ends up being bad and the second log message will
131 // cause a crash.
132 LOG(LS_VERBOSE) << "global OnSctpInboundPacket. channel="
133 << channel->debug_name() << "...";
134 // Post data to the channel's receiver thread (copying it).
135 // TODO(ldixon): Unclear if copy is needed as this method is responsible for
136 // memory cleanup. But this does simplify code.
137 SctpInboundPacket* packet = new SctpInboundPacket();
138 packet->buffer.SetData(data, length);
139 packet->params.ssrc = rcv.rcv_sid;
140 packet->params.seq_num = rcv.rcv_ssn;
141 packet->params.timestamp = rcv.rcv_tsn;
142 packet->flags = flags;
143 channel->worker_thread()->Post(channel, MSG_SCTPINBOUNDPACKET,
144 talk_base::WrapMessageData(packet));
145 free(data);
146 return 1;
147}
148
149// Set the initial value of the static SCTP Data Engines reference count.
150int SctpDataEngine::usrsctp_engines_count = 0;
151
152SctpDataEngine::SctpDataEngine() {
153 if (usrsctp_engines_count == 0) {
154 // First argument is udp_encapsulation_port, which is not releveant for our
155 // AF_CONN use of sctp.
156 usrsctp_init(0, cricket::OnSctpOutboundPacket, debug_sctp_printf);
157
158 // To turn on/off detailed SCTP debugging. You will also need to have the
159 // SCTP_DEBUG cpp defines flag.
160 // usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_ALL);
161
162 // TODO(ldixon): Consider turning this on/off.
163 usrsctp_sysctl_set_sctp_ecn_enable(0);
164
165 // TODO(ldixon): Consider turning this on/off.
166 // This is not needed right now (we don't do dynamic address changes):
167 // If SCTP Auto-ASCONF is enabled, the peer is informed automatically
168 // when a new address is added or removed. This feature is enabled by
169 // default.
170 // usrsctp_sysctl_set_sctp_auto_asconf(0);
171
172 // TODO(ldixon): Consider turning this on/off.
173 // Add a blackhole sysctl. Setting it to 1 results in no ABORTs
174 // being sent in response to INITs, setting it to 2 results
175 // in no ABORTs being sent for received OOTB packets.
176 // This is similar to the TCP sysctl.
177 //
178 // See: http://lakerest.net/pipermail/sctp-coders/2012-January/009438.html
179 // See: http://svnweb.freebsd.org/base?view=revision&revision=229805
180 // usrsctp_sysctl_set_sctp_blackhole(2);
181 }
182 usrsctp_engines_count++;
183
184 // We don't put in a codec because we don't want one offered when we
185 // use the hybrid data engine.
186 // codecs_.push_back(cricket::DataCodec( kGoogleSctpDataCodecId,
187 // kGoogleSctpDataCodecName, 0));
188}
189
190SctpDataEngine::~SctpDataEngine() {
191 // TODO(ldixon): There is currently a bug in teardown of usrsctp that blocks
192 // indefintely if a finish call made too soon after close calls. So teardown
193 // has been skipped. Once the bug is fixed, retest and enable teardown.
194 //
195 // usrsctp_engines_count--;
196 // LOG(LS_VERBOSE) << "usrsctp_engines_count:" << usrsctp_engines_count;
197 // if (usrsctp_engines_count == 0) {
198 // if (usrsctp_finish() != 0) {
199 // LOG(LS_WARNING) << "usrsctp_finish.";
200 // }
201 // }
202}
203
204DataMediaChannel* SctpDataEngine::CreateChannel(
205 DataChannelType data_channel_type) {
206 if (data_channel_type != DCT_SCTP) {
207 return NULL;
208 }
209 return new SctpDataMediaChannel(talk_base::Thread::Current());
210}
211
212SctpDataMediaChannel::SctpDataMediaChannel(talk_base::Thread* thread)
213 : worker_thread_(thread),
214 local_port_(kSctpDefaultPort),
215 remote_port_(kSctpDefaultPort),
216 sock_(NULL),
217 sending_(false),
218 receiving_(false),
219 debug_name_("SctpDataMediaChannel") {
220}
221
222SctpDataMediaChannel::~SctpDataMediaChannel() {
223 CloseSctpSocket();
224}
225
226sockaddr_conn SctpDataMediaChannel::GetSctpSockAddr(int port) {
227 sockaddr_conn sconn = {0};
228 sconn.sconn_family = AF_CONN;
229#ifdef HAVE_SCONN_LEN
230 sconn.sconn_len = sizeof(sockaddr_conn);
231#endif
232 // Note: conversion from int to uint16_t happens here.
233 sconn.sconn_port = talk_base::HostToNetwork16(port);
234 sconn.sconn_addr = this;
235 return sconn;
236}
237
238bool SctpDataMediaChannel::OpenSctpSocket() {
239 if (sock_) {
240 LOG(LS_VERBOSE) << debug_name_
241 << "->Ignoring attempt to re-create existing socket.";
242 return false;
243 }
244 sock_ = usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP,
245 cricket::OnSctpInboundPacket, NULL, 0, this);
246 if (!sock_) {
247 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to create SCTP socket.";
248 return false;
249 }
250
251 // Make the socket non-blocking. Connect, close, shutdown etc will not block
252 // the thread waiting for the socket operation to complete.
253 if (usrsctp_set_non_blocking(sock_, 1) < 0) {
254 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP to non blocking.";
255 return false;
256 }
257
258 // This ensures that the usrsctp close call deletes the association. This
259 // prevents usrsctp from calling OnSctpOutboundPacket with references to
260 // this class as the address.
261 linger linger_opt;
262 linger_opt.l_onoff = 1;
263 linger_opt.l_linger = 0;
264 if (usrsctp_setsockopt(sock_, SOL_SOCKET, SO_LINGER, &linger_opt,
265 sizeof(linger_opt))) {
266 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SO_LINGER.";
267 return false;
268 }
269
270 // Subscribe to SCTP event notifications.
271 int event_types[] = {SCTP_ASSOC_CHANGE,
272 SCTP_PEER_ADDR_CHANGE,
273 SCTP_SEND_FAILED_EVENT};
274 struct sctp_event event = {0};
275 event.se_assoc_id = SCTP_ALL_ASSOC;
276 event.se_on = 1;
277 for (size_t i = 0; i < ARRAY_SIZE(event_types); i++) {
278 event.se_type = event_types[i];
279 if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_EVENT, &event,
280 sizeof(event)) < 0) {
281 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP_EVENT type: "
282 << event.se_type;
283 return false;
284 }
285 }
286
287 // Register this class as an address for usrsctp. This is used by SCTP to
288 // direct the packets received (by the created socket) to this class.
289 usrsctp_register_address(this);
290 sending_ = true;
291 return true;
292}
293
294void SctpDataMediaChannel::CloseSctpSocket() {
295 sending_ = false;
296 if (sock_) {
297 // We assume that SO_LINGER option is set to close the association when
298 // close is called. This means that any pending packets in usrsctp will be
299 // discarded instead of being sent.
300 usrsctp_close(sock_);
301 sock_ = NULL;
302 usrsctp_deregister_address(this);
303 }
304}
305
306bool SctpDataMediaChannel::Connect() {
307 LOG(LS_VERBOSE) << debug_name_ << "->Connect().";
308
309 // If we already have a socket connection, just return.
310 if (sock_) {
311 LOG(LS_WARNING) << debug_name_ << "->Connect(): Ignored as socket "
312 "is already established.";
313 return true;
314 }
315
316 // If no socket (it was closed) try to start it again. This can happen when
317 // the socket we are connecting to closes, does an sctp shutdown handshake,
318 // or behaves unexpectedly causing us to perform a CloseSctpSocket.
319 if (!sock_ && !OpenSctpSocket()) {
320 return false;
321 }
322
323 // Note: conversion from int to uint16_t happens on assignment.
324 sockaddr_conn local_sconn = GetSctpSockAddr(local_port_);
325 if (usrsctp_bind(sock_, reinterpret_cast<sockaddr *>(&local_sconn),
326 sizeof(local_sconn)) < 0) {
327 LOG_ERRNO(LS_ERROR) << debug_name_ << "->Connect(): "
328 << ("Failed usrsctp_bind");
329 CloseSctpSocket();
330 return false;
331 }
332
333 // Note: conversion from int to uint16_t happens on assignment.
334 sockaddr_conn remote_sconn = GetSctpSockAddr(remote_port_);
335 int connect_result = usrsctp_connect(
336 sock_, reinterpret_cast<sockaddr *>(&remote_sconn), sizeof(remote_sconn));
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000337 if (connect_result < 0 && errno != SCTP_EINPROGRESS) {
338 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed usrsctp_connect. got errno="
339 << errno << ", but wanted " << SCTP_EINPROGRESS;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000340 CloseSctpSocket();
341 return false;
342 }
343 return true;
344}
345
346void SctpDataMediaChannel::Disconnect() {
347 // TODO(ldixon): Consider calling |usrsctp_shutdown(sock_, ...)| to do a
348 // shutdown handshake and remove the association.
349 CloseSctpSocket();
350}
351
352bool SctpDataMediaChannel::SetSend(bool send) {
353 if (!sending_ && send) {
354 return Connect();
355 }
356 if (sending_ && !send) {
357 Disconnect();
358 }
359 return true;
360}
361
362bool SctpDataMediaChannel::SetReceive(bool receive) {
363 receiving_ = receive;
364 return true;
365}
366
367bool SctpDataMediaChannel::AddSendStream(const StreamParams& stream) {
368 if (!stream.has_ssrcs()) {
369 return false;
370 }
371
372 StreamParams found_stream;
373 if (GetStreamBySsrc(send_streams_, stream.first_ssrc(), &found_stream)) {
374 LOG(LS_WARNING) << debug_name_ << "->AddSendStream(...): "
375 << "Not adding data send stream '" << stream.id
376 << "' with ssrc=" << stream.first_ssrc()
377 << " because stream already exists.";
378 return false;
379 }
380
381 send_streams_.push_back(stream);
382 return true;
383}
384
385bool SctpDataMediaChannel::RemoveSendStream(uint32 ssrc) {
386 StreamParams found_stream;
387 if (!GetStreamBySsrc(send_streams_, ssrc, &found_stream)) {
388 return false;
389 }
390
391 RemoveStreamBySsrc(&send_streams_, ssrc);
392 return true;
393}
394
395// Note: expects exactly one ssrc. If none are given, it will fail. If more
396// than one are given, it will use the first.
397bool SctpDataMediaChannel::AddRecvStream(const StreamParams& stream) {
398 if (!stream.has_ssrcs()) {
399 return false;
400 }
401
402 StreamParams found_stream;
403 if (GetStreamBySsrc(recv_streams_, stream.first_ssrc(), &found_stream)) {
404 LOG(LS_WARNING) << debug_name_ << "->AddRecvStream(...): "
405 << "Not adding data recv stream '" << stream.id
406 << "' with ssrc=" << stream.first_ssrc()
407 << " because stream already exists.";
408 return false;
409 }
410
411 recv_streams_.push_back(stream);
412 LOG(LS_VERBOSE) << debug_name_ << "->AddRecvStream(...): "
413 << "Added data recv stream '" << stream.id
414 << "' with ssrc=" << stream.first_ssrc();
415 return true;
416}
417
418bool SctpDataMediaChannel::RemoveRecvStream(uint32 ssrc) {
419 RemoveStreamBySsrc(&recv_streams_, ssrc);
420 return true;
421}
422
423bool SctpDataMediaChannel::SendData(
424 const SendDataParams& params,
425 const talk_base::Buffer& payload,
426 SendDataResult* result) {
427 if (result) {
428 // If we return true, we'll set this to SDR_SUCCESS.
429 *result = SDR_ERROR;
430 }
431
432 if (!sending_) {
433 LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
434 << "Not sending packet with ssrc=" << params.ssrc
435 << " len=" << payload.length() << " before SetSend(true).";
436 return false;
437 }
438
439 StreamParams found_stream;
440 if (!GetStreamBySsrc(send_streams_, params.ssrc, &found_stream)) {
441 LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
442 << "Not sending data because ssrc is unknown: "
443 << params.ssrc;
444 return false;
445 }
446
447 // TODO(ldixon): Experiment with sctp_sendv_spa instead of sctp_sndinfo. e.g.
448 // struct sctp_sendv_spa spa = {0};
449 // spa.sendv_flags |= SCTP_SEND_SNDINFO_VALID;
450 // spa.sendv_sndinfo.snd_sid = params.ssrc;
451 // spa.sendv_sndinfo.snd_context = 0;
452 // spa.sendv_sndinfo.snd_assoc_id = 0;
453 // TODO(pthatcher): Support different types of protocols (e.g. SSL) and
454 // messages (e.g. Binary) via SendDataParams.
455 // spa.sendv_sndinfo.snd_ppid = htonl(PPID_NONE);
456 // TODO(pthatcher): Support different reliability semantics.
457 // For reliable: Remove SCTP_UNORDERED.
458 // For partially-reliable: Add rtx or ttl.
459 // spa.sendv_sndinfo.snd_flags = SCTP_UNORDERED;
460 // TODO(phatcher): Try some of these things.
461 // spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
462 // spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX;
463 // spa.sendv_prinfo.pr_value = htons(max_retransmit_count);
464 // spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_TTL;
465 // spa.sendv_prinfo.pr_value = htons(max_retransmit_time);
466 //
467 // Send data using SCTP.
468 sctp_sndinfo sndinfo = {0};
469 sndinfo.snd_sid = params.ssrc;
470 sndinfo.snd_flags = 0;
471 // TODO(pthatcher): Once data types are added to SendParams, this can be set
472 // from SendParams.
473 sndinfo.snd_ppid = talk_base::HostToNetwork32(PPID_NONE);
474 sndinfo.snd_context = 0;
475 sndinfo.snd_assoc_id = 0;
476 ssize_t res = usrsctp_sendv(sock_, payload.data(),
477 static_cast<size_t>(payload.length()),
478 NULL, 0, &sndinfo,
479 static_cast<socklen_t>(sizeof(sndinfo)),
480 SCTP_SENDV_SNDINFO, 0);
481 if (res < 0) {
482 LOG_ERRNO(LS_ERROR) << "ERROR:" << debug_name_
483 << "SendData->(...): "
484 << " usrsctp_sendv: ";
485 // TODO(pthatcher): Make result SDR_BLOCK if the error is because
486 // it would block.
487 return false;
488 }
489 if (result) {
490 // If we return true, we'll set this to SDR_SUCCESS.
491 *result = SDR_SUCCESS;
492 }
493 return true;
494}
495
496// Called by network interface when a packet has been received.
497void SctpDataMediaChannel::OnPacketReceived(talk_base::Buffer* packet) {
498 LOG(LS_VERBOSE) << debug_name_ << "->OnPacketReceived(...): "
499 << " length=" << packet->length() << "; data="
500 << SctpDataToDebugString(packet->data(), packet->length(),
501 SCTP_DUMP_INBOUND);
502 // Only give receiving packets to usrsctp after if connected. This enables two
503 // peers to each make a connect call, but for them not to receive an INIT
504 // packet before they have called connect; least the last receiver of the INIT
505 // packet will have called connect, and a connection will be established.
506 if (sending_) {
507 LOG(LS_VERBOSE) << debug_name_ << "->OnPacketReceived(...):"
508 << " Passed packet to sctp.";
509 // Pass received packet to SCTP stack. Once processed by usrsctp, the data
510 // will be will be given to the global OnSctpInboundData, and then,
511 // marshalled by a Post and handled with OnMessage.
512 usrsctp_conninput(this, packet->data(), packet->length(), 0);
513 } else {
514 // TODO(ldixon): Consider caching the packet for very slightly better
515 // reliability.
516 LOG(LS_INFO) << debug_name_ << "->OnPacketReceived(...):"
517 << " Threw packet (probably an INIT) away.";
518 }
519}
520
521void SctpDataMediaChannel::OnInboundPacketFromSctpToChannel(
522 SctpInboundPacket* packet) {
523 LOG(LS_VERBOSE) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): "
524 << "Received SCTP data:"
525 << " ssrc=" << packet->params.ssrc
526 << " data='" << std::string(packet->buffer.data(),
527 packet->buffer.length())
528 << " notification: " << (packet->flags & MSG_NOTIFICATION)
529 << "' length=" << packet->buffer.length();
530 // Sending a packet with data == NULL (no data) is SCTPs "close the
531 // connection" message. This sets sock_ = NULL;
532 if (!packet->buffer.length() || !packet->buffer.data()) {
533 LOG(LS_INFO) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): "
534 "No data, closing.";
535 return;
536 }
537 if (packet->flags & MSG_NOTIFICATION) {
538 OnNotificationFromSctp(&packet->buffer);
539 } else {
540 OnDataFromSctpToChannel(packet->params, &packet->buffer);
541 }
542}
543
544void SctpDataMediaChannel::OnDataFromSctpToChannel(
545 const ReceiveDataParams& params, talk_base::Buffer* buffer) {
546 StreamParams found_stream;
547 if (!GetStreamBySsrc(recv_streams_, params.ssrc, &found_stream)) {
548 LOG(LS_WARNING) << debug_name_ << "->OnDataFromSctpToChannel(...): "
549 << "Received packet for unknown ssrc: " << params.ssrc;
550 return;
551 }
552
553 if (receiving_) {
554 LOG(LS_VERBOSE) << debug_name_ << "->OnDataFromSctpToChannel(...): "
555 << "Posting with length: " << buffer->length();
556 SignalDataReceived(params, buffer->data(), buffer->length());
557 } else {
558 LOG(LS_WARNING) << debug_name_ << "->OnDataFromSctpToChannel(...): "
559 << "Not receiving packet with sid=" << params.ssrc
560 << " len=" << buffer->length()
561 << " before SetReceive(true).";
562 }
563}
564
565void SctpDataMediaChannel::OnNotificationFromSctp(
566 talk_base::Buffer* buffer) {
567 const sctp_notification& notification =
568 reinterpret_cast<const sctp_notification&>(*buffer->data());
569 ASSERT(notification.sn_header.sn_length == buffer->length());
570
571 // TODO(ldixon): handle notifications appropriately.
572 switch (notification.sn_header.sn_type) {
573 case SCTP_ASSOC_CHANGE:
574 LOG(LS_VERBOSE) << "SCTP_ASSOC_CHANGE";
575 OnNotificationAssocChange(notification.sn_assoc_change);
576 break;
577 case SCTP_REMOTE_ERROR:
578 LOG(LS_INFO) << "SCTP_REMOTE_ERROR";
579 break;
580 case SCTP_SHUTDOWN_EVENT:
581 LOG(LS_INFO) << "SCTP_SHUTDOWN_EVENT";
582 break;
583 case SCTP_ADAPTATION_INDICATION:
584 LOG(LS_INFO) << "SCTP_ADAPTATION_INIDICATION";
585 break;
586 case SCTP_PARTIAL_DELIVERY_EVENT:
587 LOG(LS_INFO) << "SCTP_PARTIAL_DELIVERY_EVENT";
588 break;
589 case SCTP_AUTHENTICATION_EVENT:
590 LOG(LS_INFO) << "SCTP_AUTHENTICATION_EVENT";
591 break;
592 case SCTP_SENDER_DRY_EVENT:
593 LOG(LS_INFO) << "SCTP_SENDER_DRY_EVENT";
594 break;
595 // TODO(ldixon): Unblock after congestion.
596 case SCTP_NOTIFICATIONS_STOPPED_EVENT:
597 LOG(LS_INFO) << "SCTP_NOTIFICATIONS_STOPPED_EVENT";
598 break;
599 case SCTP_SEND_FAILED_EVENT:
600 LOG(LS_INFO) << "SCTP_SEND_FAILED_EVENT";
601 break;
602 case SCTP_STREAM_RESET_EVENT:
603 LOG(LS_INFO) << "SCTP_STREAM_RESET_EVENT";
604 // TODO(ldixon): Notify up to channel that stream resent has happened,
605 // and write unit test for this case.
606 break;
607 case SCTP_ASSOC_RESET_EVENT:
608 LOG(LS_INFO) << "SCTP_ASSOC_RESET_EVENT";
609 break;
610 case SCTP_STREAM_CHANGE_EVENT:
611 LOG(LS_INFO) << "SCTP_STREAM_CHANGE_EVENT";
612 break;
613 default:
614 LOG(LS_WARNING) << "Unknown SCTP event: "
615 << notification.sn_header.sn_type;
616 break;
617 }
618}
619
620void SctpDataMediaChannel::OnNotificationAssocChange(
621 const sctp_assoc_change& change) {
622 switch (change.sac_state) {
623 case SCTP_COMM_UP:
624 LOG(LS_VERBOSE) << "Association change SCTP_COMM_UP";
625 break;
626 case SCTP_COMM_LOST:
627 LOG(LS_INFO) << "Association change SCTP_COMM_LOST";
628 break;
629 case SCTP_RESTART:
630 LOG(LS_INFO) << "Association change SCTP_RESTART";
631 break;
632 case SCTP_SHUTDOWN_COMP:
633 LOG(LS_INFO) << "Association change SCTP_SHUTDOWN_COMP";
634 break;
635 case SCTP_CANT_STR_ASSOC:
636 LOG(LS_INFO) << "Association change SCTP_CANT_STR_ASSOC";
637 break;
638 default:
639 LOG(LS_INFO) << "Association change UNKNOWN";
640 break;
641 }
642}
643
644
645void SctpDataMediaChannel::OnPacketFromSctpToNetwork(
646 talk_base::Buffer* buffer) {
647 if (buffer->length() > kSctpMtu) {
648 LOG(LS_ERROR) << debug_name_ << "->OnPacketFromSctpToNetwork(...): "
649 << "SCTP seems to have made a poacket that is bigger "
650 "than its official MTU.";
651 }
652 network_interface()->SendPacket(buffer);
653}
654
655void SctpDataMediaChannel::OnMessage(talk_base::Message* msg) {
656 switch (msg->message_id) {
657 case MSG_SCTPINBOUNDPACKET: {
658 SctpInboundPacket* packet =
659 static_cast<talk_base::TypedMessageData<SctpInboundPacket*>*>(
660 msg->pdata)->data();
661 OnInboundPacketFromSctpToChannel(packet);
662 delete packet;
663 break;
664 }
665 case MSG_SCTPOUTBOUNDPACKET: {
666 talk_base::Buffer* buffer =
667 static_cast<talk_base::TypedMessageData<talk_base::Buffer*>*>(
668 msg->pdata)->data();
669 OnPacketFromSctpToNetwork(buffer);
670 delete buffer;
671 break;
672 }
673 }
674}
675
676} // namespace cricket