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"
28 // Time period for which the guid should live in time wait state..
29 const int kTimeWaitSeconds = 5;
33 // A very simple alarm that just informs the QuicTimeWaitListManager to clean
34 // up old guids. This alarm should be unregistered and deleted before the
35 // QuicTimeWaitListManager is deleted.
36 class GuidCleanUpAlarm : public EpollAlarm {
38 explicit GuidCleanUpAlarm(QuicTimeWaitListManager* time_wait_list_manager)
39 : time_wait_list_manager_(time_wait_list_manager) {
42 virtual int64 OnAlarm() OVERRIDE {
43 EpollAlarm::OnAlarm();
44 time_wait_list_manager_->CleanUpOldGuids();
45 // Let the time wait manager register the alarm at appropriate time.
51 QuicTimeWaitListManager* time_wait_list_manager_;
54 struct QuicTimeWaitListManager::GuidAddTime {
55 GuidAddTime(QuicGuid guid, const QuicTime& time)
64 // This class stores pending public reset packets to be sent to clients.
65 // server_address - server address on which a packet what was received for
66 // a guid in time wait state.
67 // client_address - address of the client that sent that packet. Needed to send
68 // the public reset packet back to the client.
69 // packet - the pending public reset packet that is to be sent to the client.
70 // created instance takes the ownership of this packet.
71 class QuicTimeWaitListManager::QueuedPacket {
73 QueuedPacket(const IPEndPoint& server_address,
74 const IPEndPoint& client_address,
75 QuicEncryptedPacket* packet)
76 : server_address_(server_address),
77 client_address_(client_address),
81 const IPEndPoint& server_address() const { return server_address_; }
82 const IPEndPoint& client_address() const { return client_address_; }
83 QuicEncryptedPacket* packet() { return packet_.get(); }
86 const IPEndPoint server_address_;
87 const IPEndPoint client_address_;
88 scoped_ptr<QuicEncryptedPacket> packet_;
90 DISALLOW_COPY_AND_ASSIGN(QueuedPacket);
93 QuicTimeWaitListManager::QuicTimeWaitListManager(
94 QuicPacketWriter* writer,
95 EpollServer* epoll_server,
96 const QuicVersionVector& supported_versions)
97 : framer_(supported_versions,
98 QuicTime::Zero(), // unused
100 epoll_server_(epoll_server),
101 kTimeWaitPeriod_(QuicTime::Delta::FromSeconds(kTimeWaitSeconds)),
102 guid_clean_up_alarm_(new GuidCleanUpAlarm(this)),
103 clock_(epoll_server),
105 is_write_blocked_(false) {
106 framer_.set_visitor(this);
107 SetGuidCleanUpAlarm();
110 QuicTimeWaitListManager::~QuicTimeWaitListManager() {
111 guid_clean_up_alarm_->UnregisterIfRegistered();
112 STLDeleteElements(&time_ordered_guid_list_);
113 STLDeleteElements(&pending_packets_queue_);
116 void QuicTimeWaitListManager::AddGuidToTimeWait(QuicGuid guid,
117 QuicVersion version) {
118 DCHECK(!IsGuidInTimeWait(guid));
119 // Initialize the guid with 0 packets received.
120 GuidData data(0, version);
121 guid_map_.insert(make_pair(guid, data));
122 time_ordered_guid_list_.push_back(new GuidAddTime(guid,
123 clock_.ApproximateNow()));
126 bool QuicTimeWaitListManager::IsGuidInTimeWait(QuicGuid guid) const {
127 return guid_map_.find(guid) != guid_map_.end();
130 void QuicTimeWaitListManager::ProcessPacket(
131 const IPEndPoint& server_address,
132 const IPEndPoint& client_address,
134 const QuicEncryptedPacket& packet) {
135 DCHECK(IsGuidInTimeWait(guid));
136 server_address_ = server_address;
137 client_address_ = client_address;
139 // Set the framer to the appropriate version for this GUID, before processing.
140 QuicVersion version = GetQuicVersionFromGuid(guid);
141 framer_.set_version(version);
143 framer_.ProcessPacket(packet);
146 QuicVersion QuicTimeWaitListManager::GetQuicVersionFromGuid(QuicGuid guid) {
147 GuidMapIterator it = guid_map_.find(guid);
148 DCHECK(it != guid_map_.end());
149 return (it->second).version;
152 bool QuicTimeWaitListManager::OnCanWrite() {
153 is_write_blocked_ = false;
154 while (!is_write_blocked_ && !pending_packets_queue_.empty()) {
155 QueuedPacket* queued_packet = pending_packets_queue_.front();
156 WriteToWire(queued_packet);
157 if (!is_write_blocked_) {
158 pending_packets_queue_.pop_front();
159 delete queued_packet;
163 return !is_write_blocked_;
166 void QuicTimeWaitListManager::OnError(QuicFramer* framer) {
167 DLOG(INFO) << QuicUtils::ErrorToString(framer->error());
170 bool QuicTimeWaitListManager::OnProtocolVersionMismatch(
171 QuicVersion received_version) {
172 // Drop such packets whose version don't match.
176 bool QuicTimeWaitListManager::OnStreamFrame(const QuicStreamFrame& frame) {
180 bool QuicTimeWaitListManager::OnAckFrame(const QuicAckFrame& frame) {
184 bool QuicTimeWaitListManager::OnCongestionFeedbackFrame(
185 const QuicCongestionFeedbackFrame& frame) {
189 bool QuicTimeWaitListManager::OnRstStreamFrame(
190 const QuicRstStreamFrame& frame) {
194 bool QuicTimeWaitListManager::OnConnectionCloseFrame(
195 const QuicConnectionCloseFrame & frame) {
199 bool QuicTimeWaitListManager::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
203 bool QuicTimeWaitListManager::OnPacketHeader(const QuicPacketHeader& header) {
204 // TODO(satyamshekhar): Think about handling packets from different client
206 GuidMapIterator it = guid_map_.find(header.public_header.guid);
207 DCHECK(it != guid_map_.end());
208 // Increment the received packet count.
209 ++((it->second).num_packets);
210 if (ShouldSendPublicReset((it->second).num_packets)) {
211 // We don't need the packet anymore. Just tell the client what sequence
212 // number we rejected.
213 SendPublicReset(server_address_,
215 header.public_header.guid,
216 header.packet_sequence_number);
218 // Never process the body of the packet in time wait state.
222 // Returns true if the number of packets received for this guid is a power of 2
223 // to throttle the number of public reset packets we send to a client.
224 bool QuicTimeWaitListManager::ShouldSendPublicReset(int received_packet_count) {
225 return (received_packet_count & (received_packet_count - 1)) == 0;
228 void QuicTimeWaitListManager::SendPublicReset(
229 const IPEndPoint& server_address,
230 const IPEndPoint& client_address,
232 QuicPacketSequenceNumber rejected_sequence_number) {
233 QuicPublicResetPacket packet;
234 packet.public_header.guid = guid;
235 packet.public_header.reset_flag = true;
236 packet.public_header.version_flag = false;
237 packet.rejected_sequence_number = rejected_sequence_number;
238 // TODO(satyamshekhar): generate a valid nonce for this guid.
239 packet.nonce_proof = 1010101;
240 QueuedPacket* queued_packet = new QueuedPacket(
243 framer_.BuildPublicResetPacket(packet));
244 // Takes ownership of the packet.
245 SendOrQueuePacket(queued_packet);
248 // Either sends the packet and deletes it or makes pending queue the
249 // owner of the packet.
250 void QuicTimeWaitListManager::SendOrQueuePacket(QueuedPacket* packet) {
251 if (!is_write_blocked_) {
252 // TODO(satyamshekhar): Handle packets that fail due to error other than
253 // EAGAIN or EWOULDBLOCK.
257 if (is_write_blocked_) {
258 // pending_packets_queue takes the ownership of the queued packet.
259 pending_packets_queue_.push_back(packet);
265 void QuicTimeWaitListManager::WriteToWire(QueuedPacket* queued_packet) {
266 DCHECK(!is_write_blocked_);
267 WriteResult result = writer_->WritePacket(
268 queued_packet->packet()->data(),
269 queued_packet->packet()->length(),
270 queued_packet->server_address().address(),
271 queued_packet->client_address(),
274 if (result.status == WRITE_STATUS_BLOCKED) {
275 is_write_blocked_ = true;
276 } else if (result.status == WRITE_STATUS_ERROR) {
277 LOG(WARNING) << "Received unknown error while sending reset packet to "
278 << queued_packet->client_address().ToString() << ": "
279 << strerror(result.error_code);
283 void QuicTimeWaitListManager::SetGuidCleanUpAlarm() {
284 guid_clean_up_alarm_->UnregisterIfRegistered();
285 int64 next_alarm_interval;
286 if (!time_ordered_guid_list_.empty()) {
287 GuidAddTime* oldest_guid = time_ordered_guid_list_.front();
288 QuicTime now = clock_.ApproximateNow();
289 DCHECK(now.Subtract(oldest_guid->time_added) < kTimeWaitPeriod_);
290 next_alarm_interval = oldest_guid->time_added
291 .Add(kTimeWaitPeriod_)
295 // No guids added so none will expire before kTimeWaitPeriod_.
296 next_alarm_interval = kTimeWaitPeriod_.ToMicroseconds();
299 epoll_server_->RegisterAlarmApproximateDelta(next_alarm_interval,
300 guid_clean_up_alarm_.get());
303 void QuicTimeWaitListManager::CleanUpOldGuids() {
304 QuicTime now = clock_.ApproximateNow();
305 while (time_ordered_guid_list_.size() > 0) {
306 DCHECK_EQ(time_ordered_guid_list_.size(), guid_map_.size());
307 GuidAddTime* oldest_guid = time_ordered_guid_list_.front();
308 if (now.Subtract(oldest_guid->time_added) < kTimeWaitPeriod_) {
311 // This guid has lived its age, retire it now.
312 guid_map_.erase(oldest_guid->guid);
313 time_ordered_guid_list_.pop_front();
316 SetGuidCleanUpAlarm();