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/quic/quic_server_session.h"
22 using base::StringPiece
;
30 // Time period for which the guid 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 guids. This alarm should be unregistered and deleted before the
37 // QuicTimeWaitListManager is deleted.
38 class GuidCleanUpAlarm
: public EpollAlarm
{
40 explicit GuidCleanUpAlarm(QuicTimeWaitListManager
* time_wait_list_manager
)
41 : time_wait_list_manager_(time_wait_list_manager
) {
44 virtual int64
OnAlarm() OVERRIDE
{
45 EpollAlarm::OnAlarm();
46 time_wait_list_manager_
->CleanUpOldGuids();
47 // Let the time wait manager register the alarm at appropriate time.
53 QuicTimeWaitListManager
* time_wait_list_manager_
;
56 // This class stores pending public reset packets to be sent to clients.
57 // server_address - server address on which a packet what was received for
58 // a guid in time wait state.
59 // client_address - address of the client that sent that packet. Needed to send
60 // the public reset packet back to the client.
61 // packet - the pending public reset packet that is to be sent to the client.
62 // created instance takes the ownership of this packet.
63 class QuicTimeWaitListManager::QueuedPacket
{
65 QueuedPacket(const IPEndPoint
& server_address
,
66 const IPEndPoint
& client_address
,
67 QuicEncryptedPacket
* packet
)
68 : server_address_(server_address
),
69 client_address_(client_address
),
73 const IPEndPoint
& server_address() const { return server_address_
; }
74 const IPEndPoint
& client_address() const { return client_address_
; }
75 QuicEncryptedPacket
* packet() { return packet_
.get(); }
78 const IPEndPoint server_address_
;
79 const IPEndPoint client_address_
;
80 scoped_ptr
<QuicEncryptedPacket
> packet_
;
82 DISALLOW_COPY_AND_ASSIGN(QueuedPacket
);
85 QuicTimeWaitListManager::QuicTimeWaitListManager(
86 QuicPacketWriter
* writer
,
87 QuicServerSessionVisitor
* visitor
,
88 EpollServer
* epoll_server
,
89 const QuicVersionVector
& supported_versions
)
90 : epoll_server_(epoll_server
),
91 kTimeWaitPeriod_(QuicTime::Delta::FromSeconds(kTimeWaitSeconds
)),
92 guid_clean_up_alarm_(new GuidCleanUpAlarm(this)),
93 clock_(epoll_server_
),
96 SetGuidCleanUpAlarm();
99 QuicTimeWaitListManager::~QuicTimeWaitListManager() {
100 guid_clean_up_alarm_
->UnregisterIfRegistered();
101 STLDeleteElements(&pending_packets_queue_
);
102 for (GuidMap::iterator it
= guid_map_
.begin(); it
!= guid_map_
.end(); ++it
) {
103 delete it
->second
.close_packet
;
107 void QuicTimeWaitListManager::AddGuidToTimeWait(
110 QuicEncryptedPacket
* close_packet
) {
112 GuidMap::iterator it
= guid_map_
.find(guid
);
113 if (it
!= guid_map_
.end()) { // Replace record if it is reinserted.
114 num_packets
= it
->second
.num_packets
;
115 delete it
->second
.close_packet
;
118 GuidData
data(num_packets
, version
, clock_
.ApproximateNow(), close_packet
);
119 guid_map_
.insert(make_pair(guid
, data
));
122 bool QuicTimeWaitListManager::IsGuidInTimeWait(QuicGuid guid
) const {
123 return guid_map_
.find(guid
) != guid_map_
.end();
126 QuicVersion
QuicTimeWaitListManager::GetQuicVersionFromGuid(QuicGuid guid
) {
127 GuidMap::iterator it
= guid_map_
.find(guid
);
128 DCHECK(it
!= guid_map_
.end());
129 return (it
->second
).version
;
132 void QuicTimeWaitListManager::OnCanWrite() {
133 while (!pending_packets_queue_
.empty()) {
134 QueuedPacket
* queued_packet
= pending_packets_queue_
.front();
135 if (!WriteToWire(queued_packet
)) {
138 pending_packets_queue_
.pop_front();
139 delete queued_packet
;
143 void QuicTimeWaitListManager::ProcessPacket(
144 const IPEndPoint
& server_address
,
145 const IPEndPoint
& client_address
,
147 QuicPacketSequenceNumber sequence_number
) {
148 DCHECK(IsGuidInTimeWait(guid
));
149 // TODO(satyamshekhar): Think about handling packets from different client
151 GuidMap::iterator it
= guid_map_
.find(guid
);
152 DCHECK(it
!= guid_map_
.end());
153 // Increment the received packet count.
154 ++((it
->second
).num_packets
);
155 if (!ShouldSendResponse((it
->second
).num_packets
)) {
158 if (it
->second
.close_packet
) {
159 QueuedPacket
* queued_packet
=
160 new QueuedPacket(server_address
,
162 it
->second
.close_packet
->Clone());
163 // Takes ownership of the packet.
164 SendOrQueuePacket(queued_packet
);
166 SendPublicReset(server_address
, client_address
, guid
, sequence_number
);
170 // Returns true if the number of packets received for this guid is a power of 2
171 // to throttle the number of public reset packets we send to a client.
172 bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count
) {
173 return (received_packet_count
& (received_packet_count
- 1)) == 0;
176 void QuicTimeWaitListManager::SendPublicReset(
177 const IPEndPoint
& server_address
,
178 const IPEndPoint
& client_address
,
180 QuicPacketSequenceNumber rejected_sequence_number
) {
181 QuicPublicResetPacket packet
;
182 packet
.public_header
.guid
= guid
;
183 packet
.public_header
.reset_flag
= true;
184 packet
.public_header
.version_flag
= false;
185 packet
.rejected_sequence_number
= rejected_sequence_number
;
186 // TODO(satyamshekhar): generate a valid nonce for this guid.
187 packet
.nonce_proof
= 1010101;
188 packet
.client_address
= client_address
;
189 QueuedPacket
* queued_packet
= new QueuedPacket(
192 QuicFramer::BuildPublicResetPacket(packet
));
193 // Takes ownership of the packet.
194 SendOrQueuePacket(queued_packet
);
197 // Either sends the packet and deletes it or makes pending queue the
198 // owner of the packet.
199 void QuicTimeWaitListManager::SendOrQueuePacket(QueuedPacket
* packet
) {
200 if (WriteToWire(packet
)) {
203 // pending_packets_queue takes the ownership of the queued packet.
204 pending_packets_queue_
.push_back(packet
);
208 bool QuicTimeWaitListManager::WriteToWire(QueuedPacket
* queued_packet
) {
209 if (writer_
->IsWriteBlocked()) {
210 visitor_
->OnWriteBlocked(this);
213 WriteResult result
= writer_
->WritePacket(
214 queued_packet
->packet()->data(),
215 queued_packet
->packet()->length(),
216 queued_packet
->server_address().address(),
217 queued_packet
->client_address());
218 if (result
.status
== WRITE_STATUS_BLOCKED
) {
219 // If blocked and unbuffered, return false to retry sending.
220 DCHECK(writer_
->IsWriteBlocked());
221 visitor_
->OnWriteBlocked(this);
222 return writer_
->IsWriteBlockedDataBuffered();
223 } else if (result
.status
== WRITE_STATUS_ERROR
) {
224 LOG(WARNING
) << "Received unknown error while sending reset packet to "
225 << queued_packet
->client_address().ToString() << ": "
226 << strerror(result
.error_code
);
231 void QuicTimeWaitListManager::SetGuidCleanUpAlarm() {
232 guid_clean_up_alarm_
->UnregisterIfRegistered();
233 int64 next_alarm_interval
;
234 if (!guid_map_
.empty()) {
235 QuicTime oldest_guid
= guid_map_
.begin()->second
.time_added
;
236 QuicTime now
= clock_
.ApproximateNow();
237 if (now
.Subtract(oldest_guid
) < kTimeWaitPeriod_
) {
238 next_alarm_interval
= oldest_guid
.Add(kTimeWaitPeriod_
)
242 LOG(ERROR
) << "GUID lingered for longer than kTimeWaitPeriod";
243 next_alarm_interval
= 0;
246 // No guids added so none will expire before kTimeWaitPeriod_.
247 next_alarm_interval
= kTimeWaitPeriod_
.ToMicroseconds();
250 epoll_server_
->RegisterAlarmApproximateDelta(next_alarm_interval
,
251 guid_clean_up_alarm_
.get());
254 void QuicTimeWaitListManager::CleanUpOldGuids() {
255 QuicTime now
= clock_
.ApproximateNow();
256 while (!guid_map_
.empty()) {
257 GuidMap::iterator it
= guid_map_
.begin();
258 QuicTime oldest_guid
= it
->second
.time_added
;
259 if (now
.Subtract(oldest_guid
) < kTimeWaitPeriod_
) {
262 // This guid has lived its age, retire it now.
263 delete it
->second
.close_packet
;
266 SetGuidCleanUpAlarm();