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/tools/quic/quic_time_wait_list_manager.h"
9 #include "base/logging.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_flags.h"
18 #include "net/quic/quic_framer.h"
19 #include "net/quic/quic_protocol.h"
20 #include "net/quic/quic_utils.h"
21 #include "net/tools/quic/quic_server_session.h"
23 using base::StringPiece
;
28 // A very simple alarm that just informs the QuicTimeWaitListManager to clean
29 // up old connection_ids. This alarm should be cancelled 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_
;
48 DISALLOW_COPY_AND_ASSIGN(ConnectionIdCleanUpAlarm
);
51 // This class stores pending public reset packets to be sent to clients.
52 // server_address - server address on which a packet what was received for
53 // a connection_id in time wait state.
54 // client_address - address of the client that sent that packet. Needed to send
55 // the public reset packet back to the client.
56 // packet - the pending public reset packet that is to be sent to the client.
57 // created instance takes the ownership of this packet.
58 class QuicTimeWaitListManager::QueuedPacket
{
60 QueuedPacket(const IPEndPoint
& server_address
,
61 const IPEndPoint
& client_address
,
62 QuicEncryptedPacket
* packet
)
63 : server_address_(server_address
),
64 client_address_(client_address
),
68 const IPEndPoint
& server_address() const { return server_address_
; }
69 const IPEndPoint
& client_address() const { return client_address_
; }
70 QuicEncryptedPacket
* packet() { return packet_
.get(); }
73 const IPEndPoint server_address_
;
74 const IPEndPoint client_address_
;
75 scoped_ptr
<QuicEncryptedPacket
> packet_
;
77 DISALLOW_COPY_AND_ASSIGN(QueuedPacket
);
80 QuicTimeWaitListManager::QuicTimeWaitListManager(
81 QuicPacketWriter
* writer
,
82 QuicServerSessionVisitor
* visitor
,
83 QuicConnectionHelperInterface
* helper
,
84 const QuicVersionVector
& supported_versions
)
86 QuicTime::Delta::FromSeconds(FLAGS_quic_time_wait_list_seconds
)),
87 connection_id_clean_up_alarm_(
88 helper
->CreateAlarm(new ConnectionIdCleanUpAlarm(this))),
89 clock_(helper
->GetClock()),
92 SetConnectionIdCleanUpAlarm();
95 QuicTimeWaitListManager::~QuicTimeWaitListManager() {
96 connection_id_clean_up_alarm_
->Cancel();
97 STLDeleteElements(&pending_packets_queue_
);
98 for (ConnectionIdMap::iterator it
= connection_id_map_
.begin();
99 it
!= connection_id_map_
.end();
101 delete it
->second
.close_packet
;
105 void QuicTimeWaitListManager::AddConnectionIdToTimeWait(
106 QuicConnectionId connection_id
,
108 bool connection_rejected_statelessly
,
109 QuicEncryptedPacket
* close_packet
) {
110 DCHECK(!connection_rejected_statelessly
|| !close_packet
)
111 << "Connections that were rejected statelessly should not "
112 << "have a close packet. connection_id = " << connection_id
;
114 ConnectionIdMap::iterator it
= connection_id_map_
.find(connection_id
);
115 const bool new_connection_id
= it
== connection_id_map_
.end();
116 if (!new_connection_id
) { // Replace record if it is reinserted.
117 num_packets
= it
->second
.num_packets
;
118 delete it
->second
.close_packet
;
119 connection_id_map_
.erase(it
);
121 TrimTimeWaitListIfNeeded();
122 DCHECK_LT(num_connections(),
123 static_cast<size_t>(FLAGS_quic_time_wait_list_max_connections
));
124 ConnectionIdData
data(num_packets
, version
, clock_
->ApproximateNow(),
125 close_packet
, connection_rejected_statelessly
);
126 connection_id_map_
.insert(std::make_pair(connection_id
, data
));
127 if (new_connection_id
) {
128 visitor_
->OnConnectionAddedToTimeWaitList(connection_id
);
132 bool QuicTimeWaitListManager::IsConnectionIdInTimeWait(
133 QuicConnectionId connection_id
) const {
134 return ContainsKey(connection_id_map_
, connection_id
);
137 QuicVersion
QuicTimeWaitListManager::GetQuicVersionFromConnectionId(
138 QuicConnectionId connection_id
) {
139 ConnectionIdMap::iterator it
= connection_id_map_
.find(connection_id
);
140 DCHECK(it
!= connection_id_map_
.end());
141 return (it
->second
).version
;
144 void QuicTimeWaitListManager::OnCanWrite() {
145 while (!pending_packets_queue_
.empty()) {
146 QueuedPacket
* queued_packet
= pending_packets_queue_
.front();
147 if (!WriteToWire(queued_packet
)) {
150 pending_packets_queue_
.pop_front();
151 delete queued_packet
;
155 void QuicTimeWaitListManager::ProcessPacket(
156 const IPEndPoint
& server_address
,
157 const IPEndPoint
& client_address
,
158 QuicConnectionId connection_id
,
159 QuicPacketSequenceNumber sequence_number
,
160 const QuicEncryptedPacket
& /*packet*/) {
161 DCHECK(IsConnectionIdInTimeWait(connection_id
));
162 DVLOG(1) << "Processing " << connection_id
<< " in time wait state.";
163 // TODO(satyamshekhar): Think about handling packets from different client
165 ConnectionIdMap::iterator it
= connection_id_map_
.find(connection_id
);
166 DCHECK(it
!= connection_id_map_
.end());
167 // Increment the received packet count.
168 ConnectionIdData
* connection_data
= &it
->second
;
169 ++(connection_data
->num_packets
);
170 if (!ShouldSendResponse(connection_data
->num_packets
)) {
173 if (connection_data
->close_packet
) {
174 QueuedPacket
* queued_packet
= new QueuedPacket(
175 server_address
, client_address
, connection_data
->close_packet
->Clone());
176 // Takes ownership of the packet.
177 SendOrQueuePacket(queued_packet
);
178 } else if (!connection_data
->connection_rejected_statelessly
) {
179 SendPublicReset(server_address
,
184 DVLOG(3) << "Time wait list not sending response for connection "
185 << connection_id
<< " due to previous stateless reject.";
189 // Returns true if the number of packets received for this connection_id is a
190 // power of 2 to throttle the number of public reset packets we send to a
192 bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count
) {
193 return (received_packet_count
& (received_packet_count
- 1)) == 0;
196 void QuicTimeWaitListManager::SendPublicReset(
197 const IPEndPoint
& server_address
,
198 const IPEndPoint
& client_address
,
199 QuicConnectionId connection_id
,
200 QuicPacketSequenceNumber rejected_sequence_number
) {
201 QuicPublicResetPacket packet
;
202 packet
.public_header
.connection_id
= connection_id
;
203 packet
.public_header
.reset_flag
= true;
204 packet
.public_header
.version_flag
= false;
205 packet
.rejected_sequence_number
= rejected_sequence_number
;
206 // TODO(satyamshekhar): generate a valid nonce for this connection_id.
207 packet
.nonce_proof
= 1010101;
208 packet
.client_address
= client_address
;
209 QueuedPacket
* queued_packet
= new QueuedPacket(
212 BuildPublicReset(packet
));
213 // Takes ownership of the packet.
214 SendOrQueuePacket(queued_packet
);
217 QuicEncryptedPacket
* QuicTimeWaitListManager::BuildPublicReset(
218 const QuicPublicResetPacket
& packet
) {
219 return QuicFramer::BuildPublicResetPacket(packet
);
222 // Either sends the packet and deletes it or makes pending queue the
223 // owner of the packet.
224 void QuicTimeWaitListManager::SendOrQueuePacket(QueuedPacket
* packet
) {
225 if (WriteToWire(packet
)) {
228 // pending_packets_queue takes the ownership of the queued packet.
229 pending_packets_queue_
.push_back(packet
);
233 bool QuicTimeWaitListManager::WriteToWire(QueuedPacket
* queued_packet
) {
234 if (writer_
->IsWriteBlocked()) {
235 visitor_
->OnWriteBlocked(this);
238 WriteResult result
= writer_
->WritePacket(
239 queued_packet
->packet()->data(),
240 queued_packet
->packet()->length(),
241 queued_packet
->server_address().address(),
242 queued_packet
->client_address());
243 if (result
.status
== WRITE_STATUS_BLOCKED
) {
244 // If blocked and unbuffered, return false to retry sending.
245 DCHECK(writer_
->IsWriteBlocked());
246 visitor_
->OnWriteBlocked(this);
247 return writer_
->IsWriteBlockedDataBuffered();
248 } else if (result
.status
== WRITE_STATUS_ERROR
) {
249 LOG(WARNING
) << "Received unknown error while sending reset packet to "
250 << queued_packet
->client_address().ToString() << ": "
251 << strerror(result
.error_code
);
256 void QuicTimeWaitListManager::SetConnectionIdCleanUpAlarm() {
257 connection_id_clean_up_alarm_
->Cancel();
258 QuicTime::Delta next_alarm_interval
= QuicTime::Delta::Zero();
259 if (!connection_id_map_
.empty()) {
260 QuicTime oldest_connection_id
=
261 connection_id_map_
.begin()->second
.time_added
;
262 QuicTime now
= clock_
->ApproximateNow();
263 if (now
.Subtract(oldest_connection_id
) < time_wait_period_
) {
264 next_alarm_interval
=
265 oldest_connection_id
.Add(time_wait_period_
).Subtract(now
);
267 LOG(ERROR
) << "ConnectionId lingered for longer than time_wait_period_";
270 // No connection_ids added so none will expire before time_wait_period_.
271 next_alarm_interval
= time_wait_period_
;
274 connection_id_clean_up_alarm_
->Set(
275 clock_
->ApproximateNow().Add(next_alarm_interval
));
278 bool QuicTimeWaitListManager::MaybeExpireOldestConnection(
279 QuicTime expiration_time
) {
280 if (connection_id_map_
.empty()) {
283 ConnectionIdMap::iterator it
= connection_id_map_
.begin();
284 QuicTime oldest_connection_id_time
= it
->second
.time_added
;
285 if (oldest_connection_id_time
> expiration_time
) {
286 // Too recent, don't retire.
289 // This connection_id has lived its age, retire it now.
290 const QuicConnectionId connection_id
= it
->first
;
291 delete it
->second
.close_packet
;
292 connection_id_map_
.erase(it
);
293 visitor_
->OnConnectionRemovedFromTimeWaitList(connection_id
);
297 void QuicTimeWaitListManager::CleanUpOldConnectionIds() {
298 QuicTime now
= clock_
->ApproximateNow();
299 QuicTime expiration
= now
.Subtract(time_wait_period_
);
301 while (MaybeExpireOldestConnection(expiration
)) {
304 SetConnectionIdCleanUpAlarm();
307 void QuicTimeWaitListManager::TrimTimeWaitListIfNeeded() {
308 if (FLAGS_quic_time_wait_list_max_connections
< 0) {
311 while (num_connections() >=
312 static_cast<size_t>(FLAGS_quic_time_wait_list_max_connections
)) {
313 MaybeExpireOldestConnection(QuicTime::Infinite());