• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * libjingle
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 <stdarg.h>
31 #include <stdio.h>
32 #include <sstream>
33 #include <vector>
34 
35 #include "talk/media/base/codec.h"
36 #include "talk/media/base/constants.h"
37 #include "talk/media/base/streamparams.h"
38 #include "usrsctplib/usrsctp.h"
39 #include "webrtc/base/arraysize.h"
40 #include "webrtc/base/buffer.h"
41 #include "webrtc/base/helpers.h"
42 #include "webrtc/base/logging.h"
43 #include "webrtc/base/safe_conversions.h"
44 
45 namespace {
46 typedef cricket::SctpDataMediaChannel::StreamSet StreamSet;
47 // Returns a comma-separated, human-readable list of the stream IDs in 's'
ListStreams(const StreamSet & s)48 std::string ListStreams(const StreamSet& s) {
49   std::stringstream result;
50   bool first = true;
51   for (StreamSet::const_iterator it = s.begin(); it != s.end(); ++it) {
52     if (!first) {
53       result << ", " << *it;
54     } else {
55       result << *it;
56       first = false;
57     }
58   }
59   return result.str();
60 }
61 
62 // Returns a pipe-separated, human-readable list of the SCTP_STREAM_RESET
63 // flags in 'flags'
ListFlags(int flags)64 std::string ListFlags(int flags) {
65   std::stringstream result;
66   bool first = true;
67   // Skip past the first 12 chars (strlen("SCTP_STREAM_"))
68 #define MAKEFLAG(X) { X, #X + 12}
69   struct flaginfo_t {
70     int value;
71     const char* name;
72   } flaginfo[] = {
73     MAKEFLAG(SCTP_STREAM_RESET_INCOMING_SSN),
74     MAKEFLAG(SCTP_STREAM_RESET_OUTGOING_SSN),
75     MAKEFLAG(SCTP_STREAM_RESET_DENIED),
76     MAKEFLAG(SCTP_STREAM_RESET_FAILED),
77     MAKEFLAG(SCTP_STREAM_CHANGE_DENIED)
78   };
79 #undef MAKEFLAG
80   for (int i = 0; i < arraysize(flaginfo); ++i) {
81     if (flags & flaginfo[i].value) {
82       if (!first) result << " | ";
83       result << flaginfo[i].name;
84       first = false;
85     }
86   }
87   return result.str();
88 }
89 
90 // Returns a comma-separated, human-readable list of the integers in 'array'.
91 // All 'num_elems' of them.
ListArray(const uint16_t * array,int num_elems)92 std::string ListArray(const uint16_t* array, int num_elems) {
93   std::stringstream result;
94   for (int i = 0; i < num_elems; ++i) {
95     if (i) {
96       result << ", " << array[i];
97     } else {
98       result << array[i];
99     }
100   }
101   return result.str();
102 }
103 }  // namespace
104 
105 namespace cricket {
106 typedef rtc::ScopedMessageData<SctpInboundPacket> InboundPacketMessage;
107 typedef rtc::ScopedMessageData<rtc::Buffer> OutboundPacketMessage;
108 
109 // The biggest SCTP packet.  Starting from a 'safe' wire MTU value of 1280,
110 // take off 80 bytes for DTLS/TURN/TCP/IP overhead.
111 static const size_t kSctpMtu = 1200;
112 
113 // The size of the SCTP association send buffer.  256kB, the usrsctp default.
114 static const int kSendBufferSize = 262144;
115 enum {
116   MSG_SCTPINBOUNDPACKET = 1,   // MessageData is SctpInboundPacket
117   MSG_SCTPOUTBOUNDPACKET = 2,  // MessageData is rtc:Buffer
118 };
119 
120 struct SctpInboundPacket {
121   rtc::Buffer buffer;
122   ReceiveDataParams params;
123   // The |flags| parameter is used by SCTP to distinguish notification packets
124   // from other types of packets.
125   int flags;
126 };
127 
128 // Helper for logging SCTP messages.
debug_sctp_printf(const char * format,...)129 static void debug_sctp_printf(const char *format, ...) {
130   char s[255];
131   va_list ap;
132   va_start(ap, format);
133   vsnprintf(s, sizeof(s), format, ap);
134   LOG(LS_INFO) << "SCTP: " << s;
135   va_end(ap);
136 }
137 
138 // Get the PPID to use for the terminating fragment of this type.
GetPpid(cricket::DataMessageType type)139 static SctpDataMediaChannel::PayloadProtocolIdentifier GetPpid(
140     cricket::DataMessageType type) {
141   switch (type) {
142   default:
143   case cricket::DMT_NONE:
144     return SctpDataMediaChannel::PPID_NONE;
145   case cricket::DMT_CONTROL:
146     return SctpDataMediaChannel::PPID_CONTROL;
147   case cricket::DMT_BINARY:
148     return SctpDataMediaChannel::PPID_BINARY_LAST;
149   case cricket::DMT_TEXT:
150     return SctpDataMediaChannel::PPID_TEXT_LAST;
151   };
152 }
153 
GetDataMediaType(SctpDataMediaChannel::PayloadProtocolIdentifier ppid,cricket::DataMessageType * dest)154 static bool GetDataMediaType(
155     SctpDataMediaChannel::PayloadProtocolIdentifier ppid,
156     cricket::DataMessageType *dest) {
157   ASSERT(dest != NULL);
158   switch (ppid) {
159     case SctpDataMediaChannel::PPID_BINARY_PARTIAL:
160     case SctpDataMediaChannel::PPID_BINARY_LAST:
161       *dest = cricket::DMT_BINARY;
162       return true;
163 
164     case SctpDataMediaChannel::PPID_TEXT_PARTIAL:
165     case SctpDataMediaChannel::PPID_TEXT_LAST:
166       *dest = cricket::DMT_TEXT;
167       return true;
168 
169     case SctpDataMediaChannel::PPID_CONTROL:
170       *dest = cricket::DMT_CONTROL;
171       return true;
172 
173     case SctpDataMediaChannel::PPID_NONE:
174       *dest = cricket::DMT_NONE;
175       return true;
176 
177     default:
178       return false;
179   }
180 }
181 
182 // Log the packet in text2pcap format, if log level is at LS_VERBOSE.
VerboseLogPacket(void * data,size_t length,int direction)183 static void VerboseLogPacket(void *data, size_t length, int direction) {
184   if (LOG_CHECK_LEVEL(LS_VERBOSE) && length > 0) {
185     char *dump_buf;
186     if ((dump_buf = usrsctp_dumppacket(
187              data, length, direction)) != NULL) {
188       LOG(LS_VERBOSE) << dump_buf;
189       usrsctp_freedumpbuffer(dump_buf);
190     }
191   }
192 }
193 
194 // This is the callback usrsctp uses when there's data to send on the network
195 // that has been wrapped appropriatly for the SCTP protocol.
OnSctpOutboundPacket(void * addr,void * data,size_t length,uint8_t tos,uint8_t set_df)196 static int OnSctpOutboundPacket(void* addr, void* data, size_t length,
197                                 uint8_t tos, uint8_t set_df) {
198   SctpDataMediaChannel* channel = static_cast<SctpDataMediaChannel*>(addr);
199   LOG(LS_VERBOSE) << "global OnSctpOutboundPacket():"
200                   << "addr: " << addr << "; length: " << length
201                   << "; tos: " << std::hex << static_cast<int>(tos)
202                   << "; set_df: " << std::hex << static_cast<int>(set_df);
203 
204   VerboseLogPacket(addr, length, SCTP_DUMP_OUTBOUND);
205   // Note: We have to copy the data; the caller will delete it.
206   auto* msg = new OutboundPacketMessage(
207       new rtc::Buffer(reinterpret_cast<uint8_t*>(data), length));
208   channel->worker_thread()->Post(channel, MSG_SCTPOUTBOUNDPACKET, msg);
209   return 0;
210 }
211 
212 // This is the callback called from usrsctp when data has been received, after
213 // a packet has been interpreted and parsed by usrsctp and found to contain
214 // payload data. It is called by a usrsctp thread. It is assumed this function
215 // will free the memory used by 'data'.
OnSctpInboundPacket(struct socket * sock,union sctp_sockstore addr,void * data,size_t length,struct sctp_rcvinfo rcv,int flags,void * ulp_info)216 static int OnSctpInboundPacket(struct socket* sock, union sctp_sockstore addr,
217                                void* data, size_t length,
218                                struct sctp_rcvinfo rcv, int flags,
219                                void* ulp_info) {
220   SctpDataMediaChannel* channel = static_cast<SctpDataMediaChannel*>(ulp_info);
221   // Post data to the channel's receiver thread (copying it).
222   // TODO(ldixon): Unclear if copy is needed as this method is responsible for
223   // memory cleanup. But this does simplify code.
224   const SctpDataMediaChannel::PayloadProtocolIdentifier ppid =
225       static_cast<SctpDataMediaChannel::PayloadProtocolIdentifier>(
226           rtc::HostToNetwork32(rcv.rcv_ppid));
227   cricket::DataMessageType type = cricket::DMT_NONE;
228   if (!GetDataMediaType(ppid, &type) && !(flags & MSG_NOTIFICATION)) {
229     // It's neither a notification nor a recognized data packet.  Drop it.
230     LOG(LS_ERROR) << "Received an unknown PPID " << ppid
231                   << " on an SCTP packet.  Dropping.";
232   } else {
233     SctpInboundPacket* packet = new SctpInboundPacket;
234     packet->buffer.SetData(reinterpret_cast<uint8_t*>(data), length);
235     packet->params.ssrc = rcv.rcv_sid;
236     packet->params.seq_num = rcv.rcv_ssn;
237     packet->params.timestamp = rcv.rcv_tsn;
238     packet->params.type = type;
239     packet->flags = flags;
240     // The ownership of |packet| transfers to |msg|.
241     InboundPacketMessage* msg = new InboundPacketMessage(packet);
242     channel->worker_thread()->Post(channel, MSG_SCTPINBOUNDPACKET, msg);
243   }
244   free(data);
245   return 1;
246 }
247 
248 // Set the initial value of the static SCTP Data Engines reference count.
249 int SctpDataEngine::usrsctp_engines_count = 0;
250 
SctpDataEngine()251 SctpDataEngine::SctpDataEngine() {
252   if (usrsctp_engines_count == 0) {
253     // First argument is udp_encapsulation_port, which is not releveant for our
254     // AF_CONN use of sctp.
255     usrsctp_init(0, cricket::OnSctpOutboundPacket, debug_sctp_printf);
256 
257     // To turn on/off detailed SCTP debugging. You will also need to have the
258     // SCTP_DEBUG cpp defines flag.
259     // usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_ALL);
260 
261     // TODO(ldixon): Consider turning this on/off.
262     usrsctp_sysctl_set_sctp_ecn_enable(0);
263 
264     // This is harmless, but we should find out when the library default
265     // changes.
266     int send_size = usrsctp_sysctl_get_sctp_sendspace();
267     if (send_size != kSendBufferSize) {
268       LOG(LS_ERROR) << "Got different send size than expected: " << send_size;
269     }
270 
271     // TODO(ldixon): Consider turning this on/off.
272     // This is not needed right now (we don't do dynamic address changes):
273     // If SCTP Auto-ASCONF is enabled, the peer is informed automatically
274     // when a new address is added or removed. This feature is enabled by
275     // default.
276     // usrsctp_sysctl_set_sctp_auto_asconf(0);
277 
278     // TODO(ldixon): Consider turning this on/off.
279     // Add a blackhole sysctl. Setting it to 1 results in no ABORTs
280     // being sent in response to INITs, setting it to 2 results
281     // in no ABORTs being sent for received OOTB packets.
282     // This is similar to the TCP sysctl.
283     //
284     // See: http://lakerest.net/pipermail/sctp-coders/2012-January/009438.html
285     // See: http://svnweb.freebsd.org/base?view=revision&revision=229805
286     // usrsctp_sysctl_set_sctp_blackhole(2);
287 
288     // Set the number of default outgoing streams.  This is the number we'll
289     // send in the SCTP INIT message.  The 'appropriate default' in the
290     // second paragraph of
291     // http://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-05#section-6.2
292     // is cricket::kMaxSctpSid.
293     usrsctp_sysctl_set_sctp_nr_outgoing_streams_default(
294         cricket::kMaxSctpSid);
295   }
296   usrsctp_engines_count++;
297 
298   cricket::DataCodec codec(kGoogleSctpDataCodecId, kGoogleSctpDataCodecName, 0);
299   codec.SetParam(kCodecParamPort, kSctpDefaultPort);
300   codecs_.push_back(codec);
301 }
302 
~SctpDataEngine()303 SctpDataEngine::~SctpDataEngine() {
304   usrsctp_engines_count--;
305   LOG(LS_VERBOSE) << "usrsctp_engines_count:" << usrsctp_engines_count;
306 
307   if (usrsctp_engines_count == 0) {
308     // usrsctp_finish() may fail if it's called too soon after the channels are
309     // closed. Wait and try again until it succeeds for up to 3 seconds.
310     for (size_t i = 0; i < 300; ++i) {
311       if (usrsctp_finish() == 0)
312         return;
313 
314       rtc::Thread::SleepMs(10);
315     }
316     LOG(LS_ERROR) << "Failed to shutdown usrsctp.";
317   }
318 }
319 
CreateChannel(DataChannelType data_channel_type)320 DataMediaChannel* SctpDataEngine::CreateChannel(
321     DataChannelType data_channel_type) {
322   if (data_channel_type != DCT_SCTP) {
323     return NULL;
324   }
325   return new SctpDataMediaChannel(rtc::Thread::Current());
326 }
327 
328 // static
GetChannelFromSocket(struct socket * sock)329 SctpDataMediaChannel* SctpDataEngine::GetChannelFromSocket(
330     struct socket* sock) {
331   struct sockaddr* addrs = nullptr;
332   int naddrs = usrsctp_getladdrs(sock, 0, &addrs);
333   if (naddrs <= 0 || addrs[0].sa_family != AF_CONN) {
334     return nullptr;
335   }
336   // usrsctp_getladdrs() returns the addresses bound to this socket, which
337   // contains the SctpDataMediaChannel* as sconn_addr.  Read the pointer,
338   // then free the list of addresses once we have the pointer.  We only open
339   // AF_CONN sockets, and they should all have the sconn_addr set to the
340   // pointer that created them, so [0] is as good as any other.
341   struct sockaddr_conn* sconn =
342       reinterpret_cast<struct sockaddr_conn*>(&addrs[0]);
343   SctpDataMediaChannel* channel =
344       reinterpret_cast<SctpDataMediaChannel*>(sconn->sconn_addr);
345   usrsctp_freeladdrs(addrs);
346 
347   return channel;
348 }
349 
350 // static
SendThresholdCallback(struct socket * sock,uint32_t sb_free)351 int SctpDataEngine::SendThresholdCallback(struct socket* sock,
352                                           uint32_t sb_free) {
353   // Fired on our I/O thread.  SctpDataMediaChannel::OnPacketReceived() gets
354   // a packet containing acknowledgments, which goes into usrsctp_conninput,
355   // and then back here.
356   SctpDataMediaChannel* channel = GetChannelFromSocket(sock);
357   if (!channel) {
358     LOG(LS_ERROR) << "SendThresholdCallback: Failed to get channel for socket "
359                   << sock;
360     return 0;
361   }
362   channel->OnSendThresholdCallback();
363   return 0;
364 }
365 
SctpDataMediaChannel(rtc::Thread * thread)366 SctpDataMediaChannel::SctpDataMediaChannel(rtc::Thread* thread)
367     : worker_thread_(thread),
368       local_port_(kSctpDefaultPort),
369       remote_port_(kSctpDefaultPort),
370       sock_(NULL),
371       sending_(false),
372       receiving_(false),
373       debug_name_("SctpDataMediaChannel") {
374 }
375 
~SctpDataMediaChannel()376 SctpDataMediaChannel::~SctpDataMediaChannel() {
377   CloseSctpSocket();
378 }
379 
OnSendThresholdCallback()380 void SctpDataMediaChannel::OnSendThresholdCallback() {
381   RTC_DCHECK(rtc::Thread::Current() == worker_thread_);
382   SignalReadyToSend(true);
383 }
384 
GetSctpSockAddr(int port)385 sockaddr_conn SctpDataMediaChannel::GetSctpSockAddr(int port) {
386   sockaddr_conn sconn = {0};
387   sconn.sconn_family = AF_CONN;
388 #ifdef HAVE_SCONN_LEN
389   sconn.sconn_len = sizeof(sockaddr_conn);
390 #endif
391   // Note: conversion from int to uint16_t happens here.
392   sconn.sconn_port = rtc::HostToNetwork16(port);
393   sconn.sconn_addr = this;
394   return sconn;
395 }
396 
OpenSctpSocket()397 bool SctpDataMediaChannel::OpenSctpSocket() {
398   if (sock_) {
399     LOG(LS_VERBOSE) << debug_name_
400                     << "->Ignoring attempt to re-create existing socket.";
401     return false;
402   }
403 
404   // If kSendBufferSize isn't reflective of reality, we log an error, but we
405   // still have to do something reasonable here.  Look up what the buffer's
406   // real size is and set our threshold to something reasonable.
407   const static int kSendThreshold = usrsctp_sysctl_get_sctp_sendspace() / 2;
408 
409   sock_ = usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP,
410                          cricket::OnSctpInboundPacket,
411                          &SctpDataEngine::SendThresholdCallback,
412                          kSendThreshold, this);
413   if (!sock_) {
414     LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to create SCTP socket.";
415     return false;
416   }
417 
418   // Make the socket non-blocking. Connect, close, shutdown etc will not block
419   // the thread waiting for the socket operation to complete.
420   if (usrsctp_set_non_blocking(sock_, 1) < 0) {
421     LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP to non blocking.";
422     return false;
423   }
424 
425   // This ensures that the usrsctp close call deletes the association. This
426   // prevents usrsctp from calling OnSctpOutboundPacket with references to
427   // this class as the address.
428   linger linger_opt;
429   linger_opt.l_onoff = 1;
430   linger_opt.l_linger = 0;
431   if (usrsctp_setsockopt(sock_, SOL_SOCKET, SO_LINGER, &linger_opt,
432                          sizeof(linger_opt))) {
433     LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SO_LINGER.";
434     return false;
435   }
436 
437   // Enable stream ID resets.
438   struct sctp_assoc_value stream_rst;
439   stream_rst.assoc_id = SCTP_ALL_ASSOC;
440   stream_rst.assoc_value = 1;
441   if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_ENABLE_STREAM_RESET,
442                          &stream_rst, sizeof(stream_rst))) {
443     LOG_ERRNO(LS_ERROR) << debug_name_
444                         << "Failed to set SCTP_ENABLE_STREAM_RESET.";
445     return false;
446   }
447 
448   // Nagle.
449   uint32_t nodelay = 1;
450   if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_NODELAY, &nodelay,
451                          sizeof(nodelay))) {
452     LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP_NODELAY.";
453     return false;
454   }
455 
456   // Disable MTU discovery
457   sctp_paddrparams params = {{0}};
458   params.spp_assoc_id = 0;
459   params.spp_flags = SPP_PMTUD_DISABLE;
460   params.spp_pathmtu = kSctpMtu;
461   if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &params,
462       sizeof(params))) {
463     LOG_ERRNO(LS_ERROR) << debug_name_
464                         << "Failed to set SCTP_PEER_ADDR_PARAMS.";
465     return false;
466   }
467 
468   // Subscribe to SCTP event notifications.
469   int event_types[] = {SCTP_ASSOC_CHANGE,
470                        SCTP_PEER_ADDR_CHANGE,
471                        SCTP_SEND_FAILED_EVENT,
472                        SCTP_SENDER_DRY_EVENT,
473                        SCTP_STREAM_RESET_EVENT};
474   struct sctp_event event = {0};
475   event.se_assoc_id = SCTP_ALL_ASSOC;
476   event.se_on = 1;
477   for (size_t i = 0; i < arraysize(event_types); i++) {
478     event.se_type = event_types[i];
479     if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_EVENT, &event,
480                            sizeof(event)) < 0) {
481       LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP_EVENT type: "
482                           << event.se_type;
483       return false;
484     }
485   }
486 
487   // Register this class as an address for usrsctp. This is used by SCTP to
488   // direct the packets received (by the created socket) to this class.
489   usrsctp_register_address(this);
490   sending_ = true;
491   return true;
492 }
493 
CloseSctpSocket()494 void SctpDataMediaChannel::CloseSctpSocket() {
495   sending_ = false;
496   if (sock_) {
497     // We assume that SO_LINGER option is set to close the association when
498     // close is called. This means that any pending packets in usrsctp will be
499     // discarded instead of being sent.
500     usrsctp_close(sock_);
501     sock_ = NULL;
502     usrsctp_deregister_address(this);
503   }
504 }
505 
Connect()506 bool SctpDataMediaChannel::Connect() {
507   LOG(LS_VERBOSE) << debug_name_ << "->Connect().";
508 
509   // If we already have a socket connection, just return.
510   if (sock_) {
511     LOG(LS_WARNING) << debug_name_ << "->Connect(): Ignored as socket "
512                                       "is already established.";
513     return true;
514   }
515 
516   // If no socket (it was closed) try to start it again. This can happen when
517   // the socket we are connecting to closes, does an sctp shutdown handshake,
518   // or behaves unexpectedly causing us to perform a CloseSctpSocket.
519   if (!sock_ && !OpenSctpSocket()) {
520     return false;
521   }
522 
523   // Note: conversion from int to uint16_t happens on assignment.
524   sockaddr_conn local_sconn = GetSctpSockAddr(local_port_);
525   if (usrsctp_bind(sock_, reinterpret_cast<sockaddr *>(&local_sconn),
526                    sizeof(local_sconn)) < 0) {
527     LOG_ERRNO(LS_ERROR) << debug_name_ << "->Connect(): "
528                         << ("Failed usrsctp_bind");
529     CloseSctpSocket();
530     return false;
531   }
532 
533   // Note: conversion from int to uint16_t happens on assignment.
534   sockaddr_conn remote_sconn = GetSctpSockAddr(remote_port_);
535   int connect_result = usrsctp_connect(
536       sock_, reinterpret_cast<sockaddr *>(&remote_sconn), sizeof(remote_sconn));
537   if (connect_result < 0 && errno != SCTP_EINPROGRESS) {
538     LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed usrsctp_connect. got errno="
539                         << errno << ", but wanted " << SCTP_EINPROGRESS;
540     CloseSctpSocket();
541     return false;
542   }
543   return true;
544 }
545 
Disconnect()546 void SctpDataMediaChannel::Disconnect() {
547   // TODO(ldixon): Consider calling |usrsctp_shutdown(sock_, ...)| to do a
548   // shutdown handshake and remove the association.
549   CloseSctpSocket();
550 }
551 
SetSend(bool send)552 bool SctpDataMediaChannel::SetSend(bool send) {
553   if (!sending_ && send) {
554     return Connect();
555   }
556   if (sending_ && !send) {
557     Disconnect();
558   }
559   return true;
560 }
561 
SetReceive(bool receive)562 bool SctpDataMediaChannel::SetReceive(bool receive) {
563   receiving_ = receive;
564   return true;
565 }
566 
SetSendParameters(const DataSendParameters & params)567 bool SctpDataMediaChannel::SetSendParameters(const DataSendParameters& params) {
568   return SetSendCodecs(params.codecs);
569 }
570 
SetRecvParameters(const DataRecvParameters & params)571 bool SctpDataMediaChannel::SetRecvParameters(const DataRecvParameters& params) {
572   return SetRecvCodecs(params.codecs);
573 }
574 
AddSendStream(const StreamParams & stream)575 bool SctpDataMediaChannel::AddSendStream(const StreamParams& stream) {
576   return AddStream(stream);
577 }
578 
RemoveSendStream(uint32_t ssrc)579 bool SctpDataMediaChannel::RemoveSendStream(uint32_t ssrc) {
580   return ResetStream(ssrc);
581 }
582 
AddRecvStream(const StreamParams & stream)583 bool SctpDataMediaChannel::AddRecvStream(const StreamParams& stream) {
584   // SCTP DataChannels are always bi-directional and calling AddSendStream will
585   // enable both sending and receiving on the stream. So AddRecvStream is a
586   // no-op.
587   return true;
588 }
589 
RemoveRecvStream(uint32_t ssrc)590 bool SctpDataMediaChannel::RemoveRecvStream(uint32_t ssrc) {
591   // SCTP DataChannels are always bi-directional and calling RemoveSendStream
592   // will disable both sending and receiving on the stream. So RemoveRecvStream
593   // is a no-op.
594   return true;
595 }
596 
SendData(const SendDataParams & params,const rtc::Buffer & payload,SendDataResult * result)597 bool SctpDataMediaChannel::SendData(
598     const SendDataParams& params,
599     const rtc::Buffer& payload,
600     SendDataResult* result) {
601   if (result) {
602     // Preset |result| to assume an error.  If SendData succeeds, we'll
603     // overwrite |*result| once more at the end.
604     *result = SDR_ERROR;
605   }
606 
607   if (!sending_) {
608     LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
609                     << "Not sending packet with ssrc=" << params.ssrc
610                     << " len=" << payload.size() << " before SetSend(true).";
611     return false;
612   }
613 
614   if (params.type != cricket::DMT_CONTROL &&
615       open_streams_.find(params.ssrc) == open_streams_.end()) {
616     LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
617                     << "Not sending data because ssrc is unknown: "
618                     << params.ssrc;
619     return false;
620   }
621 
622   //
623   // Send data using SCTP.
624   ssize_t send_res = 0;  // result from usrsctp_sendv.
625   struct sctp_sendv_spa spa = {0};
626   spa.sendv_flags |= SCTP_SEND_SNDINFO_VALID;
627   spa.sendv_sndinfo.snd_sid = params.ssrc;
628   spa.sendv_sndinfo.snd_ppid = rtc::HostToNetwork32(
629       GetPpid(params.type));
630 
631   // Ordered implies reliable.
632   if (!params.ordered) {
633     spa.sendv_sndinfo.snd_flags |= SCTP_UNORDERED;
634     if (params.max_rtx_count >= 0 || params.max_rtx_ms == 0) {
635       spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
636       spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX;
637       spa.sendv_prinfo.pr_value = params.max_rtx_count;
638     } else {
639       spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
640       spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_TTL;
641       spa.sendv_prinfo.pr_value = params.max_rtx_ms;
642     }
643   }
644 
645   // We don't fragment.
646   send_res = usrsctp_sendv(
647       sock_, payload.data(), static_cast<size_t>(payload.size()), NULL, 0, &spa,
648       rtc::checked_cast<socklen_t>(sizeof(spa)), SCTP_SENDV_SPA, 0);
649   if (send_res < 0) {
650     if (errno == SCTP_EWOULDBLOCK) {
651       *result = SDR_BLOCK;
652       LOG(LS_INFO) << debug_name_ << "->SendData(...): EWOULDBLOCK returned";
653     } else {
654       LOG_ERRNO(LS_ERROR) << "ERROR:" << debug_name_
655                           << "->SendData(...): "
656                           << " usrsctp_sendv: ";
657     }
658     return false;
659   }
660   if (result) {
661     // Only way out now is success.
662     *result = SDR_SUCCESS;
663   }
664   return true;
665 }
666 
667 // Called by network interface when a packet has been received.
OnPacketReceived(rtc::Buffer * packet,const rtc::PacketTime & packet_time)668 void SctpDataMediaChannel::OnPacketReceived(
669     rtc::Buffer* packet, const rtc::PacketTime& packet_time) {
670   RTC_DCHECK(rtc::Thread::Current() == worker_thread_);
671   LOG(LS_VERBOSE) << debug_name_ << "->OnPacketReceived(...): "
672                   << " length=" << packet->size() << ", sending: " << sending_;
673   // Only give receiving packets to usrsctp after if connected. This enables two
674   // peers to each make a connect call, but for them not to receive an INIT
675   // packet before they have called connect; least the last receiver of the INIT
676   // packet will have called connect, and a connection will be established.
677   if (sending_) {
678     // Pass received packet to SCTP stack. Once processed by usrsctp, the data
679     // will be will be given to the global OnSctpInboundData, and then,
680     // marshalled by a Post and handled with OnMessage.
681     VerboseLogPacket(packet->data(), packet->size(), SCTP_DUMP_INBOUND);
682     usrsctp_conninput(this, packet->data(), packet->size(), 0);
683   } else {
684     // TODO(ldixon): Consider caching the packet for very slightly better
685     // reliability.
686   }
687 }
688 
OnInboundPacketFromSctpToChannel(SctpInboundPacket * packet)689 void SctpDataMediaChannel::OnInboundPacketFromSctpToChannel(
690     SctpInboundPacket* packet) {
691   LOG(LS_VERBOSE) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): "
692                   << "Received SCTP data:"
693                   << " ssrc=" << packet->params.ssrc
694                   << " notification: " << (packet->flags & MSG_NOTIFICATION)
695                   << " length=" << packet->buffer.size();
696   // Sending a packet with data == NULL (no data) is SCTPs "close the
697   // connection" message. This sets sock_ = NULL;
698   if (!packet->buffer.size() || !packet->buffer.data()) {
699     LOG(LS_INFO) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): "
700                                    "No data, closing.";
701     return;
702   }
703   if (packet->flags & MSG_NOTIFICATION) {
704     OnNotificationFromSctp(&packet->buffer);
705   } else {
706     OnDataFromSctpToChannel(packet->params, &packet->buffer);
707   }
708 }
709 
OnDataFromSctpToChannel(const ReceiveDataParams & params,rtc::Buffer * buffer)710 void SctpDataMediaChannel::OnDataFromSctpToChannel(
711     const ReceiveDataParams& params, rtc::Buffer* buffer) {
712   if (receiving_) {
713     LOG(LS_VERBOSE) << debug_name_ << "->OnDataFromSctpToChannel(...): "
714                     << "Posting with length: " << buffer->size()
715                     << " on stream " << params.ssrc;
716     // Reports all received messages to upper layers, no matter whether the sid
717     // is known.
718     SignalDataReceived(params, buffer->data<char>(), buffer->size());
719   } else {
720     LOG(LS_WARNING) << debug_name_ << "->OnDataFromSctpToChannel(...): "
721                     << "Not receiving packet with sid=" << params.ssrc
722                     << " len=" << buffer->size() << " before SetReceive(true).";
723   }
724 }
725 
AddStream(const StreamParams & stream)726 bool SctpDataMediaChannel::AddStream(const StreamParams& stream) {
727   if (!stream.has_ssrcs()) {
728     return false;
729   }
730 
731   const uint32_t ssrc = stream.first_ssrc();
732   if (ssrc >= cricket::kMaxSctpSid) {
733     LOG(LS_WARNING) << debug_name_ << "->Add(Send|Recv)Stream(...): "
734                     << "Not adding data stream '" << stream.id
735                     << "' with ssrc=" << ssrc
736                     << " because stream ssrc is too high.";
737     return false;
738   } else if (open_streams_.find(ssrc) != open_streams_.end()) {
739     LOG(LS_WARNING) << debug_name_ << "->Add(Send|Recv)Stream(...): "
740                     << "Not adding data stream '" << stream.id
741                     << "' with ssrc=" << ssrc
742                     << " because stream is already open.";
743     return false;
744   } else if (queued_reset_streams_.find(ssrc) != queued_reset_streams_.end()
745              || sent_reset_streams_.find(ssrc) != sent_reset_streams_.end()) {
746     LOG(LS_WARNING) << debug_name_ << "->Add(Send|Recv)Stream(...): "
747                     << "Not adding data stream '" << stream.id
748                     << "' with ssrc=" << ssrc
749                     << " because stream is still closing.";
750     return false;
751   }
752 
753   open_streams_.insert(ssrc);
754   return true;
755 }
756 
ResetStream(uint32_t ssrc)757 bool SctpDataMediaChannel::ResetStream(uint32_t ssrc) {
758   // We typically get this called twice for the same stream, once each for
759   // Send and Recv.
760   StreamSet::iterator found = open_streams_.find(ssrc);
761 
762   if (found == open_streams_.end()) {
763     LOG(LS_VERBOSE) << debug_name_ << "->ResetStream(" << ssrc << "): "
764                     << "stream not found.";
765     return false;
766   } else {
767     LOG(LS_VERBOSE) << debug_name_ << "->ResetStream(" << ssrc << "): "
768                     << "Removing and queuing RE-CONFIG chunk.";
769     open_streams_.erase(found);
770   }
771 
772   // SCTP won't let you have more than one stream reset pending at a time, but
773   // you can close multiple streams in a single reset.  So, we keep an internal
774   // queue of streams-to-reset, and send them as one reset message in
775   // SendQueuedStreamResets().
776   queued_reset_streams_.insert(ssrc);
777 
778   // Signal our stream-reset logic that it should try to send now, if it can.
779   SendQueuedStreamResets();
780 
781   // The stream will actually get removed when we get the acknowledgment.
782   return true;
783 }
784 
OnNotificationFromSctp(rtc::Buffer * buffer)785 void SctpDataMediaChannel::OnNotificationFromSctp(rtc::Buffer* buffer) {
786   const sctp_notification& notification =
787       reinterpret_cast<const sctp_notification&>(*buffer->data());
788   ASSERT(notification.sn_header.sn_length == buffer->size());
789 
790   // TODO(ldixon): handle notifications appropriately.
791   switch (notification.sn_header.sn_type) {
792     case SCTP_ASSOC_CHANGE:
793       LOG(LS_VERBOSE) << "SCTP_ASSOC_CHANGE";
794       OnNotificationAssocChange(notification.sn_assoc_change);
795       break;
796     case SCTP_REMOTE_ERROR:
797       LOG(LS_INFO) << "SCTP_REMOTE_ERROR";
798       break;
799     case SCTP_SHUTDOWN_EVENT:
800       LOG(LS_INFO) << "SCTP_SHUTDOWN_EVENT";
801       break;
802     case SCTP_ADAPTATION_INDICATION:
803       LOG(LS_INFO) << "SCTP_ADAPTATION_INDICATION";
804       break;
805     case SCTP_PARTIAL_DELIVERY_EVENT:
806       LOG(LS_INFO) << "SCTP_PARTIAL_DELIVERY_EVENT";
807       break;
808     case SCTP_AUTHENTICATION_EVENT:
809       LOG(LS_INFO) << "SCTP_AUTHENTICATION_EVENT";
810       break;
811     case SCTP_SENDER_DRY_EVENT:
812       LOG(LS_VERBOSE) << "SCTP_SENDER_DRY_EVENT";
813       SignalReadyToSend(true);
814       break;
815     // TODO(ldixon): Unblock after congestion.
816     case SCTP_NOTIFICATIONS_STOPPED_EVENT:
817       LOG(LS_INFO) << "SCTP_NOTIFICATIONS_STOPPED_EVENT";
818       break;
819     case SCTP_SEND_FAILED_EVENT:
820       LOG(LS_INFO) << "SCTP_SEND_FAILED_EVENT";
821       break;
822     case SCTP_STREAM_RESET_EVENT:
823       OnStreamResetEvent(&notification.sn_strreset_event);
824       break;
825     case SCTP_ASSOC_RESET_EVENT:
826       LOG(LS_INFO) << "SCTP_ASSOC_RESET_EVENT";
827       break;
828     case SCTP_STREAM_CHANGE_EVENT:
829       LOG(LS_INFO) << "SCTP_STREAM_CHANGE_EVENT";
830       // An acknowledgment we get after our stream resets have gone through,
831       // if they've failed.  We log the message, but don't react -- we don't
832       // keep around the last-transmitted set of SSIDs we wanted to close for
833       // error recovery.  It doesn't seem likely to occur, and if so, likely
834       // harmless within the lifetime of a single SCTP association.
835       break;
836     default:
837       LOG(LS_WARNING) << "Unknown SCTP event: "
838                       << notification.sn_header.sn_type;
839       break;
840   }
841 }
842 
OnNotificationAssocChange(const sctp_assoc_change & change)843 void SctpDataMediaChannel::OnNotificationAssocChange(
844     const sctp_assoc_change& change) {
845   switch (change.sac_state) {
846     case SCTP_COMM_UP:
847       LOG(LS_VERBOSE) << "Association change SCTP_COMM_UP";
848       break;
849     case SCTP_COMM_LOST:
850       LOG(LS_INFO) << "Association change SCTP_COMM_LOST";
851       break;
852     case SCTP_RESTART:
853       LOG(LS_INFO) << "Association change SCTP_RESTART";
854       break;
855     case SCTP_SHUTDOWN_COMP:
856       LOG(LS_INFO) << "Association change SCTP_SHUTDOWN_COMP";
857       break;
858     case SCTP_CANT_STR_ASSOC:
859       LOG(LS_INFO) << "Association change SCTP_CANT_STR_ASSOC";
860       break;
861     default:
862       LOG(LS_INFO) << "Association change UNKNOWN";
863       break;
864   }
865 }
866 
OnStreamResetEvent(const struct sctp_stream_reset_event * evt)867 void SctpDataMediaChannel::OnStreamResetEvent(
868     const struct sctp_stream_reset_event* evt) {
869   // A stream reset always involves two RE-CONFIG chunks for us -- we always
870   // simultaneously reset a sid's sequence number in both directions.  The
871   // requesting side transmits a RE-CONFIG chunk and waits for the peer to send
872   // one back.  Both sides get this SCTP_STREAM_RESET_EVENT when they receive
873   // RE-CONFIGs.
874   const int num_ssrcs = (evt->strreset_length - sizeof(*evt)) /
875       sizeof(evt->strreset_stream_list[0]);
876   LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
877                   << "): Flags = 0x"
878                   << std::hex << evt->strreset_flags << " ("
879                   << ListFlags(evt->strreset_flags) << ")";
880   LOG(LS_VERBOSE) << "Assoc = " << evt->strreset_assoc_id << ", Streams = ["
881                   << ListArray(evt->strreset_stream_list, num_ssrcs)
882                   << "], Open: ["
883                   << ListStreams(open_streams_) << "], Q'd: ["
884                   << ListStreams(queued_reset_streams_) << "], Sent: ["
885                   << ListStreams(sent_reset_streams_) << "]";
886 
887   // If both sides try to reset some streams at the same time (even if they're
888   // disjoint sets), we can get reset failures.
889   if (evt->strreset_flags & SCTP_STREAM_RESET_FAILED) {
890     // OK, just try again.  The stream IDs sent over when the RESET_FAILED flag
891     // is set seem to be garbage values.  Ignore them.
892     queued_reset_streams_.insert(
893         sent_reset_streams_.begin(),
894         sent_reset_streams_.end());
895     sent_reset_streams_.clear();
896 
897   } else if (evt->strreset_flags & SCTP_STREAM_RESET_INCOMING_SSN) {
898     // Each side gets an event for each direction of a stream.  That is,
899     // closing sid k will make each side receive INCOMING and OUTGOING reset
900     // events for k.  As per RFC6525, Section 5, paragraph 2, each side will
901     // get an INCOMING event first.
902     for (int i = 0; i < num_ssrcs; i++) {
903       const int stream_id = evt->strreset_stream_list[i];
904 
905       // See if this stream ID was closed by our peer or ourselves.
906       StreamSet::iterator it = sent_reset_streams_.find(stream_id);
907 
908       // The reset was requested locally.
909       if (it != sent_reset_streams_.end()) {
910         LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
911                         << "): local sid " << stream_id << " acknowledged.";
912         sent_reset_streams_.erase(it);
913 
914       } else if ((it = open_streams_.find(stream_id))
915                  != open_streams_.end()) {
916         // The peer requested the reset.
917         LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
918                         << "): closing sid " << stream_id;
919         open_streams_.erase(it);
920         SignalStreamClosedRemotely(stream_id);
921 
922       } else if ((it = queued_reset_streams_.find(stream_id))
923                  != queued_reset_streams_.end()) {
924         // The peer requested the reset, but there was a local reset
925         // queued.
926         LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
927                         << "): double-sided close for sid " << stream_id;
928         // Both sides want the stream closed, and the peer got to send the
929         // RE-CONFIG first.  Treat it like the local Remove(Send|Recv)Stream
930         // finished quickly.
931         queued_reset_streams_.erase(it);
932 
933       } else {
934         // This stream is unknown.  Sometimes this can be from an
935         // RESET_FAILED-related retransmit.
936         LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_
937                         << "): Unknown sid " << stream_id;
938       }
939     }
940   }
941 
942   // Always try to send the queued RESET because this call indicates that the
943   // last local RESET or remote RESET has made some progress.
944   SendQueuedStreamResets();
945 }
946 
947 // Puts the specified |param| from the codec identified by |id| into |dest|
948 // and returns true.  Or returns false if it wasn't there, leaving |dest|
949 // untouched.
GetCodecIntParameter(const std::vector<DataCodec> & codecs,int id,const std::string & name,const std::string & param,int * dest)950 static bool GetCodecIntParameter(const std::vector<DataCodec>& codecs,
951                                  int id, const std::string& name,
952                                  const std::string& param, int* dest) {
953   std::string value;
954   Codec match_pattern;
955   match_pattern.id = id;
956   match_pattern.name = name;
957   for (size_t i = 0; i < codecs.size(); ++i) {
958     if (codecs[i].Matches(match_pattern)) {
959       if (codecs[i].GetParam(param, &value)) {
960         *dest = rtc::FromString<int>(value);
961         return true;
962       }
963     }
964   }
965   return false;
966 }
967 
SetSendCodecs(const std::vector<DataCodec> & codecs)968 bool SctpDataMediaChannel::SetSendCodecs(const std::vector<DataCodec>& codecs) {
969   return GetCodecIntParameter(
970       codecs, kGoogleSctpDataCodecId, kGoogleSctpDataCodecName, kCodecParamPort,
971       &remote_port_);
972 }
973 
SetRecvCodecs(const std::vector<DataCodec> & codecs)974 bool SctpDataMediaChannel::SetRecvCodecs(const std::vector<DataCodec>& codecs) {
975   return GetCodecIntParameter(
976       codecs, kGoogleSctpDataCodecId, kGoogleSctpDataCodecName, kCodecParamPort,
977       &local_port_);
978 }
979 
OnPacketFromSctpToNetwork(rtc::Buffer * buffer)980 void SctpDataMediaChannel::OnPacketFromSctpToNetwork(
981     rtc::Buffer* buffer) {
982   // usrsctp seems to interpret the MTU we give it strangely -- it seems to
983   // give us back packets bigger than that MTU, if only by a fixed amount.
984   // This is that amount that we've observed.
985   const int kSctpOverhead = 76;
986   if (buffer->size() > (kSctpOverhead + kSctpMtu)) {
987     LOG(LS_ERROR) << debug_name_ << "->OnPacketFromSctpToNetwork(...): "
988                   << "SCTP seems to have made a packet that is bigger "
989                   << "than its official MTU: " << buffer->size()
990                   << " vs max of " << kSctpMtu
991                   << " even after adding " << kSctpOverhead
992                   << " extra SCTP overhead";
993   }
994   MediaChannel::SendPacket(buffer, rtc::PacketOptions());
995 }
996 
SendQueuedStreamResets()997 bool SctpDataMediaChannel::SendQueuedStreamResets() {
998   if (!sent_reset_streams_.empty() || queued_reset_streams_.empty())
999     return true;
1000 
1001   LOG(LS_VERBOSE) << "SendQueuedStreamResets[" << debug_name_ << "]: Sending ["
1002                   << ListStreams(queued_reset_streams_) << "], Open: ["
1003                   << ListStreams(open_streams_) << "], Sent: ["
1004                   << ListStreams(sent_reset_streams_) << "]";
1005 
1006   const size_t num_streams = queued_reset_streams_.size();
1007   const size_t num_bytes =
1008       sizeof(struct sctp_reset_streams) + (num_streams * sizeof(uint16_t));
1009 
1010   std::vector<uint8_t> reset_stream_buf(num_bytes, 0);
1011   struct sctp_reset_streams* resetp = reinterpret_cast<sctp_reset_streams*>(
1012       &reset_stream_buf[0]);
1013   resetp->srs_assoc_id = SCTP_ALL_ASSOC;
1014   resetp->srs_flags = SCTP_STREAM_RESET_INCOMING | SCTP_STREAM_RESET_OUTGOING;
1015   resetp->srs_number_streams = rtc::checked_cast<uint16_t>(num_streams);
1016   int result_idx = 0;
1017   for (StreamSet::iterator it = queued_reset_streams_.begin();
1018        it != queued_reset_streams_.end(); ++it) {
1019     resetp->srs_stream_list[result_idx++] = *it;
1020   }
1021 
1022   int ret = usrsctp_setsockopt(
1023       sock_, IPPROTO_SCTP, SCTP_RESET_STREAMS, resetp,
1024       rtc::checked_cast<socklen_t>(reset_stream_buf.size()));
1025   if (ret < 0) {
1026     LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to send a stream reset for "
1027                         << num_streams << " streams";
1028     return false;
1029   }
1030 
1031   // sent_reset_streams_ is empty, and all the queued_reset_streams_ go into
1032   // it now.
1033   queued_reset_streams_.swap(sent_reset_streams_);
1034   return true;
1035 }
1036 
OnMessage(rtc::Message * msg)1037 void SctpDataMediaChannel::OnMessage(rtc::Message* msg) {
1038   switch (msg->message_id) {
1039     case MSG_SCTPINBOUNDPACKET: {
1040       rtc::scoped_ptr<InboundPacketMessage> pdata(
1041           static_cast<InboundPacketMessage*>(msg->pdata));
1042       OnInboundPacketFromSctpToChannel(pdata->data().get());
1043       break;
1044     }
1045     case MSG_SCTPOUTBOUNDPACKET: {
1046       rtc::scoped_ptr<OutboundPacketMessage> pdata(
1047           static_cast<OutboundPacketMessage*>(msg->pdata));
1048       OnPacketFromSctpToNetwork(pdata->data().get());
1049       break;
1050     }
1051   }
1052 }
1053 }  // namespace cricket
1054