Show Pages in chrome://md-settings
[chromium-blink-merge.git] / net / udp / udp_socket_libevent.cc
blob6fdb94e5eb15e69de304145860163de27489388e
1 // Copyright (c) 2012 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.
5 #include "net/udp/udp_socket_libevent.h"
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <netdb.h>
10 #include <net/if.h>
11 #include <netinet/in.h>
12 #include <sys/ioctl.h>
13 #include <sys/socket.h>
15 #include "base/callback.h"
16 #include "base/logging.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/metrics/sparse_histogram.h"
19 #include "base/posix/eintr_wrapper.h"
20 #include "base/rand_util.h"
21 #include "net/base/io_buffer.h"
22 #include "net/base/ip_endpoint.h"
23 #include "net/base/net_errors.h"
24 #include "net/base/net_log.h"
25 #include "net/base/net_util.h"
26 #include "net/base/network_activity_monitor.h"
27 #include "net/socket/socket_descriptor.h"
28 #include "net/udp/udp_net_log_parameters.h"
31 namespace net {
33 namespace {
35 const int kBindRetries = 10;
36 const int kPortStart = 1024;
37 const int kPortEnd = 65535;
39 #if defined(OS_MACOSX)
41 // Returns IPv4 address in network order.
42 int GetIPv4AddressFromIndex(int socket, uint32 index, uint32* address){
43 if (!index) {
44 *address = htonl(INADDR_ANY);
45 return OK;
47 ifreq ifr;
48 ifr.ifr_addr.sa_family = AF_INET;
49 if (!if_indextoname(index, ifr.ifr_name))
50 return MapSystemError(errno);
51 int rv = ioctl(socket, SIOCGIFADDR, &ifr);
52 if (rv == -1)
53 return MapSystemError(errno);
54 *address = reinterpret_cast<sockaddr_in*>(&ifr.ifr_addr)->sin_addr.s_addr;
55 return OK;
58 #endif // OS_MACOSX
60 } // namespace
62 UDPSocketLibevent::UDPSocketLibevent(
63 DatagramSocket::BindType bind_type,
64 const RandIntCallback& rand_int_cb,
65 net::NetLog* net_log,
66 const net::NetLog::Source& source)
67 : socket_(kInvalidSocket),
68 addr_family_(0),
69 is_connected_(false),
70 socket_options_(SOCKET_OPTION_MULTICAST_LOOP),
71 multicast_interface_(0),
72 multicast_time_to_live_(1),
73 bind_type_(bind_type),
74 rand_int_cb_(rand_int_cb),
75 read_watcher_(this),
76 write_watcher_(this),
77 read_buf_len_(0),
78 recv_from_address_(NULL),
79 write_buf_len_(0),
80 net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_UDP_SOCKET)) {
81 net_log_.BeginEvent(NetLog::TYPE_SOCKET_ALIVE,
82 source.ToEventParametersCallback());
83 if (bind_type == DatagramSocket::RANDOM_BIND)
84 DCHECK(!rand_int_cb.is_null());
87 UDPSocketLibevent::~UDPSocketLibevent() {
88 Close();
89 net_log_.EndEvent(NetLog::TYPE_SOCKET_ALIVE);
92 int UDPSocketLibevent::Open(AddressFamily address_family) {
93 DCHECK(CalledOnValidThread());
94 DCHECK_EQ(socket_, kInvalidSocket);
96 addr_family_ = ConvertAddressFamily(address_family);
97 socket_ = CreatePlatformSocket(addr_family_, SOCK_DGRAM, 0);
98 if (socket_ == kInvalidSocket)
99 return MapSystemError(errno);
100 if (SetNonBlocking(socket_)) {
101 const int err = MapSystemError(errno);
102 Close();
103 return err;
105 return OK;
108 void UDPSocketLibevent::Close() {
109 DCHECK(CalledOnValidThread());
111 if (socket_ == kInvalidSocket)
112 return;
114 // Zero out any pending read/write callback state.
115 read_buf_ = NULL;
116 read_buf_len_ = 0;
117 read_callback_.Reset();
118 recv_from_address_ = NULL;
119 write_buf_ = NULL;
120 write_buf_len_ = 0;
121 write_callback_.Reset();
122 send_to_address_.reset();
124 bool ok = read_socket_watcher_.StopWatchingFileDescriptor();
125 DCHECK(ok);
126 ok = write_socket_watcher_.StopWatchingFileDescriptor();
127 DCHECK(ok);
129 PCHECK(0 == IGNORE_EINTR(close(socket_)));
131 socket_ = kInvalidSocket;
132 addr_family_ = 0;
133 is_connected_ = false;
136 int UDPSocketLibevent::GetPeerAddress(IPEndPoint* address) const {
137 DCHECK(CalledOnValidThread());
138 DCHECK(address);
139 if (!is_connected())
140 return ERR_SOCKET_NOT_CONNECTED;
142 if (!remote_address_.get()) {
143 SockaddrStorage storage;
144 if (getpeername(socket_, storage.addr, &storage.addr_len))
145 return MapSystemError(errno);
146 scoped_ptr<IPEndPoint> address(new IPEndPoint());
147 if (!address->FromSockAddr(storage.addr, storage.addr_len))
148 return ERR_ADDRESS_INVALID;
149 remote_address_.reset(address.release());
152 *address = *remote_address_;
153 return OK;
156 int UDPSocketLibevent::GetLocalAddress(IPEndPoint* address) const {
157 DCHECK(CalledOnValidThread());
158 DCHECK(address);
159 if (!is_connected())
160 return ERR_SOCKET_NOT_CONNECTED;
162 if (!local_address_.get()) {
163 SockaddrStorage storage;
164 if (getsockname(socket_, storage.addr, &storage.addr_len))
165 return MapSystemError(errno);
166 scoped_ptr<IPEndPoint> address(new IPEndPoint());
167 if (!address->FromSockAddr(storage.addr, storage.addr_len))
168 return ERR_ADDRESS_INVALID;
169 local_address_.reset(address.release());
170 net_log_.AddEvent(NetLog::TYPE_UDP_LOCAL_ADDRESS,
171 CreateNetLogUDPConnectCallback(local_address_.get()));
174 *address = *local_address_;
175 return OK;
178 int UDPSocketLibevent::Read(IOBuffer* buf,
179 int buf_len,
180 const CompletionCallback& callback) {
181 return RecvFrom(buf, buf_len, NULL, callback);
184 int UDPSocketLibevent::RecvFrom(IOBuffer* buf,
185 int buf_len,
186 IPEndPoint* address,
187 const CompletionCallback& callback) {
188 DCHECK(CalledOnValidThread());
189 DCHECK_NE(kInvalidSocket, socket_);
190 CHECK(read_callback_.is_null());
191 DCHECK(!recv_from_address_);
192 DCHECK(!callback.is_null()); // Synchronous operation not supported
193 DCHECK_GT(buf_len, 0);
195 int nread = InternalRecvFrom(buf, buf_len, address);
196 if (nread != ERR_IO_PENDING)
197 return nread;
199 if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
200 socket_, true, base::MessageLoopForIO::WATCH_READ,
201 &read_socket_watcher_, &read_watcher_)) {
202 PLOG(ERROR) << "WatchFileDescriptor failed on read";
203 int result = MapSystemError(errno);
204 LogRead(result, NULL, 0, NULL);
205 return result;
208 read_buf_ = buf;
209 read_buf_len_ = buf_len;
210 recv_from_address_ = address;
211 read_callback_ = callback;
212 return ERR_IO_PENDING;
215 int UDPSocketLibevent::Write(IOBuffer* buf,
216 int buf_len,
217 const CompletionCallback& callback) {
218 return SendToOrWrite(buf, buf_len, NULL, callback);
221 int UDPSocketLibevent::SendTo(IOBuffer* buf,
222 int buf_len,
223 const IPEndPoint& address,
224 const CompletionCallback& callback) {
225 return SendToOrWrite(buf, buf_len, &address, callback);
228 int UDPSocketLibevent::SendToOrWrite(IOBuffer* buf,
229 int buf_len,
230 const IPEndPoint* address,
231 const CompletionCallback& callback) {
232 DCHECK(CalledOnValidThread());
233 DCHECK_NE(kInvalidSocket, socket_);
234 CHECK(write_callback_.is_null());
235 DCHECK(!callback.is_null()); // Synchronous operation not supported
236 DCHECK_GT(buf_len, 0);
238 int result = InternalSendTo(buf, buf_len, address);
239 if (result != ERR_IO_PENDING)
240 return result;
242 if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
243 socket_, true, base::MessageLoopForIO::WATCH_WRITE,
244 &write_socket_watcher_, &write_watcher_)) {
245 DVLOG(1) << "WatchFileDescriptor failed on write, errno " << errno;
246 int result = MapSystemError(errno);
247 LogWrite(result, NULL, NULL);
248 return result;
251 write_buf_ = buf;
252 write_buf_len_ = buf_len;
253 DCHECK(!send_to_address_.get());
254 if (address) {
255 send_to_address_.reset(new IPEndPoint(*address));
257 write_callback_ = callback;
258 return ERR_IO_PENDING;
261 int UDPSocketLibevent::Connect(const IPEndPoint& address) {
262 DCHECK_NE(socket_, kInvalidSocket);
263 net_log_.BeginEvent(NetLog::TYPE_UDP_CONNECT,
264 CreateNetLogUDPConnectCallback(&address));
265 int rv = InternalConnect(address);
266 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_UDP_CONNECT, rv);
267 is_connected_ = (rv == OK);
268 return rv;
271 int UDPSocketLibevent::InternalConnect(const IPEndPoint& address) {
272 DCHECK(CalledOnValidThread());
273 DCHECK(!is_connected());
274 DCHECK(!remote_address_.get());
276 int rv = 0;
277 if (bind_type_ == DatagramSocket::RANDOM_BIND) {
278 // Construct IPAddressNumber of appropriate size (IPv4 or IPv6) of 0s,
279 // representing INADDR_ANY or in6addr_any.
280 size_t addr_size = address.GetSockAddrFamily() == AF_INET ?
281 kIPv4AddressSize : kIPv6AddressSize;
282 IPAddressNumber addr_any(addr_size);
283 rv = RandomBind(addr_any);
285 // else connect() does the DatagramSocket::DEFAULT_BIND
287 if (rv < 0) {
288 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.UdpSocketRandomBindErrorCode", -rv);
289 return rv;
292 SockaddrStorage storage;
293 if (!address.ToSockAddr(storage.addr, &storage.addr_len))
294 return ERR_ADDRESS_INVALID;
296 rv = HANDLE_EINTR(connect(socket_, storage.addr, storage.addr_len));
297 if (rv < 0)
298 return MapSystemError(errno);
300 remote_address_.reset(new IPEndPoint(address));
301 return rv;
304 int UDPSocketLibevent::Bind(const IPEndPoint& address) {
305 DCHECK_NE(socket_, kInvalidSocket);
306 DCHECK(CalledOnValidThread());
307 DCHECK(!is_connected());
309 int rv = SetMulticastOptions();
310 if (rv < 0)
311 return rv;
313 rv = DoBind(address);
314 if (rv < 0)
315 return rv;
317 is_connected_ = true;
318 local_address_.reset();
319 return rv;
322 int UDPSocketLibevent::SetReceiveBufferSize(int32 size) {
323 DCHECK_NE(socket_, kInvalidSocket);
324 DCHECK(CalledOnValidThread());
325 int rv = setsockopt(socket_, SOL_SOCKET, SO_RCVBUF,
326 reinterpret_cast<const char*>(&size), sizeof(size));
327 return rv == 0 ? OK : MapSystemError(errno);
330 int UDPSocketLibevent::SetSendBufferSize(int32 size) {
331 DCHECK_NE(socket_, kInvalidSocket);
332 DCHECK(CalledOnValidThread());
333 int rv = setsockopt(socket_, SOL_SOCKET, SO_SNDBUF,
334 reinterpret_cast<const char*>(&size), sizeof(size));
335 return rv == 0 ? OK : MapSystemError(errno);
338 int UDPSocketLibevent::AllowAddressReuse() {
339 DCHECK_NE(socket_, kInvalidSocket);
340 DCHECK(CalledOnValidThread());
341 DCHECK(!is_connected());
342 int true_value = 1;
343 int rv = setsockopt(
344 socket_, SOL_SOCKET, SO_REUSEADDR, &true_value, sizeof(true_value));
345 return rv == 0 ? OK : MapSystemError(errno);
348 int UDPSocketLibevent::SetBroadcast(bool broadcast) {
349 DCHECK_NE(socket_, kInvalidSocket);
350 DCHECK(CalledOnValidThread());
351 int value = broadcast ? 1 : 0;
352 int rv;
353 #if defined(OS_MACOSX)
354 // SO_REUSEPORT on OSX permits multiple processes to each receive
355 // UDP multicast or broadcast datagrams destined for the bound
356 // port.
357 rv = setsockopt(socket_, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(value));
358 #else
359 rv = setsockopt(socket_, SOL_SOCKET, SO_BROADCAST, &value, sizeof(value));
360 #endif // defined(OS_MACOSX)
361 return rv == 0 ? OK : MapSystemError(errno);
364 void UDPSocketLibevent::ReadWatcher::OnFileCanReadWithoutBlocking(int) {
365 if (!socket_->read_callback_.is_null())
366 socket_->DidCompleteRead();
369 void UDPSocketLibevent::WriteWatcher::OnFileCanWriteWithoutBlocking(int) {
370 if (!socket_->write_callback_.is_null())
371 socket_->DidCompleteWrite();
374 void UDPSocketLibevent::DoReadCallback(int rv) {
375 DCHECK_NE(rv, ERR_IO_PENDING);
376 DCHECK(!read_callback_.is_null());
378 // since Run may result in Read being called, clear read_callback_ up front.
379 CompletionCallback c = read_callback_;
380 read_callback_.Reset();
381 c.Run(rv);
384 void UDPSocketLibevent::DoWriteCallback(int rv) {
385 DCHECK_NE(rv, ERR_IO_PENDING);
386 DCHECK(!write_callback_.is_null());
388 // since Run may result in Write being called, clear write_callback_ up front.
389 CompletionCallback c = write_callback_;
390 write_callback_.Reset();
391 c.Run(rv);
394 void UDPSocketLibevent::DidCompleteRead() {
395 int result =
396 InternalRecvFrom(read_buf_.get(), read_buf_len_, recv_from_address_);
397 if (result != ERR_IO_PENDING) {
398 read_buf_ = NULL;
399 read_buf_len_ = 0;
400 recv_from_address_ = NULL;
401 bool ok = read_socket_watcher_.StopWatchingFileDescriptor();
402 DCHECK(ok);
403 DoReadCallback(result);
407 void UDPSocketLibevent::LogRead(int result,
408 const char* bytes,
409 socklen_t addr_len,
410 const sockaddr* addr) const {
411 if (result < 0) {
412 net_log_.AddEventWithNetErrorCode(NetLog::TYPE_UDP_RECEIVE_ERROR, result);
413 return;
416 if (net_log_.IsLogging()) {
417 DCHECK(addr_len > 0);
418 DCHECK(addr);
420 IPEndPoint address;
421 bool is_address_valid = address.FromSockAddr(addr, addr_len);
422 net_log_.AddEvent(
423 NetLog::TYPE_UDP_BYTES_RECEIVED,
424 CreateNetLogUDPDataTranferCallback(
425 result, bytes,
426 is_address_valid ? &address : NULL));
429 NetworkActivityMonitor::GetInstance()->IncrementBytesReceived(result);
432 void UDPSocketLibevent::DidCompleteWrite() {
433 int result =
434 InternalSendTo(write_buf_.get(), write_buf_len_, send_to_address_.get());
436 if (result != ERR_IO_PENDING) {
437 write_buf_ = NULL;
438 write_buf_len_ = 0;
439 send_to_address_.reset();
440 write_socket_watcher_.StopWatchingFileDescriptor();
441 DoWriteCallback(result);
445 void UDPSocketLibevent::LogWrite(int result,
446 const char* bytes,
447 const IPEndPoint* address) const {
448 if (result < 0) {
449 net_log_.AddEventWithNetErrorCode(NetLog::TYPE_UDP_SEND_ERROR, result);
450 return;
453 if (net_log_.IsLogging()) {
454 net_log_.AddEvent(
455 NetLog::TYPE_UDP_BYTES_SENT,
456 CreateNetLogUDPDataTranferCallback(result, bytes, address));
459 NetworkActivityMonitor::GetInstance()->IncrementBytesSent(result);
462 int UDPSocketLibevent::InternalRecvFrom(IOBuffer* buf, int buf_len,
463 IPEndPoint* address) {
464 int bytes_transferred;
465 int flags = 0;
467 SockaddrStorage storage;
469 bytes_transferred =
470 HANDLE_EINTR(recvfrom(socket_,
471 buf->data(),
472 buf_len,
473 flags,
474 storage.addr,
475 &storage.addr_len));
476 int result;
477 if (bytes_transferred >= 0) {
478 result = bytes_transferred;
479 if (address && !address->FromSockAddr(storage.addr, storage.addr_len))
480 result = ERR_ADDRESS_INVALID;
481 } else {
482 result = MapSystemError(errno);
484 if (result != ERR_IO_PENDING)
485 LogRead(result, buf->data(), storage.addr_len, storage.addr);
486 return result;
489 int UDPSocketLibevent::InternalSendTo(IOBuffer* buf, int buf_len,
490 const IPEndPoint* address) {
491 SockaddrStorage storage;
492 struct sockaddr* addr = storage.addr;
493 if (!address) {
494 addr = NULL;
495 storage.addr_len = 0;
496 } else {
497 if (!address->ToSockAddr(storage.addr, &storage.addr_len)) {
498 int result = ERR_ADDRESS_INVALID;
499 LogWrite(result, NULL, NULL);
500 return result;
504 int result = HANDLE_EINTR(sendto(socket_,
505 buf->data(),
506 buf_len,
508 addr,
509 storage.addr_len));
510 if (result < 0)
511 result = MapSystemError(errno);
512 if (result != ERR_IO_PENDING)
513 LogWrite(result, buf->data(), address);
514 return result;
517 int UDPSocketLibevent::SetMulticastOptions() {
518 if (!(socket_options_ & SOCKET_OPTION_MULTICAST_LOOP)) {
519 int rv;
520 if (addr_family_ == AF_INET) {
521 u_char loop = 0;
522 rv = setsockopt(socket_, IPPROTO_IP, IP_MULTICAST_LOOP,
523 &loop, sizeof(loop));
524 } else {
525 u_int loop = 0;
526 rv = setsockopt(socket_, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
527 &loop, sizeof(loop));
529 if (rv < 0)
530 return MapSystemError(errno);
532 if (multicast_time_to_live_ != IP_DEFAULT_MULTICAST_TTL) {
533 int rv;
534 if (addr_family_ == AF_INET) {
535 u_char ttl = multicast_time_to_live_;
536 rv = setsockopt(socket_, IPPROTO_IP, IP_MULTICAST_TTL,
537 &ttl, sizeof(ttl));
538 } else {
539 // Signed integer. -1 to use route default.
540 int ttl = multicast_time_to_live_;
541 rv = setsockopt(socket_, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
542 &ttl, sizeof(ttl));
544 if (rv < 0)
545 return MapSystemError(errno);
547 if (multicast_interface_ != 0) {
548 switch (addr_family_) {
549 case AF_INET: {
550 #if !defined(OS_MACOSX)
551 ip_mreqn mreq;
552 mreq.imr_ifindex = multicast_interface_;
553 mreq.imr_address.s_addr = htonl(INADDR_ANY);
554 #else
555 ip_mreq mreq;
556 int error = GetIPv4AddressFromIndex(socket_, multicast_interface_,
557 &mreq.imr_interface.s_addr);
558 if (error != OK)
559 return error;
560 #endif
561 int rv = setsockopt(socket_, IPPROTO_IP, IP_MULTICAST_IF,
562 reinterpret_cast<const char*>(&mreq), sizeof(mreq));
563 if (rv)
564 return MapSystemError(errno);
565 break;
567 case AF_INET6: {
568 uint32 interface_index = multicast_interface_;
569 int rv = setsockopt(socket_, IPPROTO_IPV6, IPV6_MULTICAST_IF,
570 reinterpret_cast<const char*>(&interface_index),
571 sizeof(interface_index));
572 if (rv)
573 return MapSystemError(errno);
574 break;
576 default:
577 NOTREACHED() << "Invalid address family";
578 return ERR_ADDRESS_INVALID;
581 return OK;
584 int UDPSocketLibevent::DoBind(const IPEndPoint& address) {
585 SockaddrStorage storage;
586 if (!address.ToSockAddr(storage.addr, &storage.addr_len))
587 return ERR_ADDRESS_INVALID;
588 int rv = bind(socket_, storage.addr, storage.addr_len);
589 if (rv == 0)
590 return OK;
591 int last_error = errno;
592 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.UdpSocketBindErrorFromPosix", last_error);
593 #if defined(OS_CHROMEOS)
594 if (last_error == EINVAL)
595 return ERR_ADDRESS_IN_USE;
596 #elif defined(OS_MACOSX)
597 if (last_error == EADDRNOTAVAIL)
598 return ERR_ADDRESS_IN_USE;
599 #endif
600 return MapSystemError(last_error);
603 int UDPSocketLibevent::RandomBind(const IPAddressNumber& address) {
604 DCHECK(bind_type_ == DatagramSocket::RANDOM_BIND && !rand_int_cb_.is_null());
606 for (int i = 0; i < kBindRetries; ++i) {
607 int rv = DoBind(IPEndPoint(address,
608 rand_int_cb_.Run(kPortStart, kPortEnd)));
609 if (rv == OK || rv != ERR_ADDRESS_IN_USE)
610 return rv;
612 return DoBind(IPEndPoint(address, 0));
615 int UDPSocketLibevent::JoinGroup(const IPAddressNumber& group_address) const {
616 DCHECK(CalledOnValidThread());
617 if (!is_connected())
618 return ERR_SOCKET_NOT_CONNECTED;
620 switch (group_address.size()) {
621 case kIPv4AddressSize: {
622 if (addr_family_ != AF_INET)
623 return ERR_ADDRESS_INVALID;
625 #if !defined(OS_MACOSX)
626 ip_mreqn mreq;
627 mreq.imr_ifindex = multicast_interface_;
628 mreq.imr_address.s_addr = htonl(INADDR_ANY);
629 #else
630 ip_mreq mreq;
631 int error = GetIPv4AddressFromIndex(socket_, multicast_interface_,
632 &mreq.imr_interface.s_addr);
633 if (error != OK)
634 return error;
635 #endif
636 memcpy(&mreq.imr_multiaddr, &group_address[0], kIPv4AddressSize);
637 int rv = setsockopt(socket_, IPPROTO_IP, IP_ADD_MEMBERSHIP,
638 &mreq, sizeof(mreq));
639 if (rv < 0)
640 return MapSystemError(errno);
641 return OK;
643 case kIPv6AddressSize: {
644 if (addr_family_ != AF_INET6)
645 return ERR_ADDRESS_INVALID;
646 ipv6_mreq mreq;
647 mreq.ipv6mr_interface = multicast_interface_;
648 memcpy(&mreq.ipv6mr_multiaddr, &group_address[0], kIPv6AddressSize);
649 int rv = setsockopt(socket_, IPPROTO_IPV6, IPV6_JOIN_GROUP,
650 &mreq, sizeof(mreq));
651 if (rv < 0)
652 return MapSystemError(errno);
653 return OK;
655 default:
656 NOTREACHED() << "Invalid address family";
657 return ERR_ADDRESS_INVALID;
661 int UDPSocketLibevent::LeaveGroup(const IPAddressNumber& group_address) const {
662 DCHECK(CalledOnValidThread());
664 if (!is_connected())
665 return ERR_SOCKET_NOT_CONNECTED;
667 switch (group_address.size()) {
668 case kIPv4AddressSize: {
669 if (addr_family_ != AF_INET)
670 return ERR_ADDRESS_INVALID;
671 ip_mreq mreq;
672 mreq.imr_interface.s_addr = INADDR_ANY;
673 memcpy(&mreq.imr_multiaddr, &group_address[0], kIPv4AddressSize);
674 int rv = setsockopt(socket_, IPPROTO_IP, IP_DROP_MEMBERSHIP,
675 &mreq, sizeof(mreq));
676 if (rv < 0)
677 return MapSystemError(errno);
678 return OK;
680 case kIPv6AddressSize: {
681 if (addr_family_ != AF_INET6)
682 return ERR_ADDRESS_INVALID;
683 ipv6_mreq mreq;
684 mreq.ipv6mr_interface = 0; // 0 indicates default multicast interface.
685 memcpy(&mreq.ipv6mr_multiaddr, &group_address[0], kIPv6AddressSize);
686 int rv = setsockopt(socket_, IPPROTO_IPV6, IPV6_LEAVE_GROUP,
687 &mreq, sizeof(mreq));
688 if (rv < 0)
689 return MapSystemError(errno);
690 return OK;
692 default:
693 NOTREACHED() << "Invalid address family";
694 return ERR_ADDRESS_INVALID;
698 int UDPSocketLibevent::SetMulticastInterface(uint32 interface_index) {
699 DCHECK(CalledOnValidThread());
700 if (is_connected())
701 return ERR_SOCKET_IS_CONNECTED;
702 multicast_interface_ = interface_index;
703 return OK;
706 int UDPSocketLibevent::SetMulticastTimeToLive(int time_to_live) {
707 DCHECK(CalledOnValidThread());
708 if (is_connected())
709 return ERR_SOCKET_IS_CONNECTED;
711 if (time_to_live < 0 || time_to_live > 255)
712 return ERR_INVALID_ARGUMENT;
713 multicast_time_to_live_ = time_to_live;
714 return OK;
717 int UDPSocketLibevent::SetMulticastLoopbackMode(bool loopback) {
718 DCHECK(CalledOnValidThread());
719 if (is_connected())
720 return ERR_SOCKET_IS_CONNECTED;
722 if (loopback)
723 socket_options_ |= SOCKET_OPTION_MULTICAST_LOOP;
724 else
725 socket_options_ &= ~SOCKET_OPTION_MULTICAST_LOOP;
726 return OK;
729 int UDPSocketLibevent::SetDiffServCodePoint(DiffServCodePoint dscp) {
730 if (dscp == DSCP_NO_CHANGE) {
731 return OK;
733 int rv;
734 int dscp_and_ecn = dscp << 2;
735 if (addr_family_ == AF_INET) {
736 rv = setsockopt(socket_, IPPROTO_IP, IP_TOS,
737 &dscp_and_ecn, sizeof(dscp_and_ecn));
738 } else {
739 rv = setsockopt(socket_, IPPROTO_IPV6, IPV6_TCLASS,
740 &dscp_and_ecn, sizeof(dscp_and_ecn));
742 if (rv < 0)
743 return MapSystemError(errno);
745 return OK;
748 void UDPSocketLibevent::DetachFromThread() {
749 base::NonThreadSafe::DetachFromThread();
752 } // namespace net