Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / net / tools / quic / quic_time_wait_list_manager.cc
blob0068f7d77ffe0b4467c8713ac323af46601b9f26
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/logging.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_flags.h"
18 #include "net/quic/quic_framer.h"
19 #include "net/quic/quic_protocol.h"
20 #include "net/quic/quic_utils.h"
21 #include "net/tools/quic/quic_server_session.h"
23 using base::StringPiece;
25 namespace net {
26 namespace tools {
28 // A very simple alarm that just informs the QuicTimeWaitListManager to clean
29 // up old connection_ids. This alarm should be cancelled and deleted before
30 // the QuicTimeWaitListManager is deleted.
31 class ConnectionIdCleanUpAlarm : public QuicAlarm::Delegate {
32 public:
33 explicit ConnectionIdCleanUpAlarm(
34 QuicTimeWaitListManager* time_wait_list_manager)
35 : time_wait_list_manager_(time_wait_list_manager) {
38 QuicTime OnAlarm() override {
39 time_wait_list_manager_->CleanUpOldConnectionIds();
40 // Let the time wait manager register the alarm at appropriate time.
41 return QuicTime::Zero();
44 private:
45 // Not owned.
46 QuicTimeWaitListManager* time_wait_list_manager_;
48 DISALLOW_COPY_AND_ASSIGN(ConnectionIdCleanUpAlarm);
51 // This class stores pending public reset packets to be sent to clients.
52 // server_address - server address on which a packet what was received for
53 // a connection_id in time wait state.
54 // client_address - address of the client that sent that packet. Needed to send
55 // the public reset packet back to the client.
56 // packet - the pending public reset packet that is to be sent to the client.
57 // created instance takes the ownership of this packet.
58 class QuicTimeWaitListManager::QueuedPacket {
59 public:
60 QueuedPacket(const IPEndPoint& server_address,
61 const IPEndPoint& client_address,
62 QuicEncryptedPacket* packet)
63 : server_address_(server_address),
64 client_address_(client_address),
65 packet_(packet) {
68 const IPEndPoint& server_address() const { return server_address_; }
69 const IPEndPoint& client_address() const { return client_address_; }
70 QuicEncryptedPacket* packet() { return packet_.get(); }
72 private:
73 const IPEndPoint server_address_;
74 const IPEndPoint client_address_;
75 scoped_ptr<QuicEncryptedPacket> packet_;
77 DISALLOW_COPY_AND_ASSIGN(QueuedPacket);
80 QuicTimeWaitListManager::QuicTimeWaitListManager(
81 QuicPacketWriter* writer,
82 QuicServerSessionVisitor* visitor,
83 QuicConnectionHelperInterface* helper,
84 const QuicVersionVector& supported_versions)
85 : time_wait_period_(
86 // If FLAGS_increase_time_wait_list is not set and
87 // FLAGS_quic_time_wait_list_seconds has the new default value (200),
88 // replace it with the old default value (5).
89 !FLAGS_increase_time_wait_list &&
90 FLAGS_quic_time_wait_list_seconds == 200
91 ? QuicTime::Delta::FromSeconds(5)
92 : QuicTime::Delta::FromSeconds(
93 FLAGS_quic_time_wait_list_seconds)),
94 connection_id_clean_up_alarm_(
95 helper->CreateAlarm(new ConnectionIdCleanUpAlarm(this))),
96 clock_(helper->GetClock()),
97 writer_(writer),
98 visitor_(visitor) {
99 SetConnectionIdCleanUpAlarm();
102 QuicTimeWaitListManager::~QuicTimeWaitListManager() {
103 connection_id_clean_up_alarm_->Cancel();
104 STLDeleteElements(&pending_packets_queue_);
105 for (ConnectionIdMap::iterator it = connection_id_map_.begin();
106 it != connection_id_map_.end();
107 ++it) {
108 delete it->second.close_packet;
112 void QuicTimeWaitListManager::AddConnectionIdToTimeWait(
113 QuicConnectionId connection_id,
114 QuicVersion version,
115 bool connection_rejected_statelessly,
116 QuicEncryptedPacket* close_packet) {
117 DCHECK(!connection_rejected_statelessly || !close_packet)
118 << "Connections that were rejected statelessly should not "
119 << "have a close packet. connection_id = " << connection_id;
120 int num_packets = 0;
121 ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
122 const bool new_connection_id = it == connection_id_map_.end();
123 if (!new_connection_id) { // Replace record if it is reinserted.
124 num_packets = it->second.num_packets;
125 delete it->second.close_packet;
126 connection_id_map_.erase(it);
128 TrimTimeWaitListIfNeeded();
129 DCHECK_LT(
130 num_connections(),
131 // If FLAGS_increase_time_wait_list is not set and
132 // FLAGS_quic_time_wait_list_max_connections has the new default
133 // value (600000), replace it with the old default value (50000).
134 !FLAGS_increase_time_wait_list &&
135 FLAGS_quic_time_wait_list_max_connections == 600000
136 ? static_cast<size_t>(50000)
137 : static_cast<size_t>(FLAGS_quic_time_wait_list_max_connections));
138 ConnectionIdData data(num_packets, version, clock_->ApproximateNow(),
139 close_packet, connection_rejected_statelessly);
140 connection_id_map_.insert(std::make_pair(connection_id, data));
141 if (new_connection_id) {
142 visitor_->OnConnectionAddedToTimeWaitList(connection_id);
146 bool QuicTimeWaitListManager::IsConnectionIdInTimeWait(
147 QuicConnectionId connection_id) const {
148 return ContainsKey(connection_id_map_, connection_id);
151 QuicVersion QuicTimeWaitListManager::GetQuicVersionFromConnectionId(
152 QuicConnectionId connection_id) {
153 ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
154 DCHECK(it != connection_id_map_.end());
155 return (it->second).version;
158 void QuicTimeWaitListManager::OnCanWrite() {
159 while (!pending_packets_queue_.empty()) {
160 QueuedPacket* queued_packet = pending_packets_queue_.front();
161 if (!WriteToWire(queued_packet)) {
162 return;
164 pending_packets_queue_.pop_front();
165 delete queued_packet;
169 void QuicTimeWaitListManager::ProcessPacket(
170 const IPEndPoint& server_address,
171 const IPEndPoint& client_address,
172 QuicConnectionId connection_id,
173 QuicPacketSequenceNumber sequence_number,
174 const QuicEncryptedPacket& /*packet*/) {
175 DCHECK(IsConnectionIdInTimeWait(connection_id));
176 DVLOG(1) << "Processing " << connection_id << " in time wait state.";
177 // TODO(satyamshekhar): Think about handling packets from different client
178 // addresses.
179 ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
180 DCHECK(it != connection_id_map_.end());
181 // Increment the received packet count.
182 ConnectionIdData* connection_data = &it->second;
183 ++(connection_data->num_packets);
184 if (!ShouldSendResponse(connection_data->num_packets)) {
185 return;
187 if (connection_data->close_packet) {
188 QueuedPacket* queued_packet = new QueuedPacket(
189 server_address, client_address, connection_data->close_packet->Clone());
190 // Takes ownership of the packet.
191 SendOrQueuePacket(queued_packet);
192 } else if (!connection_data->connection_rejected_statelessly) {
193 SendPublicReset(server_address,
194 client_address,
195 connection_id,
196 sequence_number);
197 } else {
198 DVLOG(3) << "Time wait list not sending response for connection "
199 << connection_id << " due to previous stateless reject.";
203 // Returns true if the number of packets received for this connection_id is a
204 // power of 2 to throttle the number of public reset packets we send to a
205 // client.
206 bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count) {
207 return (received_packet_count & (received_packet_count - 1)) == 0;
210 void QuicTimeWaitListManager::SendPublicReset(
211 const IPEndPoint& server_address,
212 const IPEndPoint& client_address,
213 QuicConnectionId connection_id,
214 QuicPacketSequenceNumber rejected_sequence_number) {
215 QuicPublicResetPacket packet;
216 packet.public_header.connection_id = connection_id;
217 packet.public_header.reset_flag = true;
218 packet.public_header.version_flag = false;
219 packet.rejected_sequence_number = rejected_sequence_number;
220 // TODO(satyamshekhar): generate a valid nonce for this connection_id.
221 packet.nonce_proof = 1010101;
222 packet.client_address = client_address;
223 QueuedPacket* queued_packet = new QueuedPacket(
224 server_address,
225 client_address,
226 BuildPublicReset(packet));
227 // Takes ownership of the packet.
228 SendOrQueuePacket(queued_packet);
231 QuicEncryptedPacket* QuicTimeWaitListManager::BuildPublicReset(
232 const QuicPublicResetPacket& packet) {
233 return QuicFramer::BuildPublicResetPacket(packet);
236 // Either sends the packet and deletes it or makes pending queue the
237 // owner of the packet.
238 void QuicTimeWaitListManager::SendOrQueuePacket(QueuedPacket* packet) {
239 if (WriteToWire(packet)) {
240 delete packet;
241 } else {
242 // pending_packets_queue takes the ownership of the queued packet.
243 pending_packets_queue_.push_back(packet);
247 bool QuicTimeWaitListManager::WriteToWire(QueuedPacket* queued_packet) {
248 if (writer_->IsWriteBlocked()) {
249 visitor_->OnWriteBlocked(this);
250 return false;
252 WriteResult result = writer_->WritePacket(
253 queued_packet->packet()->data(),
254 queued_packet->packet()->length(),
255 queued_packet->server_address().address(),
256 queued_packet->client_address());
257 if (result.status == WRITE_STATUS_BLOCKED) {
258 // If blocked and unbuffered, return false to retry sending.
259 DCHECK(writer_->IsWriteBlocked());
260 visitor_->OnWriteBlocked(this);
261 return writer_->IsWriteBlockedDataBuffered();
262 } else if (result.status == WRITE_STATUS_ERROR) {
263 LOG(WARNING) << "Received unknown error while sending reset packet to "
264 << queued_packet->client_address().ToString() << ": "
265 << strerror(result.error_code);
267 return true;
270 void QuicTimeWaitListManager::SetConnectionIdCleanUpAlarm() {
271 connection_id_clean_up_alarm_->Cancel();
272 QuicTime::Delta next_alarm_interval = QuicTime::Delta::Zero();
273 if (!connection_id_map_.empty()) {
274 QuicTime oldest_connection_id =
275 connection_id_map_.begin()->second.time_added;
276 QuicTime now = clock_->ApproximateNow();
277 if (now.Subtract(oldest_connection_id) < time_wait_period_) {
278 next_alarm_interval =
279 oldest_connection_id.Add(time_wait_period_).Subtract(now);
280 } else {
281 LOG(ERROR) << "ConnectionId lingered for longer than time_wait_period_";
283 } else {
284 // No connection_ids added so none will expire before time_wait_period_.
285 next_alarm_interval = time_wait_period_;
288 connection_id_clean_up_alarm_->Set(
289 clock_->ApproximateNow().Add(next_alarm_interval));
292 bool QuicTimeWaitListManager::MaybeExpireOldestConnection(
293 QuicTime expiration_time) {
294 if (connection_id_map_.empty()) {
295 return false;
297 ConnectionIdMap::iterator it = connection_id_map_.begin();
298 QuicTime oldest_connection_id_time = it->second.time_added;
299 if (oldest_connection_id_time > expiration_time) {
300 // Too recent, don't retire.
301 return false;
303 // This connection_id has lived its age, retire it now.
304 const QuicConnectionId connection_id = it->first;
305 delete it->second.close_packet;
306 connection_id_map_.erase(it);
307 visitor_->OnConnectionRemovedFromTimeWaitList(connection_id);
308 return true;
311 void QuicTimeWaitListManager::CleanUpOldConnectionIds() {
312 QuicTime now = clock_->ApproximateNow();
313 QuicTime expiration = now.Subtract(time_wait_period_);
315 while (MaybeExpireOldestConnection(expiration)) {
318 SetConnectionIdCleanUpAlarm();
321 void QuicTimeWaitListManager::TrimTimeWaitListIfNeeded() {
322 if (FLAGS_quic_time_wait_list_max_connections < 0) {
323 return;
325 size_t temp_max_connections =
326 static_cast<size_t>(FLAGS_quic_time_wait_list_max_connections);
327 // If FLAGS_increase_time_wait_list is not set and
328 // FLAGS_quic_time_wait_list_max_connections has the new default value
329 // (600000), replace it with the old default value (50000).
330 if (!FLAGS_increase_time_wait_list &&
331 FLAGS_quic_time_wait_list_max_connections == 600000) {
332 temp_max_connections = static_cast<size_t>(50000);
334 while (num_connections() >= temp_max_connections) {
335 MaybeExpireOldestConnection(QuicTime::Infinite());
339 } // namespace tools
340 } // namespace net