blob: 0d6bc59b8ce5351aef2c58c9e2fd1a7f8851cb2c [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.
wu@webrtc.org91053e72013-08-10 07:18:04 +0000137 const uint32 native_ppid = talk_base::HostToNetwork32(rcv.rcv_ppid);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000138 SctpInboundPacket* packet = new SctpInboundPacket();
139 packet->buffer.SetData(data, length);
140 packet->params.ssrc = rcv.rcv_sid;
141 packet->params.seq_num = rcv.rcv_ssn;
142 packet->params.timestamp = rcv.rcv_tsn;
wu@webrtc.org91053e72013-08-10 07:18:04 +0000143 packet->params.type =
144 static_cast<cricket::DataMessageType>(native_ppid);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000145 packet->flags = flags;
146 channel->worker_thread()->Post(channel, MSG_SCTPINBOUNDPACKET,
147 talk_base::WrapMessageData(packet));
148 free(data);
149 return 1;
150}
151
152// Set the initial value of the static SCTP Data Engines reference count.
153int SctpDataEngine::usrsctp_engines_count = 0;
154
155SctpDataEngine::SctpDataEngine() {
156 if (usrsctp_engines_count == 0) {
157 // First argument is udp_encapsulation_port, which is not releveant for our
158 // AF_CONN use of sctp.
159 usrsctp_init(0, cricket::OnSctpOutboundPacket, debug_sctp_printf);
160
161 // To turn on/off detailed SCTP debugging. You will also need to have the
162 // SCTP_DEBUG cpp defines flag.
163 // usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_ALL);
164
165 // TODO(ldixon): Consider turning this on/off.
166 usrsctp_sysctl_set_sctp_ecn_enable(0);
167
168 // TODO(ldixon): Consider turning this on/off.
169 // This is not needed right now (we don't do dynamic address changes):
170 // If SCTP Auto-ASCONF is enabled, the peer is informed automatically
171 // when a new address is added or removed. This feature is enabled by
172 // default.
173 // usrsctp_sysctl_set_sctp_auto_asconf(0);
174
175 // TODO(ldixon): Consider turning this on/off.
176 // Add a blackhole sysctl. Setting it to 1 results in no ABORTs
177 // being sent in response to INITs, setting it to 2 results
178 // in no ABORTs being sent for received OOTB packets.
179 // This is similar to the TCP sysctl.
180 //
181 // See: http://lakerest.net/pipermail/sctp-coders/2012-January/009438.html
182 // See: http://svnweb.freebsd.org/base?view=revision&revision=229805
183 // usrsctp_sysctl_set_sctp_blackhole(2);
184 }
185 usrsctp_engines_count++;
186
187 // We don't put in a codec because we don't want one offered when we
188 // use the hybrid data engine.
189 // codecs_.push_back(cricket::DataCodec( kGoogleSctpDataCodecId,
190 // kGoogleSctpDataCodecName, 0));
191}
192
193SctpDataEngine::~SctpDataEngine() {
194 // TODO(ldixon): There is currently a bug in teardown of usrsctp that blocks
195 // indefintely if a finish call made too soon after close calls. So teardown
196 // has been skipped. Once the bug is fixed, retest and enable teardown.
197 //
198 // usrsctp_engines_count--;
199 // LOG(LS_VERBOSE) << "usrsctp_engines_count:" << usrsctp_engines_count;
200 // if (usrsctp_engines_count == 0) {
201 // if (usrsctp_finish() != 0) {
202 // LOG(LS_WARNING) << "usrsctp_finish.";
203 // }
204 // }
205}
206
207DataMediaChannel* SctpDataEngine::CreateChannel(
208 DataChannelType data_channel_type) {
209 if (data_channel_type != DCT_SCTP) {
210 return NULL;
211 }
212 return new SctpDataMediaChannel(talk_base::Thread::Current());
213}
214
215SctpDataMediaChannel::SctpDataMediaChannel(talk_base::Thread* thread)
216 : worker_thread_(thread),
217 local_port_(kSctpDefaultPort),
218 remote_port_(kSctpDefaultPort),
219 sock_(NULL),
220 sending_(false),
221 receiving_(false),
222 debug_name_("SctpDataMediaChannel") {
223}
224
225SctpDataMediaChannel::~SctpDataMediaChannel() {
226 CloseSctpSocket();
227}
228
229sockaddr_conn SctpDataMediaChannel::GetSctpSockAddr(int port) {
230 sockaddr_conn sconn = {0};
231 sconn.sconn_family = AF_CONN;
232#ifdef HAVE_SCONN_LEN
233 sconn.sconn_len = sizeof(sockaddr_conn);
234#endif
235 // Note: conversion from int to uint16_t happens here.
236 sconn.sconn_port = talk_base::HostToNetwork16(port);
237 sconn.sconn_addr = this;
238 return sconn;
239}
240
241bool SctpDataMediaChannel::OpenSctpSocket() {
242 if (sock_) {
243 LOG(LS_VERBOSE) << debug_name_
244 << "->Ignoring attempt to re-create existing socket.";
245 return false;
246 }
247 sock_ = usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP,
248 cricket::OnSctpInboundPacket, NULL, 0, this);
249 if (!sock_) {
250 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to create SCTP socket.";
251 return false;
252 }
253
254 // Make the socket non-blocking. Connect, close, shutdown etc will not block
255 // the thread waiting for the socket operation to complete.
256 if (usrsctp_set_non_blocking(sock_, 1) < 0) {
257 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP to non blocking.";
258 return false;
259 }
260
261 // This ensures that the usrsctp close call deletes the association. This
262 // prevents usrsctp from calling OnSctpOutboundPacket with references to
263 // this class as the address.
264 linger linger_opt;
265 linger_opt.l_onoff = 1;
266 linger_opt.l_linger = 0;
267 if (usrsctp_setsockopt(sock_, SOL_SOCKET, SO_LINGER, &linger_opt,
268 sizeof(linger_opt))) {
269 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SO_LINGER.";
270 return false;
271 }
272
sergeyu@chromium.orga59696b2013-09-13 23:48:58 +0000273 uint32_t nodelay = 1;
274 if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_NODELAY, &nodelay,
275 sizeof(nodelay))) {
276 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP_NODELAY.";
277 return false;
278 }
279
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000280 // Subscribe to SCTP event notifications.
281 int event_types[] = {SCTP_ASSOC_CHANGE,
282 SCTP_PEER_ADDR_CHANGE,
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000283 SCTP_SEND_FAILED_EVENT,
284 SCTP_SENDER_DRY_EVENT};
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000285 struct sctp_event event = {0};
286 event.se_assoc_id = SCTP_ALL_ASSOC;
287 event.se_on = 1;
288 for (size_t i = 0; i < ARRAY_SIZE(event_types); i++) {
289 event.se_type = event_types[i];
290 if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_EVENT, &event,
291 sizeof(event)) < 0) {
292 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP_EVENT type: "
293 << event.se_type;
294 return false;
295 }
296 }
297
298 // Register this class as an address for usrsctp. This is used by SCTP to
299 // direct the packets received (by the created socket) to this class.
300 usrsctp_register_address(this);
301 sending_ = true;
302 return true;
303}
304
305void SctpDataMediaChannel::CloseSctpSocket() {
306 sending_ = false;
307 if (sock_) {
308 // We assume that SO_LINGER option is set to close the association when
309 // close is called. This means that any pending packets in usrsctp will be
310 // discarded instead of being sent.
311 usrsctp_close(sock_);
312 sock_ = NULL;
313 usrsctp_deregister_address(this);
314 }
315}
316
317bool SctpDataMediaChannel::Connect() {
318 LOG(LS_VERBOSE) << debug_name_ << "->Connect().";
319
320 // If we already have a socket connection, just return.
321 if (sock_) {
322 LOG(LS_WARNING) << debug_name_ << "->Connect(): Ignored as socket "
323 "is already established.";
324 return true;
325 }
326
327 // If no socket (it was closed) try to start it again. This can happen when
328 // the socket we are connecting to closes, does an sctp shutdown handshake,
329 // or behaves unexpectedly causing us to perform a CloseSctpSocket.
330 if (!sock_ && !OpenSctpSocket()) {
331 return false;
332 }
333
334 // Note: conversion from int to uint16_t happens on assignment.
335 sockaddr_conn local_sconn = GetSctpSockAddr(local_port_);
336 if (usrsctp_bind(sock_, reinterpret_cast<sockaddr *>(&local_sconn),
337 sizeof(local_sconn)) < 0) {
338 LOG_ERRNO(LS_ERROR) << debug_name_ << "->Connect(): "
339 << ("Failed usrsctp_bind");
340 CloseSctpSocket();
341 return false;
342 }
343
344 // Note: conversion from int to uint16_t happens on assignment.
345 sockaddr_conn remote_sconn = GetSctpSockAddr(remote_port_);
346 int connect_result = usrsctp_connect(
347 sock_, reinterpret_cast<sockaddr *>(&remote_sconn), sizeof(remote_sconn));
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000348 if (connect_result < 0 && errno != SCTP_EINPROGRESS) {
349 LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed usrsctp_connect. got errno="
350 << errno << ", but wanted " << SCTP_EINPROGRESS;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000351 CloseSctpSocket();
352 return false;
353 }
354 return true;
355}
356
357void SctpDataMediaChannel::Disconnect() {
358 // TODO(ldixon): Consider calling |usrsctp_shutdown(sock_, ...)| to do a
359 // shutdown handshake and remove the association.
360 CloseSctpSocket();
361}
362
363bool SctpDataMediaChannel::SetSend(bool send) {
364 if (!sending_ && send) {
365 return Connect();
366 }
367 if (sending_ && !send) {
368 Disconnect();
369 }
370 return true;
371}
372
373bool SctpDataMediaChannel::SetReceive(bool receive) {
374 receiving_ = receive;
375 return true;
376}
377
378bool SctpDataMediaChannel::AddSendStream(const StreamParams& stream) {
379 if (!stream.has_ssrcs()) {
380 return false;
381 }
382
383 StreamParams found_stream;
wu@webrtc.org91053e72013-08-10 07:18:04 +0000384 // TODO(lally): Consider keeping this sorted.
385 if (GetStreamBySsrc(streams_, stream.first_ssrc(), &found_stream)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000386 LOG(LS_WARNING) << debug_name_ << "->AddSendStream(...): "
387 << "Not adding data send stream '" << stream.id
388 << "' with ssrc=" << stream.first_ssrc()
389 << " because stream already exists.";
390 return false;
391 }
392
wu@webrtc.org91053e72013-08-10 07:18:04 +0000393 streams_.push_back(stream);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000394 return true;
395}
396
397bool SctpDataMediaChannel::RemoveSendStream(uint32 ssrc) {
398 StreamParams found_stream;
wu@webrtc.org91053e72013-08-10 07:18:04 +0000399 if (!GetStreamBySsrc(streams_, ssrc, &found_stream)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000400 return false;
401 }
402
wu@webrtc.org91053e72013-08-10 07:18:04 +0000403 RemoveStreamBySsrc(&streams_, ssrc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000404 return true;
405}
406
407// Note: expects exactly one ssrc. If none are given, it will fail. If more
408// than one are given, it will use the first.
409bool SctpDataMediaChannel::AddRecvStream(const StreamParams& stream) {
410 if (!stream.has_ssrcs()) {
411 return false;
412 }
413
414 StreamParams found_stream;
wu@webrtc.org91053e72013-08-10 07:18:04 +0000415 if (GetStreamBySsrc(streams_, stream.first_ssrc(), &found_stream)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000416 LOG(LS_WARNING) << debug_name_ << "->AddRecvStream(...): "
417 << "Not adding data recv stream '" << stream.id
418 << "' with ssrc=" << stream.first_ssrc()
419 << " because stream already exists.";
420 return false;
421 }
422
wu@webrtc.org91053e72013-08-10 07:18:04 +0000423 streams_.push_back(stream);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000424 LOG(LS_VERBOSE) << debug_name_ << "->AddRecvStream(...): "
425 << "Added data recv stream '" << stream.id
426 << "' with ssrc=" << stream.first_ssrc();
427 return true;
428}
429
430bool SctpDataMediaChannel::RemoveRecvStream(uint32 ssrc) {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000431 RemoveStreamBySsrc(&streams_, ssrc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000432 return true;
433}
434
435bool SctpDataMediaChannel::SendData(
436 const SendDataParams& params,
437 const talk_base::Buffer& payload,
438 SendDataResult* result) {
439 if (result) {
440 // If we return true, we'll set this to SDR_SUCCESS.
441 *result = SDR_ERROR;
442 }
443
444 if (!sending_) {
445 LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
446 << "Not sending packet with ssrc=" << params.ssrc
447 << " len=" << payload.length() << " before SetSend(true).";
448 return false;
449 }
450
451 StreamParams found_stream;
wu@webrtc.org91053e72013-08-10 07:18:04 +0000452 if (params.type != cricket::DMT_CONTROL &&
453 !GetStreamBySsrc(streams_, params.ssrc, &found_stream)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000454 LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
455 << "Not sending data because ssrc is unknown: "
456 << params.ssrc;
457 return false;
458 }
459
460 // TODO(ldixon): Experiment with sctp_sendv_spa instead of sctp_sndinfo. e.g.
461 // struct sctp_sendv_spa spa = {0};
462 // spa.sendv_flags |= SCTP_SEND_SNDINFO_VALID;
463 // spa.sendv_sndinfo.snd_sid = params.ssrc;
464 // spa.sendv_sndinfo.snd_context = 0;
465 // spa.sendv_sndinfo.snd_assoc_id = 0;
466 // TODO(pthatcher): Support different types of protocols (e.g. SSL) and
467 // messages (e.g. Binary) via SendDataParams.
468 // spa.sendv_sndinfo.snd_ppid = htonl(PPID_NONE);
469 // TODO(pthatcher): Support different reliability semantics.
470 // For reliable: Remove SCTP_UNORDERED.
471 // For partially-reliable: Add rtx or ttl.
472 // spa.sendv_sndinfo.snd_flags = SCTP_UNORDERED;
473 // TODO(phatcher): Try some of these things.
474 // spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
475 // spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX;
476 // spa.sendv_prinfo.pr_value = htons(max_retransmit_count);
477 // spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_TTL;
478 // spa.sendv_prinfo.pr_value = htons(max_retransmit_time);
479 //
480 // Send data using SCTP.
481 sctp_sndinfo sndinfo = {0};
482 sndinfo.snd_sid = params.ssrc;
483 sndinfo.snd_flags = 0;
484 // TODO(pthatcher): Once data types are added to SendParams, this can be set
485 // from SendParams.
wu@webrtc.org91053e72013-08-10 07:18:04 +0000486 sndinfo.snd_ppid = talk_base::HostToNetwork32(params.type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000487 sndinfo.snd_context = 0;
488 sndinfo.snd_assoc_id = 0;
489 ssize_t res = usrsctp_sendv(sock_, payload.data(),
490 static_cast<size_t>(payload.length()),
491 NULL, 0, &sndinfo,
492 static_cast<socklen_t>(sizeof(sndinfo)),
493 SCTP_SENDV_SNDINFO, 0);
494 if (res < 0) {
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000495 if (errno == EWOULDBLOCK) {
496 *result = SDR_BLOCK;
497 LOG(LS_INFO) << debug_name_ << "->SendData(...): EWOULDBLOCK returned";
498 } else {
499 LOG_ERRNO(LS_ERROR) << "ERROR:" << debug_name_
500 << "->SendData(...): "
501 << " usrsctp_sendv: ";
502 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000503 return false;
504 }
505 if (result) {
506 // If we return true, we'll set this to SDR_SUCCESS.
507 *result = SDR_SUCCESS;
508 }
509 return true;
510}
511
512// Called by network interface when a packet has been received.
513void SctpDataMediaChannel::OnPacketReceived(talk_base::Buffer* packet) {
514 LOG(LS_VERBOSE) << debug_name_ << "->OnPacketReceived(...): "
515 << " length=" << packet->length() << "; data="
516 << SctpDataToDebugString(packet->data(), packet->length(),
517 SCTP_DUMP_INBOUND);
518 // Only give receiving packets to usrsctp after if connected. This enables two
519 // peers to each make a connect call, but for them not to receive an INIT
520 // packet before they have called connect; least the last receiver of the INIT
521 // packet will have called connect, and a connection will be established.
522 if (sending_) {
523 LOG(LS_VERBOSE) << debug_name_ << "->OnPacketReceived(...):"
524 << " Passed packet to sctp.";
525 // Pass received packet to SCTP stack. Once processed by usrsctp, the data
526 // will be will be given to the global OnSctpInboundData, and then,
527 // marshalled by a Post and handled with OnMessage.
528 usrsctp_conninput(this, packet->data(), packet->length(), 0);
529 } else {
530 // TODO(ldixon): Consider caching the packet for very slightly better
531 // reliability.
532 LOG(LS_INFO) << debug_name_ << "->OnPacketReceived(...):"
533 << " Threw packet (probably an INIT) away.";
534 }
535}
536
537void SctpDataMediaChannel::OnInboundPacketFromSctpToChannel(
538 SctpInboundPacket* packet) {
539 LOG(LS_VERBOSE) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): "
540 << "Received SCTP data:"
541 << " ssrc=" << packet->params.ssrc
542 << " data='" << std::string(packet->buffer.data(),
543 packet->buffer.length())
544 << " notification: " << (packet->flags & MSG_NOTIFICATION)
545 << "' length=" << packet->buffer.length();
546 // Sending a packet with data == NULL (no data) is SCTPs "close the
547 // connection" message. This sets sock_ = NULL;
548 if (!packet->buffer.length() || !packet->buffer.data()) {
549 LOG(LS_INFO) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): "
550 "No data, closing.";
551 return;
552 }
553 if (packet->flags & MSG_NOTIFICATION) {
554 OnNotificationFromSctp(&packet->buffer);
555 } else {
556 OnDataFromSctpToChannel(packet->params, &packet->buffer);
557 }
558}
559
560void SctpDataMediaChannel::OnDataFromSctpToChannel(
561 const ReceiveDataParams& params, talk_base::Buffer* buffer) {
562 StreamParams found_stream;
wu@webrtc.org91053e72013-08-10 07:18:04 +0000563 if (!GetStreamBySsrc(streams_, params.ssrc, &found_stream)) {
564 if (params.type == DMT_CONTROL) {
565 SignalDataReceived(params, buffer->data(), buffer->length());
566 } else {
567 LOG(LS_WARNING) << debug_name_ << "->OnDataFromSctpToChannel(...): "
568 << "Received packet for unknown ssrc: " << params.ssrc;
569 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000570 return;
571 }
572
573 if (receiving_) {
574 LOG(LS_VERBOSE) << debug_name_ << "->OnDataFromSctpToChannel(...): "
575 << "Posting with length: " << buffer->length();
576 SignalDataReceived(params, buffer->data(), buffer->length());
577 } else {
578 LOG(LS_WARNING) << debug_name_ << "->OnDataFromSctpToChannel(...): "
579 << "Not receiving packet with sid=" << params.ssrc
580 << " len=" << buffer->length()
581 << " before SetReceive(true).";
582 }
583}
584
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000585void SctpDataMediaChannel::OnNotificationFromSctp(talk_base::Buffer* buffer) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000586 const sctp_notification& notification =
587 reinterpret_cast<const sctp_notification&>(*buffer->data());
588 ASSERT(notification.sn_header.sn_length == buffer->length());
589
590 // TODO(ldixon): handle notifications appropriately.
591 switch (notification.sn_header.sn_type) {
592 case SCTP_ASSOC_CHANGE:
593 LOG(LS_VERBOSE) << "SCTP_ASSOC_CHANGE";
594 OnNotificationAssocChange(notification.sn_assoc_change);
595 break;
596 case SCTP_REMOTE_ERROR:
597 LOG(LS_INFO) << "SCTP_REMOTE_ERROR";
598 break;
599 case SCTP_SHUTDOWN_EVENT:
600 LOG(LS_INFO) << "SCTP_SHUTDOWN_EVENT";
601 break;
602 case SCTP_ADAPTATION_INDICATION:
603 LOG(LS_INFO) << "SCTP_ADAPTATION_INIDICATION";
604 break;
605 case SCTP_PARTIAL_DELIVERY_EVENT:
606 LOG(LS_INFO) << "SCTP_PARTIAL_DELIVERY_EVENT";
607 break;
608 case SCTP_AUTHENTICATION_EVENT:
609 LOG(LS_INFO) << "SCTP_AUTHENTICATION_EVENT";
610 break;
611 case SCTP_SENDER_DRY_EVENT:
612 LOG(LS_INFO) << "SCTP_SENDER_DRY_EVENT";
wu@webrtc.orgd64719d2013-08-01 00:00:07 +0000613 SignalReadyToSend(true);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000614 break;
615 // TODO(ldixon): Unblock after congestion.
616 case SCTP_NOTIFICATIONS_STOPPED_EVENT:
617 LOG(LS_INFO) << "SCTP_NOTIFICATIONS_STOPPED_EVENT";
618 break;
619 case SCTP_SEND_FAILED_EVENT:
620 LOG(LS_INFO) << "SCTP_SEND_FAILED_EVENT";
621 break;
622 case SCTP_STREAM_RESET_EVENT:
623 LOG(LS_INFO) << "SCTP_STREAM_RESET_EVENT";
624 // TODO(ldixon): Notify up to channel that stream resent has happened,
625 // and write unit test for this case.
626 break;
627 case SCTP_ASSOC_RESET_EVENT:
628 LOG(LS_INFO) << "SCTP_ASSOC_RESET_EVENT";
629 break;
630 case SCTP_STREAM_CHANGE_EVENT:
631 LOG(LS_INFO) << "SCTP_STREAM_CHANGE_EVENT";
632 break;
633 default:
634 LOG(LS_WARNING) << "Unknown SCTP event: "
635 << notification.sn_header.sn_type;
636 break;
637 }
638}
639
640void SctpDataMediaChannel::OnNotificationAssocChange(
641 const sctp_assoc_change& change) {
642 switch (change.sac_state) {
643 case SCTP_COMM_UP:
644 LOG(LS_VERBOSE) << "Association change SCTP_COMM_UP";
645 break;
646 case SCTP_COMM_LOST:
647 LOG(LS_INFO) << "Association change SCTP_COMM_LOST";
648 break;
649 case SCTP_RESTART:
650 LOG(LS_INFO) << "Association change SCTP_RESTART";
651 break;
652 case SCTP_SHUTDOWN_COMP:
653 LOG(LS_INFO) << "Association change SCTP_SHUTDOWN_COMP";
654 break;
655 case SCTP_CANT_STR_ASSOC:
656 LOG(LS_INFO) << "Association change SCTP_CANT_STR_ASSOC";
657 break;
658 default:
659 LOG(LS_INFO) << "Association change UNKNOWN";
660 break;
661 }
662}
663
664
665void SctpDataMediaChannel::OnPacketFromSctpToNetwork(
666 talk_base::Buffer* buffer) {
667 if (buffer->length() > kSctpMtu) {
668 LOG(LS_ERROR) << debug_name_ << "->OnPacketFromSctpToNetwork(...): "
669 << "SCTP seems to have made a poacket that is bigger "
670 "than its official MTU.";
671 }
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000672 MediaChannel::SendPacket(buffer);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000673}
674
675void SctpDataMediaChannel::OnMessage(talk_base::Message* msg) {
676 switch (msg->message_id) {
677 case MSG_SCTPINBOUNDPACKET: {
678 SctpInboundPacket* packet =
679 static_cast<talk_base::TypedMessageData<SctpInboundPacket*>*>(
680 msg->pdata)->data();
681 OnInboundPacketFromSctpToChannel(packet);
682 delete packet;
683 break;
684 }
685 case MSG_SCTPOUTBOUNDPACKET: {
686 talk_base::Buffer* buffer =
687 static_cast<talk_base::TypedMessageData<talk_base::Buffer*>*>(
688 msg->pdata)->data();
689 OnPacketFromSctpToNetwork(buffer);
690 delete buffer;
691 break;
692 }
693 }
694}
695
696} // namespace cricket