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