Home | History | Annotate | Download | only in sctp
      1 /*
      2  * libjingle SCTP
      3  * Copyright 2012 Google Inc, and Robin Seggelmann
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *  1. Redistributions of source code must retain the above copyright notice,
      9  *     this list of conditions and the following disclaimer.
     10  *  2. Redistributions in binary form must reproduce the above copyright notice,
     11  *     this list of conditions and the following disclaimer in the documentation
     12  *     and/or other materials provided with the distribution.
     13  *  3. The name of the author may not be used to endorse or promote products
     14  *     derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
     17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
     19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include "talk/media/sctp/sctpdataengine.h"
     29 
     30 #include <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 
     42 namespace 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.
     49 static const int kSctpDefaultPort = 5001;
     50 // TODO(ldixon): Find where this is defined, and also check is Sctp really
     51 // respects this.
     52 static const size_t kSctpMtu = 1280;
     53 
     54 enum {
     55   MSG_SCTPINBOUNDPACKET = 1,   // MessageData is SctpInboundPacket
     56   MSG_SCTPOUTBOUNDPACKET = 2,  // MessageData is talk_base:Buffer
     57 };
     58 
     59 struct 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.
     68 static 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.
     80 static 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.
     93 static 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'.
    113 static int OnSctpInboundPacket(struct socket* sock, union sctp_sockstore addr,
    114                                void* data, size_t length,
    115                                struct sctp_rcvinfo rcv, int flags,
    116                                void* ulp_info) {
    117   LOG(LS_VERBOSE) << "global OnSctpInboundPacket... Msg of length "
    118                   << length << " received via " << addr.sconn.sconn_addr << ":"
    119                   << talk_base::NetworkToHost16(addr.sconn.sconn_port)
    120                   << " on stream " << rcv.rcv_sid
    121                   << " with SSN " << rcv.rcv_ssn
    122                   << " and TSN " << rcv.rcv_tsn << ", PPID "
    123                   << talk_base::NetworkToHost32(rcv.rcv_ppid)
    124                   << ", context " << rcv.rcv_context
    125                   << ", data: " << data
    126                   << ", ulp_info:" << ulp_info
    127                   << ", flags:" << std::hex << flags;
    128   SctpDataMediaChannel* channel = static_cast<SctpDataMediaChannel*>(ulp_info);
    129   // The second log call is useful when the defines flags are incorrect. In
    130   // this case, ulp_info ends up being bad and the second log message will
    131   // cause a crash.
    132   LOG(LS_VERBOSE) << "global OnSctpInboundPacket. channel="
    133                   << channel->debug_name() << "...";
    134   // Post data to the channel's receiver thread (copying it).
    135   // TODO(ldixon): Unclear if copy is needed as this method is responsible for
    136   // memory cleanup. But this does simplify code.
    137   const uint32 native_ppid = talk_base::HostToNetwork32(rcv.rcv_ppid);
    138   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;
    143   packet->params.type =
    144       static_cast<cricket::DataMessageType>(native_ppid);
    145   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.
    153 int SctpDataEngine::usrsctp_engines_count = 0;
    154 
    155 SctpDataEngine::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 
    193 SctpDataEngine::~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 
    207 DataMediaChannel* 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 
    215 SctpDataMediaChannel::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 
    225 SctpDataMediaChannel::~SctpDataMediaChannel() {
    226   CloseSctpSocket();
    227 }
    228 
    229 sockaddr_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 
    241 bool 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,
    276                        SCTP_SEND_FAILED_EVENT,
    277                        SCTP_SENDER_DRY_EVENT};
    278   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 
    298 void 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 
    310 bool 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));
    341   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;
    344     CloseSctpSocket();
    345     return false;
    346   }
    347   return true;
    348 }
    349 
    350 void SctpDataMediaChannel::Disconnect() {
    351   // TODO(ldixon): Consider calling |usrsctp_shutdown(sock_, ...)| to do a
    352   // shutdown handshake and remove the association.
    353   CloseSctpSocket();
    354 }
    355 
    356 bool SctpDataMediaChannel::SetSend(bool send) {
    357   if (!sending_ && send) {
    358     return Connect();
    359   }
    360   if (sending_ && !send) {
    361     Disconnect();
    362   }
    363   return true;
    364 }
    365 
    366 bool SctpDataMediaChannel::SetReceive(bool receive) {
    367   receiving_ = receive;
    368   return true;
    369 }
    370 
    371 bool SctpDataMediaChannel::AddSendStream(const StreamParams& stream) {
    372   if (!stream.has_ssrcs()) {
    373     return false;
    374   }
    375 
    376   StreamParams found_stream;
    377   // TODO(lally): Consider keeping this sorted.
    378   if (GetStreamBySsrc(streams_, stream.first_ssrc(), &found_stream)) {
    379     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 
    386   streams_.push_back(stream);
    387   return true;
    388 }
    389 
    390 bool SctpDataMediaChannel::RemoveSendStream(uint32 ssrc) {
    391   StreamParams found_stream;
    392   if (!GetStreamBySsrc(streams_, ssrc, &found_stream)) {
    393     return false;
    394   }
    395 
    396   RemoveStreamBySsrc(&streams_, ssrc);
    397   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.
    402 bool SctpDataMediaChannel::AddRecvStream(const StreamParams& stream) {
    403   if (!stream.has_ssrcs()) {
    404     return false;
    405   }
    406 
    407   StreamParams found_stream;
    408   if (GetStreamBySsrc(streams_, stream.first_ssrc(), &found_stream)) {
    409     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 
    416   streams_.push_back(stream);
    417   LOG(LS_VERBOSE) << debug_name_ << "->AddRecvStream(...): "
    418                   << "Added data recv stream '" << stream.id
    419                   << "' with ssrc=" << stream.first_ssrc();
    420   return true;
    421 }
    422 
    423 bool SctpDataMediaChannel::RemoveRecvStream(uint32 ssrc) {
    424   RemoveStreamBySsrc(&streams_, ssrc);
    425   return true;
    426 }
    427 
    428 bool 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;
    445   if (params.type != cricket::DMT_CONTROL &&
    446       !GetStreamBySsrc(streams_, params.ssrc, &found_stream)) {
    447     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.
    479   sndinfo.snd_ppid = talk_base::HostToNetwork32(params.type);
    480   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) {
    488     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     }
    496     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.
    506 void 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 
    530 void 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 
    553 void SctpDataMediaChannel::OnDataFromSctpToChannel(
    554     const ReceiveDataParams& params, talk_base::Buffer* buffer) {
    555   StreamParams found_stream;
    556   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     }
    563     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 
    578 void SctpDataMediaChannel::OnNotificationFromSctp(talk_base::Buffer* buffer) {
    579   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";
    606       SignalReadyToSend(true);
    607       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 
    633 void 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 
    658 void 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   }
    665   MediaChannel::SendPacket(buffer);
    666 }
    667 
    668 void 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
    690