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. 4 5#include "net/tools/quic/quic_time_wait_list_manager.h" 6 7#include <errno.h> 8 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/epoll_server/epoll_server.h" 21#include "net/tools/quic/quic_server_session.h" 22 23using base::StringPiece; 24using std::make_pair; 25 26namespace net { 27namespace tools { 28 29namespace { 30 31// Time period for which the connection_id should live in time wait state.. 32const int kTimeWaitSeconds = 5; 33 34} // namespace 35 36// A very simple alarm that just informs the QuicTimeWaitListManager to clean 37// up old connection_ids. This alarm should be unregistered and deleted before 38// the QuicTimeWaitListManager is deleted. 39class ConnectionIdCleanUpAlarm : public EpollAlarm { 40 public: 41 explicit ConnectionIdCleanUpAlarm( 42 QuicTimeWaitListManager* time_wait_list_manager) 43 : time_wait_list_manager_(time_wait_list_manager) { 44 } 45 46 virtual int64 OnAlarm() OVERRIDE { 47 EpollAlarm::OnAlarm(); 48 time_wait_list_manager_->CleanUpOldConnectionIds(); 49 // Let the time wait manager register the alarm at appropriate time. 50 return 0; 51 } 52 53 private: 54 // Not owned. 55 QuicTimeWaitListManager* time_wait_list_manager_; 56}; 57 58// This class stores pending public reset packets to be sent to clients. 59// server_address - server address on which a packet what was received for 60// a connection_id in time wait state. 61// client_address - address of the client that sent that packet. Needed to send 62// the public reset packet back to the client. 63// packet - the pending public reset packet that is to be sent to the client. 64// created instance takes the ownership of this packet. 65class QuicTimeWaitListManager::QueuedPacket { 66 public: 67 QueuedPacket(const IPEndPoint& server_address, 68 const IPEndPoint& client_address, 69 QuicEncryptedPacket* packet) 70 : server_address_(server_address), 71 client_address_(client_address), 72 packet_(packet) { 73 } 74 75 const IPEndPoint& server_address() const { return server_address_; } 76 const IPEndPoint& client_address() const { return client_address_; } 77 QuicEncryptedPacket* packet() { return packet_.get(); } 78 79 private: 80 const IPEndPoint server_address_; 81 const IPEndPoint client_address_; 82 scoped_ptr<QuicEncryptedPacket> packet_; 83 84 DISALLOW_COPY_AND_ASSIGN(QueuedPacket); 85}; 86 87QuicTimeWaitListManager::QuicTimeWaitListManager( 88 QuicPacketWriter* writer, 89 QuicServerSessionVisitor* visitor, 90 EpollServer* epoll_server, 91 const QuicVersionVector& supported_versions) 92 : epoll_server_(epoll_server), 93 kTimeWaitPeriod_(QuicTime::Delta::FromSeconds(kTimeWaitSeconds)), 94 connection_id_clean_up_alarm_(new ConnectionIdCleanUpAlarm(this)), 95 clock_(epoll_server_), 96 writer_(writer), 97 visitor_(visitor) { 98 SetConnectionIdCleanUpAlarm(); 99} 100 101QuicTimeWaitListManager::~QuicTimeWaitListManager() { 102 connection_id_clean_up_alarm_->UnregisterIfRegistered(); 103 STLDeleteElements(&pending_packets_queue_); 104 for (ConnectionIdMap::iterator it = connection_id_map_.begin(); 105 it != connection_id_map_.end(); 106 ++it) { 107 delete it->second.close_packet; 108 } 109} 110 111void QuicTimeWaitListManager::AddConnectionIdToTimeWait( 112 QuicConnectionId connection_id, 113 QuicVersion version, 114 QuicEncryptedPacket* close_packet) { 115 DVLOG(1) << "Adding " << connection_id << " to the time wait list."; 116 int num_packets = 0; 117 ConnectionIdMap::iterator it = connection_id_map_.find(connection_id); 118 if (it != connection_id_map_.end()) { // Replace record if it is reinserted. 119 num_packets = it->second.num_packets; 120 delete it->second.close_packet; 121 connection_id_map_.erase(it); 122 } 123 ConnectionIdData data(num_packets, version, clock_.ApproximateNow(), 124 close_packet); 125 connection_id_map_.insert(make_pair(connection_id, data)); 126} 127 128bool QuicTimeWaitListManager::IsConnectionIdInTimeWait( 129 QuicConnectionId connection_id) const { 130 return ContainsKey(connection_id_map_, connection_id); 131} 132 133QuicVersion QuicTimeWaitListManager::GetQuicVersionFromConnectionId( 134 QuicConnectionId connection_id) { 135 ConnectionIdMap::iterator it = connection_id_map_.find(connection_id); 136 DCHECK(it != connection_id_map_.end()); 137 return (it->second).version; 138} 139 140void QuicTimeWaitListManager::OnCanWrite() { 141 while (!pending_packets_queue_.empty()) { 142 QueuedPacket* queued_packet = pending_packets_queue_.front(); 143 if (!WriteToWire(queued_packet)) { 144 return; 145 } 146 pending_packets_queue_.pop_front(); 147 delete queued_packet; 148 } 149} 150 151void QuicTimeWaitListManager::ProcessPacket( 152 const IPEndPoint& server_address, 153 const IPEndPoint& client_address, 154 QuicConnectionId connection_id, 155 QuicPacketSequenceNumber sequence_number, 156 const QuicEncryptedPacket& /*packet*/) { 157 DCHECK(IsConnectionIdInTimeWait(connection_id)); 158 DVLOG(1) << "Processing " << connection_id << " in time wait state."; 159 // TODO(satyamshekhar): Think about handling packets from different client 160 // addresses. 161 ConnectionIdMap::iterator it = connection_id_map_.find(connection_id); 162 DCHECK(it != connection_id_map_.end()); 163 // Increment the received packet count. 164 ++((it->second).num_packets); 165 if (!ShouldSendResponse((it->second).num_packets)) { 166 return; 167 } 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, 177 client_address, 178 connection_id, 179 sequence_number); 180 } 181} 182 183// Returns true if the number of packets received for this connection_id is a 184// power of 2 to throttle the number of public reset packets we send to a 185// client. 186bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count) { 187 return (received_packet_count & (received_packet_count - 1)) == 0; 188} 189 190void QuicTimeWaitListManager::SendPublicReset( 191 const IPEndPoint& server_address, 192 const IPEndPoint& client_address, 193 QuicConnectionId connection_id, 194 QuicPacketSequenceNumber rejected_sequence_number) { 195 QuicPublicResetPacket packet; 196 packet.public_header.connection_id = connection_id; 197 packet.public_header.reset_flag = true; 198 packet.public_header.version_flag = false; 199 packet.rejected_sequence_number = rejected_sequence_number; 200 // TODO(satyamshekhar): generate a valid nonce for this connection_id. 201 packet.nonce_proof = 1010101; 202 packet.client_address = client_address; 203 QueuedPacket* queued_packet = new QueuedPacket( 204 server_address, 205 client_address, 206 BuildPublicReset(packet)); 207 // Takes ownership of the packet. 208 SendOrQueuePacket(queued_packet); 209} 210 211QuicEncryptedPacket* QuicTimeWaitListManager::BuildPublicReset( 212 const QuicPublicResetPacket& packet) { 213 return QuicFramer::BuildPublicResetPacket(packet); 214} 215 216// Either sends the packet and deletes it or makes pending queue the 217// owner of the packet. 218void QuicTimeWaitListManager::SendOrQueuePacket(QueuedPacket* packet) { 219 if (WriteToWire(packet)) { 220 delete packet; 221 } else { 222 // pending_packets_queue takes the ownership of the queued packet. 223 pending_packets_queue_.push_back(packet); 224 } 225} 226 227bool QuicTimeWaitListManager::WriteToWire(QueuedPacket* queued_packet) { 228 if (writer_->IsWriteBlocked()) { 229 visitor_->OnWriteBlocked(this); 230 return false; 231 } 232 WriteResult result = writer_->WritePacket( 233 queued_packet->packet()->data(), 234 queued_packet->packet()->length(), 235 queued_packet->server_address().address(), 236 queued_packet->client_address()); 237 if (result.status == WRITE_STATUS_BLOCKED) { 238 // If blocked and unbuffered, return false to retry sending. 239 DCHECK(writer_->IsWriteBlocked()); 240 visitor_->OnWriteBlocked(this); 241 return writer_->IsWriteBlockedDataBuffered(); 242 } else if (result.status == WRITE_STATUS_ERROR) { 243 LOG(WARNING) << "Received unknown error while sending reset packet to " 244 << queued_packet->client_address().ToString() << ": " 245 << strerror(result.error_code); 246 } 247 return true; 248} 249 250void QuicTimeWaitListManager::SetConnectionIdCleanUpAlarm() { 251 connection_id_clean_up_alarm_->UnregisterIfRegistered(); 252 int64 next_alarm_interval; 253 if (!connection_id_map_.empty()) { 254 QuicTime oldest_connection_id = 255 connection_id_map_.begin()->second.time_added; 256 QuicTime now = clock_.ApproximateNow(); 257 if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) { 258 next_alarm_interval = oldest_connection_id.Add(kTimeWaitPeriod_) 259 .Subtract(now) 260 .ToMicroseconds(); 261 } else { 262 LOG(ERROR) << "ConnectionId lingered for longer than kTimeWaitPeriod"; 263 next_alarm_interval = 0; 264 } 265 } else { 266 // No connection_ids added so none will expire before kTimeWaitPeriod_. 267 next_alarm_interval = kTimeWaitPeriod_.ToMicroseconds(); 268 } 269 270 epoll_server_->RegisterAlarmApproximateDelta( 271 next_alarm_interval, connection_id_clean_up_alarm_.get()); 272} 273 274void QuicTimeWaitListManager::CleanUpOldConnectionIds() { 275 QuicTime now = clock_.ApproximateNow(); 276 while (!connection_id_map_.empty()) { 277 ConnectionIdMap::iterator it = connection_id_map_.begin(); 278 QuicTime oldest_connection_id = it->second.time_added; 279 if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) { 280 break; 281 } 282 // This connection_id has lived its age, retire it now. 283 delete it->second.close_packet; 284 connection_id_map_.erase(it); 285 } 286 SetConnectionIdCleanUpAlarm(); 287} 288 289} // namespace tools 290} // namespace net 291