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 connection_id 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 connection_ids. This alarm should be unregistered and deleted before
37 // the QuicTimeWaitListManager is deleted.
38 class ConnectionIdCleanUpAlarm : public EpollAlarm {
40 explicit ConnectionIdCleanUpAlarm(
41 QuicTimeWaitListManager* time_wait_list_manager)
42 : time_wait_list_manager_(time_wait_list_manager) {
45 virtual int64 OnAlarm() OVERRIDE {
46 EpollAlarm::OnAlarm();
47 time_wait_list_manager_->CleanUpOldConnectionIds();
48 // Let the time wait manager register the alarm at appropriate time.
54 QuicTimeWaitListManager* time_wait_list_manager_;
57 // This class stores pending public reset packets to be sent to clients.
58 // server_address - server address on which a packet what was received for
59 // a connection_id in time wait state.
60 // client_address - address of the client that sent that packet. Needed to send
61 // the public reset packet back to the client.
62 // packet - the pending public reset packet that is to be sent to the client.
63 // created instance takes the ownership of this packet.
64 class QuicTimeWaitListManager::QueuedPacket {
66 QueuedPacket(const IPEndPoint& server_address,
67 const IPEndPoint& client_address,
68 QuicEncryptedPacket* packet)
69 : server_address_(server_address),
70 client_address_(client_address),
74 const IPEndPoint& server_address() const { return server_address_; }
75 const IPEndPoint& client_address() const { return client_address_; }
76 QuicEncryptedPacket* packet() { return packet_.get(); }
79 const IPEndPoint server_address_;
80 const IPEndPoint client_address_;
81 scoped_ptr<QuicEncryptedPacket> packet_;
83 DISALLOW_COPY_AND_ASSIGN(QueuedPacket);
86 QuicTimeWaitListManager::QuicTimeWaitListManager(
87 QuicPacketWriter* writer,
88 QuicServerSessionVisitor* visitor,
89 EpollServer* epoll_server,
90 const QuicVersionVector& supported_versions)
91 : epoll_server_(epoll_server),
92 kTimeWaitPeriod_(QuicTime::Delta::FromSeconds(kTimeWaitSeconds)),
93 connection_id_clean_up_alarm_(new ConnectionIdCleanUpAlarm(this)),
94 clock_(epoll_server_),
97 SetConnectionIdCleanUpAlarm();
100 QuicTimeWaitListManager::~QuicTimeWaitListManager() {
101 connection_id_clean_up_alarm_->UnregisterIfRegistered();
102 STLDeleteElements(&pending_packets_queue_);
103 for (ConnectionIdMap::iterator it = connection_id_map_.begin();
104 it != connection_id_map_.end();
106 delete it->second.close_packet;
110 void QuicTimeWaitListManager::AddConnectionIdToTimeWait(
111 QuicConnectionId connection_id,
113 QuicEncryptedPacket* close_packet) {
115 ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
116 if (it != connection_id_map_.end()) { // Replace record if it is reinserted.
117 num_packets = it->second.num_packets;
118 delete it->second.close_packet;
119 connection_id_map_.erase(it);
121 ConnectionIdData data(num_packets, version, clock_.ApproximateNow(),
123 connection_id_map_.insert(make_pair(connection_id, data));
126 bool QuicTimeWaitListManager::IsConnectionIdInTimeWait(
127 QuicConnectionId connection_id) const {
128 return ContainsKey(connection_id_map_, connection_id);
131 QuicVersion QuicTimeWaitListManager::GetQuicVersionFromConnectionId(
132 QuicConnectionId connection_id) {
133 ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
134 DCHECK(it != connection_id_map_.end());
135 return (it->second).version;
138 void QuicTimeWaitListManager::OnCanWrite() {
139 while (!pending_packets_queue_.empty()) {
140 QueuedPacket* queued_packet = pending_packets_queue_.front();
141 if (!WriteToWire(queued_packet)) {
144 pending_packets_queue_.pop_front();
145 delete queued_packet;
149 void QuicTimeWaitListManager::ProcessPacket(
150 const IPEndPoint& server_address,
151 const IPEndPoint& client_address,
152 QuicConnectionId connection_id,
153 QuicPacketSequenceNumber sequence_number) {
154 DCHECK(IsConnectionIdInTimeWait(connection_id));
155 // TODO(satyamshekhar): Think about handling packets from different client
157 ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
158 DCHECK(it != connection_id_map_.end());
159 // Increment the received packet count.
160 ++((it->second).num_packets);
161 if (!ShouldSendResponse((it->second).num_packets)) {
164 if (it->second.close_packet) {
165 QueuedPacket* queued_packet =
166 new QueuedPacket(server_address,
168 it->second.close_packet->Clone());
169 // Takes ownership of the packet.
170 SendOrQueuePacket(queued_packet);
172 SendPublicReset(server_address,
179 // Returns true if the number of packets received for this connection_id is a
180 // power of 2 to throttle the number of public reset packets we send to a
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 QuicConnectionId connection_id,
190 QuicPacketSequenceNumber rejected_sequence_number) {
191 QuicPublicResetPacket packet;
192 packet.public_header.connection_id = connection_id;
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 connection_id.
197 packet.nonce_proof = 1010101;
198 packet.client_address = client_address;
199 QueuedPacket* queued_packet = new QueuedPacket(
202 QuicFramer::BuildPublicResetPacket(packet));
203 // Takes ownership of the packet.
204 SendOrQueuePacket(queued_packet);
207 // Either sends the packet and deletes it or makes pending queue the
208 // owner of the packet.
209 void QuicTimeWaitListManager::SendOrQueuePacket(QueuedPacket* packet) {
210 if (WriteToWire(packet)) {
213 // pending_packets_queue takes the ownership of the queued packet.
214 pending_packets_queue_.push_back(packet);
218 bool QuicTimeWaitListManager::WriteToWire(QueuedPacket* queued_packet) {
219 if (writer_->IsWriteBlocked()) {
220 visitor_->OnWriteBlocked(this);
223 WriteResult result = writer_->WritePacket(
224 queued_packet->packet()->data(),
225 queued_packet->packet()->length(),
226 queued_packet->server_address().address(),
227 queued_packet->client_address());
228 if (result.status == WRITE_STATUS_BLOCKED) {
229 // If blocked and unbuffered, return false to retry sending.
230 DCHECK(writer_->IsWriteBlocked());
231 visitor_->OnWriteBlocked(this);
232 return writer_->IsWriteBlockedDataBuffered();
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);
241 void QuicTimeWaitListManager::SetConnectionIdCleanUpAlarm() {
242 connection_id_clean_up_alarm_->UnregisterIfRegistered();
243 int64 next_alarm_interval;
244 if (!connection_id_map_.empty()) {
245 QuicTime oldest_connection_id =
246 connection_id_map_.begin()->second.time_added;
247 QuicTime now = clock_.ApproximateNow();
248 if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) {
249 next_alarm_interval = oldest_connection_id.Add(kTimeWaitPeriod_)
253 LOG(ERROR) << "ConnectionId lingered for longer than kTimeWaitPeriod";
254 next_alarm_interval = 0;
257 // No connection_ids added so none will expire before kTimeWaitPeriod_.
258 next_alarm_interval = kTimeWaitPeriod_.ToMicroseconds();
261 epoll_server_->RegisterAlarmApproximateDelta(
262 next_alarm_interval, connection_id_clean_up_alarm_.get());
265 void QuicTimeWaitListManager::CleanUpOldConnectionIds() {
266 QuicTime now = clock_.ApproximateNow();
267 while (!connection_id_map_.empty()) {
268 ConnectionIdMap::iterator it = connection_id_map_.begin();
269 QuicTime oldest_connection_id = it->second.time_added;
270 if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) {
273 // This connection_id has lived its age, retire it now.
274 delete it->second.close_packet;
275 connection_id_map_.erase(it);
277 SetConnectionIdCleanUpAlarm();