1 // Copyright 2014 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/quic/quic_time_wait_list_manager.h"
9 #include "base/containers/hash_tables.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/stl_util.h"
12 #include "net/base/ip_endpoint.h"
13 #include "net/quic/crypto/crypto_protocol.h"
14 #include "net/quic/crypto/quic_decrypter.h"
15 #include "net/quic/crypto/quic_encrypter.h"
16 #include "net/quic/quic_clock.h"
17 #include "net/quic/quic_connection_helper.h"
18 #include "net/quic/quic_flags.h"
19 #include "net/quic/quic_framer.h"
20 #include "net/quic/quic_protocol.h"
21 #include "net/quic/quic_server_session.h"
22 #include "net/quic/quic_utils.h"
24 using base::StringPiece
;
28 // A very simple alarm that just informs the QuicTimeWaitListManager to clean
29 // up old connection_ids. This alarm should be unregistered and deleted before
30 // the QuicTimeWaitListManager is deleted.
31 class ConnectionIdCleanUpAlarm
: public QuicAlarm::Delegate
{
33 explicit ConnectionIdCleanUpAlarm(
34 QuicTimeWaitListManager
* time_wait_list_manager
)
35 : time_wait_list_manager_(time_wait_list_manager
) {
38 QuicTime
OnAlarm() override
{
39 time_wait_list_manager_
->CleanUpOldConnectionIds();
40 // Let the time wait manager register the alarm at appropriate time.
41 return QuicTime::Zero();
46 QuicTimeWaitListManager
* time_wait_list_manager_
;
49 // This class stores pending public reset packets to be sent to clients.
50 // server_address - server address on which a packet what was received for
51 // a connection_id in time wait state.
52 // client_address - address of the client that sent that packet. Needed to send
53 // the public reset packet back to the client.
54 // packet - the pending public reset packet that is to be sent to the client.
55 // created instance takes the ownership of this packet.
56 class QuicTimeWaitListManager::QueuedPacket
{
58 QueuedPacket(const IPEndPoint
& server_address
,
59 const IPEndPoint
& client_address
,
60 QuicEncryptedPacket
* packet
)
61 : server_address_(server_address
),
62 client_address_(client_address
),
66 const IPEndPoint
& server_address() const { return server_address_
; }
67 const IPEndPoint
& client_address() const { return client_address_
; }
68 QuicEncryptedPacket
* packet() { return packet_
.get(); }
71 const IPEndPoint server_address_
;
72 const IPEndPoint client_address_
;
73 scoped_ptr
<QuicEncryptedPacket
> packet_
;
75 DISALLOW_COPY_AND_ASSIGN(QueuedPacket
);
78 QuicTimeWaitListManager::QuicTimeWaitListManager(
79 QuicPacketWriter
* writer
,
80 QuicServerSessionVisitor
* visitor
,
81 QuicConnectionHelperInterface
* helper
,
82 const QuicVersionVector
& supported_versions
)
85 QuicTime::Delta::FromSeconds(FLAGS_quic_time_wait_list_seconds
)),
86 connection_id_clean_up_alarm_(
87 helper_
->CreateAlarm(new ConnectionIdCleanUpAlarm(this))),
90 SetConnectionIdCleanUpAlarm();
93 QuicTimeWaitListManager::~QuicTimeWaitListManager() {
94 connection_id_clean_up_alarm_
->Cancel();
95 STLDeleteElements(&pending_packets_queue_
);
96 for (ConnectionIdMap::iterator it
= connection_id_map_
.begin();
97 it
!= connection_id_map_
.end();
99 delete it
->second
.close_packet
;
103 void QuicTimeWaitListManager::AddConnectionIdToTimeWait(
104 QuicConnectionId connection_id
,
106 QuicEncryptedPacket
* close_packet
) {
108 ConnectionIdMap::iterator it
= connection_id_map_
.find(connection_id
);
109 const bool new_connection_id
= it
== connection_id_map_
.end();
110 if (!new_connection_id
) { // Replace record if it is reinserted.
111 num_packets
= it
->second
.num_packets
;
112 delete it
->second
.close_packet
;
113 connection_id_map_
.erase(it
);
115 TrimTimeWaitListIfNeeded();
116 if (FLAGS_quic_limit_time_wait_list_size
) {
117 DCHECK_LT(num_connections(),
118 static_cast<size_t>(FLAGS_quic_time_wait_list_max_connections
));
120 ConnectionIdData
data(num_packets
,
122 helper_
->GetClock()->ApproximateNow(),
124 connection_id_map_
.insert(std::make_pair(connection_id
, data
));
125 if (new_connection_id
) {
126 visitor_
->OnConnectionAddedToTimeWaitList(connection_id
);
130 bool QuicTimeWaitListManager::IsConnectionIdInTimeWait(
131 QuicConnectionId connection_id
) const {
132 return ContainsKey(connection_id_map_
, connection_id
);
135 QuicVersion
QuicTimeWaitListManager::GetQuicVersionFromConnectionId(
136 QuicConnectionId connection_id
) {
137 ConnectionIdMap::iterator it
= connection_id_map_
.find(connection_id
);
138 DCHECK(it
!= connection_id_map_
.end());
139 return (it
->second
).version
;
142 void QuicTimeWaitListManager::OnCanWrite() {
143 while (!pending_packets_queue_
.empty()) {
144 QueuedPacket
* queued_packet
= pending_packets_queue_
.front();
145 if (!WriteToWire(queued_packet
)) {
148 pending_packets_queue_
.pop_front();
149 delete queued_packet
;
153 void QuicTimeWaitListManager::ProcessPacket(
154 const IPEndPoint
& server_address
,
155 const IPEndPoint
& client_address
,
156 QuicConnectionId connection_id
,
157 QuicPacketSequenceNumber sequence_number
,
158 const QuicEncryptedPacket
& /*packet*/) {
159 DCHECK(IsConnectionIdInTimeWait(connection_id
));
160 DVLOG(1) << "Processing " << connection_id
<< " in time wait state.";
161 // TODO(satyamshekhar): Think about handling packets from different client
163 ConnectionIdMap::iterator it
= connection_id_map_
.find(connection_id
);
164 DCHECK(it
!= connection_id_map_
.end());
165 // Increment the received packet count.
166 ++((it
->second
).num_packets
);
167 if (!ShouldSendResponse((it
->second
).num_packets
)) {
170 if (it
->second
.close_packet
) {
171 QueuedPacket
* queued_packet
=
172 new QueuedPacket(server_address
,
174 it
->second
.close_packet
->Clone());
175 // Takes ownership of the packet.
176 SendOrQueuePacket(queued_packet
);
178 SendPublicReset(server_address
,
185 // Returns true if the number of packets received for this connection_id is a
186 // power of 2 to throttle the number of public reset packets we send to a
188 bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count
) {
189 return (received_packet_count
& (received_packet_count
- 1)) == 0;
192 void QuicTimeWaitListManager::SendPublicReset(
193 const IPEndPoint
& server_address
,
194 const IPEndPoint
& client_address
,
195 QuicConnectionId connection_id
,
196 QuicPacketSequenceNumber rejected_sequence_number
) {
197 QuicPublicResetPacket packet
;
198 packet
.public_header
.connection_id
= connection_id
;
199 packet
.public_header
.reset_flag
= true;
200 packet
.public_header
.version_flag
= false;
201 packet
.rejected_sequence_number
= rejected_sequence_number
;
202 // TODO(satyamshekhar): generate a valid nonce for this connection_id.
203 packet
.nonce_proof
= 1010101;
204 packet
.client_address
= client_address
;
205 QueuedPacket
* queued_packet
= new QueuedPacket(
208 BuildPublicReset(packet
));
209 // Takes ownership of the packet.
210 SendOrQueuePacket(queued_packet
);
213 QuicEncryptedPacket
* QuicTimeWaitListManager::BuildPublicReset(
214 const QuicPublicResetPacket
& packet
) {
215 return QuicFramer::BuildPublicResetPacket(packet
);
218 // Either sends the packet and deletes it or makes pending queue the
219 // owner of the packet.
220 void QuicTimeWaitListManager::SendOrQueuePacket(QueuedPacket
* packet
) {
221 if (WriteToWire(packet
)) {
224 // pending_packets_queue takes the ownership of the queued packet.
225 pending_packets_queue_
.push_back(packet
);
229 bool QuicTimeWaitListManager::WriteToWire(QueuedPacket
* queued_packet
) {
230 if (writer_
->IsWriteBlocked()) {
231 visitor_
->OnWriteBlocked(this);
234 WriteResult result
= writer_
->WritePacket(
235 queued_packet
->packet()->data(),
236 queued_packet
->packet()->length(),
237 queued_packet
->server_address().address(),
238 queued_packet
->client_address());
239 if (result
.status
== WRITE_STATUS_BLOCKED
) {
240 // If blocked and unbuffered, return false to retry sending.
241 DCHECK(writer_
->IsWriteBlocked());
242 visitor_
->OnWriteBlocked(this);
243 return writer_
->IsWriteBlockedDataBuffered();
244 } else if (result
.status
== WRITE_STATUS_ERROR
) {
245 LOG(WARNING
) << "Received unknown error while sending reset packet to "
246 << queued_packet
->client_address().ToString() << ": "
247 << strerror(result
.error_code
);
252 void QuicTimeWaitListManager::SetConnectionIdCleanUpAlarm() {
253 connection_id_clean_up_alarm_
->Cancel();
254 QuicTime now
= helper_
->GetClock()->ApproximateNow();
255 QuicTime next_alarm_time
= now
;
256 if (!connection_id_map_
.empty()) {
257 QuicTime oldest_connection_id
=
258 connection_id_map_
.begin()->second
.time_added
;
259 if (now
.Subtract(oldest_connection_id
) < kTimeWaitPeriod_
) {
260 next_alarm_time
= oldest_connection_id
.Add(kTimeWaitPeriod_
);
262 LOG(ERROR
) << "ConnectionId lingered for longer than kTimeWaitPeriod";
265 // No connection_ids added so none will expire before kTimeWaitPeriod_.
266 next_alarm_time
= now
.Add(kTimeWaitPeriod_
);
269 connection_id_clean_up_alarm_
->Set(next_alarm_time
);
272 bool QuicTimeWaitListManager::MaybeExpireOldestConnection(
273 QuicTime expiration_time
) {
274 if (connection_id_map_
.empty()) {
277 ConnectionIdMap::iterator it
= connection_id_map_
.begin();
278 QuicTime oldest_connection_id_time
= it
->second
.time_added
;
279 if (oldest_connection_id_time
> expiration_time
) {
280 // Too recent, don't retire.
283 // This connection_id has lived its age, retire it now.
284 const QuicConnectionId connection_id
= it
->first
;
285 delete it
->second
.close_packet
;
286 connection_id_map_
.erase(it
);
287 visitor_
->OnConnectionRemovedFromTimeWaitList(connection_id
);
291 void QuicTimeWaitListManager::CleanUpOldConnectionIds() {
292 QuicTime now
= helper_
->GetClock()->ApproximateNow();
293 QuicTime expiration
= now
.Subtract(kTimeWaitPeriod_
);
294 if (FLAGS_quic_limit_time_wait_list_size
) {
295 while (MaybeExpireOldestConnection(expiration
)) {
298 while (!connection_id_map_
.empty()) {
299 ConnectionIdMap::iterator it
= connection_id_map_
.begin();
300 QuicTime oldest_connection_id
= it
->second
.time_added
;
301 if (now
.Subtract(oldest_connection_id
) < kTimeWaitPeriod_
) {
304 const QuicConnectionId connection_id
= it
->first
;
305 // This connection_id has lived its age, retire it now.
306 delete it
->second
.close_packet
;
307 connection_id_map_
.erase(it
);
308 visitor_
->OnConnectionRemovedFromTimeWaitList(connection_id
);
312 SetConnectionIdCleanUpAlarm();
315 void QuicTimeWaitListManager::TrimTimeWaitListIfNeeded() {
316 if (FLAGS_quic_limit_time_wait_list_size
) {
317 if (FLAGS_quic_time_wait_list_max_connections
< 0) {
320 while (num_connections() >=
321 static_cast<size_t>(FLAGS_quic_time_wait_list_max_connections
)) {
322 MaybeExpireOldestConnection(QuicTime::Infinite());