|
OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "net/socket/sctp_client_socket.h" | |
6 #include "net/socket/sctp_support.h" | |
7 | |
8 #include <errno.h> | |
9 #include <fcntl.h> | |
10 #include <netdb.h> | |
11 #include <sys/socket.h> | |
12 #include <sys/types.h> | |
13 #include <netinet/sctp.h> | |
14 #include <netinet/tcp.h> | |
15 #if defined(OS_POSIX) | |
16 #include <netinet/in.h> | |
17 #include <arpa/inet.h> | |
18 #endif | |
19 | |
20 #include "base/eintr_wrapper.h" | |
21 #include "base/logging.h" | |
22 #include "base/message_loop.h" | |
23 #include "base/metrics/stats_counters.h" | |
24 #include "base/string_util.h" | |
25 #include "net/base/address_list_net_log_param.h" | |
26 #include "net/base/connection_type_histograms.h" | |
27 #include "net/base/io_buffer.h" | |
28 #include "net/base/net_errors.h" | |
29 #include "net/base/net_log.h" | |
30 #include "net/base/net_util.h" | |
31 #include "net/base/network_change_notifier.h" | |
32 #if defined(USE_SYSTEM_LIBEVENT) | |
33 #include <event.h> | |
34 #else | |
35 #include "third_party/libevent/event.h" | |
36 #endif | |
37 | |
38 namespace net { | |
39 | |
40 namespace { | |
41 | |
42 const int kInvalidSocket = -1; | |
43 | |
44 // These values need to be reviewed and set based on experimental results. | |
45 // TODO(jtl): Need to come up with a rationale for choosing this value. | |
46 const int kSctpMaxBurst = 4096; // Arbitrary value, but high enough so | |
47 // that maxburst has no effect. | |
48 const uint16 kNumberOfSctpStreamsToRequest = 50; | |
49 | |
50 // DisableNagle turns off buffering in the kernel. By default, SCTP sockets will | |
51 // wait up to 200ms for more data to complete a packet before transmitting. | |
52 // After calling this function, the kernel will not wait. See SCTP_NODELAY in | |
53 // `man 7 sctp`. | |
54 void DisableNagle(int fd) { | |
55 int on = 1; | |
56 if (setsockopt(fd, IPPROTO_SCTP, SCTP_NODELAY, &on, sizeof(on)) < 0) | |
57 PLOG(ERROR) << "Failed to set SCTP_NODELAY on fd: " << fd; | |
58 return; | |
59 } | |
60 | |
61 // This is SCTP's equivalent to TCP's keepalive interval | |
62 void SetSCTPHeartbeatInterval(int fd) { | |
63 struct sctp_paddrparams params; | |
64 socklen_t params_len = sizeof(params); | |
65 memset(¶ms, 0, params_len); | |
66 if (getsockopt(fd, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, ¶ms, | |
67 ¶ms_len)) { | |
68 PLOG(ERROR) << "Failed to get SCTP_PEER_ADDR_PARAMS on fd: " << fd; | |
69 return; | |
70 } | |
71 // Increase hearbeat interval to 45 seconds. | |
72 params.spp_hbinterval = 45000; | |
73 if (setsockopt(fd, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, ¶ms, | |
74 params_len)) { | |
75 PLOG(ERROR) << "Failed to set SCTP_PEER_ADDR_PARAMS on fd: " << fd; | |
76 return; | |
77 } | |
78 return; | |
79 } | |
80 | |
81 void SetSCTPMaxBurst(int fd) { | |
82 int maxburst = kSctpMaxBurst; | |
83 socklen_t maxburst_len = sizeof(maxburst); | |
84 if (setsockopt(fd, IPPROTO_SCTP, SCTP_MAX_BURST, &maxburst, | |
85 maxburst_len) < 0) { | |
86 PLOG(ERROR) << "Failed to set SCTP_MAX_BURST on fd: " << fd << "\n"; | |
87 } | |
88 return; | |
89 } | |
90 | |
91 // Set number of outgoing SCTP streams to request | |
92 void SetSCTPInitMsg(int fd) { | |
Mike Belshe
2011/04/06 18:32:53
I would rename to "SetSCTPInitialStreams"
| |
93 struct sctp_initmsg initmsg; | |
94 socklen_t initmsg_len = sizeof(initmsg); | |
95 memset(&initmsg, 0, initmsg_len); | |
96 if (getsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, &initmsg_len) < 0) { | |
97 PLOG(ERROR) << "Failed to get SCTP_INITMSG on fd:" << fd << "\n"; | |
98 return; | |
99 } | |
100 initmsg.sinit_num_ostreams = kNumberOfSctpStreamsToRequest; | |
101 initmsg_len = sizeof(initmsg); | |
102 if (setsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, initmsg_len) < 0) | |
103 PLOG(ERROR) << "Failed to set SCTP_INITMSG on fd:" << fd << "\n"; | |
104 | |
Mike Belshe
2011/04/06 18:32:53
nit: remove lines 104/105
| |
105 return; | |
106 } | |
107 | |
108 // Convert values from <errno.h> to values from "net/base/net_errors.h" | |
109 int MapPosixError(int os_error) { | |
110 // There are numerous posix error codes, but these are the ones we thus far | |
111 // find interesting. | |
112 switch (os_error) { | |
113 case EAGAIN: | |
114 #if EWOULDBLOCK != EAGAIN | |
115 case EWOULDBLOCK: | |
116 #endif | |
117 return ERR_IO_PENDING; | |
118 case EACCES: | |
119 return ERR_ACCESS_DENIED; | |
120 case ENETDOWN: | |
121 return ERR_INTERNET_DISCONNECTED; | |
122 case ETIMEDOUT: | |
123 return ERR_TIMED_OUT; | |
124 case ECONNRESET: | |
125 case ENETRESET: // Related to keep-alive | |
126 case EPIPE: | |
127 return ERR_CONNECTION_RESET; | |
128 case ECONNABORTED: | |
129 return ERR_CONNECTION_ABORTED; | |
130 case ECONNREFUSED: | |
131 return ERR_CONNECTION_REFUSED; | |
132 case EHOSTUNREACH: | |
133 case EHOSTDOWN: | |
134 case ENETUNREACH: | |
135 return ERR_ADDRESS_UNREACHABLE; | |
136 case EADDRNOTAVAIL: | |
137 return ERR_ADDRESS_INVALID; | |
138 case 0: | |
139 return OK; | |
140 default: | |
141 LOG(WARNING) << "Unknown error " << os_error | |
142 << " mapped to net::ERR_FAILED"; | |
143 return ERR_FAILED; | |
144 } | |
145 } | |
146 | |
147 int MapConnectError(int os_error) { | |
148 switch (os_error) { | |
149 case EACCES: | |
150 return ERR_NETWORK_ACCESS_DENIED; | |
151 case ETIMEDOUT: | |
152 return ERR_CONNECTION_TIMED_OUT; | |
153 default: { | |
154 int net_error = MapPosixError(os_error); | |
155 if (net_error == ERR_FAILED) | |
156 return ERR_CONNECTION_FAILED; // More specific than ERR_FAILED. | |
157 | |
158 // Give a more specific error when the user is offline. | |
159 if (net_error == ERR_ADDRESS_UNREACHABLE && | |
160 NetworkChangeNotifier::IsOffline()) { | |
161 return ERR_INTERNET_DISCONNECTED; | |
162 } | |
163 return net_error; | |
164 } | |
165 } | |
166 } | |
167 | |
168 } // namespace | |
169 | |
170 //----------------------------------------------------------------------------- | |
171 | |
172 SCTPClientSocketLibevent::SCTPClientSocketLibevent( | |
173 const AddressList& addresses, | |
174 net::NetLog* net_log, | |
175 const net::NetLog::Source& source) | |
176 : socket_(kInvalidSocket), | |
177 addresses_(addresses), | |
178 current_ai_(NULL), | |
179 using_sctp_control_stream_(sctp_control_stream_enabled()), | |
180 max_sctp_streams_(0), | |
181 read_watcher_(this), | |
Mike Belshe
2011/04/06 18:32:53
nit: missed
read_buf_len_(0)
write_buf_len_(0
| |
182 write_watcher_(this), | |
183 read_callback_(NULL), | |
184 write_callback_(NULL), | |
185 next_connect_state_(CONNECT_STATE_NONE), | |
186 connect_os_error_(0), | |
187 net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_SOCKET)), | |
188 previously_disconnected_(false) { | |
189 scoped_refptr<NetLog::EventParameters> params; | |
190 if (source.is_valid()) | |
191 params = new NetLogSourceParameter("source_dependency", source); | |
192 net_log_.BeginEvent(NetLog::TYPE_SOCKET_ALIVE, params); | |
193 } | |
194 | |
195 SCTPClientSocketLibevent::~SCTPClientSocketLibevent() { | |
196 Disconnect(); | |
197 net_log_.EndEvent(NetLog::TYPE_SOCKET_ALIVE, NULL); | |
198 } | |
199 | |
200 void SCTPClientSocketLibevent::AdoptSocket(int socket) { | |
201 DCHECK_EQ(socket_, kInvalidSocket); | |
202 socket_ = socket; | |
203 int error = SetupSocket(); | |
204 DCHECK_EQ(0, error); | |
205 // This is to make GetPeerAddress work. It's up to the test that is calling | |
206 // this function to ensure that address_ contains a reasonable address for | |
207 // this socket. (i.e. at least match IPv4 vs IPv6!). | |
208 current_ai_ = addresses_.head(); | |
209 use_history_.set_was_ever_connected(); | |
210 } | |
211 | |
212 int SCTPClientSocketLibevent::Connect(CompletionCallback* callback) { | |
213 DCHECK(CalledOnValidThread()); | |
214 | |
215 // If already connected, then just return OK. | |
216 if (socket_ != kInvalidSocket) | |
217 return OK; | |
218 | |
219 static base::StatsCounter connects("sctp.connect"); | |
220 connects.Increment(); | |
221 | |
222 DCHECK(!waiting_connect()); | |
223 | |
224 net_log_.BeginEvent( | |
225 NetLog::TYPE_SCTP_CONNECT, | |
226 make_scoped_refptr(new AddressListNetLogParam(addresses_))); | |
227 | |
228 // We will try to connect to each address in addresses_. Start with the | |
229 // first one in the list. | |
230 next_connect_state_ = CONNECT_STATE_CONNECT; | |
231 current_ai_ = addresses_.head(); | |
232 | |
233 int rv = DoConnectLoop(OK); | |
234 if (rv == ERR_IO_PENDING) { | |
235 // Synchronous operation not supported. | |
236 DCHECK(callback); | |
237 write_callback_ = callback; | |
238 } else { | |
239 LogConnectCompletion(rv); | |
240 } | |
241 | |
242 return rv; | |
243 } | |
244 | |
245 int SCTPClientSocketLibevent::DoConnectLoop(int result) { | |
246 DCHECK_NE(next_connect_state_, CONNECT_STATE_NONE); | |
247 | |
248 int rv = result; | |
249 do { | |
250 ConnectState state = next_connect_state_; | |
251 next_connect_state_ = CONNECT_STATE_NONE; | |
252 switch (state) { | |
253 case CONNECT_STATE_CONNECT: | |
254 DCHECK_EQ(OK, rv); | |
255 rv = DoConnect(); | |
256 break; | |
257 case CONNECT_STATE_CONNECT_COMPLETE: | |
258 rv = DoConnectComplete(rv); | |
259 break; | |
260 default: | |
261 LOG(DFATAL) << "bad state"; | |
262 rv = ERR_UNEXPECTED; | |
263 break; | |
264 } | |
265 } while (rv != ERR_IO_PENDING && next_connect_state_ != CONNECT_STATE_NONE); | |
266 | |
267 return rv; | |
268 } | |
269 | |
270 int SCTPClientSocketLibevent::DoConnect() { | |
271 DCHECK(current_ai_); | |
272 | |
273 DCHECK_EQ(0, connect_os_error_); | |
274 | |
275 if (previously_disconnected_) { | |
276 use_history_.Reset(); | |
277 previously_disconnected_ = false; | |
278 } | |
279 | |
280 net_log_.BeginEvent(NetLog::TYPE_SCTP_CONNECT_ATTEMPT, | |
281 make_scoped_refptr(new NetLogStringParameter( | |
282 "address", NetAddressToStringWithPort(current_ai_)))); | |
283 | |
284 next_connect_state_ = CONNECT_STATE_CONNECT_COMPLETE; | |
285 | |
286 // Create a non-blocking socket. | |
287 connect_os_error_ = CreateSocket(current_ai_); | |
288 if (connect_os_error_) | |
289 return MapPosixError(connect_os_error_); | |
290 | |
291 // Connect the socket. | |
292 if (!HANDLE_EINTR(connect(socket_, current_ai_->ai_addr, | |
293 static_cast<int>(current_ai_->ai_addrlen)))) { | |
294 // Connected without waiting! | |
295 return OK; | |
296 } | |
297 | |
298 // Check if the connect() failed synchronously. | |
299 connect_os_error_ = errno; | |
300 if (connect_os_error_ != EINPROGRESS) | |
301 return MapConnectError(connect_os_error_); | |
302 | |
303 // Otherwise the connect() is going to complete asynchronously, so watch | |
304 // for its completion. | |
305 if (!MessageLoopForIO::current()->WatchFileDescriptor( | |
306 socket_, true, MessageLoopForIO::WATCH_WRITE, &write_socket_watcher_, | |
307 &write_watcher_)) { | |
308 connect_os_error_ = errno; | |
309 DVLOG(1) << "WatchFileDescriptor failed: " << connect_os_error_; | |
310 return MapPosixError(connect_os_error_); | |
311 } | |
312 | |
313 return ERR_IO_PENDING; | |
314 } | |
315 | |
316 int SCTPClientSocketLibevent::DoConnectComplete(int result) { | |
317 // Log the end of this attempt (and any OS error it threw). | |
318 int os_error = connect_os_error_; | |
319 connect_os_error_ = 0; | |
320 scoped_refptr<NetLog::EventParameters> params; | |
321 if (result != OK) | |
322 params = new NetLogIntegerParameter("os_error", os_error); | |
323 net_log_.EndEvent(NetLog::TYPE_SCTP_CONNECT_ATTEMPT, params); | |
324 | |
325 write_socket_watcher_.StopWatchingFileDescriptor(); | |
326 | |
327 if (result == OK) { | |
328 use_history_.set_was_ever_connected(); | |
329 | |
330 // Get number of allowable outgoing SCTP streams and set max_sctp_streams_. | |
331 struct sctp_status status; | |
332 socklen_t status_len = sizeof(status); | |
333 int rv; | |
334 memset(&status, 0, status_len); | |
335 rv = getsockopt(socket_, IPPROTO_SCTP, SCTP_STATUS, &status, &status_len); | |
336 if (rv < 0 ) | |
Mike Belshe
2011/04/06 18:32:53
This must be a bug - missing curly braces, causes
| |
337 LOG(ERROR) << "Unable to get status of SCTP socket."; | |
338 rv = ERR_UNEXPECTED; | |
339 | |
340 if (status.sstat_state == SCTP_ESTABLISHED) { | |
341 max_sctp_streams_ = status.sstat_instrms <= status.sstat_outstrms ? | |
342 status.sstat_instrms : status.sstat_outstrms; | |
343 return OK; // Done! | |
344 } else if (status.sstat_state == SCTP_COOKIE_WAIT) { | |
345 // TODO(jtl): Need to figure out how and where to best handle this case. | |
346 // This is actually the prefered outcome because it potentially saves an | |
347 // RTT, and it can occur on FreeBSD and Mac OS X. | |
348 LOG(ERROR) << "Unable to set max_sctp_streams_ until association is " | |
349 << "established."; | |
350 rv = ERR_UNEXPECTED; | |
Mike Belshe
2011/04/06 18:32:53
the return value is |result| not |rv|, so these ar
| |
351 } else { | |
352 LOG(ERROR) << "Unexpected association state " << status.sstat_state | |
353 << " on connecting socket " << socket_; | |
354 rv = ERR_UNEXPECTED; | |
355 } | |
356 } | |
357 | |
358 // Close whatever partially connected socket we currently have. | |
359 DoDisconnect(); | |
360 | |
361 // Try to fall back to the next address in the list. | |
362 if (current_ai_->ai_next) { | |
363 next_connect_state_ = CONNECT_STATE_CONNECT; | |
364 current_ai_ = current_ai_->ai_next; | |
365 return OK; | |
366 } | |
367 | |
368 // Otherwise there is nothing to fall back to, so give up. | |
369 return result; | |
370 } | |
371 | |
372 void SCTPClientSocketLibevent::Disconnect() { | |
373 DCHECK(CalledOnValidThread()); | |
374 | |
375 DoDisconnect(); | |
376 current_ai_ = NULL; | |
377 } | |
378 | |
379 void SCTPClientSocketLibevent::DoDisconnect() { | |
380 if (socket_ == kInvalidSocket) | |
381 return; | |
382 | |
383 bool ok = read_socket_watcher_.StopWatchingFileDescriptor(); | |
384 DCHECK(ok); | |
385 ok = write_socket_watcher_.StopWatchingFileDescriptor(); | |
386 DCHECK(ok); | |
387 if (HANDLE_EINTR(close(socket_)) < 0) | |
388 PLOG(ERROR) << "close"; | |
389 socket_ = kInvalidSocket; | |
390 previously_disconnected_ = true; | |
391 } | |
392 | |
393 bool SCTPClientSocketLibevent::IsConnected() const { | |
394 DCHECK(CalledOnValidThread()); | |
395 | |
396 if (socket_ == kInvalidSocket || waiting_connect()) | |
397 return false; | |
398 | |
399 // Check if connection is alive. | |
400 char c; | |
401 int rv = HANDLE_EINTR(recv(socket_, &c, 1, MSG_PEEK)); | |
402 if (rv == 0) | |
403 return false; | |
404 if (rv == -1 && errno != EAGAIN && errno != EWOULDBLOCK) | |
405 return false; | |
406 | |
407 return true; | |
408 } | |
409 | |
410 bool SCTPClientSocketLibevent::IsConnectedAndIdle() const { | |
411 DCHECK(CalledOnValidThread()); | |
412 | |
413 if (socket_ == kInvalidSocket || waiting_connect()) | |
414 return false; | |
415 | |
416 // Check if connection is alive and we haven't received any data | |
417 // unexpectedly. | |
418 char c; | |
419 int rv = HANDLE_EINTR(recv(socket_, &c, 1, MSG_PEEK)); | |
420 if (rv >= 0) | |
421 return false; | |
422 if (errno != EAGAIN && errno != EWOULDBLOCK) | |
423 return false; | |
424 | |
425 return true; | |
426 } | |
427 | |
428 uint16 SCTPClientSocketLibevent::MapSpdyToSctp(uint32 stream_id) { | |
429 DCHECK_GT(max_sctp_streams_, 0); | |
430 | |
431 uint16 value = max_sctp_streams_; | |
432 if (!(stream_id & 0x00000001)) | |
Mike Belshe
2011/04/06 18:32:53
What is this doing?
| |
433 value -= 1; | |
434 | |
435 return (stream_id - 1) % (value & 0xfffe) + 1; | |
436 } | |
437 | |
438 int SCTPClientSocketLibevent::Read(IOBuffer* buf, | |
439 int buf_len, | |
440 CompletionCallback* callback) { | |
441 DCHECK(CalledOnValidThread()); | |
442 DCHECK_NE(kInvalidSocket, socket_); | |
443 DCHECK(!waiting_connect()); | |
444 DCHECK(!read_callback_); | |
445 // Synchronous operation not supported | |
446 DCHECK(callback); | |
447 DCHECK_GT(buf_len, 0); | |
448 | |
449 int nread = HANDLE_EINTR(read(socket_, buf->data(), buf_len)); | |
Mike Belshe
2011/04/06 18:32:53
I was surprised to see we didn't need to know the
| |
450 if (nread >= 0) { | |
451 static base::StatsCounter read_bytes("sctp.read_bytes"); | |
452 read_bytes.Add(nread); | |
453 if (nread > 0) | |
454 use_history_.set_was_used_to_convey_data(); | |
455 LogByteTransfer( | |
456 net_log_, NetLog::TYPE_SOCKET_BYTES_RECEIVED, nread, buf->data()); | |
457 return nread; | |
458 } | |
459 if (errno != EAGAIN && errno != EWOULDBLOCK) { | |
460 DVLOG(1) << "read failed, errno " << errno; | |
461 return MapPosixError(errno); | |
462 } | |
463 | |
464 if (!MessageLoopForIO::current()->WatchFileDescriptor( | |
465 socket_, true, MessageLoopForIO::WATCH_READ, | |
466 &read_socket_watcher_, &read_watcher_)) { | |
467 DVLOG(1) << "WatchFileDescriptor failed on read, errno " << errno; | |
468 return MapPosixError(errno); | |
469 } | |
470 | |
471 read_buf_ = buf; | |
472 read_buf_len_ = buf_len; | |
473 read_callback_ = callback; | |
474 return ERR_IO_PENDING; | |
475 } | |
476 | |
477 int SCTPClientSocketLibevent::Write(IOBuffer* buf, | |
478 int buf_len, | |
479 CompletionCallback* callback) { | |
480 DCHECK(CalledOnValidThread()); | |
481 DCHECK_NE(kInvalidSocket, socket_); | |
482 DCHECK(!waiting_connect()); | |
483 DCHECK(!write_callback_); | |
484 // Synchronous operation not supported | |
485 DCHECK(callback); | |
486 DCHECK_GT(buf_len, 0); | |
487 | |
488 int nwrite = InternalWrite(buf, buf_len); | |
489 if (nwrite >= 0) { | |
490 static base::StatsCounter write_bytes("sctp.write_bytes"); | |
491 write_bytes.Add(nwrite); | |
492 if (nwrite > 0) | |
493 use_history_.set_was_used_to_convey_data(); | |
494 LogByteTransfer( | |
495 net_log_, NetLog::TYPE_SOCKET_BYTES_SENT, nwrite, buf->data()); | |
496 return nwrite; | |
497 } | |
498 if (errno != EAGAIN && errno != EWOULDBLOCK) | |
499 return MapPosixError(errno); | |
500 | |
501 if (!MessageLoopForIO::current()->WatchFileDescriptor( | |
502 socket_, true, MessageLoopForIO::WATCH_WRITE, | |
503 &write_socket_watcher_, &write_watcher_)) { | |
504 DVLOG(1) << "WatchFileDescriptor failed on write, errno " << errno; | |
505 return MapPosixError(errno); | |
506 } | |
507 | |
508 write_buf_ = buf; | |
509 write_buf_len_ = buf_len; | |
510 write_callback_ = callback; | |
511 return ERR_IO_PENDING; | |
512 } | |
513 | |
514 int SCTPClientSocketLibevent::InternalWrite(IOBuffer* buf, int buf_len) { | |
515 return HANDLE_EINTR(sctp_sendmsg(socket_, buf->data(), buf_len, | |
516 NULL, 0, 0, 0, buf->sctp_stream_id(), 0, 0)); | |
517 } | |
518 | |
519 bool SCTPClientSocketLibevent::SetReceiveBufferSize(int32 size) { | |
520 DCHECK(CalledOnValidThread()); | |
521 int rv = setsockopt(socket_, SOL_SOCKET, SO_RCVBUF, | |
522 reinterpret_cast<const char*>(&size), | |
523 sizeof(size)); | |
524 DCHECK(!rv) << "Could not set socket receive buffer size: " << errno; | |
525 return rv == 0; | |
526 } | |
527 | |
528 bool SCTPClientSocketLibevent::SetSendBufferSize(int32 size) { | |
529 DCHECK(CalledOnValidThread()); | |
530 int rv = setsockopt(socket_, SOL_SOCKET, SO_SNDBUF, | |
531 reinterpret_cast<const char*>(&size), | |
532 sizeof(size)); | |
533 DCHECK(!rv) << "Could not set socket send buffer size: " << errno; | |
534 return rv == 0; | |
535 } | |
536 | |
537 | |
538 int SCTPClientSocketLibevent::CreateSocket(const addrinfo* ai) { | |
539 socket_ = socket(ai->ai_family, ai->ai_socktype, IPPROTO_SCTP); | |
540 if (socket_ == kInvalidSocket) | |
541 return errno; | |
542 return SetupSocket(); | |
543 } | |
544 | |
545 int SCTPClientSocketLibevent::SetupSocket() { | |
546 // Linux SCTP has a bug which causes retransmissions to go to unconfirmed | |
547 // addresses. Since the default behaviour, when calling connect() with an | |
548 // unbound socket, is to bind the socket to all local addresses, the server | |
549 // may retransmit packets to some or all of these interfaces even if they are | |
550 // unreachable. One work around is to bind the socket to a single local | |
551 // address prior to calling connect(). An alternative is to use sctp_bindx() | |
552 // to bind only those addresses that are reachable. | |
553 // The bug exists in 2.6.31 and persists at least through 2.6.34. I do not | |
554 // know about earlier or later versions. | |
555 // TODO(jtl): Need to implement a way to select a single local IP address to | |
556 // bind to. | |
557 struct sockaddr_in sin; | |
558 sin.sin_family = AF_INET; | |
559 sin.sin_port = htons(0); // let system choose ephemeral port number | |
560 //inet_aton("127.0.0.1", &(sin.sin_addr)); | |
Mike Belshe
2011/04/06 18:32:53
Wow - what a mess!
Not sure what to do to make th
| |
561 inet_aton("10.1.207.2", &(sin.sin_addr)); | |
562 //inet_aton("128.4.30.27", &(sin.sin_addr)); | |
563 if (bind(socket_, (struct sockaddr*) &sin, sizeof(sin)) < 0) { | |
564 const int bind_os_error_ = errno; | |
565 printf("SCTPClientSocketLibevent::SetupSocket: bind() failed with errno = " | |
566 "%d (%s)\n", errno, strerror(errno)); | |
567 return bind_os_error_; | |
568 } | |
569 | |
570 if (SetNonBlocking(socket_)) { | |
571 const int err = errno; | |
572 close(socket_); | |
573 socket_ = kInvalidSocket; | |
574 return err; | |
575 } | |
576 | |
577 // This mirrors the behaviour on Windows. See the comment in | |
578 // tcp_client_socket_win.cc after searching for "NODELAY". | |
579 DisableNagle(socket_); // If DisableNagle fails, we don't care. | |
580 | |
581 // Heartbeats are SCTP's equivalent to TCP's keepalive, and they're always on. | |
582 SetSCTPHeartbeatInterval(socket_); | |
583 | |
584 // TODO(jtl): Make this a command line option. | |
585 // set max_burst | |
586 SetSCTPMaxBurst(socket_); | |
587 | |
588 // Set the number of outgoing sctp streams to request. | |
589 SetSCTPInitMsg(socket_); | |
590 return 0; | |
591 } | |
592 | |
593 void SCTPClientSocketLibevent::LogConnectCompletion(int net_error) { | |
594 if (net_error != OK) { | |
595 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SCTP_CONNECT, net_error); | |
596 return; | |
597 } | |
598 | |
599 struct sockaddr_storage source_address; | |
600 socklen_t addrlen = sizeof(source_address); | |
601 int rv = getsockname( | |
602 socket_, reinterpret_cast<struct sockaddr*>(&source_address), &addrlen); | |
603 if (rv != 0) { | |
604 PLOG(ERROR) << "getsockname() [rv: " << rv << "] error: "; | |
605 NOTREACHED(); | |
606 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SCTP_CONNECT, rv); | |
607 return; | |
608 } | |
609 | |
610 const std::string source_address_str = | |
611 NetAddressToStringWithPort( | |
612 reinterpret_cast<const struct sockaddr*>(&source_address), | |
613 sizeof(source_address)); | |
614 net_log_.EndEvent(NetLog::TYPE_SCTP_CONNECT, | |
615 make_scoped_refptr(new NetLogStringParameter( | |
616 "source address", | |
617 source_address_str))); | |
618 } | |
619 | |
620 void SCTPClientSocketLibevent::DoReadCallback(int rv) { | |
621 DCHECK_NE(rv, ERR_IO_PENDING); | |
622 DCHECK(read_callback_); | |
623 | |
624 // since Run may result in Read being called, clear read_callback_ up front. | |
625 CompletionCallback* c = read_callback_; | |
626 read_callback_ = NULL; | |
627 c->Run(rv); | |
628 } | |
629 | |
630 void SCTPClientSocketLibevent::DoWriteCallback(int rv) { | |
631 DCHECK_NE(rv, ERR_IO_PENDING); | |
632 DCHECK(write_callback_); | |
633 | |
634 // since Run may result in Write being called, clear write_callback_ up front. | |
635 CompletionCallback* c = write_callback_; | |
636 write_callback_ = NULL; | |
637 c->Run(rv); | |
638 } | |
639 | |
640 void SCTPClientSocketLibevent::DidCompleteConnect() { | |
641 DCHECK_EQ(next_connect_state_, CONNECT_STATE_CONNECT_COMPLETE); | |
642 | |
643 // Get the error that connect() completed with. | |
644 int os_error = 0; | |
645 socklen_t len = sizeof(os_error); | |
646 if (getsockopt(socket_, SOL_SOCKET, SO_ERROR, &os_error, &len) < 0) | |
647 os_error = errno; | |
648 | |
649 // TODO(eroman): Is this check really necessary? | |
650 if (os_error == EINPROGRESS || os_error == EALREADY) { | |
651 NOTREACHED(); // This indicates a bug in libevent or our code. | |
652 return; | |
653 } | |
654 | |
655 connect_os_error_ = os_error; | |
656 int rv = DoConnectLoop(MapConnectError(os_error)); | |
657 if (rv != ERR_IO_PENDING) { | |
658 LogConnectCompletion(rv); | |
659 DoWriteCallback(rv); | |
660 } | |
661 } | |
662 | |
663 void SCTPClientSocketLibevent::DidCompleteRead() { | |
664 int bytes_transferred; | |
665 bytes_transferred = HANDLE_EINTR(read(socket_, read_buf_->data(), | |
666 read_buf_len_)); | |
667 | |
668 int result; | |
669 if (bytes_transferred >= 0) { | |
670 result = bytes_transferred; | |
671 static base::StatsCounter read_bytes("sctp.read_bytes"); | |
672 read_bytes.Add(bytes_transferred); | |
673 if (bytes_transferred > 0) | |
674 use_history_.set_was_used_to_convey_data(); | |
675 LogByteTransfer(net_log_, NetLog::TYPE_SOCKET_BYTES_RECEIVED, result, | |
676 read_buf_->data()); | |
677 } else { | |
678 result = MapPosixError(errno); | |
679 } | |
680 | |
681 if (result != ERR_IO_PENDING) { | |
682 read_buf_ = NULL; | |
683 read_buf_len_ = 0; | |
684 bool ok = read_socket_watcher_.StopWatchingFileDescriptor(); | |
685 DCHECK(ok); | |
686 DoReadCallback(result); | |
687 } | |
688 } | |
689 | |
690 void SCTPClientSocketLibevent::DidCompleteWrite() { | |
691 int bytes_transferred; | |
692 // TODO(jtl): Need to fix this to use sctp_sendmsg and need to get SCTP stream | |
693 // ID. Perhaps use a form of IOBuffer that includes the SCTP stream ID? | |
694 bytes_transferred = HANDLE_EINTR(write(socket_, write_buf_->data(), | |
Mike Belshe
2011/04/06 18:32:53
i think this should be InternalWrite()?
| |
695 write_buf_len_)); | |
696 | |
697 int result; | |
698 if (bytes_transferred >= 0) { | |
699 result = bytes_transferred; | |
700 static base::StatsCounter write_bytes("sctp.write_bytes"); | |
701 write_bytes.Add(bytes_transferred); | |
702 if (bytes_transferred > 0) | |
703 use_history_.set_was_used_to_convey_data(); | |
704 LogByteTransfer(net_log_, NetLog::TYPE_SOCKET_BYTES_SENT, result, | |
705 write_buf_->data()); | |
706 } else { | |
707 result = MapPosixError(errno); | |
708 } | |
709 | |
710 if (result != ERR_IO_PENDING) { | |
711 write_buf_ = NULL; | |
712 write_buf_len_ = 0; | |
713 write_socket_watcher_.StopWatchingFileDescriptor(); | |
714 DoWriteCallback(result); | |
715 } | |
716 } | |
717 | |
718 int SCTPClientSocketLibevent::GetPeerAddress(AddressList* address) const { | |
719 DCHECK(CalledOnValidThread()); | |
720 DCHECK(address); | |
721 if (!IsConnected()) | |
722 return ERR_SOCKET_NOT_CONNECTED; | |
723 address->Copy(current_ai_, false); | |
724 return OK; | |
725 } | |
726 | |
727 const BoundNetLog& SCTPClientSocketLibevent::NetLog() const { | |
728 return net_log_; | |
729 } | |
730 | |
731 } // namespace net | |
OLD | NEW |