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"
21 using base::StringPiece
;
29 // Time period for which the guid should live in time wait state..
30 const int kTimeWaitSeconds
= 5;
34 // A very simple alarm that just informs the QuicTimeWaitListManager to clean
35 // up old guids. This alarm should be unregistered and deleted before the
36 // QuicTimeWaitListManager is deleted.
37 class GuidCleanUpAlarm
: public EpollAlarm
{
39 explicit GuidCleanUpAlarm(QuicTimeWaitListManager
* time_wait_list_manager
)
40 : time_wait_list_manager_(time_wait_list_manager
) {
43 virtual int64
OnAlarm() OVERRIDE
{
44 EpollAlarm::OnAlarm();
45 time_wait_list_manager_
->CleanUpOldGuids();
46 // Let the time wait manager register the alarm at appropriate time.
52 QuicTimeWaitListManager
* time_wait_list_manager_
;
55 struct QuicTimeWaitListManager::GuidAddTime
{
56 GuidAddTime(QuicGuid guid
, const QuicTime
& time
)
65 // This class stores pending public reset packets to be sent to clients.
66 // server_address - server address on which a packet what was received for
67 // a guid in time wait state.
68 // client_address - address of the client that sent that packet. Needed to send
69 // the public reset packet back to the client.
70 // packet - the pending public reset packet that is to be sent to the client.
71 // created instance takes the ownership of this packet.
72 class QuicTimeWaitListManager::QueuedPacket
{
74 QueuedPacket(const IPEndPoint
& server_address
,
75 const IPEndPoint
& client_address
,
76 QuicEncryptedPacket
* packet
)
77 : server_address_(server_address
),
78 client_address_(client_address
),
82 const IPEndPoint
& server_address() const { return server_address_
; }
83 const IPEndPoint
& client_address() const { return client_address_
; }
84 QuicEncryptedPacket
* packet() { return packet_
.get(); }
87 const IPEndPoint server_address_
;
88 const IPEndPoint client_address_
;
89 scoped_ptr
<QuicEncryptedPacket
> packet_
;
91 DISALLOW_COPY_AND_ASSIGN(QueuedPacket
);
94 QuicTimeWaitListManager::QuicTimeWaitListManager(
95 QuicPacketWriter
* writer
,
96 EpollServer
* epoll_server
,
97 const QuicVersionVector
& supported_versions
)
98 : epoll_server_(epoll_server
),
99 kTimeWaitPeriod_(QuicTime::Delta::FromSeconds(kTimeWaitSeconds
)),
100 guid_clean_up_alarm_(new GuidCleanUpAlarm(this)),
101 clock_(epoll_server_
),
103 is_write_blocked_(false) {
104 SetGuidCleanUpAlarm();
107 QuicTimeWaitListManager::~QuicTimeWaitListManager() {
108 guid_clean_up_alarm_
->UnregisterIfRegistered();
109 STLDeleteElements(&time_ordered_guid_list_
);
110 STLDeleteElements(&pending_packets_queue_
);
111 for (GuidMapIterator it
= guid_map_
.begin(); it
!= guid_map_
.end(); ++it
) {
112 delete it
->second
.close_packet
;
116 void QuicTimeWaitListManager::AddGuidToTimeWait(
119 QuicEncryptedPacket
* close_packet
) {
120 DCHECK(!IsGuidInTimeWait(guid
));
121 // Initialize the guid with 0 packets received.
122 GuidData
data(0, version
, close_packet
);
123 guid_map_
.insert(make_pair(guid
, data
));
124 time_ordered_guid_list_
.push_back(new GuidAddTime(guid
,
125 clock_
.ApproximateNow()));
126 DCHECK(IsGuidInTimeWait(guid
));
129 bool QuicTimeWaitListManager::IsGuidInTimeWait(QuicGuid guid
) const {
130 return guid_map_
.find(guid
) != guid_map_
.end();
133 QuicVersion
QuicTimeWaitListManager::GetQuicVersionFromGuid(QuicGuid guid
) {
134 GuidMapIterator it
= guid_map_
.find(guid
);
135 DCHECK(it
!= guid_map_
.end());
136 return (it
->second
).version
;
139 bool QuicTimeWaitListManager::OnCanWrite() {
140 is_write_blocked_
= false;
141 while (!is_write_blocked_
&& !pending_packets_queue_
.empty()) {
142 QueuedPacket
* queued_packet
= pending_packets_queue_
.front();
143 WriteToWire(queued_packet
);
144 if (!is_write_blocked_
) {
145 pending_packets_queue_
.pop_front();
146 delete queued_packet
;
150 return !is_write_blocked_
;
153 void QuicTimeWaitListManager::ProcessPacket(
154 const IPEndPoint
& server_address
,
155 const IPEndPoint
& client_address
,
157 QuicPacketSequenceNumber sequence_number
) {
158 DCHECK(IsGuidInTimeWait(guid
));
159 // TODO(satyamshekhar): Think about handling packets from different client
161 GuidMapIterator it
= guid_map_
.find(guid
);
162 DCHECK(it
!= guid_map_
.end());
163 // Increment the received packet count.
164 ++((it
->second
).num_packets
);
165 if (!ShouldSendResponse((it
->second
).num_packets
)) {
168 if (it
->second
.close_packet
) {
169 QueuedPacket
* queued_packet
=
170 new QueuedPacket(server_address
,
172 it
->second
.close_packet
->Clone());
173 // Takes ownership of the packet.
174 SendOrQueuePacket(queued_packet
);
176 SendPublicReset(server_address
, client_address
, guid
, sequence_number
);
180 // Returns true if the number of packets received for this guid is a power of 2
181 // to throttle the number of public reset packets we send to a client.
182 bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count
) {
183 return (received_packet_count
& (received_packet_count
- 1)) == 0;
186 void QuicTimeWaitListManager::SendPublicReset(
187 const IPEndPoint
& server_address
,
188 const IPEndPoint
& client_address
,
190 QuicPacketSequenceNumber rejected_sequence_number
) {
191 QuicPublicResetPacket packet
;
192 packet
.public_header
.guid
= guid
;
193 packet
.public_header
.reset_flag
= true;
194 packet
.public_header
.version_flag
= false;
195 packet
.rejected_sequence_number
= rejected_sequence_number
;
196 // TODO(satyamshekhar): generate a valid nonce for this guid.
197 packet
.nonce_proof
= 1010101;
198 QueuedPacket
* queued_packet
= new QueuedPacket(
201 QuicFramer::BuildPublicResetPacket(packet
));
202 // Takes ownership of the packet.
203 SendOrQueuePacket(queued_packet
);
206 // Either sends the packet and deletes it or makes pending queue the
207 // owner of the packet.
208 void QuicTimeWaitListManager::SendOrQueuePacket(QueuedPacket
* packet
) {
209 if (!is_write_blocked_
) {
210 // TODO(satyamshekhar): Handle packets that fail due to error other than
211 // EAGAIN or EWOULDBLOCK.
215 if (is_write_blocked_
) {
216 // pending_packets_queue takes the ownership of the queued packet.
217 pending_packets_queue_
.push_back(packet
);
223 void QuicTimeWaitListManager::WriteToWire(QueuedPacket
* queued_packet
) {
224 DCHECK(!is_write_blocked_
);
225 WriteResult result
= writer_
->WritePacket(
226 queued_packet
->packet()->data(),
227 queued_packet
->packet()->length(),
228 queued_packet
->server_address().address(),
229 queued_packet
->client_address(),
231 if (result
.status
== WRITE_STATUS_BLOCKED
) {
232 is_write_blocked_
= true;
233 } else if (result
.status
== WRITE_STATUS_ERROR
) {
234 LOG(WARNING
) << "Received unknown error while sending reset packet to "
235 << queued_packet
->client_address().ToString() << ": "
236 << strerror(result
.error_code
);
240 void QuicTimeWaitListManager::SetGuidCleanUpAlarm() {
241 guid_clean_up_alarm_
->UnregisterIfRegistered();
242 int64 next_alarm_interval
;
243 if (!time_ordered_guid_list_
.empty()) {
244 GuidAddTime
* oldest_guid
= time_ordered_guid_list_
.front();
245 QuicTime now
= clock_
.ApproximateNow();
246 DCHECK(now
.Subtract(oldest_guid
->time_added
) < kTimeWaitPeriod_
);
247 next_alarm_interval
= oldest_guid
->time_added
248 .Add(kTimeWaitPeriod_
)
252 // No guids added so none will expire before kTimeWaitPeriod_.
253 next_alarm_interval
= kTimeWaitPeriod_
.ToMicroseconds();
256 epoll_server_
->RegisterAlarmApproximateDelta(next_alarm_interval
,
257 guid_clean_up_alarm_
.get());
260 void QuicTimeWaitListManager::CleanUpOldGuids() {
261 QuicTime now
= clock_
.ApproximateNow();
262 while (time_ordered_guid_list_
.size() > 0) {
263 DCHECK_EQ(time_ordered_guid_list_
.size(), guid_map_
.size());
264 GuidAddTime
* oldest_guid
= time_ordered_guid_list_
.front();
265 if (now
.Subtract(oldest_guid
->time_added
) < kTimeWaitPeriod_
) {
268 // This guid has lived its age, retire it now.
269 GuidMapIterator it
= guid_map_
.find(oldest_guid
->guid
);
270 DCHECK(it
!= guid_map_
.end());
271 delete it
->second
.close_packet
;
272 guid_map_
.erase(oldest_guid
->guid
);
273 time_ordered_guid_list_
.pop_front();
276 SetGuidCleanUpAlarm();