diff options
Diffstat (limited to 'chromium/net/tools/quic/quic_time_wait_list_manager.cc')
-rw-r--r-- | chromium/net/tools/quic/quic_time_wait_list_manager.cc | 322 |
1 files changed, 322 insertions, 0 deletions
diff --git a/chromium/net/tools/quic/quic_time_wait_list_manager.cc b/chromium/net/tools/quic/quic_time_wait_list_manager.cc new file mode 100644 index 00000000000..a2fc4f84cb6 --- /dev/null +++ b/chromium/net/tools/quic/quic_time_wait_list_manager.cc @@ -0,0 +1,322 @@ +// Copyright (c) 2012 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/tools/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_framer.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/quic_utils.h" + +using std::make_pair; + +namespace net { +namespace tools { + +namespace { + +// Time period for which the guid should live in time wait state.. +const int kTimeWaitSeconds = 5; + +} // namespace + +// A very simple alarm that just informs the QuicTimeWaitListManager to clean +// up old guids. This alarm should be unregistered and deleted before the +// QuicTimeWaitListManager is deleted. +class GuidCleanUpAlarm : public EpollAlarm { + public: + explicit GuidCleanUpAlarm(QuicTimeWaitListManager* time_wait_list_manager) + : time_wait_list_manager_(time_wait_list_manager) { + } + + virtual int64 OnAlarm() OVERRIDE { + EpollAlarm::OnAlarm(); + time_wait_list_manager_->CleanUpOldGuids(); + // Let the time wait manager register the alarm at appropriate time. + return 0; + } + + private: + // Not owned. + QuicTimeWaitListManager* time_wait_list_manager_; +}; + +struct QuicTimeWaitListManager::GuidAddTime { + GuidAddTime(QuicGuid guid, const QuicTime& time) + : guid(guid), + time_added(time) { + } + + QuicGuid guid; + QuicTime time_added; +}; + +// 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 guid 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, + EpollServer* epoll_server) + : framer_(QuicVersionMax(), + QuicTime::Zero(), // unused + true), + epoll_server_(epoll_server), + kTimeWaitPeriod_(QuicTime::Delta::FromSeconds(kTimeWaitSeconds)), + guid_clean_up_alarm_(new GuidCleanUpAlarm(this)), + clock_(epoll_server), + writer_(writer), + is_write_blocked_(false) { + framer_.set_visitor(this); + SetGuidCleanUpAlarm(); +} + +QuicTimeWaitListManager::~QuicTimeWaitListManager() { + guid_clean_up_alarm_->UnregisterIfRegistered(); + STLDeleteElements(&time_ordered_guid_list_); + STLDeleteElements(&pending_packets_queue_); +} + +void QuicTimeWaitListManager::AddGuidToTimeWait(QuicGuid guid, + QuicVersion version) { + DCHECK(!IsGuidInTimeWait(guid)); + // Initialize the guid with 0 packets received. + GuidData data(0, version); + guid_map_.insert(make_pair(guid, data)); + time_ordered_guid_list_.push_back(new GuidAddTime(guid, + clock_.ApproximateNow())); +} + +bool QuicTimeWaitListManager::IsGuidInTimeWait(QuicGuid guid) const { + return guid_map_.find(guid) != guid_map_.end(); +} + +void QuicTimeWaitListManager::ProcessPacket( + const IPEndPoint& server_address, + const IPEndPoint& client_address, + QuicGuid guid, + const QuicEncryptedPacket& packet) { + DCHECK(IsGuidInTimeWait(guid)); + server_address_ = server_address; + client_address_ = client_address; + + // Set the framer to the appropriate version for this GUID, before processing. + QuicVersion version = GetQuicVersionFromGuid(guid); + framer_.set_version(version); + + framer_.ProcessPacket(packet); +} + +QuicVersion QuicTimeWaitListManager::GetQuicVersionFromGuid(QuicGuid guid) { + GuidMapIterator it = guid_map_.find(guid); + DCHECK(it != guid_map_.end()); + return (it->second).version; +} + +bool QuicTimeWaitListManager::OnCanWrite() { + is_write_blocked_ = false; + while (!is_write_blocked_ && !pending_packets_queue_.empty()) { + QueuedPacket* queued_packet = pending_packets_queue_.front(); + WriteToWire(queued_packet); + if (!is_write_blocked_) { + pending_packets_queue_.pop_front(); + delete queued_packet; + } + } + + return !is_write_blocked_; +} + +void QuicTimeWaitListManager::OnError(QuicFramer* framer) { + DLOG(INFO) << QuicUtils::ErrorToString(framer->error()); +} + +bool QuicTimeWaitListManager::OnProtocolVersionMismatch( + QuicVersion received_version) { + // Drop such packets whose version don't match. + return false; +} + +bool QuicTimeWaitListManager::OnStreamFrame(const QuicStreamFrame& frame) { + return false; +} + +bool QuicTimeWaitListManager::OnAckFrame(const QuicAckFrame& frame) { + return false; +} + +bool QuicTimeWaitListManager::OnCongestionFeedbackFrame( + const QuicCongestionFeedbackFrame& frame) { + return false; +} + +bool QuicTimeWaitListManager::OnRstStreamFrame( + const QuicRstStreamFrame& frame) { + return false; +} + +bool QuicTimeWaitListManager::OnConnectionCloseFrame( + const QuicConnectionCloseFrame & frame) { + return false; +} + +bool QuicTimeWaitListManager::OnGoAwayFrame(const QuicGoAwayFrame& frame) { + return false; +} + +bool QuicTimeWaitListManager::OnPacketHeader(const QuicPacketHeader& header) { + // TODO(satyamshekhar): Think about handling packets from different client + // addresses. + GuidMapIterator it = guid_map_.find(header.public_header.guid); + DCHECK(it != guid_map_.end()); + // Increment the received packet count. + ++((it->second).num_packets); + if (ShouldSendPublicReset((it->second).num_packets)) { + // We don't need the packet anymore. Just tell the client what sequence + // number we rejected. + SendPublicReset(server_address_, + client_address_, + header.public_header.guid, + header.packet_sequence_number); + } + // Never process the body of the packet in time wait state. + return false; +} + +// Returns true if the number of packets received for this guid is a power of 2 +// to throttle the number of public reset packets we send to a client. +bool QuicTimeWaitListManager::ShouldSendPublicReset(int received_packet_count) { + return (received_packet_count & (received_packet_count - 1)) == 0; +} + +void QuicTimeWaitListManager::SendPublicReset( + const IPEndPoint& server_address, + const IPEndPoint& client_address, + QuicGuid guid, + QuicPacketSequenceNumber rejected_sequence_number) { + QuicPublicResetPacket packet; + packet.public_header.guid = guid; + 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 guid. + packet.nonce_proof = 1010101; + QueuedPacket* queued_packet = new QueuedPacket( + server_address, + client_address, + framer_.BuildPublicResetPacket(packet)); + // Takes ownership of the packet. + SendOrQueuePacket(queued_packet); +} + +// Either sends the packet and deletes it or makes pending queue the +// owner of the packet. +void QuicTimeWaitListManager::SendOrQueuePacket(QueuedPacket* packet) { + if (!is_write_blocked_) { + // TODO(satyamshekhar): Handle packets that fail due to error other than + // EAGAIN or EWOULDBLOCK. + WriteToWire(packet); + } + + if (is_write_blocked_) { + // pending_packets_queue takes the ownership of the queued packet. + pending_packets_queue_.push_back(packet); + } else { + delete packet; + } +} + +void QuicTimeWaitListManager::WriteToWire(QueuedPacket* queued_packet) { + DCHECK(!is_write_blocked_); + int error; + int rc = writer_->WritePacket(queued_packet->packet()->data(), + queued_packet->packet()->length(), + queued_packet->server_address().address(), + queued_packet->client_address(), + this, + &error); + + if (rc == -1) { + if (error == EAGAIN || error == EWOULDBLOCK) { + is_write_blocked_ = true; + } else { + LOG(WARNING) << "Received unknown error while sending reset packet to " + << queued_packet->client_address().ToString() << ": " + << strerror(error); + } + } +} + +void QuicTimeWaitListManager::SetGuidCleanUpAlarm() { + guid_clean_up_alarm_->UnregisterIfRegistered(); + int64 next_alarm_interval; + if (!time_ordered_guid_list_.empty()) { + GuidAddTime* oldest_guid = time_ordered_guid_list_.front(); + QuicTime now = clock_.ApproximateNow(); + DCHECK(now.Subtract(oldest_guid->time_added) < kTimeWaitPeriod_); + next_alarm_interval = oldest_guid->time_added + .Add(kTimeWaitPeriod_) + .Subtract(now) + .ToMicroseconds(); + } else { + // No guids added so none will expire before kTimeWaitPeriod_. + next_alarm_interval = kTimeWaitPeriod_.ToMicroseconds(); + } + + epoll_server_->RegisterAlarmApproximateDelta(next_alarm_interval, + guid_clean_up_alarm_.get()); +} + +void QuicTimeWaitListManager::CleanUpOldGuids() { + QuicTime now = clock_.ApproximateNow(); + while (time_ordered_guid_list_.size() > 0) { + DCHECK_EQ(time_ordered_guid_list_.size(), guid_map_.size()); + GuidAddTime* oldest_guid = time_ordered_guid_list_.front(); + if (now.Subtract(oldest_guid->time_added) < kTimeWaitPeriod_) { + break; + } + // This guid has lived its age, retire it now. + guid_map_.erase(oldest_guid->guid); + time_ordered_guid_list_.pop_front(); + delete oldest_guid; + } + SetGuidCleanUpAlarm(); +} + +} // namespace tools +} // namespace net |