OLD | NEW |
| (Empty) |
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' | |
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' | |
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. | |
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. | |
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. | |
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 | |
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. | |
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. | |
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'. | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
376 SctpDataMediaChannel::~SctpDataMediaChannel() { | |
377 CloseSctpSocket(); | |
378 } | |
379 | |
380 void SctpDataMediaChannel::OnSendThresholdCallback() { | |
381 RTC_DCHECK(rtc::Thread::Current() == worker_thread_); | |
382 SignalReadyToSend(true); | |
383 } | |
384 | |
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 | |
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, ¶ms, | |
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 | |
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 | |
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 | |
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 | |
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 | |
562 bool SctpDataMediaChannel::SetReceive(bool receive) { | |
563 receiving_ = receive; | |
564 return true; | |
565 } | |
566 | |
567 bool SctpDataMediaChannel::SetSendParameters(const DataSendParameters& params) { | |
568 return SetSendCodecs(params.codecs); | |
569 } | |
570 | |
571 bool SctpDataMediaChannel::SetRecvParameters(const DataRecvParameters& params) { | |
572 return SetRecvCodecs(params.codecs); | |
573 } | |
574 | |
575 bool SctpDataMediaChannel::AddSendStream(const StreamParams& stream) { | |
576 return AddStream(stream); | |
577 } | |
578 | |
579 bool SctpDataMediaChannel::RemoveSendStream(uint32_t ssrc) { | |
580 return ResetStream(ssrc); | |
581 } | |
582 | |
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 | |
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 | |
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. | |
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 | |
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 | |
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 | |
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 | |
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 | |
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(¬ification.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 | |
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 | |
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. | |
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 | |
968 bool SctpDataMediaChannel::SetSendCodecs(const std::vector<DataCodec>& codecs) { | |
969 return GetCodecIntParameter( | |
970 codecs, kGoogleSctpDataCodecId, kGoogleSctpDataCodecName, kCodecParamPort, | |
971 &remote_port_); | |
972 } | |
973 | |
974 bool SctpDataMediaChannel::SetRecvCodecs(const std::vector<DataCodec>& codecs) { | |
975 return GetCodecIntParameter( | |
976 codecs, kGoogleSctpDataCodecId, kGoogleSctpDataCodecName, kCodecParamPort, | |
977 &local_port_); | |
978 } | |
979 | |
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 | |
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 | |
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 | |
OLD | NEW |