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/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_framer.h"
18 #include "net/quic/quic_protocol.h"
19 #include "net/quic/quic_utils.h"
20 #include "net/tools/epoll_server/epoll_server.h"
21 #include "net/tools/quic/quic_server_session.h"
23 using base::StringPiece
;
31 // Time period for which a given connection_id should live in the time-wait
33 int64 FLAGS_quic_time_wait_list_seconds
= 5;
37 // A very simple alarm that just informs the QuicTimeWaitListManager to clean
38 // up old connection_ids. This alarm should be unregistered and deleted before
39 // the QuicTimeWaitListManager is deleted.
40 class ConnectionIdCleanUpAlarm
: public EpollAlarm
{
42 explicit ConnectionIdCleanUpAlarm(
43 QuicTimeWaitListManager
* time_wait_list_manager
)
44 : time_wait_list_manager_(time_wait_list_manager
) {
47 int64
OnAlarm() override
{
48 EpollAlarm::OnAlarm();
49 time_wait_list_manager_
->CleanUpOldConnectionIds();
50 // Let the time wait manager register the alarm at appropriate time.
56 QuicTimeWaitListManager
* time_wait_list_manager_
;
59 // This class stores pending public reset packets to be sent to clients.
60 // server_address - server address on which a packet what was received for
61 // a connection_id in time wait state.
62 // client_address - address of the client that sent that packet. Needed to send
63 // the public reset packet back to the client.
64 // packet - the pending public reset packet that is to be sent to the client.
65 // created instance takes the ownership of this packet.
66 class QuicTimeWaitListManager::QueuedPacket
{
68 QueuedPacket(const IPEndPoint
& server_address
,
69 const IPEndPoint
& client_address
,
70 QuicEncryptedPacket
* packet
)
71 : server_address_(server_address
),
72 client_address_(client_address
),
76 const IPEndPoint
& server_address() const { return server_address_
; }
77 const IPEndPoint
& client_address() const { return client_address_
; }
78 QuicEncryptedPacket
* packet() { return packet_
.get(); }
81 const IPEndPoint server_address_
;
82 const IPEndPoint client_address_
;
83 scoped_ptr
<QuicEncryptedPacket
> packet_
;
85 DISALLOW_COPY_AND_ASSIGN(QueuedPacket
);
88 QuicTimeWaitListManager::QuicTimeWaitListManager(
89 QuicPacketWriter
* writer
,
90 QuicServerSessionVisitor
* visitor
,
91 EpollServer
* epoll_server
,
92 const QuicVersionVector
& supported_versions
)
93 : epoll_server_(epoll_server
),
95 QuicTime::Delta::FromSeconds(FLAGS_quic_time_wait_list_seconds
)),
96 connection_id_clean_up_alarm_(new ConnectionIdCleanUpAlarm(this)),
97 clock_(epoll_server_
),
100 SetConnectionIdCleanUpAlarm();
103 QuicTimeWaitListManager::~QuicTimeWaitListManager() {
104 connection_id_clean_up_alarm_
->UnregisterIfRegistered();
105 STLDeleteElements(&pending_packets_queue_
);
106 for (ConnectionIdMap::iterator it
= connection_id_map_
.begin();
107 it
!= connection_id_map_
.end();
109 delete it
->second
.close_packet
;
113 void QuicTimeWaitListManager::AddConnectionIdToTimeWait(
114 QuicConnectionId connection_id
,
116 QuicEncryptedPacket
* close_packet
) {
117 DVLOG(1) << "Adding " << connection_id
<< " to the time wait list.";
119 ConnectionIdMap::iterator it
= connection_id_map_
.find(connection_id
);
120 if (it
!= connection_id_map_
.end()) { // Replace record if it is reinserted.
121 num_packets
= it
->second
.num_packets
;
122 delete it
->second
.close_packet
;
123 connection_id_map_
.erase(it
);
125 ConnectionIdData
data(num_packets
,
127 clock_
.ApproximateNow(),
129 connection_id_map_
.insert(make_pair(connection_id
, data
));
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 ++((it
->second
).num_packets
);
169 if (!ShouldSendResponse((it
->second
).num_packets
)) {
172 if (it
->second
.close_packet
) {
173 QueuedPacket
* queued_packet
=
174 new QueuedPacket(server_address
,
176 it
->second
.close_packet
->Clone());
177 // Takes ownership of the packet.
178 SendOrQueuePacket(queued_packet
);
180 SendPublicReset(server_address
,
187 // Returns true if the number of packets received for this connection_id is a
188 // power of 2 to throttle the number of public reset packets we send to a
190 bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count
) {
191 return (received_packet_count
& (received_packet_count
- 1)) == 0;
194 void QuicTimeWaitListManager::SendPublicReset(
195 const IPEndPoint
& server_address
,
196 const IPEndPoint
& client_address
,
197 QuicConnectionId connection_id
,
198 QuicPacketSequenceNumber rejected_sequence_number
) {
199 QuicPublicResetPacket packet
;
200 packet
.public_header
.connection_id
= connection_id
;
201 packet
.public_header
.reset_flag
= true;
202 packet
.public_header
.version_flag
= false;
203 packet
.rejected_sequence_number
= rejected_sequence_number
;
204 // TODO(satyamshekhar): generate a valid nonce for this connection_id.
205 packet
.nonce_proof
= 1010101;
206 packet
.client_address
= client_address
;
207 QueuedPacket
* queued_packet
= new QueuedPacket(
210 BuildPublicReset(packet
));
211 // Takes ownership of the packet.
212 SendOrQueuePacket(queued_packet
);
215 QuicEncryptedPacket
* QuicTimeWaitListManager::BuildPublicReset(
216 const QuicPublicResetPacket
& packet
) {
217 return QuicFramer::BuildPublicResetPacket(packet
);
220 // Either sends the packet and deletes it or makes pending queue the
221 // owner of the packet.
222 void QuicTimeWaitListManager::SendOrQueuePacket(QueuedPacket
* packet
) {
223 if (WriteToWire(packet
)) {
226 // pending_packets_queue takes the ownership of the queued packet.
227 pending_packets_queue_
.push_back(packet
);
231 bool QuicTimeWaitListManager::WriteToWire(QueuedPacket
* queued_packet
) {
232 if (writer_
->IsWriteBlocked()) {
233 visitor_
->OnWriteBlocked(this);
236 WriteResult result
= writer_
->WritePacket(
237 queued_packet
->packet()->data(),
238 queued_packet
->packet()->length(),
239 queued_packet
->server_address().address(),
240 queued_packet
->client_address());
241 if (result
.status
== WRITE_STATUS_BLOCKED
) {
242 // If blocked and unbuffered, return false to retry sending.
243 DCHECK(writer_
->IsWriteBlocked());
244 visitor_
->OnWriteBlocked(this);
245 return writer_
->IsWriteBlockedDataBuffered();
246 } else if (result
.status
== WRITE_STATUS_ERROR
) {
247 LOG(WARNING
) << "Received unknown error while sending reset packet to "
248 << queued_packet
->client_address().ToString() << ": "
249 << strerror(result
.error_code
);
254 void QuicTimeWaitListManager::SetConnectionIdCleanUpAlarm() {
255 connection_id_clean_up_alarm_
->UnregisterIfRegistered();
256 int64 next_alarm_interval
;
257 if (!connection_id_map_
.empty()) {
258 QuicTime oldest_connection_id
=
259 connection_id_map_
.begin()->second
.time_added
;
260 QuicTime now
= clock_
.ApproximateNow();
261 if (now
.Subtract(oldest_connection_id
) < kTimeWaitPeriod_
) {
262 next_alarm_interval
= oldest_connection_id
.Add(kTimeWaitPeriod_
)
266 LOG(ERROR
) << "ConnectionId lingered for longer than kTimeWaitPeriod";
267 next_alarm_interval
= 0;
270 // No connection_ids added so none will expire before kTimeWaitPeriod_.
271 next_alarm_interval
= kTimeWaitPeriod_
.ToMicroseconds();
274 epoll_server_
->RegisterAlarmApproximateDelta(
275 next_alarm_interval
, connection_id_clean_up_alarm_
.get());
278 void QuicTimeWaitListManager::CleanUpOldConnectionIds() {
279 QuicTime now
= clock_
.ApproximateNow();
280 while (!connection_id_map_
.empty()) {
281 ConnectionIdMap::iterator it
= connection_id_map_
.begin();
282 QuicTime oldest_connection_id
= it
->second
.time_added
;
283 if (now
.Subtract(oldest_connection_id
) < kTimeWaitPeriod_
) {
286 // This connection_id has lived its age, retire it now.
287 DVLOG(1) << "Retiring " << it
->first
<< " from the time-wait state.";
288 delete it
->second
.close_packet
;
289 connection_id_map_
.erase(it
);
291 SetConnectionIdCleanUpAlarm();