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 "content/browser/renderer_host/p2p/socket_host_udp.h"
8 #include "base/metrics/field_trial.h"
9 #include "base/metrics/histogram.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/trace_event/trace_event.h"
13 #include "content/browser/renderer_host/p2p/socket_host_throttler.h"
14 #include "content/common/p2p_messages.h"
15 #include "content/public/browser/content_browser_client.h"
16 #include "content/public/common/content_client.h"
17 #include "ipc/ipc_sender.h"
18 #include "net/base/io_buffer.h"
19 #include "net/base/net_errors.h"
20 #include "net/base/net_util.h"
21 #include "third_party/webrtc/base/asyncpacketsocket.h"
25 // UDP packets cannot be bigger than 64k.
26 const int kReadBufferSize
= 65536;
27 // Socket receive buffer size.
28 const int kRecvSocketBufferSize
= 65536; // 64K
30 // Defines set of transient errors. These errors are ignored when we get them
31 // from sendto() or recvfrom() calls.
33 // net::ERR_OUT_OF_MEMORY
35 // This is caused by ENOBUFS which means the buffer of the network interface
38 // net::ERR_CONNECTION_RESET
40 // This is caused by WSAENETRESET or WSAECONNRESET which means the
41 // last send resulted in an "ICMP Port Unreachable" message.
45 } static const kTransientErrors
[] {
46 {net::ERR_ADDRESS_UNREACHABLE
, "net::ERR_ADDRESS_UNREACHABLE"},
47 {net::ERR_ADDRESS_INVALID
, "net::ERR_ADDRESS_INVALID"},
48 {net::ERR_ACCESS_DENIED
, "net::ERR_ACCESS_DENIED"},
49 {net::ERR_CONNECTION_RESET
, "net::ERR_CONNECTION_RESET"},
50 {net::ERR_OUT_OF_MEMORY
, "net::ERR_OUT_OF_MEMORY"},
51 {net::ERR_INTERNET_DISCONNECTED
, "net::ERR_INTERNET_DISCONNECTED"}
54 bool IsTransientError(int error
) {
55 for (const auto& transient_error
: kTransientErrors
) {
56 if (transient_error
.code
== error
)
62 const char* GetTransientErrorName(int error
) {
63 for (const auto& transient_error
: kTransientErrors
) {
64 if (transient_error
.code
== error
)
65 return transient_error
.name
;
74 P2PSocketHostUdp::PendingPacket::PendingPacket(
75 const net::IPEndPoint
& to
,
76 const std::vector
<char>& content
,
77 const rtc::PacketOptions
& options
,
80 data(new net::IOBuffer(content
.size())),
82 packet_options(options
),
84 memcpy(data
->data(), &content
[0], size
);
87 P2PSocketHostUdp::PendingPacket::~PendingPacket() {
90 P2PSocketHostUdp::P2PSocketHostUdp(IPC::Sender
* message_sender
,
92 P2PMessageThrottler
* throttler
)
93 : P2PSocketHost(message_sender
, socket_id
, P2PSocketHost::UDP
),
95 last_dscp_(net::DSCP_CS0
),
96 throttler_(throttler
),
97 send_buffer_size_(0) {
98 net::UDPServerSocket
* socket
= new net::UDPServerSocket(
99 GetContentClient()->browser()->GetNetLog(), net::NetLog::Source());
101 // If configured for finch experiment, use nonblocking IO.
102 if (base::FieldTrialList::FindFullName("WebRTC-UDPSocketNonBlockingIO") ==
104 socket
->UseNonBlockingIO();
107 socket_
.reset(socket
);
110 P2PSocketHostUdp::~P2PSocketHostUdp() {
111 if (state_
== STATE_OPEN
) {
112 DCHECK(socket_
.get());
117 void P2PSocketHostUdp::SetSendBufferSize() {
118 unsigned int send_buffer_size
= 0;
121 base::FieldTrialList::FindFullName("WebRTC-SystemUDPSendSocketSize"),
124 if (send_buffer_size
> 0) {
125 if (!SetOption(P2P_SOCKET_OPT_SNDBUF
, send_buffer_size
)) {
126 LOG(WARNING
) << "Failed to set socket send buffer size to "
129 send_buffer_size_
= send_buffer_size
;
134 bool P2PSocketHostUdp::Init(const net::IPEndPoint
& local_address
,
135 const P2PHostAndIPEndPoint
& remote_address
) {
136 DCHECK_EQ(state_
, STATE_UNINITIALIZED
);
138 int result
= socket_
->Listen(local_address
);
140 LOG(ERROR
) << "bind() failed: " << result
;
145 // Setting recv socket buffer size.
146 if (socket_
->SetReceiveBufferSize(kRecvSocketBufferSize
) != net::OK
) {
147 LOG(WARNING
) << "Failed to set socket receive buffer size to "
148 << kRecvSocketBufferSize
;
151 net::IPEndPoint address
;
152 result
= socket_
->GetLocalAddress(&address
);
154 LOG(ERROR
) << "P2PSocketHostUdp::Init(): unable to get local address: "
159 VLOG(1) << "Local address: " << address
.ToString();
165 // NOTE: Remote address will be same as what renderer provided.
166 message_sender_
->Send(new P2PMsg_OnSocketCreated(
167 id_
, address
, remote_address
.ip_address
));
169 recv_buffer_
= new net::IOBuffer(kReadBufferSize
);
175 void P2PSocketHostUdp::OnError() {
179 if (state_
== STATE_UNINITIALIZED
|| state_
== STATE_OPEN
)
180 message_sender_
->Send(new P2PMsg_OnError(id_
));
182 state_
= STATE_ERROR
;
185 void P2PSocketHostUdp::DoRead() {
188 result
= socket_
->RecvFrom(
192 base::Bind(&P2PSocketHostUdp::OnRecv
, base::Unretained(this)));
193 if (result
== net::ERR_IO_PENDING
)
195 HandleReadResult(result
);
196 } while (state_
== STATE_OPEN
);
199 void P2PSocketHostUdp::OnRecv(int result
) {
200 HandleReadResult(result
);
201 if (state_
== STATE_OPEN
) {
206 void P2PSocketHostUdp::HandleReadResult(int result
) {
207 DCHECK_EQ(STATE_OPEN
, state_
);
210 std::vector
<char> data(recv_buffer_
->data(), recv_buffer_
->data() + result
);
212 if (!ContainsKey(connected_peers_
, recv_address_
)) {
213 P2PSocketHost::StunMessageType type
;
214 bool stun
= GetStunPacketType(&*data
.begin(), data
.size(), &type
);
215 if ((stun
&& IsRequestOrResponse(type
))) {
216 connected_peers_
.insert(recv_address_
);
217 } else if (!stun
|| type
== STUN_DATA_INDICATION
) {
218 LOG(ERROR
) << "Received unexpected data packet from "
219 << recv_address_
.ToString()
220 << " before STUN binding is finished.";
225 message_sender_
->Send(new P2PMsg_OnDataReceived(
226 id_
, recv_address_
, data
, base::TimeTicks::Now()));
228 if (dump_incoming_rtp_packet_
)
229 DumpRtpPacket(&data
[0], data
.size(), true);
230 } else if (result
< 0 && !IsTransientError(result
)) {
231 LOG(ERROR
) << "Error when reading from UDP socket: " << result
;
236 void P2PSocketHostUdp::Send(const net::IPEndPoint
& to
,
237 const std::vector
<char>& data
,
238 const rtc::PacketOptions
& options
,
241 // The Send message may be sent after the an OnError message was
242 // sent by hasn't been processed the renderer.
246 if (!ContainsKey(connected_peers_
, to
)) {
247 P2PSocketHost::StunMessageType type
= P2PSocketHost::StunMessageType();
248 bool stun
= GetStunPacketType(&*data
.begin(), data
.size(), &type
);
249 if (!stun
|| type
== STUN_DATA_INDICATION
) {
250 LOG(ERROR
) << "Page tried to send a data packet to " << to
.ToString()
251 << " before STUN binding is finished.";
256 if (throttler_
->DropNextPacket(data
.size())) {
257 VLOG(0) << "STUN message is dropped due to high volume.";
258 // Do not reset socket.
263 IncrementTotalSentPackets();
266 send_queue_
.push_back(PendingPacket(to
, data
, options
, packet_id
));
267 IncrementDelayedBytes(data
.size());
268 IncrementDelayedPackets();
270 // TODO(mallinath: Remove unnecessary memcpy in this case.
271 PendingPacket
packet(to
, data
, options
, packet_id
);
276 void P2PSocketHostUdp::DoSend(const PendingPacket
& packet
) {
277 TRACE_EVENT_ASYNC_STEP_INTO1("p2p", "Send", packet
.id
, "UdpAsyncSendTo",
278 "size", packet
.size
);
279 // Don't try to set DSCP in following conditions,
280 // 1. If the outgoing packet is set to DSCP_NO_CHANGE
281 // 2. If no change in DSCP value from last packet
282 // 3. If there is any error in setting DSCP on socket.
283 net::DiffServCodePoint dscp
=
284 static_cast<net::DiffServCodePoint
>(packet
.packet_options
.dscp
);
285 if (dscp
!= net::DSCP_NO_CHANGE
&& last_dscp_
!= dscp
&&
286 last_dscp_
!= net::DSCP_NO_CHANGE
) {
287 int result
= socket_
->SetDiffServCodePoint(dscp
);
288 if (result
== net::OK
) {
290 } else if (!IsTransientError(result
) && last_dscp_
!= net::DSCP_CS0
) {
291 // We receieved a non-transient error, and it seems we have
292 // not changed the DSCP in the past, disable DSCP as it unlikely
293 // to work in the future.
294 last_dscp_
= net::DSCP_NO_CHANGE
;
298 uint64 tick_received
= base::TimeTicks::Now().ToInternalValue();
300 packet_processing_helpers::ApplyPacketOptions(
301 packet
.data
->data(), packet
.size
, packet
.packet_options
, 0);
302 int result
= socket_
->SendTo(packet
.data
.get(),
305 base::Bind(&P2PSocketHostUdp::OnSend
,
306 base::Unretained(this),
310 // sendto() may return an error, e.g. if we've received an ICMP Destination
311 // Unreachable message. When this happens try sending the same packet again,
312 // and just drop it if it fails again.
313 if (IsTransientError(result
)) {
314 result
= socket_
->SendTo(packet
.data
.get(),
317 base::Bind(&P2PSocketHostUdp::OnSend
,
318 base::Unretained(this),
323 if (result
== net::ERR_IO_PENDING
) {
324 send_pending_
= true;
326 HandleSendResult(packet
.id
, tick_received
, result
);
329 if (dump_outgoing_rtp_packet_
)
330 DumpRtpPacket(packet
.data
->data(), packet
.size
, false);
333 void P2PSocketHostUdp::OnSend(uint64 packet_id
,
334 uint64 tick_received
,
336 DCHECK(send_pending_
);
337 DCHECK_NE(result
, net::ERR_IO_PENDING
);
339 send_pending_
= false;
341 HandleSendResult(packet_id
, tick_received
, result
);
343 // Send next packets if we have them waiting in the buffer.
344 while (state_
== STATE_OPEN
&& !send_queue_
.empty() && !send_pending_
) {
345 PendingPacket packet
= send_queue_
.front();
347 send_queue_
.pop_front();
348 DecrementDelayedBytes(packet
.size
);
352 void P2PSocketHostUdp::HandleSendResult(uint64 packet_id
,
353 uint64 tick_received
,
355 TRACE_EVENT_ASYNC_END1("p2p", "Send", packet_id
,
358 if (!IsTransientError(result
)) {
359 LOG(ERROR
) << "Error when sending data in UDP socket: " << result
;
363 VLOG(0) << "sendto() has failed twice returning a "
364 " transient error " << GetTransientErrorName(result
)
365 << ". Dropping the packet.";
368 // UMA to track the histograms from 1ms to 1 sec for how long a packet spends
369 // in the browser process.
371 "WebRTC.SystemSendPacketDuration_UDP" /* name */,
372 base::TimeTicks::Now() -
373 base::TimeTicks::FromInternalValue(tick_received
) /* sample */);
375 message_sender_
->Send(
376 new P2PMsg_OnSendComplete(id_
, P2PSendPacketMetrics(packet_id
)));
379 P2PSocketHost
* P2PSocketHostUdp::AcceptIncomingTcpConnection(
380 const net::IPEndPoint
& remote_address
, int id
) {
386 bool P2PSocketHostUdp::SetOption(P2PSocketOption option
, int value
) {
387 DCHECK_EQ(STATE_OPEN
, state_
);
389 case P2P_SOCKET_OPT_RCVBUF
:
390 return socket_
->SetReceiveBufferSize(value
) == net::OK
;
391 case P2P_SOCKET_OPT_SNDBUF
:
392 // Ignore any following call to set the send buffer size if we're under
394 if (send_buffer_size_
> 0) {
397 return socket_
->SetSendBufferSize(value
) == net::OK
;
398 case P2P_SOCKET_OPT_DSCP
:
399 return (net::OK
== socket_
->SetDiffServCodePoint(
400 static_cast<net::DiffServCodePoint
>(value
))) ? true : false;
407 } // namespace content