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