// Copyright 2013 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_received_packet_manager.h" #include #include #include "base/logging.h" #include "base/stl_util.h" #include "net/base/linked_hash_map.h" #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/quic_connection_stats.h" using std::max; using std::min; using std::numeric_limits; namespace net { namespace { // The maximum number of packets to ack immediately after a missing packet for // fast retransmission to kick in at the sender. This limit is created to // reduce the number of acks sent that have no benefit for fast retransmission. // Set to the number of nacks needed for fast retransmit plus one for protection // against an ack loss const size_t kMaxPacketsAfterNewMissing = 4; } QuicReceivedPacketManager::EntropyTracker::EntropyTracker() : packets_entropy_hash_(0), first_gap_(1), largest_observed_(0) { } QuicReceivedPacketManager::EntropyTracker::~EntropyTracker() {} QuicPacketEntropyHash QuicReceivedPacketManager::EntropyTracker::EntropyHash( QuicPacketSequenceNumber sequence_number) const { DCHECK_LE(sequence_number, largest_observed_); if (sequence_number == largest_observed_) { return packets_entropy_hash_; } DCHECK_GE(sequence_number, first_gap_); DCHECK_EQ(first_gap_ + packets_entropy_.size() - 1, largest_observed_); QuicPacketEntropyHash hash = packets_entropy_hash_; ReceivedEntropyHashes::const_reverse_iterator it = packets_entropy_.rbegin(); for (QuicPacketSequenceNumber i = 0; i < (largest_observed_ - sequence_number); ++i, ++it) { hash ^= it->first; } return hash; } void QuicReceivedPacketManager::EntropyTracker::RecordPacketEntropyHash( QuicPacketSequenceNumber sequence_number, QuicPacketEntropyHash entropy_hash) { if (sequence_number < first_gap_) { DVLOG(1) << "Ignoring received packet entropy for sequence_number:" << sequence_number << " less than largest_peer_sequence_number:" << first_gap_; return; } // RecordPacketEntropyHash is only intended to be called once per packet. DCHECK(sequence_number > largest_observed_ || !packets_entropy_[sequence_number - first_gap_].second); packets_entropy_hash_ ^= entropy_hash; // Optimize the typical case of no gaps. if (sequence_number == largest_observed_ + 1 && packets_entropy_.empty()) { ++first_gap_; largest_observed_ = sequence_number; return; } if (sequence_number > largest_observed_) { for (QuicPacketSequenceNumber i = 0; i < (sequence_number - largest_observed_ - 1); ++i) { packets_entropy_.push_back(std::make_pair(0, false)); } packets_entropy_.push_back(std::make_pair(entropy_hash, true)); largest_observed_ = sequence_number; } else { packets_entropy_[sequence_number - first_gap_] = std::make_pair(entropy_hash, true); AdvanceFirstGapAndGarbageCollectEntropyMap(); } DVLOG(2) << "setting cumulative received entropy hash to: " << static_cast(packets_entropy_hash_) << " updated with sequence number " << sequence_number << " entropy hash: " << static_cast(entropy_hash); } void QuicReceivedPacketManager::EntropyTracker::SetCumulativeEntropyUpTo( QuicPacketSequenceNumber sequence_number, QuicPacketEntropyHash entropy_hash) { DCHECK_LE(sequence_number, largest_observed_); if (sequence_number < first_gap_) { DVLOG(1) << "Ignoring set entropy at:" << sequence_number << " less than first_gap_:" << first_gap_; return; } while (first_gap_ < sequence_number) { ++first_gap_; if (!packets_entropy_.empty()) { packets_entropy_.pop_front(); } } // Compute the current entropy by XORing in all entropies received including // and since sequence_number. packets_entropy_hash_ = entropy_hash; for (ReceivedEntropyHashes::const_iterator it = packets_entropy_.begin(); it != packets_entropy_.end(); ++it) { packets_entropy_hash_ ^= it->first; } // Garbage collect entries from the beginning of the map. AdvanceFirstGapAndGarbageCollectEntropyMap(); } void QuicReceivedPacketManager::EntropyTracker:: AdvanceFirstGapAndGarbageCollectEntropyMap() { while (!packets_entropy_.empty() && packets_entropy_.front().second) { ++first_gap_; packets_entropy_.pop_front(); } } QuicReceivedPacketManager::QuicReceivedPacketManager(QuicConnectionStats* stats) : peer_least_packet_awaiting_ack_(0), time_largest_observed_(QuicTime::Zero()), stats_(stats) { ack_frame_.largest_observed = 0; ack_frame_.entropy_hash = 0; } QuicReceivedPacketManager::~QuicReceivedPacketManager() {} void QuicReceivedPacketManager::RecordPacketReceived( QuicByteCount bytes, const QuicPacketHeader& header, QuicTime receipt_time) { QuicPacketSequenceNumber sequence_number = header.packet_sequence_number; DCHECK(IsAwaitingPacket(sequence_number)); InsertMissingPacketsBetween( &ack_frame_, max(ack_frame_.largest_observed + 1, peer_least_packet_awaiting_ack_), sequence_number); if (ack_frame_.largest_observed > sequence_number) { // We've gotten one of the out of order packets - remove it from our // "missing packets" list. DVLOG(1) << "Removing " << sequence_number << " from missing list"; ack_frame_.missing_packets.erase(sequence_number); // Record how out of order stats. ++stats_->packets_reordered; stats_->max_sequence_reordering = max(stats_->max_sequence_reordering, ack_frame_.largest_observed - sequence_number); int64 reordering_time_us = receipt_time.Subtract(time_largest_observed_).ToMicroseconds(); stats_->max_time_reordering_us = max(stats_->max_time_reordering_us, reordering_time_us); } if (sequence_number > ack_frame_.largest_observed) { ack_frame_.largest_observed = sequence_number; time_largest_observed_ = receipt_time; } entropy_tracker_.RecordPacketEntropyHash(sequence_number, header.entropy_hash); received_packet_times_.push_back( std::make_pair(sequence_number, receipt_time)); ack_frame_.revived_packets.erase(sequence_number); } void QuicReceivedPacketManager::RecordPacketRevived( QuicPacketSequenceNumber sequence_number) { LOG_IF(DFATAL, !IsAwaitingPacket(sequence_number)); ack_frame_.revived_packets.insert(sequence_number); } bool QuicReceivedPacketManager::IsMissing( QuicPacketSequenceNumber sequence_number) { return ContainsKey(ack_frame_.missing_packets, sequence_number); } bool QuicReceivedPacketManager::IsAwaitingPacket( QuicPacketSequenceNumber sequence_number) { return ::net::IsAwaitingPacket(ack_frame_, sequence_number); } namespace { struct isTooLarge { explicit isTooLarge(QuicPacketSequenceNumber n) : largest_observed_(n) {} QuicPacketSequenceNumber largest_observed_; // Return true if the packet in p is too different from largest_observed_ // to express. bool operator() ( const std::pair& p) const { return largest_observed_ - p.first >= numeric_limits::max(); } }; } // namespace void QuicReceivedPacketManager::UpdateReceivedPacketInfo( QuicAckFrame* ack_frame, QuicTime approximate_now) { *ack_frame = ack_frame_; ack_frame->entropy_hash = EntropyHash(ack_frame_.largest_observed); if (time_largest_observed_ == QuicTime::Zero()) { // We have received no packets. ack_frame->delta_time_largest_observed = QuicTime::Delta::Infinite(); return; } // Ensure the delta is zero if approximate now is "in the past". ack_frame->delta_time_largest_observed = approximate_now < time_largest_observed_ ? QuicTime::Delta::Zero() : approximate_now.Subtract(time_largest_observed_); // Remove all packets that are too far from largest_observed to express. received_packet_times_.remove_if(isTooLarge(ack_frame_.largest_observed)); ack_frame->received_packet_times.clear(); ack_frame->received_packet_times.swap(received_packet_times_); } QuicPacketEntropyHash QuicReceivedPacketManager::EntropyHash( QuicPacketSequenceNumber sequence_number) const { return entropy_tracker_.EntropyHash(sequence_number); } bool QuicReceivedPacketManager::DontWaitForPacketsBefore( QuicPacketSequenceNumber least_unacked) { ack_frame_.revived_packets.erase( ack_frame_.revived_packets.begin(), ack_frame_.revived_packets.lower_bound(least_unacked)); size_t missing_packets_count = ack_frame_.missing_packets.size(); ack_frame_.missing_packets.erase( ack_frame_.missing_packets.begin(), ack_frame_.missing_packets.lower_bound(least_unacked)); return missing_packets_count != ack_frame_.missing_packets.size(); } void QuicReceivedPacketManager::UpdatePacketInformationSentByPeer( const QuicStopWaitingFrame& stop_waiting) { // ValidateAck() should fail if peer_least_packet_awaiting_ack_ shrinks. DCHECK_LE(peer_least_packet_awaiting_ack_, stop_waiting.least_unacked); if (stop_waiting.least_unacked > peer_least_packet_awaiting_ack_) { bool missed_packets = DontWaitForPacketsBefore(stop_waiting.least_unacked); if (missed_packets) { DVLOG(1) << "Updating entropy hashed since we missed packets"; // There were some missing packets that we won't ever get now. Recalculate // the received entropy hash. entropy_tracker_.SetCumulativeEntropyUpTo(stop_waiting.least_unacked, stop_waiting.entropy_hash); } peer_least_packet_awaiting_ack_ = stop_waiting.least_unacked; } DCHECK(ack_frame_.missing_packets.empty() || *ack_frame_.missing_packets.begin() >= peer_least_packet_awaiting_ack_); } bool QuicReceivedPacketManager::HasNewMissingPackets() const { return !ack_frame_.missing_packets.empty() && (ack_frame_.largest_observed - *ack_frame_.missing_packets.rbegin()) <= kMaxPacketsAfterNewMissing; } size_t QuicReceivedPacketManager::NumTrackedPackets() const { return entropy_tracker_.size(); } } // namespace net