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"
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"
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
){
44 *address
= htonl(INADDR_ANY
);
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
);
53 return MapSystemError(errno
);
54 *address
= reinterpret_cast<sockaddr_in
*>(&ifr
.ifr_addr
)->sin_addr
.s_addr
;
62 UDPSocketLibevent::UDPSocketLibevent(
63 DatagramSocket::BindType bind_type
,
64 const RandIntCallback
& rand_int_cb
,
66 const net::NetLog::Source
& source
)
67 : socket_(kInvalidSocket
),
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
),
78 recv_from_address_(NULL
),
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() {
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
);
108 void UDPSocketLibevent::Close() {
109 DCHECK(CalledOnValidThread());
111 if (socket_
== kInvalidSocket
)
114 // Zero out any pending read/write callback state.
117 read_callback_
.Reset();
118 recv_from_address_
= NULL
;
121 write_callback_
.Reset();
122 send_to_address_
.reset();
124 bool ok
= read_socket_watcher_
.StopWatchingFileDescriptor();
126 ok
= write_socket_watcher_
.StopWatchingFileDescriptor();
129 PCHECK(0 == IGNORE_EINTR(close(socket_
)));
131 socket_
= kInvalidSocket
;
133 is_connected_
= false;
136 int UDPSocketLibevent::GetPeerAddress(IPEndPoint
* address
) const {
137 DCHECK(CalledOnValidThread());
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_
;
156 int UDPSocketLibevent::GetLocalAddress(IPEndPoint
* address
) const {
157 DCHECK(CalledOnValidThread());
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_
;
178 int UDPSocketLibevent::Read(IOBuffer
* buf
,
180 const CompletionCallback
& callback
) {
181 return RecvFrom(buf
, buf_len
, NULL
, callback
);
184 int UDPSocketLibevent::RecvFrom(IOBuffer
* buf
,
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
)
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
);
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
,
217 const CompletionCallback
& callback
) {
218 return SendToOrWrite(buf
, buf_len
, NULL
, callback
);
221 int UDPSocketLibevent::SendTo(IOBuffer
* buf
,
223 const IPEndPoint
& address
,
224 const CompletionCallback
& callback
) {
225 return SendToOrWrite(buf
, buf_len
, &address
, callback
);
228 int UDPSocketLibevent::SendToOrWrite(IOBuffer
* buf
,
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
)
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
);
252 write_buf_len_
= buf_len
;
253 DCHECK(!send_to_address_
.get());
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
);
271 int UDPSocketLibevent::InternalConnect(const IPEndPoint
& address
) {
272 DCHECK(CalledOnValidThread());
273 DCHECK(!is_connected());
274 DCHECK(!remote_address_
.get());
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
288 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.UdpSocketRandomBindErrorCode", -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
));
298 return MapSystemError(errno
);
300 remote_address_
.reset(new IPEndPoint(address
));
304 int UDPSocketLibevent::Bind(const IPEndPoint
& address
) {
305 DCHECK_NE(socket_
, kInvalidSocket
);
306 DCHECK(CalledOnValidThread());
307 DCHECK(!is_connected());
309 int rv
= SetMulticastOptions();
313 rv
= DoBind(address
);
317 is_connected_
= true;
318 local_address_
.reset();
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());
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;
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
357 rv
= setsockopt(socket_
, SOL_SOCKET
, SO_REUSEPORT
, &value
, sizeof(value
));
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();
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();
394 void UDPSocketLibevent::DidCompleteRead() {
396 InternalRecvFrom(read_buf_
.get(), read_buf_len_
, recv_from_address_
);
397 if (result
!= ERR_IO_PENDING
) {
400 recv_from_address_
= NULL
;
401 bool ok
= read_socket_watcher_
.StopWatchingFileDescriptor();
403 DoReadCallback(result
);
407 void UDPSocketLibevent::LogRead(int result
,
410 const sockaddr
* addr
) const {
412 net_log_
.AddEventWithNetErrorCode(NetLog::TYPE_UDP_RECEIVE_ERROR
, result
);
416 if (net_log_
.IsLogging()) {
417 DCHECK(addr_len
> 0);
421 bool is_address_valid
= address
.FromSockAddr(addr
, addr_len
);
423 NetLog::TYPE_UDP_BYTES_RECEIVED
,
424 CreateNetLogUDPDataTranferCallback(
426 is_address_valid
? &address
: NULL
));
429 NetworkActivityMonitor::GetInstance()->IncrementBytesReceived(result
);
432 void UDPSocketLibevent::DidCompleteWrite() {
434 InternalSendTo(write_buf_
.get(), write_buf_len_
, send_to_address_
.get());
436 if (result
!= ERR_IO_PENDING
) {
439 send_to_address_
.reset();
440 write_socket_watcher_
.StopWatchingFileDescriptor();
441 DoWriteCallback(result
);
445 void UDPSocketLibevent::LogWrite(int result
,
447 const IPEndPoint
* address
) const {
449 net_log_
.AddEventWithNetErrorCode(NetLog::TYPE_UDP_SEND_ERROR
, result
);
453 if (net_log_
.IsLogging()) {
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
;
467 SockaddrStorage storage
;
470 HANDLE_EINTR(recvfrom(socket_
,
477 if (bytes_transferred
>= 0) {
478 result
= bytes_transferred
;
479 if (address
&& !address
->FromSockAddr(storage
.addr
, storage
.addr_len
))
480 result
= ERR_ADDRESS_INVALID
;
482 result
= MapSystemError(errno
);
484 if (result
!= ERR_IO_PENDING
)
485 LogRead(result
, buf
->data(), storage
.addr_len
, storage
.addr
);
489 int UDPSocketLibevent::InternalSendTo(IOBuffer
* buf
, int buf_len
,
490 const IPEndPoint
* address
) {
491 SockaddrStorage storage
;
492 struct sockaddr
* addr
= storage
.addr
;
495 storage
.addr_len
= 0;
497 if (!address
->ToSockAddr(storage
.addr
, &storage
.addr_len
)) {
498 int result
= ERR_ADDRESS_INVALID
;
499 LogWrite(result
, NULL
, NULL
);
504 int result
= HANDLE_EINTR(sendto(socket_
,
511 result
= MapSystemError(errno
);
512 if (result
!= ERR_IO_PENDING
)
513 LogWrite(result
, buf
->data(), address
);
517 int UDPSocketLibevent::SetMulticastOptions() {
518 if (!(socket_options_
& SOCKET_OPTION_MULTICAST_LOOP
)) {
520 if (addr_family_
== AF_INET
) {
522 rv
= setsockopt(socket_
, IPPROTO_IP
, IP_MULTICAST_LOOP
,
523 &loop
, sizeof(loop
));
526 rv
= setsockopt(socket_
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
,
527 &loop
, sizeof(loop
));
530 return MapSystemError(errno
);
532 if (multicast_time_to_live_
!= IP_DEFAULT_MULTICAST_TTL
) {
534 if (addr_family_
== AF_INET
) {
535 u_char ttl
= multicast_time_to_live_
;
536 rv
= setsockopt(socket_
, IPPROTO_IP
, IP_MULTICAST_TTL
,
539 // Signed integer. -1 to use route default.
540 int ttl
= multicast_time_to_live_
;
541 rv
= setsockopt(socket_
, IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
,
545 return MapSystemError(errno
);
547 if (multicast_interface_
!= 0) {
548 switch (addr_family_
) {
550 #if !defined(OS_MACOSX)
552 mreq
.imr_ifindex
= multicast_interface_
;
553 mreq
.imr_address
.s_addr
= htonl(INADDR_ANY
);
556 int error
= GetIPv4AddressFromIndex(socket_
, multicast_interface_
,
557 &mreq
.imr_interface
.s_addr
);
561 int rv
= setsockopt(socket_
, IPPROTO_IP
, IP_MULTICAST_IF
,
562 reinterpret_cast<const char*>(&mreq
), sizeof(mreq
));
564 return MapSystemError(errno
);
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
));
573 return MapSystemError(errno
);
577 NOTREACHED() << "Invalid address family";
578 return ERR_ADDRESS_INVALID
;
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
);
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
;
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
)
612 return DoBind(IPEndPoint(address
, 0));
615 int UDPSocketLibevent::JoinGroup(const IPAddressNumber
& group_address
) const {
616 DCHECK(CalledOnValidThread());
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)
627 mreq
.imr_ifindex
= multicast_interface_
;
628 mreq
.imr_address
.s_addr
= htonl(INADDR_ANY
);
631 int error
= GetIPv4AddressFromIndex(socket_
, multicast_interface_
,
632 &mreq
.imr_interface
.s_addr
);
636 memcpy(&mreq
.imr_multiaddr
, &group_address
[0], kIPv4AddressSize
);
637 int rv
= setsockopt(socket_
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
638 &mreq
, sizeof(mreq
));
640 return MapSystemError(errno
);
643 case kIPv6AddressSize
: {
644 if (addr_family_
!= AF_INET6
)
645 return ERR_ADDRESS_INVALID
;
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
));
652 return MapSystemError(errno
);
656 NOTREACHED() << "Invalid address family";
657 return ERR_ADDRESS_INVALID
;
661 int UDPSocketLibevent::LeaveGroup(const IPAddressNumber
& group_address
) const {
662 DCHECK(CalledOnValidThread());
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
;
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
));
677 return MapSystemError(errno
);
680 case kIPv6AddressSize
: {
681 if (addr_family_
!= AF_INET6
)
682 return ERR_ADDRESS_INVALID
;
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
));
689 return MapSystemError(errno
);
693 NOTREACHED() << "Invalid address family";
694 return ERR_ADDRESS_INVALID
;
698 int UDPSocketLibevent::SetMulticastInterface(uint32 interface_index
) {
699 DCHECK(CalledOnValidThread());
701 return ERR_SOCKET_IS_CONNECTED
;
702 multicast_interface_
= interface_index
;
706 int UDPSocketLibevent::SetMulticastTimeToLive(int time_to_live
) {
707 DCHECK(CalledOnValidThread());
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
;
717 int UDPSocketLibevent::SetMulticastLoopbackMode(bool loopback
) {
718 DCHECK(CalledOnValidThread());
720 return ERR_SOCKET_IS_CONNECTED
;
723 socket_options_
|= SOCKET_OPTION_MULTICAST_LOOP
;
725 socket_options_
&= ~SOCKET_OPTION_MULTICAST_LOOP
;
729 int UDPSocketLibevent::SetDiffServCodePoint(DiffServCodePoint dscp
) {
730 if (dscp
== DSCP_NO_CHANGE
) {
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
));
739 rv
= setsockopt(socket_
, IPPROTO_IPV6
, IPV6_TCLASS
,
740 &dscp_and_ecn
, sizeof(dscp_and_ecn
));
743 return MapSystemError(errno
);
748 void UDPSocketLibevent::DetachFromThread() {
749 base::NonThreadSafe::DetachFromThread();