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 bool QuicTimeWaitListManager::OnCanWrite() {
133 while (!pending_packets_queue_.empty()) {
134 QueuedPacket* queued_packet = pending_packets_queue_.front();
135 if (WriteToWire(queued_packet)) {
136 pending_packets_queue_.pop_front();
137 delete queued_packet;
143 return !writer_->IsWriteBlocked();
146 void QuicTimeWaitListManager::ProcessPacket(
147 const IPEndPoint& server_address,
148 const IPEndPoint& client_address,
150 QuicPacketSequenceNumber sequence_number) {
151 DCHECK(IsGuidInTimeWait(guid));
152 // TODO(satyamshekhar): Think about handling packets from different client
154 GuidMap::iterator it = guid_map_.find(guid);
155 DCHECK(it != guid_map_.end());
156 // Increment the received packet count.
157 ++((it->second).num_packets);
158 if (!ShouldSendResponse((it->second).num_packets)) {
161 if (it->second.close_packet) {
162 QueuedPacket* queued_packet =
163 new QueuedPacket(server_address,
165 it->second.close_packet->Clone());
166 // Takes ownership of the packet.
167 SendOrQueuePacket(queued_packet);
169 SendPublicReset(server_address, client_address, guid, sequence_number);
173 // Returns true if the number of packets received for this guid is a power of 2
174 // to throttle the number of public reset packets we send to a client.
175 bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count) {
176 return (received_packet_count & (received_packet_count - 1)) == 0;
179 void QuicTimeWaitListManager::SendPublicReset(
180 const IPEndPoint& server_address,
181 const IPEndPoint& client_address,
183 QuicPacketSequenceNumber rejected_sequence_number) {
184 QuicPublicResetPacket packet;
185 packet.public_header.guid = guid;
186 packet.public_header.reset_flag = true;
187 packet.public_header.version_flag = false;
188 packet.rejected_sequence_number = rejected_sequence_number;
189 // TODO(satyamshekhar): generate a valid nonce for this guid.
190 packet.nonce_proof = 1010101;
191 packet.client_address = client_address;
192 QueuedPacket* queued_packet = new QueuedPacket(
195 QuicFramer::BuildPublicResetPacket(packet));
196 // Takes ownership of the packet.
197 SendOrQueuePacket(queued_packet);
200 // Either sends the packet and deletes it or makes pending queue the
201 // owner of the packet.
202 void QuicTimeWaitListManager::SendOrQueuePacket(QueuedPacket* packet) {
203 if (WriteToWire(packet)) {
206 // pending_packets_queue takes the ownership of the queued packet.
207 pending_packets_queue_.push_back(packet);
211 bool QuicTimeWaitListManager::WriteToWire(QueuedPacket* queued_packet) {
212 if (writer_->IsWriteBlocked()) {
213 visitor_->OnWriteBlocked(this);
216 WriteResult result = writer_->WritePacket(
217 queued_packet->packet()->data(),
218 queued_packet->packet()->length(),
219 queued_packet->server_address().address(),
220 queued_packet->client_address());
221 if (result.status == WRITE_STATUS_BLOCKED) {
222 // If blocked and unbuffered, return false to retry sending.
223 DCHECK(writer_->IsWriteBlocked());
224 visitor_->OnWriteBlocked(this);
225 return writer_->IsWriteBlockedDataBuffered();
226 } else if (result.status == WRITE_STATUS_ERROR) {
227 LOG(WARNING) << "Received unknown error while sending reset packet to "
228 << queued_packet->client_address().ToString() << ": "
229 << strerror(result.error_code);
234 void QuicTimeWaitListManager::SetGuidCleanUpAlarm() {
235 guid_clean_up_alarm_->UnregisterIfRegistered();
236 int64 next_alarm_interval;
237 if (!guid_map_.empty()) {
238 QuicTime oldest_guid = guid_map_.begin()->second.time_added;
239 QuicTime now = clock_.ApproximateNow();
240 if (now.Subtract(oldest_guid) < kTimeWaitPeriod_) {
241 next_alarm_interval = oldest_guid.Add(kTimeWaitPeriod_)
245 LOG(ERROR) << "GUID lingered for longer than kTimeWaitPeriod";
246 next_alarm_interval = 0;
249 // No guids added so none will expire before kTimeWaitPeriod_.
250 next_alarm_interval = kTimeWaitPeriod_.ToMicroseconds();
253 epoll_server_->RegisterAlarmApproximateDelta(next_alarm_interval,
254 guid_clean_up_alarm_.get());
257 void QuicTimeWaitListManager::CleanUpOldGuids() {
258 QuicTime now = clock_.ApproximateNow();
259 while (!guid_map_.empty()) {
260 GuidMap::iterator it = guid_map_.begin();
261 QuicTime oldest_guid = it->second.time_added;
262 if (now.Subtract(oldest_guid) < kTimeWaitPeriod_) {
265 // This guid has lived its age, retire it now.
266 delete it->second.close_packet;
269 SetGuidCleanUpAlarm();