diff options
Diffstat (limited to 'chromium/net/quic/quic_time_wait_list_manager.cc')
-rw-r--r-- | chromium/net/quic/quic_time_wait_list_manager.cc | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/chromium/net/quic/quic_time_wait_list_manager.cc b/chromium/net/quic/quic_time_wait_list_manager.cc new file mode 100644 index 00000000000..d1f3419e77d --- /dev/null +++ b/chromium/net/quic/quic_time_wait_list_manager.cc @@ -0,0 +1,283 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/quic_time_wait_list_manager.h" + +#include <errno.h> + +#include "base/containers/hash_tables.h" +#include "base/memory/scoped_ptr.h" +#include "base/stl_util.h" +#include "net/base/ip_endpoint.h" +#include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/crypto/quic_decrypter.h" +#include "net/quic/crypto/quic_encrypter.h" +#include "net/quic/quic_clock.h" +#include "net/quic/quic_connection_helper.h" +#include "net/quic/quic_framer.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/quic_server_session.h" +#include "net/quic/quic_utils.h" + +using base::StringPiece; +using std::make_pair; + +namespace net { + +namespace { + +// Time period for which the connection_id should live in time wait state.. +const int kTimeWaitSeconds = 5; + +} // namespace + +// A very simple alarm that just informs the QuicTimeWaitListManager to clean +// up old connection_ids. This alarm should be unregistered and deleted before +// the QuicTimeWaitListManager is deleted. +class ConnectionIdCleanUpAlarm : public QuicAlarm::Delegate { + public: + explicit ConnectionIdCleanUpAlarm( + QuicTimeWaitListManager* time_wait_list_manager) + : time_wait_list_manager_(time_wait_list_manager) {} + + virtual QuicTime OnAlarm() OVERRIDE { + time_wait_list_manager_->CleanUpOldConnectionIds(); + // Let the time wait manager register the alarm at appropriate time. + return QuicTime::Zero(); + } + + private: + // Not owned. + QuicTimeWaitListManager* time_wait_list_manager_; +}; + +// This class stores pending public reset packets to be sent to clients. +// server_address - server address on which a packet what was received for +// a connection_id in time wait state. +// client_address - address of the client that sent that packet. Needed to send +// the public reset packet back to the client. +// packet - the pending public reset packet that is to be sent to the client. +// created instance takes the ownership of this packet. +class QuicTimeWaitListManager::QueuedPacket { + public: + QueuedPacket(const IPEndPoint& server_address, + const IPEndPoint& client_address, + QuicEncryptedPacket* packet) + : server_address_(server_address), + client_address_(client_address), + packet_(packet) {} + + const IPEndPoint& server_address() const { return server_address_; } + const IPEndPoint& client_address() const { return client_address_; } + QuicEncryptedPacket* packet() { return packet_.get(); } + + private: + const IPEndPoint server_address_; + const IPEndPoint client_address_; + scoped_ptr<QuicEncryptedPacket> packet_; + + DISALLOW_COPY_AND_ASSIGN(QueuedPacket); +}; + +QuicTimeWaitListManager::QuicTimeWaitListManager( + QuicPacketWriter* writer, + QuicServerSessionVisitor* visitor, + QuicConnectionHelperInterface* helper, + const QuicVersionVector& supported_versions) + : helper_(helper), + kTimeWaitPeriod_(QuicTime::Delta::FromSeconds(kTimeWaitSeconds)), + connection_id_clean_up_alarm_( + helper_->CreateAlarm(new ConnectionIdCleanUpAlarm(this))), + writer_(writer), + visitor_(visitor) { + SetConnectionIdCleanUpAlarm(); +} + +QuicTimeWaitListManager::~QuicTimeWaitListManager() { + connection_id_clean_up_alarm_->Cancel(); + STLDeleteElements(&pending_packets_queue_); + for (ConnectionIdMap::iterator it = connection_id_map_.begin(); + it != connection_id_map_.end(); + ++it) { + delete it->second.close_packet; + } +} + +void QuicTimeWaitListManager::AddConnectionIdToTimeWait( + QuicConnectionId connection_id, + QuicVersion version, + QuicEncryptedPacket* close_packet) { + DVLOG(1) << "Adding " << connection_id << " to the time wait list."; + int num_packets = 0; + ConnectionIdMap::iterator it = connection_id_map_.find(connection_id); + if (it != connection_id_map_.end()) { // Replace record if it is reinserted. + num_packets = it->second.num_packets; + delete it->second.close_packet; + connection_id_map_.erase(it); + } + ConnectionIdData data(num_packets, + version, + helper_->GetClock()->ApproximateNow(), + close_packet); + connection_id_map_.insert(make_pair(connection_id, data)); +} + +bool QuicTimeWaitListManager::IsConnectionIdInTimeWait( + QuicConnectionId connection_id) const { + return ContainsKey(connection_id_map_, connection_id); +} + +QuicVersion QuicTimeWaitListManager::GetQuicVersionFromConnectionId( + QuicConnectionId connection_id) { + ConnectionIdMap::iterator it = connection_id_map_.find(connection_id); + DCHECK(it != connection_id_map_.end()); + return (it->second).version; +} + +void QuicTimeWaitListManager::OnCanWrite() { + while (!pending_packets_queue_.empty()) { + QueuedPacket* queued_packet = pending_packets_queue_.front(); + if (!WriteToWire(queued_packet)) { + return; + } + pending_packets_queue_.pop_front(); + delete queued_packet; + } +} + +void QuicTimeWaitListManager::ProcessPacket( + const IPEndPoint& server_address, + const IPEndPoint& client_address, + QuicConnectionId connection_id, + QuicPacketSequenceNumber sequence_number, + const QuicEncryptedPacket& /*packet*/) { + DCHECK(IsConnectionIdInTimeWait(connection_id)); + DVLOG(1) << "Processing " << connection_id << " in time wait state."; + // TODO(satyamshekhar): Think about handling packets from different client + // addresses. + ConnectionIdMap::iterator it = connection_id_map_.find(connection_id); + DCHECK(it != connection_id_map_.end()); + // Increment the received packet count. + ++((it->second).num_packets); + if (!ShouldSendResponse((it->second).num_packets)) { + return; + } + if (it->second.close_packet) { + QueuedPacket* queued_packet = + new QueuedPacket(server_address, + client_address, + it->second.close_packet->Clone()); + // Takes ownership of the packet. + SendOrQueuePacket(queued_packet); + } else { + SendPublicReset(server_address, + client_address, + connection_id, + sequence_number); + } +} + +// Returns true if the number of packets received for this connection_id is a +// power of 2 to throttle the number of public reset packets we send to a +// client. +bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count) { + return (received_packet_count & (received_packet_count - 1)) == 0; +} + +void QuicTimeWaitListManager::SendPublicReset( + const IPEndPoint& server_address, + const IPEndPoint& client_address, + QuicConnectionId connection_id, + QuicPacketSequenceNumber rejected_sequence_number) { + QuicPublicResetPacket packet; + packet.public_header.connection_id = connection_id; + packet.public_header.reset_flag = true; + packet.public_header.version_flag = false; + packet.rejected_sequence_number = rejected_sequence_number; + // TODO(satyamshekhar): generate a valid nonce for this connection_id. + packet.nonce_proof = 1010101; + packet.client_address = client_address; + QueuedPacket* queued_packet = new QueuedPacket( + server_address, + client_address, + BuildPublicReset(packet)); + // Takes ownership of the packet. + SendOrQueuePacket(queued_packet); +} + +QuicEncryptedPacket* QuicTimeWaitListManager::BuildPublicReset( + const QuicPublicResetPacket& packet) { + return QuicFramer::BuildPublicResetPacket(packet); +} + +// Either sends the packet and deletes it or makes pending queue the +// owner of the packet. +void QuicTimeWaitListManager::SendOrQueuePacket(QueuedPacket* packet) { + if (WriteToWire(packet)) { + delete packet; + } else { + // pending_packets_queue takes the ownership of the queued packet. + pending_packets_queue_.push_back(packet); + } +} + +bool QuicTimeWaitListManager::WriteToWire(QueuedPacket* queued_packet) { + if (writer_->IsWriteBlocked()) { + visitor_->OnWriteBlocked(this); + return false; + } + WriteResult result = writer_->WritePacket( + queued_packet->packet()->data(), + queued_packet->packet()->length(), + queued_packet->server_address().address(), + queued_packet->client_address()); + if (result.status == WRITE_STATUS_BLOCKED) { + // If blocked and unbuffered, return false to retry sending. + DCHECK(writer_->IsWriteBlocked()); + visitor_->OnWriteBlocked(this); + return writer_->IsWriteBlockedDataBuffered(); + } else if (result.status == WRITE_STATUS_ERROR) { + LOG(WARNING) << "Received unknown error while sending reset packet to " + << queued_packet->client_address().ToString() << ": " + << strerror(result.error_code); + } + return true; +} + +void QuicTimeWaitListManager::SetConnectionIdCleanUpAlarm() { + connection_id_clean_up_alarm_->Cancel(); + QuicTime now = helper_->GetClock()->ApproximateNow(); + QuicTime next_alarm_time = now; + if (!connection_id_map_.empty()) { + QuicTime oldest_connection_id = + connection_id_map_.begin()->second.time_added; + if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) { + next_alarm_time = oldest_connection_id.Add(kTimeWaitPeriod_); + } else { + LOG(ERROR) << "ConnectionId lingered for longer than kTimeWaitPeriod"; + } + } else { + // No connection_ids added so none will expire before kTimeWaitPeriod_. + next_alarm_time = now.Add(kTimeWaitPeriod_); + } + + connection_id_clean_up_alarm_->Set(next_alarm_time); +} + +void QuicTimeWaitListManager::CleanUpOldConnectionIds() { + QuicTime now = helper_->GetClock()->ApproximateNow(); + while (!connection_id_map_.empty()) { + ConnectionIdMap::iterator it = connection_id_map_.begin(); + QuicTime oldest_connection_id = it->second.time_added; + if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) { + break; + } + // This connection_id has lived its age, retire it now. + delete it->second.close_packet; + connection_id_map_.erase(it); + } + SetConnectionIdCleanUpAlarm(); +} + +} // namespace net |