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_framer.h"
19 #include "net/quic/quic_protocol.h"
20 #include "net/quic/quic_server_session.h"
21 #include "net/quic/quic_utils.h"
23 using base::StringPiece
;
30 // Time period for which the connection_id should live in time wait state..
31 const int kTimeWaitSeconds
= 5;
35 // A very simple alarm that just informs the QuicTimeWaitListManager to clean
36 // up old connection_ids. This alarm should be unregistered and deleted before
37 // the QuicTimeWaitListManager is deleted.
38 class ConnectionIdCleanUpAlarm
: public QuicAlarm::Delegate
{
40 explicit ConnectionIdCleanUpAlarm(
41 QuicTimeWaitListManager
* time_wait_list_manager
)
42 : time_wait_list_manager_(time_wait_list_manager
) {}
44 virtual QuicTime
OnAlarm() OVERRIDE
{
45 time_wait_list_manager_
->CleanUpOldConnectionIds();
46 // Let the time wait manager register the alarm at appropriate time.
47 return QuicTime::Zero();
52 QuicTimeWaitListManager
* time_wait_list_manager_
;
55 // This class stores pending public reset packets to be sent to clients.
56 // server_address - server address on which a packet what was received for
57 // a connection_id in time wait state.
58 // client_address - address of the client that sent that packet. Needed to send
59 // the public reset packet back to the client.
60 // packet - the pending public reset packet that is to be sent to the client.
61 // created instance takes the ownership of this packet.
62 class QuicTimeWaitListManager::QueuedPacket
{
64 QueuedPacket(const IPEndPoint
& server_address
,
65 const IPEndPoint
& client_address
,
66 QuicEncryptedPacket
* packet
)
67 : server_address_(server_address
),
68 client_address_(client_address
),
71 const IPEndPoint
& server_address() const { return server_address_
; }
72 const IPEndPoint
& client_address() const { return client_address_
; }
73 QuicEncryptedPacket
* packet() { return packet_
.get(); }
76 const IPEndPoint server_address_
;
77 const IPEndPoint client_address_
;
78 scoped_ptr
<QuicEncryptedPacket
> packet_
;
80 DISALLOW_COPY_AND_ASSIGN(QueuedPacket
);
83 QuicTimeWaitListManager::QuicTimeWaitListManager(
84 QuicPacketWriter
* writer
,
85 QuicServerSessionVisitor
* visitor
,
86 QuicConnectionHelperInterface
* helper
,
87 const QuicVersionVector
& supported_versions
)
89 kTimeWaitPeriod_(QuicTime::Delta::FromSeconds(kTimeWaitSeconds
)),
90 connection_id_clean_up_alarm_(
91 helper_
->CreateAlarm(new ConnectionIdCleanUpAlarm(this))),
94 SetConnectionIdCleanUpAlarm();
97 QuicTimeWaitListManager::~QuicTimeWaitListManager() {
98 connection_id_clean_up_alarm_
->Cancel();
99 STLDeleteElements(&pending_packets_queue_
);
100 for (ConnectionIdMap::iterator it
= connection_id_map_
.begin();
101 it
!= connection_id_map_
.end();
103 delete it
->second
.close_packet
;
107 void QuicTimeWaitListManager::AddConnectionIdToTimeWait(
108 QuicConnectionId connection_id
,
110 QuicEncryptedPacket
* close_packet
) {
111 DVLOG(1) << "Adding " << connection_id
<< " to the time wait list.";
113 ConnectionIdMap::iterator it
= connection_id_map_
.find(connection_id
);
114 if (it
!= connection_id_map_
.end()) { // Replace record if it is reinserted.
115 num_packets
= it
->second
.num_packets
;
116 delete it
->second
.close_packet
;
117 connection_id_map_
.erase(it
);
119 ConnectionIdData
data(num_packets
,
121 helper_
->GetClock()->ApproximateNow(),
123 connection_id_map_
.insert(make_pair(connection_id
, data
));
126 bool QuicTimeWaitListManager::IsConnectionIdInTimeWait(
127 QuicConnectionId connection_id
) const {
128 return ContainsKey(connection_id_map_
, connection_id
);
131 QuicVersion
QuicTimeWaitListManager::GetQuicVersionFromConnectionId(
132 QuicConnectionId connection_id
) {
133 ConnectionIdMap::iterator it
= connection_id_map_
.find(connection_id
);
134 DCHECK(it
!= connection_id_map_
.end());
135 return (it
->second
).version
;
138 void QuicTimeWaitListManager::OnCanWrite() {
139 while (!pending_packets_queue_
.empty()) {
140 QueuedPacket
* queued_packet
= pending_packets_queue_
.front();
141 if (!WriteToWire(queued_packet
)) {
144 pending_packets_queue_
.pop_front();
145 delete queued_packet
;
149 void QuicTimeWaitListManager::ProcessPacket(
150 const IPEndPoint
& server_address
,
151 const IPEndPoint
& client_address
,
152 QuicConnectionId connection_id
,
153 QuicPacketSequenceNumber sequence_number
,
154 const QuicEncryptedPacket
& /*packet*/) {
155 DCHECK(IsConnectionIdInTimeWait(connection_id
));
156 DVLOG(1) << "Processing " << connection_id
<< " in time wait state.";
157 // TODO(satyamshekhar): Think about handling packets from different client
159 ConnectionIdMap::iterator it
= connection_id_map_
.find(connection_id
);
160 DCHECK(it
!= connection_id_map_
.end());
161 // Increment the received packet count.
162 ++((it
->second
).num_packets
);
163 if (!ShouldSendResponse((it
->second
).num_packets
)) {
166 if (it
->second
.close_packet
) {
167 QueuedPacket
* queued_packet
=
168 new QueuedPacket(server_address
,
170 it
->second
.close_packet
->Clone());
171 // Takes ownership of the packet.
172 SendOrQueuePacket(queued_packet
);
174 SendPublicReset(server_address
,
181 // Returns true if the number of packets received for this connection_id is a
182 // power of 2 to throttle the number of public reset packets we send to a
184 bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count
) {
185 return (received_packet_count
& (received_packet_count
- 1)) == 0;
188 void QuicTimeWaitListManager::SendPublicReset(
189 const IPEndPoint
& server_address
,
190 const IPEndPoint
& client_address
,
191 QuicConnectionId connection_id
,
192 QuicPacketSequenceNumber rejected_sequence_number
) {
193 QuicPublicResetPacket packet
;
194 packet
.public_header
.connection_id
= connection_id
;
195 packet
.public_header
.reset_flag
= true;
196 packet
.public_header
.version_flag
= false;
197 packet
.rejected_sequence_number
= rejected_sequence_number
;
198 // TODO(satyamshekhar): generate a valid nonce for this connection_id.
199 packet
.nonce_proof
= 1010101;
200 packet
.client_address
= client_address
;
201 QueuedPacket
* queued_packet
= new QueuedPacket(
204 BuildPublicReset(packet
));
205 // Takes ownership of the packet.
206 SendOrQueuePacket(queued_packet
);
209 QuicEncryptedPacket
* QuicTimeWaitListManager::BuildPublicReset(
210 const QuicPublicResetPacket
& packet
) {
211 return QuicFramer::BuildPublicResetPacket(packet
);
214 // Either sends the packet and deletes it or makes pending queue the
215 // owner of the packet.
216 void QuicTimeWaitListManager::SendOrQueuePacket(QueuedPacket
* packet
) {
217 if (WriteToWire(packet
)) {
220 // pending_packets_queue takes the ownership of the queued packet.
221 pending_packets_queue_
.push_back(packet
);
225 bool QuicTimeWaitListManager::WriteToWire(QueuedPacket
* queued_packet
) {
226 if (writer_
->IsWriteBlocked()) {
227 visitor_
->OnWriteBlocked(this);
230 WriteResult result
= writer_
->WritePacket(
231 queued_packet
->packet()->data(),
232 queued_packet
->packet()->length(),
233 queued_packet
->server_address().address(),
234 queued_packet
->client_address());
235 if (result
.status
== WRITE_STATUS_BLOCKED
) {
236 // If blocked and unbuffered, return false to retry sending.
237 DCHECK(writer_
->IsWriteBlocked());
238 visitor_
->OnWriteBlocked(this);
239 return writer_
->IsWriteBlockedDataBuffered();
240 } else if (result
.status
== WRITE_STATUS_ERROR
) {
241 LOG(WARNING
) << "Received unknown error while sending reset packet to "
242 << queued_packet
->client_address().ToString() << ": "
243 << strerror(result
.error_code
);
248 void QuicTimeWaitListManager::SetConnectionIdCleanUpAlarm() {
249 connection_id_clean_up_alarm_
->Cancel();
250 QuicTime now
= helper_
->GetClock()->ApproximateNow();
251 QuicTime next_alarm_time
= now
;
252 if (!connection_id_map_
.empty()) {
253 QuicTime oldest_connection_id
=
254 connection_id_map_
.begin()->second
.time_added
;
255 if (now
.Subtract(oldest_connection_id
) < kTimeWaitPeriod_
) {
256 next_alarm_time
= oldest_connection_id
.Add(kTimeWaitPeriod_
);
258 LOG(ERROR
) << "ConnectionId lingered for longer than kTimeWaitPeriod";
261 // No connection_ids added so none will expire before kTimeWaitPeriod_.
262 next_alarm_time
= now
.Add(kTimeWaitPeriod_
);
265 connection_id_clean_up_alarm_
->Set(next_alarm_time
);
268 void QuicTimeWaitListManager::CleanUpOldConnectionIds() {
269 QuicTime now
= helper_
->GetClock()->ApproximateNow();
270 while (!connection_id_map_
.empty()) {
271 ConnectionIdMap::iterator it
= connection_id_map_
.begin();
272 QuicTime oldest_connection_id
= it
->second
.time_added
;
273 if (now
.Subtract(oldest_connection_id
) < kTimeWaitPeriod_
) {
276 // This connection_id has lived its age, retire it now.
277 delete it
->second
.close_packet
;
278 connection_id_map_
.erase(it
);
280 SetConnectionIdCleanUpAlarm();