• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/app/webrtc/datachannelinterface.h"
35 #include "talk/base/buffer.h"
36 #include "talk/base/helpers.h"
37 #include "talk/base/logging.h"
38 #include "talk/media/base/codec.h"
39 #include "talk/media/base/constants.h"
40 #include "talk/media/base/streamparams.h"
41 #include "talk/media/sctp/sctputils.h"
42 #include "usrsctplib/usrsctp.h"
43 
44 namespace cricket {
45 
46 // This is the SCTP port to use. It is passed along the wire and the listener
47 // and connector must be using the same port. It is not related to the ports at
48 // the IP level. (Corresponds to: sockaddr_conn.sconn_port in usrsctp.h)
49 //
50 // TODO(ldixon): Allow port to be set from higher level code.
51 static const int kSctpDefaultPort = 5001;
52 // TODO(ldixon): Find where this is defined, and also check is Sctp really
53 // respects this.
54 static const size_t kSctpMtu = 1280;
55 
56 enum {
57   MSG_SCTPINBOUNDPACKET = 1,   // MessageData is SctpInboundPacket
58   MSG_SCTPOUTBOUNDPACKET = 2,  // MessageData is talk_base:Buffer
59 };
60 
61 struct SctpInboundPacket {
62   talk_base::Buffer buffer;
63   ReceiveDataParams params;
64   // The |flags| parameter is used by SCTP to distinguish notification packets
65   // from other types of packets.
66   int flags;
67 };
68 
69 // Helper for logging SCTP messages.
debug_sctp_printf(const char * format,...)70 static void debug_sctp_printf(const char *format, ...) {
71   char s[255];
72   va_list ap;
73   va_start(ap, format);
74   vsnprintf(s, sizeof(s), format, ap);
75   LOG(LS_INFO) << "SCTP: " << s;
76   va_end(ap);
77 }
78 
79 // Get the PPID to use for the terminating fragment of this type.
GetPpid(cricket::DataMessageType type)80 static SctpDataMediaChannel::PayloadProtocolIdentifier GetPpid(
81     cricket::DataMessageType type) {
82   switch (type) {
83   default:
84   case cricket::DMT_NONE:
85     return SctpDataMediaChannel::PPID_NONE;
86   case cricket::DMT_CONTROL:
87     return SctpDataMediaChannel::PPID_CONTROL;
88   case cricket::DMT_BINARY:
89     return SctpDataMediaChannel::PPID_BINARY_LAST;
90   case cricket::DMT_TEXT:
91     return SctpDataMediaChannel::PPID_TEXT_LAST;
92   };
93 }
94 
GetDataMediaType(SctpDataMediaChannel::PayloadProtocolIdentifier ppid,cricket::DataMessageType * dest)95 static bool GetDataMediaType(
96     SctpDataMediaChannel::PayloadProtocolIdentifier ppid,
97     cricket::DataMessageType *dest) {
98   ASSERT(dest != NULL);
99   switch (ppid) {
100     case SctpDataMediaChannel::PPID_BINARY_PARTIAL:
101     case SctpDataMediaChannel::PPID_BINARY_LAST:
102       *dest = cricket::DMT_BINARY;
103       return true;
104 
105     case SctpDataMediaChannel::PPID_TEXT_PARTIAL:
106     case SctpDataMediaChannel::PPID_TEXT_LAST:
107       *dest = cricket::DMT_TEXT;
108       return true;
109 
110     case SctpDataMediaChannel::PPID_CONTROL:
111       *dest = cricket::DMT_CONTROL;
112       return true;
113 
114     case SctpDataMediaChannel::PPID_NONE:
115       *dest = cricket::DMT_NONE;
116       return true;
117 
118     default:
119       return false;
120   }
121 }
122 
123 // This is the callback usrsctp uses when there's data to send on the network
124 // that has been wrapped appropriatly for the SCTP protocol.
OnSctpOutboundPacket(void * addr,void * data,size_t length,uint8_t tos,uint8_t set_df)125 static int OnSctpOutboundPacket(void* addr, void* data, size_t length,
126                                 uint8_t tos, uint8_t set_df) {
127   SctpDataMediaChannel* channel = static_cast<SctpDataMediaChannel*>(addr);
128   LOG(LS_VERBOSE) << "global OnSctpOutboundPacket():"
129                   << "addr: " << addr << "; length: " << length
130                   << "; tos: " << std::hex << static_cast<int>(tos)
131                   << "; set_df: " << std::hex << static_cast<int>(set_df);
132   // Note: We have to copy the data; the caller will delete it.
133   talk_base::Buffer* buffer = new talk_base::Buffer(data, length);
134   channel->worker_thread()->Post(channel, MSG_SCTPOUTBOUNDPACKET,
135                                  talk_base::WrapMessageData(buffer));
136   return 0;
137 }
138 
139 // This is the callback called from usrsctp when data has been received, after
140 // a packet has been interpreted and parsed by usrsctp and found to contain
141 // payload data. It is called by a usrsctp thread. It is assumed this function
142 // 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)143 static int OnSctpInboundPacket(struct socket* sock, union sctp_sockstore addr,
144                                void* data, size_t length,
145                                struct sctp_rcvinfo rcv, int flags,
146                                void* ulp_info) {
147   SctpDataMediaChannel* channel = static_cast<SctpDataMediaChannel*>(ulp_info);
148   // Post data to the channel's receiver thread (copying it).
149   // TODO(ldixon): Unclear if copy is needed as this method is responsible for
150   // memory cleanup. But this does simplify code.
151   const SctpDataMediaChannel::PayloadProtocolIdentifier ppid =
152       static_cast<SctpDataMediaChannel::PayloadProtocolIdentifier>(
153           talk_base::HostToNetwork32(rcv.rcv_ppid));
154   cricket::DataMessageType type = cricket::DMT_NONE;
155   if (!GetDataMediaType(ppid, &type) && !(flags & MSG_NOTIFICATION)) {
156     // It's neither a notification nor a recognized data packet.  Drop it.
157     LOG(LS_ERROR) << "Received an unknown PPID " << ppid
158                   << " on an SCTP packet.  Dropping.";
159   } else {
160     SctpInboundPacket* packet = new SctpInboundPacket;
161     packet->buffer.SetData(data, length);
162     packet->params.ssrc = rcv.rcv_sid;
163     packet->params.seq_num = rcv.rcv_ssn;
164     packet->params.timestamp = rcv.rcv_tsn;
165     packet->params.type = type;
166     packet->flags = flags;
167     channel->worker_thread()->Post(channel, MSG_SCTPINBOUNDPACKET,
168                                    talk_base::WrapMessageData(packet));
169   }
170   free(data);
171   return 1;
172 }
173 
174 // Set the initial value of the static SCTP Data Engines reference count.
175 int SctpDataEngine::usrsctp_engines_count = 0;
176 
SctpDataEngine()177 SctpDataEngine::SctpDataEngine() {
178   if (usrsctp_engines_count == 0) {
179     // First argument is udp_encapsulation_port, which is not releveant for our
180     // AF_CONN use of sctp.
181     usrsctp_init(0, cricket::OnSctpOutboundPacket, debug_sctp_printf);
182 
183     // To turn on/off detailed SCTP debugging. You will also need to have the
184     // SCTP_DEBUG cpp defines flag.
185     // usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_ALL);
186 
187     // TODO(ldixon): Consider turning this on/off.
188     usrsctp_sysctl_set_sctp_ecn_enable(0);
189 
190     // TODO(ldixon): Consider turning this on/off.
191     // This is not needed right now (we don't do dynamic address changes):
192     // If SCTP Auto-ASCONF is enabled, the peer is informed automatically
193     // when a new address is added or removed. This feature is enabled by
194     // default.
195     // usrsctp_sysctl_set_sctp_auto_asconf(0);
196 
197     // TODO(ldixon): Consider turning this on/off.
198     // Add a blackhole sysctl. Setting it to 1 results in no ABORTs
199     // being sent in response to INITs, setting it to 2 results
200     // in no ABORTs being sent for received OOTB packets.
201     // This is similar to the TCP sysctl.
202     //
203     // See: http://lakerest.net/pipermail/sctp-coders/2012-January/009438.html
204     // See: http://svnweb.freebsd.org/base?view=revision&revision=229805
205     // usrsctp_sysctl_set_sctp_blackhole(2);
206 
207     // Set the number of default outgoing streams.  This is the number we'll
208     // send in the SCTP INIT message.  The 'appropriate default' in the
209     // second paragraph of
210     // http://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-05#section-6.2
211     // is cricket::kMaxSctpSid.
212     usrsctp_sysctl_set_sctp_nr_outgoing_streams_default(
213         cricket::kMaxSctpSid);
214   }
215   usrsctp_engines_count++;
216 
217   // We don't put in a codec because we don't want one offered when we
218   // use the hybrid data engine.
219   // codecs_.push_back(cricket::DataCodec( kGoogleSctpDataCodecId,
220   // kGoogleSctpDataCodecName, 0));
221 }
222 
~SctpDataEngine()223 SctpDataEngine::~SctpDataEngine() {
224   // TODO(ldixon): There is currently a bug in teardown of usrsctp that blocks
225   // indefintely if a finish call made too soon after close calls. So teardown
226   // has been skipped. Once the bug is fixed, retest and enable teardown.
227   //
228   // usrsctp_engines_count--;
229   // LOG(LS_VERBOSE) << "usrsctp_engines_count:" << usrsctp_engines_count;
230   // if (usrsctp_engines_count == 0) {
231   //   if (usrsctp_finish() != 0) {
232   //     LOG(LS_WARNING) << "usrsctp_finish.";
233   //   }
234   // }
235 }
236 
CreateChannel(DataChannelType data_channel_type)237 DataMediaChannel* SctpDataEngine::CreateChannel(
238     DataChannelType data_channel_type) {
239   if (data_channel_type != DCT_SCTP) {
240     return NULL;
241   }
242   return new SctpDataMediaChannel(talk_base::Thread::Current());
243 }
244 
SctpDataMediaChannel(talk_base::Thread * thread)245 SctpDataMediaChannel::SctpDataMediaChannel(talk_base::Thread* thread)
246     : worker_thread_(thread),
247       local_port_(-1),
248       remote_port_(-1),
249       sock_(NULL),
250       sending_(false),
251       receiving_(false),
252       debug_name_("SctpDataMediaChannel") {
253 }
254 
~SctpDataMediaChannel()255 SctpDataMediaChannel::~SctpDataMediaChannel() {
256   CloseSctpSocket();
257 }
258 
GetSctpSockAddr(int port)259 sockaddr_conn SctpDataMediaChannel::GetSctpSockAddr(int port) {
260   sockaddr_conn sconn = {0};
261   sconn.sconn_family = AF_CONN;
262 #ifdef HAVE_SCONN_LEN
263   sconn.sconn_len = sizeof(sockaddr_conn);
264 #endif
265   // Note: conversion from int to uint16_t happens here.
266   sconn.sconn_port = talk_base::HostToNetwork16(port);
267   sconn.sconn_addr = this;
268   return sconn;
269 }
270 
OpenSctpSocket()271 bool SctpDataMediaChannel::OpenSctpSocket() {
272   if (sock_) {
273     LOG(LS_VERBOSE) << debug_name_
274                     << "->Ignoring attempt to re-create existing socket.";
275     return false;
276   }
277   sock_ = usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP,
278                          cricket::OnSctpInboundPacket, NULL, 0, this);
279   if (!sock_) {
280     LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to create SCTP socket.";
281     return false;
282   }
283 
284   // Make the socket non-blocking. Connect, close, shutdown etc will not block
285   // the thread waiting for the socket operation to complete.
286   if (usrsctp_set_non_blocking(sock_, 1) < 0) {
287     LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP to non blocking.";
288     return false;
289   }
290 
291   // This ensures that the usrsctp close call deletes the association. This
292   // prevents usrsctp from calling OnSctpOutboundPacket with references to
293   // this class as the address.
294   linger linger_opt;
295   linger_opt.l_onoff = 1;
296   linger_opt.l_linger = 0;
297   if (usrsctp_setsockopt(sock_, SOL_SOCKET, SO_LINGER, &linger_opt,
298                          sizeof(linger_opt))) {
299     LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SO_LINGER.";
300     return false;
301   }
302 
303   uint32_t nodelay = 1;
304   if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_NODELAY, &nodelay,
305                          sizeof(nodelay))) {
306     LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP_NODELAY.";
307     return false;
308   }
309 
310   // Subscribe to SCTP event notifications.
311   int event_types[] = {SCTP_ASSOC_CHANGE,
312                        SCTP_PEER_ADDR_CHANGE,
313                        SCTP_SEND_FAILED_EVENT,
314                        SCTP_SENDER_DRY_EVENT};
315   struct sctp_event event = {0};
316   event.se_assoc_id = SCTP_ALL_ASSOC;
317   event.se_on = 1;
318   for (size_t i = 0; i < ARRAY_SIZE(event_types); i++) {
319     event.se_type = event_types[i];
320     if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_EVENT, &event,
321                            sizeof(event)) < 0) {
322       LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP_EVENT type: "
323                           << event.se_type;
324       return false;
325     }
326   }
327 
328   // Register this class as an address for usrsctp. This is used by SCTP to
329   // direct the packets received (by the created socket) to this class.
330   usrsctp_register_address(this);
331   sending_ = true;
332   return true;
333 }
334 
CloseSctpSocket()335 void SctpDataMediaChannel::CloseSctpSocket() {
336   sending_ = false;
337   if (sock_) {
338     // We assume that SO_LINGER option is set to close the association when
339     // close is called. This means that any pending packets in usrsctp will be
340     // discarded instead of being sent.
341     usrsctp_close(sock_);
342     sock_ = NULL;
343     usrsctp_deregister_address(this);
344   }
345 }
346 
Connect()347 bool SctpDataMediaChannel::Connect() {
348   LOG(LS_VERBOSE) << debug_name_ << "->Connect().";
349   if (remote_port_ < 0) {
350     remote_port_ = kSctpDefaultPort;
351   }
352   if (local_port_ < 0) {
353     local_port_ = kSctpDefaultPort;
354   }
355 
356   // If we already have a socket connection, just return.
357   if (sock_) {
358     LOG(LS_WARNING) << debug_name_ << "->Connect(): Ignored as socket "
359                                       "is already established.";
360     return true;
361   }
362 
363   // If no socket (it was closed) try to start it again. This can happen when
364   // the socket we are connecting to closes, does an sctp shutdown handshake,
365   // or behaves unexpectedly causing us to perform a CloseSctpSocket.
366   if (!sock_ && !OpenSctpSocket()) {
367     return false;
368   }
369 
370   // Note: conversion from int to uint16_t happens on assignment.
371   sockaddr_conn local_sconn = GetSctpSockAddr(local_port_);
372   if (usrsctp_bind(sock_, reinterpret_cast<sockaddr *>(&local_sconn),
373                    sizeof(local_sconn)) < 0) {
374     LOG_ERRNO(LS_ERROR) << debug_name_ << "->Connect(): "
375                         << ("Failed usrsctp_bind");
376     CloseSctpSocket();
377     return false;
378   }
379 
380   // Note: conversion from int to uint16_t happens on assignment.
381   sockaddr_conn remote_sconn = GetSctpSockAddr(remote_port_);
382   int connect_result = usrsctp_connect(
383       sock_, reinterpret_cast<sockaddr *>(&remote_sconn), sizeof(remote_sconn));
384   if (connect_result < 0 && errno != SCTP_EINPROGRESS) {
385     LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed usrsctp_connect. got errno="
386                         << errno << ", but wanted " << SCTP_EINPROGRESS;
387     CloseSctpSocket();
388     return false;
389   }
390   return true;
391 }
392 
Disconnect()393 void SctpDataMediaChannel::Disconnect() {
394   // TODO(ldixon): Consider calling |usrsctp_shutdown(sock_, ...)| to do a
395   // shutdown handshake and remove the association.
396   CloseSctpSocket();
397 }
398 
SetSend(bool send)399 bool SctpDataMediaChannel::SetSend(bool send) {
400   if (!sending_ && send) {
401     return Connect();
402   }
403   if (sending_ && !send) {
404     Disconnect();
405   }
406   return true;
407 }
408 
SetReceive(bool receive)409 bool SctpDataMediaChannel::SetReceive(bool receive) {
410   receiving_ = receive;
411   return true;
412 }
413 
AddSendStream(const StreamParams & stream)414 bool SctpDataMediaChannel::AddSendStream(const StreamParams& stream) {
415   if (!stream.has_ssrcs()) {
416     return false;
417   }
418 
419   StreamParams found_stream;
420   // TODO(lally): Consider keeping this sorted.
421   if (GetStreamBySsrc(streams_, stream.first_ssrc(), &found_stream)) {
422     LOG(LS_WARNING) << debug_name_ << "->AddSendStream(...): "
423                     << "Not adding data send stream '" << stream.id
424                     << "' with ssrc=" << stream.first_ssrc()
425                     << " because stream already exists.";
426     return false;
427   }
428 
429   streams_.push_back(stream);
430   return true;
431 }
432 
RemoveSendStream(uint32 ssrc)433 bool SctpDataMediaChannel::RemoveSendStream(uint32 ssrc) {
434   StreamParams found_stream;
435   if (!GetStreamBySsrc(streams_, ssrc, &found_stream)) {
436     return false;
437   }
438 
439   RemoveStreamBySsrc(&streams_, ssrc);
440   return true;
441 }
442 
443 // Note: expects exactly one ssrc.  If none are given, it will fail.  If more
444 // than one are given, it will use the first.
AddRecvStream(const StreamParams & stream)445 bool SctpDataMediaChannel::AddRecvStream(const StreamParams& stream) {
446   if (!stream.has_ssrcs()) {
447     return false;
448   }
449 
450   StreamParams found_stream;
451   if (GetStreamBySsrc(streams_, stream.first_ssrc(), &found_stream)) {
452     LOG(LS_WARNING) << debug_name_ << "->AddRecvStream(...): "
453                     << "Not adding data recv stream '" << stream.id
454                     << "' with ssrc=" << stream.first_ssrc()
455                     << " because stream already exists.";
456     return false;
457   }
458 
459   streams_.push_back(stream);
460   LOG(LS_VERBOSE) << debug_name_ << "->AddRecvStream(...): "
461                   << "Added data recv stream '" << stream.id
462                   << "' with ssrc=" << stream.first_ssrc();
463   return true;
464 }
465 
RemoveRecvStream(uint32 ssrc)466 bool SctpDataMediaChannel::RemoveRecvStream(uint32 ssrc) {
467   RemoveStreamBySsrc(&streams_, ssrc);
468   return true;
469 }
470 
SendData(const SendDataParams & params,const talk_base::Buffer & payload,SendDataResult * result)471 bool SctpDataMediaChannel::SendData(
472     const SendDataParams& params,
473     const talk_base::Buffer& payload,
474     SendDataResult* result) {
475   if (result) {
476     // Preset |result| to assume an error.  If SendData succeeds, we'll
477     // overwrite |*result| once more at the end.
478     *result = SDR_ERROR;
479   }
480 
481   if (!sending_) {
482     LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
483                     << "Not sending packet with ssrc=" << params.ssrc
484                     << " len=" << payload.length() << " before SetSend(true).";
485     return false;
486   }
487 
488   StreamParams found_stream;
489   if (params.type != cricket::DMT_CONTROL &&
490       !GetStreamBySsrc(streams_, params.ssrc, &found_stream)) {
491     LOG(LS_WARNING) << debug_name_ << "->SendData(...): "
492                     << "Not sending data because ssrc is unknown: "
493                     << params.ssrc;
494     return false;
495   }
496 
497   //
498   // Send data using SCTP.
499   ssize_t send_res = 0;  // result from usrsctp_sendv.
500   struct sctp_sendv_spa spa = {0};
501   spa.sendv_flags |= SCTP_SEND_SNDINFO_VALID;
502   spa.sendv_sndinfo.snd_sid = params.ssrc;
503   spa.sendv_sndinfo.snd_ppid = talk_base::HostToNetwork32(
504       GetPpid(params.type));
505 
506   // Ordered implies reliable.
507   if (!params.ordered) {
508     spa.sendv_sndinfo.snd_flags |= SCTP_UNORDERED;
509     if (params.max_rtx_count >= 0 || params.max_rtx_ms == 0) {
510       spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
511       spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX;
512       spa.sendv_prinfo.pr_value = params.max_rtx_count;
513     } else {
514       spa.sendv_flags |= SCTP_SEND_PRINFO_VALID;
515       spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_TTL;
516       spa.sendv_prinfo.pr_value = params.max_rtx_ms;
517     }
518   }
519 
520   // We don't fragment.
521   send_res = usrsctp_sendv(sock_, payload.data(),
522                            static_cast<size_t>(payload.length()),
523                            NULL, 0, &spa,
524                            static_cast<socklen_t>(sizeof(spa)),
525                            SCTP_SENDV_SPA, 0);
526   if (send_res < 0) {
527     if (errno == EWOULDBLOCK) {
528       *result = SDR_BLOCK;
529       LOG(LS_INFO) << debug_name_ << "->SendData(...): EWOULDBLOCK returned";
530     } else {
531       LOG_ERRNO(LS_ERROR) << "ERROR:" << debug_name_
532                           << "->SendData(...): "
533                           << " usrsctp_sendv: ";
534     }
535     return false;
536   }
537   if (result) {
538     // Only way out now is success.
539     *result = SDR_SUCCESS;
540   }
541   return true;
542 }
543 
544 // Called by network interface when a packet has been received.
OnPacketReceived(talk_base::Buffer * packet,const talk_base::PacketTime & packet_time)545 void SctpDataMediaChannel::OnPacketReceived(
546     talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) {
547   LOG(LS_VERBOSE) << debug_name_ << "->OnPacketReceived(...): " << " length="
548                   << packet->length() << ", sending: " << sending_;
549   // Only give receiving packets to usrsctp after if connected. This enables two
550   // peers to each make a connect call, but for them not to receive an INIT
551   // packet before they have called connect; least the last receiver of the INIT
552   // packet will have called connect, and a connection will be established.
553   if (sending_) {
554     // Pass received packet to SCTP stack. Once processed by usrsctp, the data
555     // will be will be given to the global OnSctpInboundData, and then,
556     // marshalled by a Post and handled with OnMessage.
557     usrsctp_conninput(this, packet->data(), packet->length(), 0);
558   } else {
559     // TODO(ldixon): Consider caching the packet for very slightly better
560     // reliability.
561   }
562 }
563 
OnInboundPacketFromSctpToChannel(SctpInboundPacket * packet)564 void SctpDataMediaChannel::OnInboundPacketFromSctpToChannel(
565     SctpInboundPacket* packet) {
566   LOG(LS_VERBOSE) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): "
567                   << "Received SCTP data:"
568                   << " ssrc=" << packet->params.ssrc
569                   << " notification: " << (packet->flags & MSG_NOTIFICATION)
570                   << " length=" << packet->buffer.length();
571   // Sending a packet with data == NULL (no data) is SCTPs "close the
572   // connection" message. This sets sock_ = NULL;
573   if (!packet->buffer.length() || !packet->buffer.data()) {
574     LOG(LS_INFO) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): "
575                                    "No data, closing.";
576     return;
577   }
578   if (packet->flags & MSG_NOTIFICATION) {
579     OnNotificationFromSctp(&packet->buffer);
580   } else {
581     OnDataFromSctpToChannel(packet->params, &packet->buffer);
582   }
583 }
584 
OnDataFromSctpToChannel(const ReceiveDataParams & params,talk_base::Buffer * buffer)585 void SctpDataMediaChannel::OnDataFromSctpToChannel(
586     const ReceiveDataParams& params, talk_base::Buffer* buffer) {
587   StreamParams found_stream;
588   if (!GetStreamBySsrc(streams_, params.ssrc, &found_stream)) {
589     if (params.type == DMT_CONTROL) {
590       std::string label;
591       webrtc::DataChannelInit config;
592       if (ParseDataChannelOpenMessage(*buffer, &label, &config)) {
593         config.id = params.ssrc;
594         // Do not send the OPEN message for this data channel.
595         config.negotiated = true;
596         SignalNewStreamReceived(label, config);
597 
598         // Add the stream immediately.
599         cricket::StreamParams sparams =
600             cricket::StreamParams::CreateLegacy(params.ssrc);
601         AddSendStream(sparams);
602         AddRecvStream(sparams);
603       } else {
604         LOG(LS_ERROR) << debug_name_ << "->OnDataFromSctpToChannel(...): "
605                       << "Received malformed control message";
606       }
607     } else {
608       LOG(LS_WARNING) << debug_name_ << "->OnDataFromSctpToChannel(...): "
609                       << "Received packet for unknown ssrc: " << params.ssrc;
610     }
611     return;
612   }
613 
614   if (receiving_) {
615     LOG(LS_VERBOSE) << debug_name_ << "->OnDataFromSctpToChannel(...): "
616                     << "Posting with length: " << buffer->length();
617     SignalDataReceived(params, buffer->data(), buffer->length());
618   } else {
619     LOG(LS_WARNING) << debug_name_ << "->OnDataFromSctpToChannel(...): "
620                     << "Not receiving packet with sid=" << params.ssrc
621                     << " len=" <<  buffer->length()
622                     << " before SetReceive(true).";
623   }
624 }
625 
OnNotificationFromSctp(talk_base::Buffer * buffer)626 void SctpDataMediaChannel::OnNotificationFromSctp(talk_base::Buffer* buffer) {
627   const sctp_notification& notification =
628       reinterpret_cast<const sctp_notification&>(*buffer->data());
629   ASSERT(notification.sn_header.sn_length == buffer->length());
630 
631   // TODO(ldixon): handle notifications appropriately.
632   switch (notification.sn_header.sn_type) {
633     case SCTP_ASSOC_CHANGE:
634       LOG(LS_VERBOSE) << "SCTP_ASSOC_CHANGE";
635       OnNotificationAssocChange(notification.sn_assoc_change);
636       break;
637     case SCTP_REMOTE_ERROR:
638       LOG(LS_INFO) << "SCTP_REMOTE_ERROR";
639       break;
640     case SCTP_SHUTDOWN_EVENT:
641       LOG(LS_INFO) << "SCTP_SHUTDOWN_EVENT";
642       break;
643     case SCTP_ADAPTATION_INDICATION:
644       LOG(LS_INFO) << "SCTP_ADAPTATION_INIDICATION";
645       break;
646     case SCTP_PARTIAL_DELIVERY_EVENT:
647       LOG(LS_INFO) << "SCTP_PARTIAL_DELIVERY_EVENT";
648       break;
649     case SCTP_AUTHENTICATION_EVENT:
650       LOG(LS_INFO) << "SCTP_AUTHENTICATION_EVENT";
651       break;
652     case SCTP_SENDER_DRY_EVENT:
653       LOG(LS_INFO) << "SCTP_SENDER_DRY_EVENT";
654       SignalReadyToSend(true);
655       break;
656     // TODO(ldixon): Unblock after congestion.
657     case SCTP_NOTIFICATIONS_STOPPED_EVENT:
658       LOG(LS_INFO) << "SCTP_NOTIFICATIONS_STOPPED_EVENT";
659       break;
660     case SCTP_SEND_FAILED_EVENT:
661       LOG(LS_INFO) << "SCTP_SEND_FAILED_EVENT";
662       break;
663     case SCTP_STREAM_RESET_EVENT:
664       LOG(LS_INFO) << "SCTP_STREAM_RESET_EVENT";
665       // TODO(ldixon): Notify up to channel that stream resent has happened,
666       // and write unit test for this case.
667       break;
668     case SCTP_ASSOC_RESET_EVENT:
669       LOG(LS_INFO) << "SCTP_ASSOC_RESET_EVENT";
670       break;
671     case SCTP_STREAM_CHANGE_EVENT:
672       LOG(LS_INFO) << "SCTP_STREAM_CHANGE_EVENT";
673       break;
674     default:
675       LOG(LS_WARNING) << "Unknown SCTP event: "
676                       << notification.sn_header.sn_type;
677       break;
678   }
679 }
680 
OnNotificationAssocChange(const sctp_assoc_change & change)681 void SctpDataMediaChannel::OnNotificationAssocChange(
682     const sctp_assoc_change& change) {
683   switch (change.sac_state) {
684     case SCTP_COMM_UP:
685       LOG(LS_VERBOSE) << "Association change SCTP_COMM_UP";
686       break;
687     case SCTP_COMM_LOST:
688       LOG(LS_INFO) << "Association change SCTP_COMM_LOST";
689       break;
690     case SCTP_RESTART:
691       LOG(LS_INFO) << "Association change SCTP_RESTART";
692       break;
693     case SCTP_SHUTDOWN_COMP:
694       LOG(LS_INFO) << "Association change SCTP_SHUTDOWN_COMP";
695       break;
696     case SCTP_CANT_STR_ASSOC:
697       LOG(LS_INFO) << "Association change SCTP_CANT_STR_ASSOC";
698       break;
699     default:
700       LOG(LS_INFO) << "Association change UNKNOWN";
701       break;
702   }
703 }
704 
705 // Puts the specified |param| from the codec identified by |id| into |dest|
706 // and returns true.  Or returns false if it wasn't there, leaving |dest|
707 // untouched.
GetCodecIntParameter(const std::vector<DataCodec> & codecs,int id,const std::string & name,const std::string & param,int * dest)708 static bool GetCodecIntParameter(const std::vector<DataCodec>& codecs,
709                                  int id, const std::string& name,
710                                  const std::string& param, int* dest) {
711   std::string value;
712   Codec match_pattern;
713   match_pattern.id = id;
714   match_pattern.name = name;
715   for (size_t i = 0; i < codecs.size(); ++i) {
716     if (codecs[i].Matches(match_pattern)) {
717       if (codecs[i].GetParam(param, &value)) {
718         *dest = talk_base::FromString<int>(value);
719         return true;
720       }
721     }
722   }
723   return false;
724 }
725 
SetSendCodecs(const std::vector<DataCodec> & codecs)726 bool SctpDataMediaChannel::SetSendCodecs(const std::vector<DataCodec>& codecs) {
727   return GetCodecIntParameter(
728       codecs, kGoogleSctpDataCodecId, kGoogleSctpDataCodecName, kCodecParamPort,
729       &remote_port_);
730 }
731 
SetRecvCodecs(const std::vector<DataCodec> & codecs)732 bool SctpDataMediaChannel::SetRecvCodecs(const std::vector<DataCodec>& codecs) {
733   return GetCodecIntParameter(
734       codecs, kGoogleSctpDataCodecId, kGoogleSctpDataCodecName, kCodecParamPort,
735       &local_port_);
736 }
737 
OnPacketFromSctpToNetwork(talk_base::Buffer * buffer)738 void SctpDataMediaChannel::OnPacketFromSctpToNetwork(
739     talk_base::Buffer* buffer) {
740   if (buffer->length() > kSctpMtu) {
741     LOG(LS_ERROR) << debug_name_ << "->OnPacketFromSctpToNetwork(...): "
742                   << "SCTP seems to have made a poacket that is bigger "
743                      "than its official MTU.";
744   }
745   MediaChannel::SendPacket(buffer);
746 }
747 
OnMessage(talk_base::Message * msg)748 void SctpDataMediaChannel::OnMessage(talk_base::Message* msg) {
749   switch (msg->message_id) {
750     case MSG_SCTPINBOUNDPACKET: {
751       SctpInboundPacket* packet =
752           static_cast<talk_base::TypedMessageData<SctpInboundPacket*>*>(
753               msg->pdata)->data();
754       OnInboundPacketFromSctpToChannel(packet);
755       delete packet;
756       break;
757     }
758     case MSG_SCTPOUTBOUNDPACKET: {
759       talk_base::Buffer* buffer =
760           static_cast<talk_base::TypedMessageData<talk_base::Buffer*>*>(
761               msg->pdata)->data();
762       OnPacketFromSctpToNetwork(buffer);
763       delete buffer;
764       break;
765     }
766   }
767 }
768 
769 }  // namespace cricket
770