blob: 71ef73c8318d5e76be4b7e7864c8b37121aff4c7 [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,
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000273 SCTP_SEND_FAILED_EVENT,
274 SCTP_SENDER_DRY_EVENT};
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000275 struct sctp_event event = {0};
276 event.se_assoc_id = SCTP_ALL_ASSOC;
277 event.se_on = 1;
278 for (size_t i = 0; i < ARRAY_SIZE(event_types); i++) {
279 event.se_type = event_types[i];
280 if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_EVENT, &event,
281 sizeof(event)) < 0) {
282 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP_EVENT type: "
283 << event.se_type;
284 return false;
285 }
286 }
287
288 // Register this class as an address for usrsctp. This is used by SCTP to
289 // direct the packets received (by the created socket) to this class.
290 usrsctp_register_address(this);
291 sending_ = true;
292 return true;
293}
294
295void SctpDataMediaChannel::CloseSctpSocket() {
296 sending_ = false;
297 if (sock_) {
298 // We assume that SO_LINGER option is set to close the association when
299 // close is called. This means that any pending packets in usrsctp will be
300 // discarded instead of being sent.
301 usrsctp_close(sock_);
302 sock_ = NULL;
303 usrsctp_deregister_address(this);
304 }
305}
306
307bool SctpDataMediaChannel::Connect() {
308 LOG(LS_VERBOSE) << debug_name_ << "->Connect().";
309
310 // If we already have a socket connection, just return.
311 if (sock_) {
312 LOG(LS_WARNING) << debug_name_ << "->Connect(): Ignored as socket "
313 "is already established.";
314 return true;
315 }
316
317 // If no socket (it was closed) try to start it again. This can happen when
318 // the socket we are connecting to closes, does an sctp shutdown handshake,
319 // or behaves unexpectedly causing us to perform a CloseSctpSocket.
320 if (!sock_ && !OpenSctpSocket()) {
321 return false;
322 }
323
324 // Note: conversion from int to uint16_t happens on assignment.
325 sockaddr_conn local_sconn = GetSctpSockAddr(local_port_);
326 if (usrsctp_bind(sock_, reinterpret_cast<sockaddr *>(&local_sconn),
327 sizeof(local_sconn)) < 0) {
328 LOG_ERRNO(LS_ERROR) << debug_name_ << "->Connect(): "
329 << ("Failed usrsctp_bind");
330 CloseSctpSocket();
331 return false;
332 }
333
334 // Note: conversion from int to uint16_t happens on assignment.
335 sockaddr_conn remote_sconn = GetSctpSockAddr(remote_port_);
336 int connect_result = usrsctp_connect(
337 sock_, reinterpret_cast<sockaddr *>(&remote_sconn), sizeof(remote_sconn));
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000338 if (connect_result < 0 && errno != SCTP_EINPROGRESS) {
339 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed usrsctp_connect. got errno="
340 << errno << ", but wanted " << SCTP_EINPROGRESS;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000341 CloseSctpSocket();
342 return false;
343 }
344 return true;
345}
346
347void SctpDataMediaChannel::Disconnect() {
348 // TODO(ldixon): Consider calling |usrsctp_shutdown(sock_, ...)| to do a
349 // shutdown handshake and remove the association.
350 CloseSctpSocket();
351}
352
353bool SctpDataMediaChannel::SetSend(bool send) {
354 if (!sending_ && send) {
355 return Connect();
356 }
357 if (sending_ && !send) {
358 Disconnect();
359 }
360 return true;
361}
362
363bool SctpDataMediaChannel::SetReceive(bool receive) {
364 receiving_ = receive;
365 return true;
366}
367
368bool SctpDataMediaChannel::AddSendStream(const StreamParams& stream) {
369 if (!stream.has_ssrcs()) {
370 return false;
371 }
372
373 StreamParams found_stream;
374 if (GetStreamBySsrc(send_streams_, stream.first_ssrc(), &found_stream)) {
375 LOG(LS_WARNING) << debug_name_ << "->AddSendStream(...): "
376 << "Not adding data send stream '" << stream.id
377 << "' with ssrc=" << stream.first_ssrc()
378 << " because stream already exists.";
379 return false;
380 }
381
382 send_streams_.push_back(stream);
383 return true;
384}
385
386bool SctpDataMediaChannel::RemoveSendStream(uint32 ssrc) {
387 StreamParams found_stream;
388 if (!GetStreamBySsrc(send_streams_, ssrc, &found_stream)) {
389 return false;
390 }
391
392 RemoveStreamBySsrc(&send_streams_, ssrc);
393 return true;
394}
395
396// Note: expects exactly one ssrc. If none are given, it will fail. If more
397// than one are given, it will use the first.
398bool SctpDataMediaChannel::AddRecvStream(const StreamParams& stream) {
399 if (!stream.has_ssrcs()) {
400 return false;
401 }
402
403 StreamParams found_stream;
404 if (GetStreamBySsrc(recv_streams_, stream.first_ssrc(), &found_stream)) {
405 LOG(LS_WARNING) << debug_name_ << "->AddRecvStream(...): "
406 << "Not adding data recv stream '" << stream.id
407 << "' with ssrc=" << stream.first_ssrc()
408 << " because stream already exists.";
409 return false;
410 }
411
412 recv_streams_.push_back(stream);
413 LOG(LS_VERBOSE) << debug_name_ << "->AddRecvStream(...): "
414 << "Added data recv stream '" << stream.id
415 << "' with ssrc=" << stream.first_ssrc();
416 return true;
417}
418
419bool SctpDataMediaChannel::RemoveRecvStream(uint32 ssrc) {
420 RemoveStreamBySsrc(&recv_streams_, ssrc);
421 return true;
422}
423
424bool SctpDataMediaChannel::SendData(
425 const SendDataParams& params,
426 const talk_base::Buffer& payload,
427 SendDataResult* result) {
428 if (result) {
429 // If we return true, we'll set this to SDR_SUCCESS.
430 *result = SDR_ERROR;
431 }
432
433 if (!sending_) {
434 LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
435 << "Not sending packet with ssrc=" << params.ssrc
436 << " len=" << payload.length() << " before SetSend(true).";
437 return false;
438 }
439
440 StreamParams found_stream;
441 if (!GetStreamBySsrc(send_streams_, params.ssrc, &found_stream)) {
442 LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
443 << "Not sending data because ssrc is unknown: "
444 << params.ssrc;
445 return false;
446 }
447
448 // TODO(ldixon): Experiment with sctp_sendv_spa instead of sctp_sndinfo. e.g.
449 // struct sctp_sendv_spa spa = {0};
450 // spa.sendv_flags |= SCTP_SEND_SNDINFO_VALID;
451 // spa.sendv_sndinfo.snd_sid = params.ssrc;
452 // spa.sendv_sndinfo.snd_context = 0;
453 // spa.sendv_sndinfo.snd_assoc_id = 0;
454 // TODO(pthatcher): Support different types of protocols (e.g. SSL) and
455 // messages (e.g. Binary) via SendDataParams.
456 // spa.sendv_sndinfo.snd_ppid = htonl(PPID_NONE);
457 // TODO(pthatcher): Support different reliability semantics.
458 // For reliable: Remove SCTP_UNORDERED.
459 // For partially-reliable: Add rtx or ttl.
460 // spa.sendv_sndinfo.snd_flags = SCTP_UNORDERED;
461 // TODO(phatcher): Try some of these things.
462 // spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
463 // spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX;
464 // spa.sendv_prinfo.pr_value = htons(max_retransmit_count);
465 // spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_TTL;
466 // spa.sendv_prinfo.pr_value = htons(max_retransmit_time);
467 //
468 // Send data using SCTP.
469 sctp_sndinfo sndinfo = {0};
470 sndinfo.snd_sid = params.ssrc;
471 sndinfo.snd_flags = 0;
472 // TODO(pthatcher): Once data types are added to SendParams, this can be set
473 // from SendParams.
474 sndinfo.snd_ppid = talk_base::HostToNetwork32(PPID_NONE);
475 sndinfo.snd_context = 0;
476 sndinfo.snd_assoc_id = 0;
477 ssize_t res = usrsctp_sendv(sock_, payload.data(),
478 static_cast<size_t>(payload.length()),
479 NULL, 0, &sndinfo,
480 static_cast<socklen_t>(sizeof(sndinfo)),
481 SCTP_SENDV_SNDINFO, 0);
482 if (res < 0) {
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000483 if (errno == EWOULDBLOCK) {
484 *result = SDR_BLOCK;
485 LOG(LS_INFO) << debug_name_ << "->SendData(...): EWOULDBLOCK returned";
486 } else {
487 LOG_ERRNO(LS_ERROR) << "ERROR:" << debug_name_
488 << "->SendData(...): "
489 << " usrsctp_sendv: ";
490 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000491 return false;
492 }
493 if (result) {
494 // If we return true, we'll set this to SDR_SUCCESS.
495 *result = SDR_SUCCESS;
496 }
497 return true;
498}
499
500// Called by network interface when a packet has been received.
501void SctpDataMediaChannel::OnPacketReceived(talk_base::Buffer* packet) {
502 LOG(LS_VERBOSE) << debug_name_ << "->OnPacketReceived(...): "
503 << " length=" << packet->length() << "; data="
504 << SctpDataToDebugString(packet->data(), packet->length(),
505 SCTP_DUMP_INBOUND);
506 // Only give receiving packets to usrsctp after if connected. This enables two
507 // peers to each make a connect call, but for them not to receive an INIT
508 // packet before they have called connect; least the last receiver of the INIT
509 // packet will have called connect, and a connection will be established.
510 if (sending_) {
511 LOG(LS_VERBOSE) << debug_name_ << "->OnPacketReceived(...):"
512 << " Passed packet to sctp.";
513 // Pass received packet to SCTP stack. Once processed by usrsctp, the data
514 // will be will be given to the global OnSctpInboundData, and then,
515 // marshalled by a Post and handled with OnMessage.
516 usrsctp_conninput(this, packet->data(), packet->length(), 0);
517 } else {
518 // TODO(ldixon): Consider caching the packet for very slightly better
519 // reliability.
520 LOG(LS_INFO) << debug_name_ << "->OnPacketReceived(...):"
521 << " Threw packet (probably an INIT) away.";
522 }
523}
524
525void SctpDataMediaChannel::OnInboundPacketFromSctpToChannel(
526 SctpInboundPacket* packet) {
527 LOG(LS_VERBOSE) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): "
528 << "Received SCTP data:"
529 << " ssrc=" << packet->params.ssrc
530 << " data='" << std::string(packet->buffer.data(),
531 packet->buffer.length())
532 << " notification: " << (packet->flags & MSG_NOTIFICATION)
533 << "' length=" << packet->buffer.length();
534 // Sending a packet with data == NULL (no data) is SCTPs "close the
535 // connection" message. This sets sock_ = NULL;
536 if (!packet->buffer.length() || !packet->buffer.data()) {
537 LOG(LS_INFO) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): "
538 "No data, closing.";
539 return;
540 }
541 if (packet->flags & MSG_NOTIFICATION) {
542 OnNotificationFromSctp(&packet->buffer);
543 } else {
544 OnDataFromSctpToChannel(packet->params, &packet->buffer);
545 }
546}
547
548void SctpDataMediaChannel::OnDataFromSctpToChannel(
549 const ReceiveDataParams& params, talk_base::Buffer* buffer) {
550 StreamParams found_stream;
551 if (!GetStreamBySsrc(recv_streams_, params.ssrc, &found_stream)) {
552 LOG(LS_WARNING) << debug_name_ << "->OnDataFromSctpToChannel(...): "
553 << "Received packet for unknown ssrc: " << params.ssrc;
554 return;
555 }
556
557 if (receiving_) {
558 LOG(LS_VERBOSE) << debug_name_ << "->OnDataFromSctpToChannel(...): "
559 << "Posting with length: " << buffer->length();
560 SignalDataReceived(params, buffer->data(), buffer->length());
561 } else {
562 LOG(LS_WARNING) << debug_name_ << "->OnDataFromSctpToChannel(...): "
563 << "Not receiving packet with sid=" << params.ssrc
564 << " len=" << buffer->length()
565 << " before SetReceive(true).";
566 }
567}
568
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000569void SctpDataMediaChannel::OnNotificationFromSctp(talk_base::Buffer* buffer) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000570 const sctp_notification& notification =
571 reinterpret_cast<const sctp_notification&>(*buffer->data());
572 ASSERT(notification.sn_header.sn_length == buffer->length());
573
574 // TODO(ldixon): handle notifications appropriately.
575 switch (notification.sn_header.sn_type) {
576 case SCTP_ASSOC_CHANGE:
577 LOG(LS_VERBOSE) << "SCTP_ASSOC_CHANGE";
578 OnNotificationAssocChange(notification.sn_assoc_change);
579 break;
580 case SCTP_REMOTE_ERROR:
581 LOG(LS_INFO) << "SCTP_REMOTE_ERROR";
582 break;
583 case SCTP_SHUTDOWN_EVENT:
584 LOG(LS_INFO) << "SCTP_SHUTDOWN_EVENT";
585 break;
586 case SCTP_ADAPTATION_INDICATION:
587 LOG(LS_INFO) << "SCTP_ADAPTATION_INIDICATION";
588 break;
589 case SCTP_PARTIAL_DELIVERY_EVENT:
590 LOG(LS_INFO) << "SCTP_PARTIAL_DELIVERY_EVENT";
591 break;
592 case SCTP_AUTHENTICATION_EVENT:
593 LOG(LS_INFO) << "SCTP_AUTHENTICATION_EVENT";
594 break;
595 case SCTP_SENDER_DRY_EVENT:
596 LOG(LS_INFO) << "SCTP_SENDER_DRY_EVENT";
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000597 SignalReadyToSend(true);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000598 break;
599 // TODO(ldixon): Unblock after congestion.
600 case SCTP_NOTIFICATIONS_STOPPED_EVENT:
601 LOG(LS_INFO) << "SCTP_NOTIFICATIONS_STOPPED_EVENT";
602 break;
603 case SCTP_SEND_FAILED_EVENT:
604 LOG(LS_INFO) << "SCTP_SEND_FAILED_EVENT";
605 break;
606 case SCTP_STREAM_RESET_EVENT:
607 LOG(LS_INFO) << "SCTP_STREAM_RESET_EVENT";
608 // TODO(ldixon): Notify up to channel that stream resent has happened,
609 // and write unit test for this case.
610 break;
611 case SCTP_ASSOC_RESET_EVENT:
612 LOG(LS_INFO) << "SCTP_ASSOC_RESET_EVENT";
613 break;
614 case SCTP_STREAM_CHANGE_EVENT:
615 LOG(LS_INFO) << "SCTP_STREAM_CHANGE_EVENT";
616 break;
617 default:
618 LOG(LS_WARNING) << "Unknown SCTP event: "
619 << notification.sn_header.sn_type;
620 break;
621 }
622}
623
624void SctpDataMediaChannel::OnNotificationAssocChange(
625 const sctp_assoc_change& change) {
626 switch (change.sac_state) {
627 case SCTP_COMM_UP:
628 LOG(LS_VERBOSE) << "Association change SCTP_COMM_UP";
629 break;
630 case SCTP_COMM_LOST:
631 LOG(LS_INFO) << "Association change SCTP_COMM_LOST";
632 break;
633 case SCTP_RESTART:
634 LOG(LS_INFO) << "Association change SCTP_RESTART";
635 break;
636 case SCTP_SHUTDOWN_COMP:
637 LOG(LS_INFO) << "Association change SCTP_SHUTDOWN_COMP";
638 break;
639 case SCTP_CANT_STR_ASSOC:
640 LOG(LS_INFO) << "Association change SCTP_CANT_STR_ASSOC";
641 break;
642 default:
643 LOG(LS_INFO) << "Association change UNKNOWN";
644 break;
645 }
646}
647
648
649void SctpDataMediaChannel::OnPacketFromSctpToNetwork(
650 talk_base::Buffer* buffer) {
651 if (buffer->length() > kSctpMtu) {
652 LOG(LS_ERROR) << debug_name_ << "->OnPacketFromSctpToNetwork(...): "
653 << "SCTP seems to have made a poacket that is bigger "
654 "than its official MTU.";
655 }
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000656 MediaChannel::SendPacket(buffer);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000657}
658
659void SctpDataMediaChannel::OnMessage(talk_base::Message* msg) {
660 switch (msg->message_id) {
661 case MSG_SCTPINBOUNDPACKET: {
662 SctpInboundPacket* packet =
663 static_cast<talk_base::TypedMessageData<SctpInboundPacket*>*>(
664 msg->pdata)->data();
665 OnInboundPacketFromSctpToChannel(packet);
666 delete packet;
667 break;
668 }
669 case MSG_SCTPOUTBOUNDPACKET: {
670 talk_base::Buffer* buffer =
671 static_cast<talk_base::TypedMessageData<talk_base::Buffer*>*>(
672 msg->pdata)->data();
673 OnPacketFromSctpToNetwork(buffer);
674 delete buffer;
675 break;
676 }
677 }
678}
679
680} // namespace cricket