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