Land Recent QUIC Changes.
[chromium-blink-merge.git] / net / tools / quic / quic_time_wait_list_manager.cc
blob962dfa3cd302228c80ddc3ffa714466f3e96e637
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"
7 #include <errno.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;
22 using std::make_pair;
24 namespace net {
25 namespace tools {
27 namespace {
29 // Time period for which the guid should live in time wait state..
30 const int kTimeWaitSeconds = 5;
32 } // namespace
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 {
38 public:
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.
47 return 0;
50 private:
51 // Not owned.
52 QuicTimeWaitListManager* time_wait_list_manager_;
55 struct QuicTimeWaitListManager::GuidAddTime {
56 GuidAddTime(QuicGuid guid, const QuicTime& time)
57 : guid(guid),
58 time_added(time) {
61 QuicGuid guid;
62 QuicTime time_added;
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 {
73 public:
74 QueuedPacket(const IPEndPoint& server_address,
75 const IPEndPoint& client_address,
76 QuicEncryptedPacket* packet)
77 : server_address_(server_address),
78 client_address_(client_address),
79 packet_(packet) {
82 const IPEndPoint& server_address() const { return server_address_; }
83 const IPEndPoint& client_address() const { return client_address_; }
84 QuicEncryptedPacket* packet() { return packet_.get(); }
86 private:
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_),
102 writer_(writer),
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(
117 QuicGuid guid,
118 QuicVersion version,
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,
156 QuicGuid guid,
157 QuicPacketSequenceNumber sequence_number) {
158 DCHECK(IsGuidInTimeWait(guid));
159 // TODO(satyamshekhar): Think about handling packets from different client
160 // addresses.
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)) {
166 return;
168 if (it->second.close_packet) {
169 QueuedPacket* queued_packet =
170 new QueuedPacket(server_address,
171 client_address,
172 it->second.close_packet->Clone());
173 // Takes ownership of the packet.
174 SendOrQueuePacket(queued_packet);
175 } else {
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,
189 QuicGuid guid,
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(
199 server_address,
200 client_address,
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.
212 WriteToWire(packet);
215 if (is_write_blocked_) {
216 // pending_packets_queue takes the ownership of the queued packet.
217 pending_packets_queue_.push_back(packet);
218 } else {
219 delete 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(),
230 this);
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_)
249 .Subtract(now)
250 .ToMicroseconds();
251 } else {
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_) {
266 break;
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();
274 delete oldest_guid;
276 SetGuidCleanUpAlarm();
279 } // namespace tools
280 } // namespace net