diff options
author | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-23 21:16:04 +0000 |
---|---|---|
committer | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-23 21:16:04 +0000 |
commit | 86a318db1d9c40724fdf062d3f2ef19cf6b2961c (patch) | |
tree | fc9808f5ac1e4d20e7d27d0e180e6311101049a9 | |
parent | 037366110f4f8593d826f731fc268c9fdb172fb2 (diff) | |
download | chromium_src-86a318db1d9c40724fdf062d3f2ef19cf6b2961c.zip chromium_src-86a318db1d9c40724fdf062d3f2ef19cf6b2961c.tar.gz chromium_src-86a318db1d9c40724fdf062d3f2ef19cf6b2961c.tar.bz2 |
Added QuicBandwidth class
Merge internal change: 41339414
Adding an interface between the dispatcher and the connection that represents entities that want to be notified when the underlying socket becomes writable again.
Merge internal change: 41313884
Refactored how QUIC handles historic sent packets and how we calculate sent bitrate.
Merge internal change: 41311241
Fixed one spot where the frame count was still included in the packet overhead calculations.
Merge internal change: 41291694
Cleanup CL based on Jim's comments in Chromium 11958018 and 11968021.
Merge internal change: 41287854
Add FromSeconds method to QuicTime.
Merge internal change: 41259799
Adding framing for public reset packet.
Merge internal change: 41251245
Do not send ack after connection is closed by other send.
Merge internal change: 41245780
Removing the number of frames from the header and fix ack truncating such that acks are being truncated at a clean boundary, not a random byte.
Merge internal change: 41232422
TBR=jar@chromium.org
Review URL: https://chromiumcodereview.appspot.com/11953033
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@178382 0039d316-1c4b-4281-b951-d872f2087c98
29 files changed, 915 insertions, 314 deletions
diff --git a/net/net.gyp b/net/net.gyp index 1a5e872..15e6c14 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -694,6 +694,9 @@ 'quic/crypto/quic_encrypter.h', 'quic/crypto/quic_random.cc', 'quic/crypto/quic_random.h', + 'quic/quic_bandwidth.cc', + 'quic/quic_bandwidth.h', + 'quic/quic_blocked_writer_interface.h', 'quic/quic_client_session.cc', 'quic/quic_client_session.h', 'quic/quic_crypto_client_stream.cc', @@ -1493,6 +1496,7 @@ 'quic/test_tools/reliable_quic_stream_peer.h', 'quic/test_tools/test_task_runner.cc', 'quic/test_tools/test_task_runner.h', + 'quic/quic_bandwidth_test.cc', 'quic/quic_client_session_test.cc', 'quic/quic_clock_test.cc', 'quic/quic_connection_helper_test.cc', diff --git a/net/quic/congestion_control/fix_rate_sender.cc b/net/quic/congestion_control/fix_rate_sender.cc index 46ca0b5..758a6c1 100644 --- a/net/quic/congestion_control/fix_rate_sender.cc +++ b/net/quic/congestion_control/fix_rate_sender.cc @@ -26,7 +26,8 @@ FixRateSender::FixRateSender(const QuicClock* clock) } void FixRateSender::OnIncomingQuicCongestionFeedbackFrame( - const QuicCongestionFeedbackFrame& feedback) { + const QuicCongestionFeedbackFrame& feedback, + const SentPacketsMap& /*sent_packets*/) { DCHECK(feedback.type == kFixRate) << "Invalid incoming CongestionFeedbackType:" << feedback.type; if (feedback.type == kFixRate) { diff --git a/net/quic/congestion_control/fix_rate_sender.h b/net/quic/congestion_control/fix_rate_sender.h index 146ea23..1c91fd0 100644 --- a/net/quic/congestion_control/fix_rate_sender.h +++ b/net/quic/congestion_control/fix_rate_sender.h @@ -24,7 +24,8 @@ class NET_EXPORT_PRIVATE FixRateSender : public SendAlgorithmInterface { // Start implementation of SendAlgorithmInterface. virtual void OnIncomingQuicCongestionFeedbackFrame( - const QuicCongestionFeedbackFrame& feedback) OVERRIDE; + const QuicCongestionFeedbackFrame& feedback, + const SentPacketsMap& sent_packets) OVERRIDE; virtual void OnIncomingAck(QuicPacketSequenceNumber acked_sequence_number, size_t acked_bytes, QuicTime::Delta rtt) OVERRIDE; diff --git a/net/quic/congestion_control/fix_rate_test.cc b/net/quic/congestion_control/fix_rate_test.cc index 6cb4956..33a739f 100644 --- a/net/quic/congestion_control/fix_rate_test.cc +++ b/net/quic/congestion_control/fix_rate_test.cc @@ -28,6 +28,7 @@ class FixRateTest : public ::testing::Test { } const QuicTime::Delta rtt_; MockClock clock_; + SendAlgorithmInterface::SentPacketsMap not_used_; scoped_ptr<FixRateSender> sender_; scoped_ptr<FixRateReceiver> receiver_; }; @@ -46,7 +47,7 @@ TEST_F(FixRateTest, SenderAPI) { QuicCongestionFeedbackFrame feedback; feedback.type = kFixRate; feedback.fix_rate.bitrate_in_bytes_per_second = 300000; - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback); + sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, not_used_); EXPECT_EQ(300000, sender_->BandwidthEstimate()); EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero()); EXPECT_EQ(kMaxPacketSize * 2, sender_->AvailableCongestionWindow()); @@ -74,7 +75,7 @@ TEST_F(FixRateTest, FixRatePacing) { QuicCongestionFeedbackFrame feedback; receiver_->SetBitrate(240000); // Bytes per second. ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback); + sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, not_used_); QuicTime acc_advance_time(QuicTime::Zero()); QuicPacketSequenceNumber sequence_number = 0; for (int i = 0; i < num_packets; i += 2) { diff --git a/net/quic/congestion_control/quic_send_scheduler.cc b/net/quic/congestion_control/quic_send_scheduler.cc index ba18c96..8b9acf5 100644 --- a/net/quic/congestion_control/quic_send_scheduler.cc +++ b/net/quic/congestion_control/quic_send_scheduler.cc @@ -14,6 +14,15 @@ #include "net/quic/congestion_control/send_algorithm_interface.h" //#include "util/gtl/map-util.h" +namespace { +const int kBitrateSmoothingPeriodMs = 3000; +const int kMinBitrateSmoothingPeriodMs = 500; +const int kHistoryPeriodMs = 5000; + +COMPILE_ASSERT(kHistoryPeriodMs >= kBitrateSmoothingPeriodMs, + history_must_be_longer_or_equal_to_the_smoothing_period); +} + using std::map; using std::max; @@ -25,74 +34,68 @@ QuicSendScheduler::QuicSendScheduler( const QuicClock* clock, CongestionFeedbackType type) : clock_(clock), - current_estimated_bandwidth_(-1), + current_estimated_bandwidth_(0), max_estimated_bandwidth_(-1), last_sent_packet_(QuicTime::FromMicroseconds(0)), - current_packet_bucket_(-1), - first_packet_bucket_(-1), send_algorithm_(SendAlgorithmInterface::Create(clock, type)) { - memset(packet_history_, 0, sizeof(packet_history_)); } QuicSendScheduler::~QuicSendScheduler() { - STLDeleteContainerPairSecondPointers(pending_packets_.begin(), - pending_packets_.end()); -} - -int QuicSendScheduler::UpdatePacketHistory() { - int timestamp_scaled = clock_->Now().ToMicroseconds() / - kBitrateSmoothingPeriod; - int bucket = timestamp_scaled % kBitrateSmoothingBuckets; - if (!HasSentPacket()) { - // First packet. - current_packet_bucket_ = bucket; - first_packet_bucket_ = bucket; - } - if (current_packet_bucket_ != bucket) { - // We need to make sure to zero out any skipped buckets. - // Max loop count is kBitrateSmoothingBuckets. - do { - current_packet_bucket_ = - (1 + current_packet_bucket_) % kBitrateSmoothingBuckets; - packet_history_[current_packet_bucket_] = 0; - if (first_packet_bucket_ == current_packet_bucket_) { - // We have filled the whole window, no need to keep track of first - // bucket. - first_packet_bucket_ = -1; - } - } while (current_packet_bucket_ != bucket); - } - return bucket; + STLDeleteValues(&packet_history_map_); } void QuicSendScheduler::SentPacket(QuicPacketSequenceNumber sequence_number, size_t bytes, bool is_retransmission) { - int bucket = UpdatePacketHistory(); - packet_history_[bucket] += bytes; + DCHECK(!ContainsKey(pending_packets_, sequence_number)); send_algorithm_->SentPacket(sequence_number, bytes, is_retransmission); - if (!is_retransmission) { - pending_packets_[sequence_number] = - new PendingPacket(bytes, clock_->Now()); - } + + packet_history_map_[sequence_number] = + new class SendAlgorithmInterface::SentPacket(bytes, clock_->Now()); + pending_packets_[sequence_number] = bytes; + CleanupPacketHistory(); DLOG(INFO) << "Sent sequence number:" << sequence_number; } void QuicSendScheduler::OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& congestion_feedback_frame) { send_algorithm_->OnIncomingQuicCongestionFeedbackFrame( - congestion_feedback_frame); + congestion_feedback_frame, packet_history_map_); +} + +void QuicSendScheduler::CleanupPacketHistory() { + const QuicTime::Delta kHistoryPeriod = + QuicTime::Delta::FromMilliseconds(kHistoryPeriodMs); + QuicTime Now = clock_->Now(); + + SendAlgorithmInterface::SentPacketsMap::iterator history_it = + packet_history_map_.begin(); + for (; history_it != packet_history_map_.end(); ++history_it) { + if (Now.Subtract(history_it->second->SendTimestamp()) <= kHistoryPeriod) { + return; + } + DLOG(INFO) << "Clear sequence number:" << history_it->first + << "from history"; + delete history_it->second; + packet_history_map_.erase(history_it); + history_it = packet_history_map_.begin(); } +} void QuicSendScheduler::OnIncomingAckFrame(const QuicAckFrame& ack_frame) { + // We calculate the RTT based on the highest ACKed sequence number, the lower + // sequence numbers will include the ACK aggregation delay. + QuicTime::Delta rtt = QuicTime::Delta::Zero(); + SendAlgorithmInterface::SentPacketsMap::iterator history_it = + packet_history_map_.find(ack_frame.received_info.largest_observed); + if (history_it != packet_history_map_.end()) { + rtt = clock_->Now().Subtract(history_it->second->SendTimestamp()); + } // We want to. // * Get all packets lower(including) than largest_observed // from pending_packets_. // * Remove all missing packets. // * Send each ACK in the list to send_algorithm_. - QuicTime last_timestamp(QuicTime::FromMicroseconds(0)); - map<QuicPacketSequenceNumber, size_t> acked_packets; - PendingPacketsMap::iterator it, it_upper; it = pending_packets_.begin(); it_upper = pending_packets_.upper_bound( @@ -102,27 +105,15 @@ void QuicSendScheduler::OnIncomingAckFrame(const QuicAckFrame& ack_frame) { QuicPacketSequenceNumber sequence_number = it->first; if (!ack_frame.received_info.IsAwaitingPacket(sequence_number)) { // Not missing, hence implicitly acked. - scoped_ptr<PendingPacket> pending_packet_cleaner(it->second); - acked_packets[sequence_number] = pending_packet_cleaner->BytesSent(); - last_timestamp = pending_packet_cleaner->SendTimestamp(); + send_algorithm_->OnIncomingAck(sequence_number, + it->second, + rtt); + DLOG(INFO) << "ACKed sequence number:" << sequence_number; pending_packets_.erase(it++); // Must be incremented post to work. } else { ++it; } } - // We calculate the RTT based on the highest ACKed sequence number, the lower - // sequence numbers will include the ACK aggregation delay. - QuicTime::Delta rtt = clock_->Now().Subtract(last_timestamp); - - map<QuicPacketSequenceNumber, size_t>::iterator it_acked_packets; - for (it_acked_packets = acked_packets.begin(); - it_acked_packets != acked_packets.end(); - ++it_acked_packets) { - send_algorithm_->OnIncomingAck(it_acked_packets->first, - it_acked_packets->second, - rtt); - DLOG(INFO) << "ACKed sequence number:" << it_acked_packets->first; - } } QuicTime::Delta QuicSendScheduler::TimeUntilSend(bool is_retransmission) { @@ -142,37 +133,35 @@ int QuicSendScheduler::BandwidthEstimate() { return bandwidth_estimate; } -bool QuicSendScheduler::HasSentPacket() { - return (current_packet_bucket_ != -1); -} - -// TODO(pwestin) add a timer to make this accurate even if not called. +// TODO(pwestin): add a timer to make max_estimated_bandwidth_ accurate. int QuicSendScheduler::SentBandwidth() { - UpdatePacketHistory(); - - if (first_packet_bucket_ != -1) { - // We don't have a full set of data. - int number_of_buckets = (current_packet_bucket_ - first_packet_bucket_) + 1; - if (number_of_buckets < 0) { - // We have a wrap in bucket index. - number_of_buckets = kBitrateSmoothingBuckets + number_of_buckets; - } - int64 sum = 0; - int bucket = first_packet_bucket_; - for (int n = 0; n < number_of_buckets; bucket++, n++) { - bucket = bucket % kBitrateSmoothingBuckets; - sum += packet_history_[bucket]; + const QuicTime::Delta kBitrateSmoothingPeriod = + QuicTime::Delta::FromMilliseconds(kBitrateSmoothingPeriodMs); + const QuicTime::Delta kMinBitrateSmoothingPeriod = + QuicTime::Delta::FromMilliseconds(kMinBitrateSmoothingPeriodMs); + + QuicTime Now = clock_->Now(); + size_t sum_bytes_sent = 0; + + // Sum packet from new until they are kBitrateSmoothingPeriod old. + SendAlgorithmInterface::SentPacketsMap::reverse_iterator history_rit = + packet_history_map_.rbegin(); + + QuicTime::Delta max_diff = QuicTime::Delta::Zero(); + for (; history_rit != packet_history_map_.rend(); ++history_rit) { + QuicTime::Delta diff = Now.Subtract(history_rit->second->SendTimestamp()); + if (diff > kBitrateSmoothingPeriod) { + break; } - current_estimated_bandwidth_ = (sum * - (kNumMicrosPerSecond / kBitrateSmoothingPeriod)) / number_of_buckets; - } else { - int64 sum = 0; - for (uint32 bucket = 0; bucket < kBitrateSmoothingBuckets; ++bucket) { - sum += packet_history_[bucket]; - } - current_estimated_bandwidth_ = (sum * (kNumMicrosPerSecond / - kBitrateSmoothingPeriod)) / kBitrateSmoothingBuckets; + sum_bytes_sent += history_rit->second->BytesSent(); + max_diff = diff; + } + if (max_diff < kMinBitrateSmoothingPeriod) { + // No estimate. + return 0; } + current_estimated_bandwidth_ = sum_bytes_sent * kNumMicrosPerSecond / + max_diff.ToMicroseconds(); max_estimated_bandwidth_ = max(max_estimated_bandwidth_, current_estimated_bandwidth_); return current_estimated_bandwidth_; @@ -180,9 +169,7 @@ int QuicSendScheduler::SentBandwidth() { int QuicSendScheduler::PeakSustainedBandwidth() { // To make sure that we get the latest estimate we call SentBandwidth. - if (HasSentPacket()) { SentBandwidth(); - } return max_estimated_bandwidth_; } diff --git a/net/quic/congestion_control/quic_send_scheduler.h b/net/quic/congestion_control/quic_send_scheduler.h index c5f1c83..53793bd 100644 --- a/net/quic/congestion_control/quic_send_scheduler.h +++ b/net/quic/congestion_control/quic_send_scheduler.h @@ -23,29 +23,8 @@ namespace net { -const uint32 kBitrateSmoothingBuckets = 300; - -// 10 ms in micro seconds, must be less than 1 second for current -// implementation due to overflow resulting in a potential divide by zero. -const uint32 kBitrateSmoothingPeriod = 10000; - class NET_EXPORT_PRIVATE QuicSendScheduler { public: - class PendingPacket { - public: - PendingPacket(size_t bytes, QuicTime timestamp) - : bytes_sent_(bytes), - send_timestamp_(timestamp) { - } - size_t BytesSent() { return bytes_sent_; } - QuicTime& SendTimestamp() { return send_timestamp_; } - - private: - size_t bytes_sent_; - QuicTime send_timestamp_; - }; - typedef std::map<QuicPacketSequenceNumber, PendingPacket*> PendingPacketsMap; - // Enable pacing to prevent a large congestion window to be sent all at once, // when pacing is enabled a large congestion window will be sent in multiple // bursts of packet(s) instead of one big burst that might introduce packet @@ -88,24 +67,16 @@ class NET_EXPORT_PRIVATE QuicSendScheduler { int PeakSustainedBandwidth(); // In bytes per second. private: - // Have we sent any packets during this session? - bool HasSentPacket(); - int UpdatePacketHistory(); + typedef std::map<QuicPacketSequenceNumber, size_t> PendingPacketsMap; + + void CleanupPacketHistory(); const QuicClock* clock_; - int current_estimated_bandwidth_; - int max_estimated_bandwidth_; + int64 current_estimated_bandwidth_; + int64 max_estimated_bandwidth_; QuicTime last_sent_packet_; - // To keep track of the real sent bitrate we keep track of the last sent bytes - // by keeping an array containing the number of bytes sent in a short timespan - // kBitrateSmoothingPeriod; multiple of these buckets kBitrateSmoothingBuckets - // create a time window in which we calculate the average bitrate. - int current_packet_bucket_; // Last active bucket in window. - int first_packet_bucket_; // First active bucket in window. - uint32 packet_history_[kBitrateSmoothingBuckets]; // The window. scoped_ptr<SendAlgorithmInterface> send_algorithm_; - // TODO(pwestin): should we combine the packet_history_ bucket with this map? - // For now I keep it separate for easy implementation. + SendAlgorithmInterface::SentPacketsMap packet_history_map_; PendingPacketsMap pending_packets_; }; diff --git a/net/quic/congestion_control/quic_send_scheduler_test.cc b/net/quic/congestion_control/quic_send_scheduler_test.cc index a59c0e7..9347b2c 100644 --- a/net/quic/congestion_control/quic_send_scheduler_test.cc +++ b/net/quic/congestion_control/quic_send_scheduler_test.cc @@ -116,8 +116,8 @@ TEST_F(QuicSendSchedulerTest, FixedRateBandwidth) { sender_->OnIncomingAckFrame(ack); } EXPECT_EQ(100000, sender_->BandwidthEstimate()); - EXPECT_EQ(101010, sender_->PeakSustainedBandwidth()); - EXPECT_EQ(101010, sender_->SentBandwidth()); + EXPECT_NEAR(100000, sender_->PeakSustainedBandwidth(), 4000); + EXPECT_NEAR(100000, sender_->SentBandwidth(), 4000); } TEST_F(QuicSendSchedulerTest, BandwidthWith3SecondGap) { @@ -140,13 +140,13 @@ TEST_F(QuicSendSchedulerTest, BandwidthWith3SecondGap) { sender_->OnIncomingAckFrame(ack); } EXPECT_EQ(100000, sender_->BandwidthEstimate()); - EXPECT_EQ(100000, sender_->PeakSustainedBandwidth()); - EXPECT_EQ(100000, sender_->SentBandwidth()); + EXPECT_NEAR(100000, sender_->PeakSustainedBandwidth(), 2000); + EXPECT_NEAR(100000, sender_->SentBandwidth(), 2000); clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1000)); - EXPECT_EQ(50000, sender_->SentBandwidth()); + EXPECT_NEAR(50000, sender_->SentBandwidth(), 1000); clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2100)); - EXPECT_EQ(100000, sender_->BandwidthEstimate()); - EXPECT_EQ(100000, sender_->PeakSustainedBandwidth()); + EXPECT_NEAR(100000, sender_->BandwidthEstimate(), 2000); + EXPECT_NEAR(100000, sender_->PeakSustainedBandwidth(), 2000); EXPECT_EQ(0, sender_->SentBandwidth()); for (int i = 1; i <= 150; ++i) { EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero()); @@ -158,8 +158,8 @@ TEST_F(QuicSendSchedulerTest, BandwidthWith3SecondGap) { sender_->OnIncomingAckFrame(ack); } EXPECT_EQ(100000, sender_->BandwidthEstimate()); - EXPECT_EQ(100000, sender_->PeakSustainedBandwidth()); - EXPECT_EQ(50000, sender_->SentBandwidth()); + EXPECT_NEAR(100000, sender_->PeakSustainedBandwidth(), 2000); + EXPECT_NEAR(100000, sender_->SentBandwidth(), 2000); } TEST_F(QuicSendSchedulerTest, Pacing) { diff --git a/net/quic/congestion_control/send_algorithm_interface.h b/net/quic/congestion_control/send_algorithm_interface.h index 63aef3a..8f83615 100644 --- a/net/quic/congestion_control/send_algorithm_interface.h +++ b/net/quic/congestion_control/send_algorithm_interface.h @@ -19,6 +19,22 @@ const int kNoValidEstimate = -1; class NET_EXPORT_PRIVATE SendAlgorithmInterface { public: + class SentPacket { + public: + SentPacket(size_t bytes, QuicTime timestamp) + : bytes_sent_(bytes), + send_timestamp_(timestamp) { + } + size_t BytesSent() { return bytes_sent_; } + QuicTime& SendTimestamp() { return send_timestamp_; } + + private: + size_t bytes_sent_; + QuicTime send_timestamp_; + }; + + typedef std::map<QuicPacketSequenceNumber, SentPacket*> SentPacketsMap; + static SendAlgorithmInterface* Create(const QuicClock* clock, CongestionFeedbackType type); @@ -26,7 +42,8 @@ class NET_EXPORT_PRIVATE SendAlgorithmInterface { // Called when we receive congestion feedback from remote peer. virtual void OnIncomingQuicCongestionFeedbackFrame( - const QuicCongestionFeedbackFrame& feedback) = 0; + const QuicCongestionFeedbackFrame& feedback, + const SentPacketsMap& sent_packets) = 0; // Called for each received ACK, with sequence number from remote peer. virtual void OnIncomingAck(QuicPacketSequenceNumber acked_sequence_number, diff --git a/net/quic/congestion_control/tcp_cubic_sender.cc b/net/quic/congestion_control/tcp_cubic_sender.cc index 001fff5..8087283 100644 --- a/net/quic/congestion_control/tcp_cubic_sender.cc +++ b/net/quic/congestion_control/tcp_cubic_sender.cc @@ -34,7 +34,8 @@ TcpCubicSender::TcpCubicSender(const QuicClock* clock, bool reno) } void TcpCubicSender::OnIncomingQuicCongestionFeedbackFrame( - const QuicCongestionFeedbackFrame& feedback) { + const QuicCongestionFeedbackFrame& feedback, + const SentPacketsMap& /*sent_packets*/) { if (last_received_accumulated_number_of_lost_packets_ != feedback.tcp.accumulated_number_of_lost_packets) { int recovered_lost_packets = diff --git a/net/quic/congestion_control/tcp_cubic_sender.h b/net/quic/congestion_control/tcp_cubic_sender.h index 628ff56..54efedd 100644 --- a/net/quic/congestion_control/tcp_cubic_sender.h +++ b/net/quic/congestion_control/tcp_cubic_sender.h @@ -26,7 +26,8 @@ class NET_EXPORT_PRIVATE TcpCubicSender : public SendAlgorithmInterface { // Start implementation of SendAlgorithmInterface. virtual void OnIncomingQuicCongestionFeedbackFrame( - const QuicCongestionFeedbackFrame& feedback) OVERRIDE; + const QuicCongestionFeedbackFrame& feedback, + const SentPacketsMap& sent_packets) OVERRIDE; virtual void OnIncomingAck(QuicPacketSequenceNumber acked_sequence_number, size_t acked_bytes, QuicTime::Delta rtt) OVERRIDE; diff --git a/net/quic/congestion_control/tcp_cubic_sender_test.cc b/net/quic/congestion_control/tcp_cubic_sender_test.cc index 3f48c22..1ce7d7a 100644 --- a/net/quic/congestion_control/tcp_cubic_sender_test.cc +++ b/net/quic/congestion_control/tcp_cubic_sender_test.cc @@ -51,6 +51,7 @@ class QuicTcpCubicSenderTest : public ::testing::Test { const QuicTime::Delta rtt_; const QuicTime::Delta one_ms_; MockClock clock_; + SendAlgorithmInterface::SentPacketsMap not_used_; scoped_ptr<TcpCubicSender> sender_; scoped_ptr<TcpReceiver> receiver_; QuicPacketSequenceNumber sequence_number_; @@ -66,7 +67,7 @@ TEST_F(QuicTcpCubicSenderTest, SimpleSender) { EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero()); // Get default QuicCongestionFeedbackFrame from receiver. ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback); + sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, not_used_); // Make sure we can send. EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero()); // And that window is un-affected. @@ -83,7 +84,7 @@ TEST_F(QuicTcpCubicSenderTest, ExponentialSlowStart) { EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero()); // Get default QuicCongestionFeedbackFrame from receiver. ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback); + sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, not_used_); // Make sure we can send. EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero()); @@ -109,7 +110,7 @@ TEST_F(QuicTcpCubicSenderTest, SlowStartAckTrain) { EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero()); // Get default QuicCongestionFeedbackFrame from receiver. ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback); + sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, not_used_); // Make sure we can send. EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero()); @@ -149,7 +150,7 @@ TEST_F(QuicTcpCubicSenderTest, SlowStartPacketLoss) { EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero()); // Get default QuicCongestionFeedbackFrame from receiver. ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback); + sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, not_used_); // Make sure we can send. EXPECT_TRUE(sender_->TimeUntilSend(false).IsZero()); diff --git a/net/quic/quic_bandwidth.cc b/net/quic/quic_bandwidth.cc new file mode 100644 index 0000000..c270d01 --- /dev/null +++ b/net/quic/quic_bandwidth.cc @@ -0,0 +1,86 @@ +// 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/quic/quic_bandwidth.h" + +#include "base/logging.h" +#include "base/time.h" + +namespace net { + +// Highest number that QuicBandwidth can hold. +const int64 kQuicInfiniteBandwidth = 0x7fffffffffffffffll; + +// static +QuicBandwidth QuicBandwidth::Zero() { + return QuicBandwidth(0); +} + +// static +QuicBandwidth QuicBandwidth::FromBitsPerSecond(int64 bits_per_second) { + return QuicBandwidth(bits_per_second); +} + +// static +QuicBandwidth QuicBandwidth::FromKBitsPerSecond(int64 k_bits_per_second) { + DCHECK(k_bits_per_second < kQuicInfiniteBandwidth / 1000); + return QuicBandwidth(k_bits_per_second * 1000); +} + +// static +QuicBandwidth QuicBandwidth::FromBytesPerSecond(int64 bytes_per_second) { + DCHECK(bytes_per_second < kQuicInfiniteBandwidth / 8); + return QuicBandwidth(bytes_per_second * 8); +} + +// static +QuicBandwidth QuicBandwidth::FromKBytesPerSecond(int64 k_bytes_per_second) { + DCHECK(k_bytes_per_second < kQuicInfiniteBandwidth / 8000); + return QuicBandwidth(k_bytes_per_second * 8000); +} + +// static +QuicBandwidth QuicBandwidth::FromBytesAndTimeDelta(int64 bytes, + QuicTime::Delta delta) { + DCHECK_LT(bytes, + kQuicInfiniteBandwidth / (8 * base::Time::kMicrosecondsPerSecond)); + int64 bytes_per_second = (bytes * base::Time::kMicrosecondsPerSecond) / + delta.ToMicroseconds(); + return QuicBandwidth(bytes_per_second * 8); +} + +QuicBandwidth::QuicBandwidth(int64 bits_per_second) + : bits_per_second_(bits_per_second) { + DCHECK_GE(bits_per_second, 0); +} + +int64 QuicBandwidth::ToBitsPerSecond() const { + return bits_per_second_; +} + +int64 QuicBandwidth::ToKBitsPerSecond() const { + return bits_per_second_ / 1000; +} + +int64 QuicBandwidth::ToBytesPerSecond() const { + return bits_per_second_ / 8; +} + +int64 QuicBandwidth::ToKBytesPerSecond() const { + return bits_per_second_ / 8000; +} + +bool QuicBandwidth::IsZero() const { + return (bits_per_second_ == 0); +} + +QuicBandwidth QuicBandwidth::Add(const QuicBandwidth& delta) const { + return QuicBandwidth(bits_per_second_ + delta.bits_per_second_); +} + +QuicBandwidth QuicBandwidth::Subtract(const QuicBandwidth& delta) const { + return QuicBandwidth(bits_per_second_ - delta.bits_per_second_); +} + +} // namespace net diff --git a/net/quic/quic_bandwidth.h b/net/quic/quic_bandwidth.h new file mode 100644 index 0000000..522bd4c --- /dev/null +++ b/net/quic/quic_bandwidth.h @@ -0,0 +1,76 @@ +// 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. +// +// QuicBandwidth represents a bandwidth, stored in bits per second resolution. + +#ifndef NET_QUIC_QUIC_BANDWIDTH_H_ +#define NET_QUIC_QUIC_BANDWIDTH_H_ + +#include "base/basictypes.h" +#include "net/quic/quic_time.h" + +namespace net { + +class NET_EXPORT_PRIVATE QuicBandwidth { + public: + // Creates a new QuicBandwidth with an internal value of 0. + static QuicBandwidth Zero(); + + // Create a new QuicBandwidth holding the bits per second. + static QuicBandwidth FromBitsPerSecond(int64 bits_per_second); + + // Create a new QuicBandwidth holding the kilo bits per second. + static QuicBandwidth FromKBitsPerSecond(int64 k_bits_per_second); + + // Create a new QuicBandwidth holding the bytes per second. + static QuicBandwidth FromBytesPerSecond(int64 bytes_per_second); + + // Create a new QuicBandwidth holding the kilo bytes per second. + static QuicBandwidth FromKBytesPerSecond(int64 k_bytes_per_second); + + // Create a new QuicBandwidth based on the bytes per the elapsed delta. + static QuicBandwidth FromBytesAndTimeDelta(int64 bytes, + QuicTime::Delta delta); + + int64 ToBitsPerSecond() const; + + int64 ToKBitsPerSecond() const; + + int64 ToBytesPerSecond() const; + + int64 ToKBytesPerSecond() const; + + bool IsZero() const; + + QuicBandwidth Add(const QuicBandwidth& delta) const; + + QuicBandwidth Subtract(const QuicBandwidth& delta) const; + + private: + explicit QuicBandwidth(int64 bits_per_second); + int64 bits_per_second_; +}; + +// Non-member relational operators for QuicBandwidth. +inline bool operator==(QuicBandwidth lhs, QuicBandwidth rhs) { + return lhs.ToBitsPerSecond() == rhs.ToBitsPerSecond(); +} +inline bool operator!=(QuicBandwidth lhs, QuicBandwidth rhs) { + return !(lhs == rhs); +} +inline bool operator<(QuicBandwidth lhs, QuicBandwidth rhs) { + return lhs.ToBitsPerSecond() < rhs.ToBitsPerSecond(); +} +inline bool operator>(QuicBandwidth lhs, QuicBandwidth rhs) { + return rhs < lhs; +} +inline bool operator<=(QuicBandwidth lhs, QuicBandwidth rhs) { + return !(rhs < lhs); +} +inline bool operator>=(QuicBandwidth lhs, QuicBandwidth rhs) { + return !(lhs < rhs); +} + +} // namespace net +#endif // NET_QUIC_QUIC_BANDWIDTH_H_ diff --git a/net/quic/quic_bandwidth_test.cc b/net/quic/quic_bandwidth_test.cc new file mode 100644 index 0000000..aa089e6 --- /dev/null +++ b/net/quic/quic_bandwidth_test.cc @@ -0,0 +1,61 @@ +// 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/quic/quic_bandwidth.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace testing { + +class QuicBandwidthTest : public ::testing::Test { +}; + +TEST_F(QuicBandwidthTest, FromTo) { + EXPECT_EQ(QuicBandwidth::FromKBitsPerSecond(1), + QuicBandwidth::FromBitsPerSecond(1000)); + EXPECT_EQ(QuicBandwidth::FromKBytesPerSecond(1), + QuicBandwidth::FromBytesPerSecond(1000)); + EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(8000), + QuicBandwidth::FromBytesPerSecond(1000)); + EXPECT_EQ(QuicBandwidth::FromKBitsPerSecond(8), + QuicBandwidth::FromKBytesPerSecond(1)); + + EXPECT_EQ(0, QuicBandwidth::Zero().ToBitsPerSecond()); + EXPECT_EQ(0, QuicBandwidth::Zero().ToKBitsPerSecond()); + EXPECT_EQ(0, QuicBandwidth::Zero().ToBytesPerSecond()); + EXPECT_EQ(0, QuicBandwidth::Zero().ToKBytesPerSecond()); + + EXPECT_EQ(1, QuicBandwidth::FromBitsPerSecond(1000).ToKBitsPerSecond()); + EXPECT_EQ(1000, QuicBandwidth::FromKBitsPerSecond(1).ToBitsPerSecond()); + EXPECT_EQ(1, QuicBandwidth::FromBytesPerSecond(1000).ToKBytesPerSecond()); + EXPECT_EQ(1000, QuicBandwidth::FromKBytesPerSecond(1).ToBytesPerSecond()); +} + +TEST_F(QuicBandwidthTest, Add) { + QuicBandwidth bandwidht_1 = QuicBandwidth::FromKBitsPerSecond(1); + QuicBandwidth bandwidht_2 = QuicBandwidth::FromKBytesPerSecond(1); + + EXPECT_EQ(9000, bandwidht_1.Add(bandwidht_2).ToBitsPerSecond()); + EXPECT_EQ(9000, bandwidht_2.Add(bandwidht_1).ToBitsPerSecond()); +} + +TEST_F(QuicBandwidthTest, Subtract) { + QuicBandwidth bandwidht_1 = QuicBandwidth::FromKBitsPerSecond(1); + QuicBandwidth bandwidht_2 = QuicBandwidth::FromKBytesPerSecond(1); + + EXPECT_EQ(7000, bandwidht_2.Subtract(bandwidht_1).ToBitsPerSecond()); +} + +TEST_F(QuicBandwidthTest, TimeDelta) { + EXPECT_EQ(QuicBandwidth::FromKBytesPerSecond(1000), + QuicBandwidth::FromBytesAndTimeDelta( + 1000, QuicTime::Delta::FromMilliseconds(1))); + + EXPECT_EQ(QuicBandwidth::FromKBytesPerSecond(10), + QuicBandwidth::FromBytesAndTimeDelta( + 1000, QuicTime::Delta::FromMilliseconds(100))); +} + +} // namespace testing +} // namespace net diff --git a/net/quic/quic_blocked_writer_interface.h b/net/quic/quic_blocked_writer_interface.h new file mode 100644 index 0000000..d9cd6ad --- /dev/null +++ b/net/quic/quic_blocked_writer_interface.h @@ -0,0 +1,26 @@ +// 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. + +// This is an interface for all objects that want to be notified that +// the underlying UDP socket is available for writing (not write blocked +// anymore). + +#ifndef NET_QUIC_QUIC_BLOCKED_WRITER_INTERFACE_H_ +#define NET_QUIC_QUIC_BLOCKED_WRITER_INTERFACE_H_ + +namespace net { + +class NET_EXPORT_PRIVATE QuicBlockedWriterInterface { + public: + virtual ~QuicBlockedWriterInterface() {} + + // Called by the PacketWriter when the underlying socket becomes writable + // so that the BlockedWriter can go ahead and try writing. This methods + // should return false if the socket has become blocked while writing. + virtual bool OnCanWrite() = 0; +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_BLOCKED_WRITER_INTERFACE_H_ diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc index bf09e91..dcfcb1d 100644 --- a/net/quic/quic_connection.cc +++ b/net/quic/quic_connection.cc @@ -123,15 +123,17 @@ QuicConnection::QuicConnection(QuicGuid guid, } QuicConnection::~QuicConnection() { - for (UnackedPacketMap::iterator u = unacked_packets_.begin(); - u != unacked_packets_.end(); ++u) { - DeleteUnackedPacket(u->second); + // Call DeleteEnclosedFrames on each QuicPacket because the destructor does + // not delete enclosed frames. + for (UnackedPacketMap::iterator it = unacked_packets_.begin(); + it != unacked_packets_.end(); ++it) { + DeleteEnclosedFrames(it->second); } STLDeleteValues(&unacked_packets_); STLDeleteValues(&group_map_); - for (QueuedPacketList::iterator q = queued_packets_.begin(); - q != queued_packets_.end(); ++q) { - delete q->packet; + for (QueuedPacketList::iterator it = queued_packets_.begin(); + it != queued_packets_.end(); ++it) { + delete it->packet; } } @@ -160,7 +162,7 @@ void QuicConnection::DeleteEnclosedFrame(QuicFrame* frame) { } } -void QuicConnection::DeleteUnackedPacket(UnackedPacket* unacked) { +void QuicConnection::DeleteEnclosedFrames(UnackedPacket* unacked) { for (QuicFrames::iterator it = unacked->frames.begin(); it != unacked->frames.end(); ++it) { DCHECK(ShouldRetransmit(*it)); @@ -186,10 +188,21 @@ void QuicConnection::OnPacket(const IPEndPoint& self_address, peer_address_ = peer_address; } +void QuicConnection::OnPublicResetPacket( + const QuicPublicResetPacket& packet) { + CloseConnection(QUIC_PUBLIC_RESET, true); +} + void QuicConnection::OnRevivedPacket() { } bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { + if (header.public_header.guid != guid_) { + DLOG(INFO) << "Ignoring packet from unexpected GUID: " + << header.public_header.guid << " instead of " << guid_; + return false; + } + if (!Near(header.packet_sequence_number, last_header_.packet_sequence_number)) { DLOG(INFO) << "Packet " << header.packet_sequence_number @@ -344,7 +357,7 @@ void QuicConnection::UpdatePacketInformationReceivedByPeer( } } acked_packets.insert(it->first); - DeleteUnackedPacket(it->second); + DeleteEnclosedFrames(it->second); delete it->second; UnackedPacketMap::iterator it_tmp = it; ++it; @@ -690,6 +703,13 @@ bool QuicConnection::SendPacket(QuicPacketSequenceNumber sequence_number, bool should_retransmit, bool force, bool is_retransmission) { + if (!connected_) { + DLOG(INFO) + << "Dropping packet to be sent since connection is disconnected."; + delete packet; + return false; + } + // If this packet is being forced, don't bother checking to see if we should // write, just write. if (!force) { @@ -834,17 +854,19 @@ void QuicConnection::MaybeProcessRevivedPacket() { if (group == NULL || !group->CanRevive()) { return; } - DCHECK(!revived_payload_.get()); - revived_payload_.reset(new char[kMaxPacketSize]); - size_t len = group->Revive(&revived_header_, revived_payload_.get(), - kMaxPacketSize); + QuicPacketHeader revived_header; + char revived_payload[kMaxPacketSize]; + size_t len = group->Revive(&revived_header, revived_payload, kMaxPacketSize); + revived_header.public_header.guid = guid_; + revived_header.public_header.flags = PACKET_PUBLIC_FLAGS_NONE; + revived_header.private_flags = PACKET_PRIVATE_FLAGS_NONE; + revived_header.fec_group = kNoFecOffset; group_map_.erase(last_header_.fec_group); delete group; last_packet_revived_ = true; - framer_.ProcessRevivedPacket(revived_header_, - StringPiece(revived_payload_.get(), len)); - revived_payload_.reset(); + framer_.ProcessRevivedPacket(revived_header, + StringPiece(revived_payload, len)); } QuicFecGroup* QuicConnection::GetFecGroup() { diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h index 6515e01..97cc61c 100644 --- a/net/quic/quic_connection.h +++ b/net/quic/quic_connection.h @@ -23,6 +23,7 @@ #include "base/hash_tables.h" #include "net/base/ip_endpoint.h" #include "net/quic/congestion_control/quic_congestion_manager.h" +#include "net/quic/quic_blocked_writer_interface.h" #include "net/quic/quic_fec_group.h" #include "net/quic/quic_framer.h" #include "net/quic/quic_packet_creator.h" @@ -126,7 +127,8 @@ class NET_EXPORT_PRIVATE QuicConnectionHelperInterface { virtual void ClearAckAlarm() = 0; }; -class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface { +class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface, + public QuicBlockedWriterInterface { public: // Constructs a new QuicConnection for the specified |guid| and |address|. // |helper| will be owned by this connection. @@ -164,14 +166,17 @@ class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface { const IPEndPoint& peer_address, const QuicEncryptedPacket& packet); + // QuicBlockedWriterInterface // Called when the underlying connection becomes writable to allow // queued writes to happen. Returns false if the socket has become blocked. - virtual bool OnCanWrite(); + virtual bool OnCanWrite() OVERRIDE; // From QuicFramerVisitorInterface virtual void OnError(QuicFramer* framer) OVERRIDE; virtual void OnPacket(const IPEndPoint& self_address, const IPEndPoint& peer_address) OVERRIDE; + virtual void OnPublicResetPacket( + const QuicPublicResetPacket& packet) OVERRIDE; virtual void OnRevivedPacket() OVERRIDE; virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE; virtual void OnFecProtectedPayload(base::StringPiece payload) OVERRIDE; @@ -314,7 +319,7 @@ class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface { return QuicTime::Delta::FromMilliseconds(500); } - static void DeleteUnackedPacket(UnackedPacket* unacked); + static void DeleteEnclosedFrames(UnackedPacket* unacked); static bool ShouldRetransmit(const QuicFrame& frame); // Checks if a packet can be written now, and sets the timer if necessary. @@ -390,8 +395,6 @@ class NET_EXPORT_PRIVATE QuicConnection : public QuicFramerVisitorInterface { bool write_blocked_; FecGroupMap group_map_; - QuicPacketHeader revived_header_; - scoped_array<char> revived_payload_; QuicConnectionVisitorInterface* visitor_; QuicPacketCreator packet_creator_; diff --git a/net/quic/quic_connection_test.cc b/net/quic/quic_connection_test.cc index 3e80dc9..2a91326 100644 --- a/net/quic/quic_connection_test.cc +++ b/net/quic/quic_connection_test.cc @@ -251,6 +251,13 @@ class QuicConnectionTest : public ::testing::Test { connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); } + void ProcessClosePacket(QuicPacketSequenceNumber number, + QuicFecGroupNumber fec_group) { + scoped_ptr<QuicPacket> packet(ConstructClosePacket(number, fec_group)); + scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(*packet)); + connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); + } + // Sends an FEC packet that covers the packets that would have been sent. void ProcessFecPacket(QuicPacketSequenceNumber number, QuicPacketSequenceNumber min_protected_packet, @@ -341,6 +348,25 @@ class QuicConnectionTest : public ::testing::Test { return packet; } + QuicPacket* ConstructClosePacket(QuicPacketSequenceNumber number, + QuicFecGroupNumber fec_group) { + header_.public_header.guid = guid_; + header_.packet_sequence_number = number; + header_.public_header.flags = PACKET_PUBLIC_FLAGS_NONE; + header_.fec_group = fec_group; + + QuicConnectionCloseFrame qccf; + qccf.error_code = QUIC_CLIENT_GOING_AWAY; + qccf.ack_frame = QuicAckFrame(0, 1); + + QuicFrames frames; + QuicFrame frame(&qccf); + frames.push_back(frame); + QuicPacket* packet = framer_.ConstructFrameDataPacket(header_, frames); + EXPECT_TRUE(packet != NULL); + return packet; + } + void SetFeedback(QuicCongestionFeedbackFrame* feedback) { collector_ = new TestCollector(feedback); connection_.SetCollector(collector_); @@ -638,7 +664,7 @@ TEST_F(QuicConnectionTest, RetransmitOnNack) { ProcessAckPacket(&nack_two); // The third nack should trigger a retransimission. - EXPECT_CALL(*scheduler_, SentPacket(_, 38, true)).Times(1); + EXPECT_CALL(*scheduler_, SentPacket(_, 37, true)).Times(1); ProcessAckPacket(&nack_two); } @@ -1078,6 +1104,35 @@ TEST_F(QuicConnectionTest, LoopThroughSendingPackets) { 1, "EnoughDataToQueue", 0, false).bytes_consumed); } +TEST_F(QuicConnectionTest, NoAckForClose) { + ProcessPacket(1); + EXPECT_CALL(*scheduler_, OnIncomingAckFrame(_)); + EXPECT_CALL(visitor_, ConnectionClose(QUIC_CLIENT_GOING_AWAY, true)); + EXPECT_CALL(*scheduler_, SentPacket(_, _, _)).Times(0); + ProcessClosePacket(2, 0); +} + +TEST_F(QuicConnectionTest, SendWhenDisconnected) { + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(visitor_, ConnectionClose(QUIC_CLIENT_GOING_AWAY, false)); + connection_.CloseConnection(QUIC_CLIENT_GOING_AWAY, false); + EXPECT_FALSE(connection_.connected()); + QuicPacket* packet = ConstructDataPacket(1, 0); + EXPECT_CALL(*scheduler_, SentPacket(1, _, _)).Times(0); + connection_.SendPacket(1, packet, true, false, false); +} + +TEST_F(QuicConnectionTest, PublicReset) { + QuicPublicResetPacket header; + header.public_header.guid = guid_; + header.public_header.flags = PACKET_PUBLIC_FLAGS_RST; + header.rejected_sequence_number = 10101; + scoped_ptr<QuicEncryptedPacket> packet( + framer_.ConstructPublicResetPacket(header)); + EXPECT_CALL(visitor_, ConnectionClose(QUIC_PUBLIC_RESET, true)); + connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *packet); +} + } // namespace } // namespace test } // namespace net diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc index 2c359ed..b59f0d4 100644 --- a/net/quic/quic_framer.cc +++ b/net/quic/quic_framer.cc @@ -80,7 +80,6 @@ QuicPacket* QuicFramer::ConstructMaxFrameDataPacket( // sizeof(member_) is not necessarily the same as sizeof(member_wire_format). const size_t max_plaintext_size = GetMaxPlaintextSize(kMaxPacketSize); size_t len = kPacketHeaderSize; - len += 1; // frame count bool truncating = false; for (size_t i = 0; i < frames.size(); ++i) { if (frames[i].type == PADDING_FRAME) { @@ -114,6 +113,9 @@ QuicPacket* QuicFramer::ConstructMaxFrameDataPacket( len += frame_len; *num_consumed = i + 1; } + if (truncating) { + DCHECK_EQ(1u, *num_consumed); + } QuicDataWriter writer(len); @@ -125,9 +127,6 @@ QuicPacket* QuicFramer::ConstructMaxFrameDataPacket( if (*num_consumed > 256u) { return NULL; } - if (!writer.WriteUInt8(*num_consumed)) { - return NULL; - } for (size_t i = 0; i < *num_consumed; ++i) { const QuicFrame& frame = frames[i]; @@ -140,8 +139,7 @@ QuicPacket* QuicFramer::ConstructMaxFrameDataPacket( writer.WritePadding(); break; case STREAM_FRAME: - if (!AppendStreamFramePayload(*frame.stream_frame, - &writer)) { + if (!AppendStreamFramePayload(*frame.stream_frame, &writer)) { return NULL; } break; @@ -157,8 +155,7 @@ QuicPacket* QuicFramer::ConstructMaxFrameDataPacket( } break; case RST_STREAM_FRAME: - if (!AppendRstStreamFramePayload(*frame.rst_stream_frame, - &writer)) { + if (!AppendRstStreamFramePayload(*frame.rst_stream_frame, &writer)) { return NULL; } break; @@ -175,6 +172,8 @@ QuicPacket* QuicFramer::ConstructMaxFrameDataPacket( } DCHECK(truncating || len == writer.length()); + // Save the length before writing, because take clears it. + len = writer.length(); QuicPacket* packet = QuicPacket::NewDataPacket(writer.take(), len, true); if (fec_builder_) { @@ -203,6 +202,35 @@ QuicPacket* QuicFramer::ConstructFecPacket(const QuicPacketHeader& header, return QuicPacket::NewFecPacket(writer.take(), len, true); } +// static +QuicEncryptedPacket* QuicFramer::ConstructPublicResetPacket( + const QuicPublicResetPacket& packet) { + DCHECK_EQ(PACKET_PUBLIC_FLAGS_RST, + PACKET_PUBLIC_FLAGS_RST & packet.public_header.flags); + size_t len = kPublicResetPacketSize; + QuicDataWriter writer(len); + + if (!writer.WriteUInt64(packet.public_header.guid)) { + return NULL; + } + + uint8 flags = static_cast<uint8>(packet.public_header.flags); + if (!writer.WriteUInt8(flags)) { + return NULL; + } + + if (!writer.WriteUInt64(packet.nonce_proof)) { + return NULL; + } + + if (!AppendPacketSequenceNumber(packet.rejected_sequence_number, + &writer)) { + return NULL; + } + + return new QuicEncryptedPacket(writer.take(), len, true); +} + // TODO(satyamshekhar): Framer doesn't need addresses. Get rid of them. bool QuicFramer::ProcessPacket(const IPEndPoint& self_address, const IPEndPoint& peer_address, @@ -213,18 +241,20 @@ bool QuicFramer::ProcessPacket(const IPEndPoint& self_address, // First parse the public header. QuicPacketPublicHeader public_header; if (!ProcessPublicHeader(&public_header)) { - DLOG(WARNING) << "Unable to process header."; + DLOG(WARNING) << "Unable to process public header."; reader_.reset(NULL); return RaiseError(QUIC_INVALID_PACKET_HEADER); } - if (!ProcessDataPacket(public_header, self_address, peer_address, packet)) { - reader_.reset(NULL); - return false; - }; + bool rv; + if (public_header.flags & PACKET_PUBLIC_FLAGS_RST) { + rv = ProcessPublicResetPacket(public_header); + } else { + rv = ProcessDataPacket(public_header, self_address, peer_address, packet); + } reader_.reset(NULL); - return true; + return rv; } bool QuicFramer::ProcessDataPacket( @@ -271,6 +301,24 @@ bool QuicFramer::ProcessDataPacket( return true; } +bool QuicFramer::ProcessPublicResetPacket( + const QuicPacketPublicHeader& public_header) { + QuicPublicResetPacket packet(public_header); + if (!reader_->ReadUInt64(&packet.nonce_proof)) { + set_detailed_error("Unable to read nonce proof."); + return false; + } + // TODO(satyamshekhar): validate nonce to protect against DoS. + + if (!reader_->ReadUInt48(&packet.rejected_sequence_number)) { + set_detailed_error("Unable to read rejected sequence number."); + return false; + } + + visitor_->OnPublicResetPacket(packet); + return true; +} + bool QuicFramer::ProcessRevivedPacket(const QuicPacketHeader& header, StringPiece payload) { DCHECK(!reader_.get()); @@ -303,7 +351,7 @@ bool QuicFramer::WritePacketHeader(const QuicPacketHeader& header, } uint8 flags =static_cast<uint8>(header.public_header.flags); - if (!writer->WriteBytes(&flags, 1)) { + if (!writer->WriteUInt8(flags)) { return false; } @@ -312,8 +360,8 @@ bool QuicFramer::WritePacketHeader(const QuicPacketHeader& header, } flags = static_cast<uint8>(header.private_flags); - if (!writer->WriteBytes(&flags, 1)) { - return false; + if (!writer->WriteUInt8(flags)) { + return false; } // Offset from the current packet sequence number to the first fec @@ -352,6 +400,13 @@ QuicPacketSequenceNumber QuicFramer::CalculatePacketSequenceNumberFromWire( next_epoch + packet_sequence_number)); } +/* static */ +bool QuicFramer::ReadGuidFromPacket(const QuicEncryptedPacket& packet, + QuicGuid* guid) { + QuicDataReader reader(packet.data(), packet.length()); + return reader.ReadUInt64(guid); +} + bool QuicFramer::ProcessPublicHeader(QuicPacketPublicHeader* public_header) { if (!reader_->ReadUInt64(&public_header->guid)) { set_detailed_error("Unable to read GUID."); @@ -365,7 +420,7 @@ bool QuicFramer::ProcessPublicHeader(QuicPacketPublicHeader* public_header) { } if (public_flags > PACKET_PUBLIC_FLAGS_MAX) { - set_detailed_error("Illegal flags value."); + set_detailed_error("Illegal public flags value."); return false; } @@ -431,14 +486,11 @@ bool QuicFramer::ProcessPacketSequenceNumber( } bool QuicFramer::ProcessFrameData() { - // TODO(ianswett): remove frame_count - uint8 frame_count; - if (!reader_->ReadBytes(&frame_count, 1)) { - set_detailed_error("Unable to read frame count."); + if (reader_->IsDoneReading()) { + set_detailed_error("Unable to read frame type."); return RaiseError(QUIC_INVALID_FRAME_DATA); } - - for (uint8 i = 0; i < frame_count; ++i) { + while (!reader_->IsDoneReading()) { uint8 frame_type; if (!reader_->ReadBytes(&frame_type, 1)) { set_detailed_error("Unable to read frame type."); @@ -479,7 +531,8 @@ bool QuicFramer::ProcessFrameData() { break; default: set_detailed_error("Illegal frame type."); - DLOG(WARNING) << "Illegal frame type: " << (int)frame_type; + DLOG(WARNING) << "Illegal frame type: " + << static_cast<int>(frame_type); return RaiseError(QUIC_INVALID_FRAME_DATA); } } @@ -752,7 +805,7 @@ bool QuicFramer::DecryptPayload(const QuicEncryptedPacket& packet) { size_t QuicFramer::ComputeFramePayloadLength(const QuicFrame& frame) { size_t len = 0; - // We use "magic numbers" here because sizeof(member_) is not necessairly the + // We use "magic numbers" here because sizeof(member_) is not necessarily the // same as sizeof(member_wire_format). switch (frame.type) { case STREAM_FRAME: @@ -824,9 +877,14 @@ size_t QuicFramer::ComputeFramePayloadLength(const QuicFrame& frame) { return len; } +// static bool QuicFramer::AppendPacketSequenceNumber( QuicPacketSequenceNumber packet_sequence_number, QuicDataWriter* writer) { + // Ensure the entire sequence number can be written. + if (writer->capacity() - writer->length() < kSequenceNumberSize) { + return false; + } return writer->WriteUInt48(packet_sequence_number & kSequenceNumberMask); } diff --git a/net/quic/quic_framer.h b/net/quic/quic_framer.h index 81c944a..31105f1 100644 --- a/net/quic/quic_framer.h +++ b/net/quic/quic_framer.h @@ -42,6 +42,11 @@ class NET_EXPORT_PRIVATE QuicFramerVisitorInterface { virtual void OnPacket(const IPEndPoint& self_address, const IPEndPoint& peer_address) = 0; + // Called when a public reset packet has been parsed but has not yet + // been validated. + virtual void OnPublicResetPacket( + const QuicPublicResetPacket& packet) = 0; + // Called when a lost packet has been recovered via FEC, // before it has been processed. virtual void OnRevivedPacket() = 0; @@ -158,6 +163,10 @@ class NET_EXPORT_PRIVATE QuicFramer { QuicPacket* ConstructFecPacket(const QuicPacketHeader& header, const QuicFecData& fec); + // Returns a new public reset packet, owned by the caller. + static QuicEncryptedPacket* ConstructPublicResetPacket( + const QuicPublicResetPacket& packet); + // Returns a new encrypted packet, owned by the caller. QuicEncryptedPacket* EncryptPacket(const QuicPacket& packet); @@ -167,6 +176,11 @@ class NET_EXPORT_PRIVATE QuicFramer { const std::string& detailed_error() { return detailed_error_; } + // Read the guid from a packet header. + // Return true on success, else false. + static bool ReadGuidFromPacket(const QuicEncryptedPacket& packet, + QuicGuid* guid); + private: friend class test::QuicFramerPeer; @@ -175,6 +189,8 @@ class NET_EXPORT_PRIVATE QuicFramer { const IPEndPoint& peer_address, const QuicEncryptedPacket& packet); + bool ProcessPublicResetPacket(const QuicPacketPublicHeader& public_header); + bool WritePacketHeader(const QuicPacketHeader& header, QuicDataWriter* writer); @@ -204,7 +220,7 @@ class NET_EXPORT_PRIVATE QuicFramer { // Computes the wire size in bytes of the payload of |frame|. size_t ComputeFramePayloadLength(const QuicFrame& frame); - bool AppendPacketSequenceNumber( + static bool AppendPacketSequenceNumber( QuicPacketSequenceNumber packet_sequence_number, QuicDataWriter* writer); diff --git a/net/quic/quic_framer_test.cc b/net/quic/quic_framer_test.cc index 6b10890..0133148 100644 --- a/net/quic/quic_framer_test.cc +++ b/net/quic/quic_framer_test.cc @@ -108,6 +108,10 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface { peer_address_ = peer_address; } + virtual void OnPublicResetPacket(const QuicPublicResetPacket& packet) { + public_reset_packet_.reset(new QuicPublicResetPacket(packet)); + } + virtual void OnRevivedPacket() { revived_packets_++; } @@ -169,6 +173,7 @@ class TestQuicVisitor : public ::net::QuicFramerVisitorInterface { IPEndPoint self_address_; IPEndPoint peer_address_; scoped_ptr<QuicPacketHeader> header_; + scoped_ptr<QuicPublicResetPacket> public_reset_packet_; vector<QuicStreamFrame*> stream_frames_; vector<QuicAckFrame*> ack_frames_; vector<QuicCongestionFeedbackFrame*> congestion_feedback_frames_; @@ -391,9 +396,7 @@ TEST_F(QuicFramerTest, LargePacket) { // private flags 0x00, // first fec protected packet offset - 0xFF, - // frame count - 0x01, + 0xFF }; memset(packet + kPacketHeaderSize, 0, kMaxPacketSize - kPacketHeaderSize + 1); @@ -405,7 +408,7 @@ TEST_F(QuicFramerTest, LargePacket) { // Make sure we've parsed the packet header, so we can send an error. EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), visitor_.header_->public_header.guid); - // Make sure the correct error is propogated. + // Make sure the correct error is propagated. EXPECT_EQ(QUIC_PACKET_TOO_LARGE, framer_.error()); } @@ -455,13 +458,13 @@ TEST_F(QuicFramerTest, PacketHeader) { } } -TEST_F(QuicFramerTest, PaddingFrame) { +TEST_F(QuicFramerTest, InvalidPublicFlag) { unsigned char packet[] = { // guid 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // public flags - 0x00, + 0x07, // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, @@ -472,6 +475,82 @@ TEST_F(QuicFramerTest, PaddingFrame) { // frame count 0x01, + // frame type (stream frame) + 0x01, + // stream id + 0x04, 0x03, 0x02, 0x01, + // fin + 0x01, + // offset + 0x54, 0x76, 0x10, 0x32, + 0xDC, 0xFE, 0x98, 0xBA, + // data length + 0x0c, 0x00, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + CheckProcessingFails(packet, + arraysize(packet), + "Illegal public flags value.", + QUIC_INVALID_PACKET_HEADER); +}; + +TEST_F(QuicFramerTest, InvalidPrivateFlag) { + unsigned char packet[] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // public flags + 0x00, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags + 0x07, + // first fec protected packet offset + 0xFF, + + // frame count + 0x01, + // frame type (stream frame) + 0x01, + // stream id + 0x04, 0x03, 0x02, 0x01, + // fin + 0x01, + // offset + 0x54, 0x76, 0x10, 0x32, + 0xDC, 0xFE, 0x98, 0xBA, + // data length + 0x0c, 0x00, + // data + 'h', 'e', 'l', 'l', + 'o', ' ', 'w', 'o', + 'r', 'l', 'd', '!', + }; + CheckProcessingFails(packet, + arraysize(packet), + "Illegal private flags value.", + QUIC_INVALID_PACKET_HEADER); +}; + +TEST_F(QuicFramerTest, PaddingFrame) { + unsigned char packet[] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // public flags + 0x00, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags + 0x00, + // first fec protected packet offset + 0xFF, + // frame type (padding frame) 0x00, // Ignored data (which in this case is a stream frame) @@ -498,11 +577,9 @@ TEST_F(QuicFramerTest, PaddingFrame) { EXPECT_EQ(0u, visitor_.ack_frames_.size()); // Now test framing boundaries - for (size_t i = 0; i < 2; ++i) { + for (size_t i = 0; i < 1; ++i) { string expected_error; if (i < 1) { - expected_error = "Unable to read frame count."; - } else if (i < 2) { expected_error = "Unable to read frame type."; } CheckProcessingFails(packet, i + kPacketHeaderSize, expected_error, @@ -525,8 +602,6 @@ TEST_F(QuicFramerTest, StreamFrame) { // first fec protected packet offset 0xFF, - // frame count - 0x01, // frame type (stream frame) 0x01, // stream id @@ -563,19 +638,17 @@ TEST_F(QuicFramerTest, StreamFrame) { EXPECT_EQ("hello world!", visitor_.stream_frames_[0]->data); // Now test framing boundaries - for (size_t i = 0; i < 29; ++i) { + for (size_t i = 0; i < 28; ++i) { string expected_error; if (i < 1) { - expected_error = "Unable to read frame count."; - } else if (i < 2) { expected_error = "Unable to read frame type."; - } else if (i < 6) { + } else if (i < 5) { expected_error = "Unable to read stream_id."; - } else if (i < 7) { + } else if (i < 6) { expected_error = "Unable to read fin."; - } else if (i < 15) { + } else if (i < 14) { expected_error = "Unable to read offset."; - } else if (i < 29) { + } else if (i < 28) { expected_error = "Unable to read frame data."; } CheckProcessingFails(packet, i + kPacketHeaderSize, expected_error, @@ -600,8 +673,6 @@ TEST_F(QuicFramerTest, RejectPacket) { // first fec protected packet offset 0xFF, - // frame count - 0x01, // frame type (stream frame) 0x01, // stream id @@ -633,8 +704,6 @@ TEST_F(QuicFramerTest, RejectPacket) { TEST_F(QuicFramerTest, RevivedStreamFrame) { unsigned char payload[] = { - // frame count - 0x01, // frame type (stream frame) 0x01, // stream id @@ -699,8 +768,6 @@ TEST_F(QuicFramerTest, StreamFrameInFecGroup) { // first fec protected packet offset 0x02, - // frame count - 0x01, // frame type (stream frame) 0x01, // stream id @@ -755,8 +822,6 @@ TEST_F(QuicFramerTest, AckFrame) { // first fec protected packet offset 0xFF, - // frame count - 0x01, // frame type (ack frame) 0x02, // least packet sequence number awaiting an ack @@ -790,19 +855,17 @@ TEST_F(QuicFramerTest, AckFrame) { EXPECT_EQ(GG_UINT64_C(0x0123456789AA0), frame.sent_info.least_unacked); // Now test framing boundaries - for (size_t i = 0; i < 21; ++i) { + for (size_t i = 0; i < 20; ++i) { string expected_error; if (i < 1) { - expected_error = "Unable to read frame count."; - } else if (i < 2) { expected_error = "Unable to read frame type."; - } else if (i < 8) { + } else if (i < 7) { expected_error = "Unable to read least unacked."; - } else if (i < 14) { + } else if (i < 13) { expected_error = "Unable to read largest observed."; - } else if (i < 15) { + } else if (i < 14) { expected_error = "Unable to read num missing packets."; - } else if (i < 21) { + } else if (i < 20) { expected_error = "Unable to read sequence number in missing packets."; } CheckProcessingFails(packet, i + kPacketHeaderSize, expected_error, QUIC_INVALID_FRAME_DATA); @@ -824,8 +887,6 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameTCP) { // first fec protected packet offset 0xFF, - // frame count - 0x01, // frame type (congestion feedback frame) 0x03, // congestion feedback type (tcp) @@ -853,17 +914,15 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameTCP) { EXPECT_EQ(0x0403, frame.tcp.receive_window); // Now test framing boundaries - for (size_t i = 0; i < 7; ++i) { + for (size_t i = 0; i < 6; ++i) { string expected_error; if (i < 1) { - expected_error = "Unable to read frame count."; - } else if (i < 2) { expected_error = "Unable to read frame type."; - } else if (i < 3) { + } else if (i < 2) { expected_error = "Unable to read congestion feedback type."; - } else if (i < 5) { + } else if (i < 4) { expected_error = "Unable to read accumulated number of lost packets."; - } else if (i < 7) { + } else if (i < 6) { expected_error = "Unable to read receive window."; } CheckProcessingFails(packet, i + kPacketHeaderSize, expected_error, @@ -886,8 +945,6 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameInterArrival) { // first fec protected packet offset 0xFF, - // frame count - 0x01, // frame type (congestion feedback frame) 0x03, // congestion feedback type (inter arrival) @@ -942,29 +999,27 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameInterArrival) { iter->second); // Now test framing boundaries - for (size_t i = 0; i < 32; ++i) { + for (size_t i = 0; i < 31; ++i) { string expected_error; if (i < 1) { - expected_error = "Unable to read frame count."; - } else if (i < 2) { expected_error = "Unable to read frame type."; - } else if (i < 3) { + } else if (i < 2) { expected_error = "Unable to read congestion feedback type."; - } else if (i < 5) { + } else if (i < 4) { expected_error = "Unable to read accumulated number of lost packets."; - } else if (i < 6) { + } else if (i < 5) { expected_error = "Unable to read num received packets."; - } else if (i < 12) { + } else if (i < 11) { expected_error = "Unable to read smallest received."; - } else if (i < 20) { + } else if (i < 19) { expected_error = "Unable to read time received."; - } else if (i < 22) { + } else if (i < 21) { expected_error = "Unable to read sequence delta in received packets."; - } else if (i < 26) { + } else if (i < 25) { expected_error = "Unable to read time delta in received packets."; - } else if (i < 28) { + } else if (i < 27) { expected_error = "Unable to read sequence delta in received packets."; - } else if (i < 32) { + } else if (i < 31) { expected_error = "Unable to read time delta in received packets."; } CheckProcessingFails(packet, i + kPacketHeaderSize, expected_error, @@ -987,8 +1042,6 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameFixRate) { // first fec protected packet offset 0xFF, - // frame count - 0x01, // frame type (congestion feedback frame) 0x03, // congestion feedback type (fix rate) @@ -1013,15 +1066,13 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameFixRate) { frame.fix_rate.bitrate_in_bytes_per_second); // Now test framing boundaries - for (size_t i = 0; i < 7; ++i) { + for (size_t i = 0; i < 6; ++i) { string expected_error; if (i < 1) { - expected_error = "Unable to read frame count."; - } else if (i < 2) { expected_error = "Unable to read frame type."; - } else if (i < 3) { + } else if (i < 2) { expected_error = "Unable to read congestion feedback type."; - } else if (i < 7) { + } else if (i < 6) { expected_error = "Unable to read bitrate."; } CheckProcessingFails(packet, i + kPacketHeaderSize, expected_error, @@ -1045,8 +1096,6 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameInvalidFeedback) { // first fec protected packet offset 0xFF, - // frame count - 0x01, // frame type (congestion feedback frame) 0x03, // congestion feedback type (invalid) @@ -1074,8 +1123,6 @@ TEST_F(QuicFramerTest, RstStreamFrame) { // first fec protected packet offset 0xFF, - // frame count - 0x01, // frame type (rst stream frame) 0x04, // stream id @@ -1110,15 +1157,15 @@ TEST_F(QuicFramerTest, RstStreamFrame) { EXPECT_EQ("because I can", visitor_.rst_stream_frame_.error_details); // Now test framing boundaries - for (size_t i = 3; i < 33; ++i) { + for (size_t i = 2; i < 32; ++i) { string expected_error; - if (i < 6) { + if (i < 5) { expected_error = "Unable to read stream_id."; - } else if (i < 14) { + } else if (i < 13) { expected_error = "Unable to read offset in rst frame."; - } else if (i < 18) { + } else if (i < 17) { expected_error = "Unable to read rst stream error code."; - } else if (i < 33) { + } else if (i < 32) { expected_error = "Unable to read rst stream error details."; } CheckProcessingFails(packet, i + kPacketHeaderSize, expected_error, @@ -1141,8 +1188,6 @@ TEST_F(QuicFramerTest, ConnectionCloseFrame) { // first fec protected packet offset 0xFF, - // frame count - 0x01, // frame type (connection close frame) 0x05, // error code @@ -1168,14 +1213,6 @@ TEST_F(QuicFramerTest, ConnectionCloseFrame) { // missing packet 0xBE, 0x9A, 0x78, 0x56, 0x34, 0x12, - // congestion feedback type (inter arrival) - 0x02, - // accumulated_number_of_lost_packets - 0x02, 0x03, - // offset_time - 0x04, 0x05, - // delta_time - 0x06, 0x07, }; QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); @@ -1200,11 +1237,11 @@ TEST_F(QuicFramerTest, ConnectionCloseFrame) { EXPECT_EQ(GG_UINT64_C(0x0123456789AA0), frame.sent_info.least_unacked); // Now test framing boundaries - for (size_t i = 3; i < 21; ++i) { + for (size_t i = 2; i < 20; ++i) { string expected_error; - if (i < 6) { + if (i < 5) { expected_error = "Unable to read connection close error code."; - } else if (i < 21) { + } else if (i < 20) { expected_error = "Unable to read connection close error details."; } CheckProcessingFails(packet, i + kPacketHeaderSize, expected_error, @@ -1212,6 +1249,50 @@ TEST_F(QuicFramerTest, ConnectionCloseFrame) { } } +TEST_F(QuicFramerTest, PublicResetPacket) { + unsigned char packet[] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // public flags (public reset) + 0x02, + // nonce proof + 0x89, 0x67, 0x45, 0x23, + 0x01, 0xEF, 0xCD, 0xAB, + // rejected sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(self_address_, peer_address_, encrypted)); + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.public_reset_packet_.get()); + EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), + visitor_.public_reset_packet_->public_header.guid); + EXPECT_EQ(PACKET_PUBLIC_FLAGS_RST, + visitor_.public_reset_packet_->public_header.flags); + EXPECT_EQ(GG_UINT64_C(0xABCDEF0123456789), + visitor_.public_reset_packet_->nonce_proof); + EXPECT_EQ(GG_UINT64_C(0x123456789ABC), + visitor_.public_reset_packet_->rejected_sequence_number); + + // Now test framing boundaries + for (size_t i = 0; i < kPublicResetPacketSize; ++i) { + string expected_error; + if (i < kPublicFlagsOffset) { + expected_error = "Unable to read GUID."; + } else if (i < kPublicResetPacketNonceProofOffset) { + expected_error = "Unable to read public flags."; + } else if (i < kPublicResetPacketRejectedSequenceNumberOffset) { + expected_error = "Unable to read nonce proof."; + } else { + expected_error = "Unable to read rejected sequence number."; + } + CheckProcessingFails(packet, i, expected_error, QUIC_INVALID_PACKET_HEADER); + } +} + TEST_F(QuicFramerTest, FecPacket) { unsigned char packet[] = { // guid @@ -1276,8 +1357,6 @@ TEST_F(QuicFramerTest, ConstructPaddingFramePacket) { // first fec protected packet offset 0xFF, - // frame count - 0x01, // frame type (padding frame) 0x00, }; @@ -1325,8 +1404,6 @@ TEST_F(QuicFramerTest, ConstructStreamFramePacket) { // first fec protected packet offset 0xFF, - // frame count - 0x01, // frame type (stream frame) 0x01, // stream id @@ -1383,8 +1460,6 @@ TEST_F(QuicFramerTest, ConstructAckFramePacket) { // first fec protected packet offset 0xFF, - // frame count - 0x01, // frame type (ack frame) 0x02, // least packet sequence number awaiting an ack @@ -1438,8 +1513,6 @@ TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketTCP) { // first fec protected packet offset 0xFF, - // frame count - 0x01, // frame type (congestion feedback frame) 0x03, // congestion feedback type (TCP) @@ -1495,8 +1568,6 @@ TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketInterArrival) { // first fec protected packet offset 0xFF, - // frame count - 0x01, // frame type (congestion feedback frame) 0x03, // congestion feedback type (inter arrival) @@ -1559,8 +1630,6 @@ TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketFixRate) { // first fec protected packet offset 0xFF, - // frame count - 0x01, // frame type (congestion feedback frame) 0x03, // congestion feedback type (fix rate) @@ -1624,8 +1693,6 @@ TEST_F(QuicFramerTest, ConstructRstFramePacket) { // first fec protected packet offset 0xFF, - // frame count - 0x01, // frame type (rst stream frame) 0x04, // stream id @@ -1689,8 +1756,6 @@ TEST_F(QuicFramerTest, ConstructCloseFramePacket) { // first fec protected packet offset 0xFF, - // frame count - 0x01, // frame type (connection close frame) 0x05, // error code @@ -1725,6 +1790,36 @@ TEST_F(QuicFramerTest, ConstructCloseFramePacket) { AsChars(packet), arraysize(packet)); } +TEST_F(QuicFramerTest, ConstructPublicResetPacket) { + QuicPublicResetPacket reset_packet; + reset_packet.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); + reset_packet.public_header.flags = PACKET_PUBLIC_FLAGS_RST; + reset_packet.rejected_sequence_number = GG_UINT64_C(0x123456789ABC); + reset_packet.nonce_proof = GG_UINT64_C(0xABCDEF0123456789); + + unsigned char packet[] = { + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // public flags + 0x02, + // nonce proof + 0x89, 0x67, 0x45, 0x23, + 0x01, 0xEF, 0xCD, 0xAB, + // rejected sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + }; + + scoped_ptr<QuicEncryptedPacket> data( + framer_.ConstructPublicResetPacket(reset_packet)); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + TEST_F(QuicFramerTest, ConstructFecPacket) { QuicPacketHeader header; header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); @@ -1864,21 +1959,86 @@ TEST_F(QuicFramerTest, DISABLED_Truncation) { // Now make sure we can turn our ack packet back into an ack frame ASSERT_TRUE(framer_.ProcessPacket(self_address_, peer_address_, *ack_packet)); - EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); - ASSERT_TRUE(visitor_.header_.get()); - ASSERT_EQ(peer_address_, visitor_.peer_address_); - ASSERT_EQ(self_address_, visitor_.self_address_); - EXPECT_EQ(1u, visitor_.ack_frames_.size()); // And do the same for the close frame. ASSERT_TRUE(framer_.ProcessPacket(self_address_, peer_address_, *close_packet)); - EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); - EXPECT_EQ(0x05060708, visitor_.connection_close_frame_.error_code); - EXPECT_EQ("because I can", visitor_.connection_close_frame_.error_details); +} + +TEST_F(QuicFramerTest, CleanTruncation) { + QuicPacketHeader header; + header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.flags = PACKET_PUBLIC_FLAGS_NONE; + header.private_flags = PACKET_PRIVATE_FLAGS_NONE; + header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); + header.fec_group = 0; + + QuicConnectionCloseFrame close_frame; + QuicAckFrame* ack_frame = &close_frame.ack_frame; + close_frame.error_code = static_cast<QuicErrorCode>(0x05060708); + close_frame.error_details = "because I can"; + ack_frame->received_info.largest_observed = 201; + for (uint64 i = 1; i < ack_frame->received_info.largest_observed; ++i) { + ack_frame->received_info.missing_packets.insert(i); + } + + // Create a packet with just the ack + QuicFrame frame; + frame.type = ACK_FRAME; + frame.ack_frame = ack_frame; + QuicFrames frames; + frames.push_back(frame); + + scoped_ptr<QuicPacket> raw_ack_packet( + framer_.ConstructFrameDataPacket(header, frames)); + ASSERT_TRUE(raw_ack_packet != NULL); + + scoped_ptr<QuicEncryptedPacket> ack_packet( + framer_.EncryptPacket(*raw_ack_packet)); - ValidateTruncatedAck(visitor_.ack_frames_[0], 194); - ValidateTruncatedAck(&visitor_.connection_close_frame_.ack_frame, 191); + // Create a packet with just connection close. + frames.clear(); + frame.type = CONNECTION_CLOSE_FRAME; + frame.connection_close_frame = &close_frame; + frames.push_back(frame); + + scoped_ptr<QuicPacket> raw_close_packet( + framer_.ConstructFrameDataPacket(header, frames)); + ASSERT_TRUE(raw_close_packet != NULL); + + scoped_ptr<QuicEncryptedPacket> close_packet( + framer_.EncryptPacket(*raw_close_packet)); + + // Now make sure we can turn our ack packet back into an ack frame + ASSERT_TRUE(framer_.ProcessPacket(self_address_, peer_address_, *ack_packet)); + + // And do the same for the close frame. + ASSERT_TRUE(framer_.ProcessPacket(self_address_, peer_address_, + *close_packet)); + + // Test for clean truncation of the ack by comparing the length of the + // original packets to the re-serialized packets. + frames.clear(); + frame.type = ACK_FRAME; + frame.ack_frame = visitor_.ack_frames_[0]; + frames.push_back(frame); + + size_t original_raw_length = raw_ack_packet->length(); + raw_ack_packet.reset( + framer_.ConstructFrameDataPacket(header, frames)); + ASSERT_TRUE(raw_ack_packet != NULL); + EXPECT_EQ(original_raw_length, raw_ack_packet->length()); + + frames.clear(); + frame.type = CONNECTION_CLOSE_FRAME; + frame.connection_close_frame = &visitor_.connection_close_frame_; + frames.push_back(frame); + + original_raw_length = raw_close_packet->length(); + raw_close_packet.reset( + framer_.ConstructFrameDataPacket(header, frames)); + ASSERT_TRUE(raw_ack_packet != NULL); + EXPECT_EQ(original_raw_length, raw_close_packet->length()); } } // namespace test diff --git a/net/quic/quic_packet_creator.cc b/net/quic/quic_packet_creator.cc index cd79bbb..39d9f7e 100644 --- a/net/quic/quic_packet_creator.cc +++ b/net/quic/quic_packet_creator.cc @@ -44,6 +44,7 @@ bool QuicPacketCreator::ShouldSendFec(bool force_close) const { void QuicPacketCreator::MaybeStartFEC() { if (options_.max_packets_per_fec_group > 0) { DCHECK(fec_group_.get() == NULL); + // Set the fec group number to the sequence number of the next packet. fec_group_number_ = sequence_number() + 1; fec_group_.reset(new QuicFecGroup()); } diff --git a/net/quic/quic_packet_creator.h b/net/quic/quic_packet_creator.h index 0fc8176..f9836f3 100644 --- a/net/quic/quic_packet_creator.h +++ b/net/quic/quic_packet_creator.h @@ -73,6 +73,8 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { // Packetize FEC data. PacketPair SerializeFec(); + // Creates a packet with connection close frame. Caller owns the created + // packet. PacketPair CloseConnection(QuicConnectionCloseFrame* close_frame); QuicPacketSequenceNumber sequence_number() const { diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h index bc486dc..83f4ae8 100644 --- a/net/quic/quic_protocol.h +++ b/net/quic/quic_protocol.h @@ -33,6 +33,7 @@ typedef uint32 QuicStreamId; typedef uint64 QuicStreamOffset; typedef uint64 QuicPacketSequenceNumber; typedef QuicPacketSequenceNumber QuicFecGroupNumber; +typedef uint64 QuicPublicResetNonceProof; // TODO(rch): Consider Quic specific names for these constants. const size_t kMaxPacketSize = 1200; // Maximum size in bytes of a QUIC packet. @@ -50,10 +51,15 @@ const size_t kSequenceNumberSize = 6; const size_t kPrivateFlagsSize = 1; // Number of bytes reserved for FEC group in the packet header. const size_t kFecGroupSize = 1; +// Number of bytes reserved for the nonce proof in public reset packet. +const size_t kPublicResetNonceSize = 8; // Size in bytes of the data or fec packet header. const size_t kPacketHeaderSize = kQuicGuidSize + kPublicFlagsSize + kPrivateFlagsSize + kSequenceNumberSize + kFecGroupSize; +// Size in bytes of the public reset packet. +const size_t kPublicResetPacketSize = kQuicGuidSize + kPublicFlagsSize + + kPublicResetNonceSize + kSequenceNumberSize; // Index into the guid offset in the header. const size_t kGuidOffset = 0; @@ -67,6 +73,13 @@ const size_t kPrivateFlagsOffset = kSequenceNumberOffset + kSequenceNumberSize; // Index into the fec group offset in the header. const size_t kFecGroupOffset = kPrivateFlagsOffset + kPrivateFlagsSize; +// Index into the nonce proof of the public reset packet. +const size_t kPublicResetPacketNonceProofOffset = kPublicFlagsOffset + + kPublicFlagsSize; +// Index into the rejected sequence number of the public reset packet. +const size_t kPublicResetPacketRejectedSequenceNumberOffset = + kPublicResetPacketNonceProofOffset + kPublicResetNonceSize; + // Index of the first byte in a QUIC packet of FEC protected data. const size_t kStartOfFecProtectedData = kPacketHeaderSize; // Index of the first byte in a QUIC packet of encrypted data. @@ -162,6 +175,8 @@ enum QuicErrorCode { QUIC_INVALID_STREAM_ID, // Too many streams already open. QUIC_TOO_MANY_OPEN_STREAMS, + // Received public reset for this connection. + QUIC_PUBLIC_RESET, // We hit our prenegotiated (or default) timeout QUIC_CONNECTION_TIMED_OUT, @@ -199,6 +214,15 @@ struct QuicPacketHeader { QuicFecGroupNumber fec_group; }; +struct QuicPublicResetPacket { + QuicPublicResetPacket() {} + explicit QuicPublicResetPacket(const QuicPacketPublicHeader& header) + : public_header(header) {} + QuicPacketPublicHeader public_header; + QuicPacketSequenceNumber rejected_sequence_number; + QuicPublicResetNonceProof nonce_proof; +}; + // A padding frame contains no payload. struct NET_EXPORT_PRIVATE QuicPaddingFrame { }; diff --git a/net/quic/quic_time.cc b/net/quic/quic_time.cc index 7d3ba66..a2c6901 100644 --- a/net/quic/quic_time.cc +++ b/net/quic/quic_time.cc @@ -15,26 +15,27 @@ QuicTime::Delta::Delta(base::TimeDelta delta) : delta_(delta) { } +// static QuicTime::Delta QuicTime::Delta::Zero() { return QuicTime::Delta::FromMicroseconds(0); } +// static QuicTime::Delta QuicTime::Delta::Infinite() { return QuicTime::Delta::FromMicroseconds(kQuicInfiniteTimeUs); } -bool QuicTime::Delta::IsZero() const { - return delta_.InMicroseconds() == 0; -} - -bool QuicTime::Delta::IsInfinite() const { - return delta_.InMicroseconds() == kQuicInfiniteTimeUs; +// static +QuicTime::Delta QuicTime::Delta::FromSeconds(int64 seconds) { + return QuicTime::Delta(base::TimeDelta::FromSeconds(seconds)); } +// static QuicTime::Delta QuicTime::Delta::FromMilliseconds(int64 ms) { return QuicTime::Delta(base::TimeDelta::FromMilliseconds(ms)); } +// static QuicTime::Delta QuicTime::Delta::FromMicroseconds(int64 us) { return QuicTime::Delta(base::TimeDelta::FromMicroseconds(us)); } @@ -61,15 +62,20 @@ QuicTime::Delta QuicTime::Delta::Subtract(const Delta& delta) const { delta.ToMicroseconds()); } +bool QuicTime::Delta::IsZero() const { + return delta_.InMicroseconds() == 0; +} + +bool QuicTime::Delta::IsInfinite() const { + return delta_.InMicroseconds() == kQuicInfiniteTimeUs; +} + // static QuicTime QuicTime::Zero() { return QuicTime::FromMilliseconds(0); } -QuicTime::QuicTime(base::TimeTicks ticks) - : ticks_(ticks) { -} - +// static QuicTime QuicTime::FromMilliseconds(int64 time_ms) { // DateTime use 100 ns as resolution make sure we don't pass down too high // values. @@ -78,6 +84,7 @@ QuicTime QuicTime::FromMilliseconds(int64 time_ms) { base::TimeDelta::FromMilliseconds(time_ms)); } +// static QuicTime QuicTime::FromMicroseconds(int64 time_us) { // DateTime use 100 ns as resolution make sure we don't pass down too high // values. @@ -86,6 +93,10 @@ QuicTime QuicTime::FromMicroseconds(int64 time_us) { base::TimeDelta::FromMicroseconds(time_us)); } +QuicTime::QuicTime(base::TimeTicks ticks) + : ticks_(ticks) { +} + int64 QuicTime::ToMilliseconds() const { return (ticks_ - base::TimeTicks()).InMilliseconds(); } diff --git a/net/quic/quic_time.h b/net/quic/quic_time.h index a1fb6a3..db95241 100644 --- a/net/quic/quic_time.h +++ b/net/quic/quic_time.h @@ -25,11 +25,14 @@ class NET_EXPORT_PRIVATE QuicTime { public: explicit Delta(base::TimeDelta delta); + // Create a object with an offset of 0. + static Delta Zero(); + // Create a object with infinite offset time. static Delta Infinite(); - // Create a object with infinite offset time. - static Delta Zero(); + // Converts a number of seconds to a time offset. + static Delta FromSeconds(int64 secs); // Converts a number of milliseconds to a time offset. static Delta FromMilliseconds(int64 ms); @@ -58,6 +61,7 @@ class NET_EXPORT_PRIVATE QuicTime { base::TimeDelta delta_; friend class QuicTime; + friend class QuicClock; }; explicit QuicTime(base::TimeTicks ticks); diff --git a/net/quic/quic_time_test.cc b/net/quic/quic_time_test.cc index 5353289..811204bd 100644 --- a/net/quic/quic_time_test.cc +++ b/net/quic/quic_time_test.cc @@ -28,9 +28,17 @@ TEST_F(QuicTimeDeltaTest, Infinite) { TEST_F(QuicTimeDeltaTest, FromTo) { EXPECT_EQ(QuicTime::Delta::FromMilliseconds(1), QuicTime::Delta::FromMicroseconds(1000)); + EXPECT_EQ(QuicTime::Delta::FromSeconds(1), + QuicTime::Delta::FromMilliseconds(1000)); + EXPECT_EQ(QuicTime::Delta::FromSeconds(1), + QuicTime::Delta::FromMicroseconds(1000000)); + EXPECT_EQ(1, QuicTime::Delta::FromMicroseconds(1000).ToMilliseconds()); EXPECT_EQ(2, QuicTime::Delta::FromMilliseconds(2000).ToSeconds()); EXPECT_EQ(1000, QuicTime::Delta::FromMilliseconds(1).ToMicroseconds()); + EXPECT_EQ(1, QuicTime::Delta::FromMicroseconds(1000).ToMilliseconds()); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(2000).ToMicroseconds(), + QuicTime::Delta::FromSeconds(2).ToMicroseconds()); } TEST_F(QuicTimeDeltaTest, Add) { diff --git a/net/quic/quic_utils.cc b/net/quic/quic_utils.cc index 0ff25d0..ee88289 100644 --- a/net/quic/quic_utils.cc +++ b/net/quic/quic_utils.cc @@ -13,7 +13,6 @@ namespace net { size_t QuicUtils::StreamFramePacketOverhead(int num_frames) { // TODO(jar): Use sizeof(some name). return kPacketHeaderSize + - 1 + // frame count (1 + // 8 bit type kMinStreamFrameLength) * num_frames; } @@ -68,6 +67,7 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) { RETURN_STRING_LITERAL(QUIC_INVALID_CRYPTO_MESSAGE_TYPE); RETURN_STRING_LITERAL(QUIC_INVALID_STREAM_ID); RETURN_STRING_LITERAL(QUIC_TOO_MANY_OPEN_STREAMS); + RETURN_STRING_LITERAL(QUIC_PUBLIC_RESET); RETURN_STRING_LITERAL(QUIC_CONNECTION_TIMED_OUT); // Intentionally have no default case, so we'll break the build // if we add errors and don't put them here. diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h index b2600df..61d204b 100644 --- a/net/quic/test_tools/quic_test_utils.h +++ b/net/quic/test_tools/quic_test_utils.h @@ -45,6 +45,7 @@ class MockFramerVisitor : public QuicFramerVisitorInterface { MOCK_METHOD1(OnError, void(QuicFramer* framer)); MOCK_METHOD2(OnPacket, void(const IPEndPoint& self_address, const IPEndPoint& peer_address)); + MOCK_METHOD1(OnPublicResetPacket, void(const QuicPublicResetPacket& header)); MOCK_METHOD0(OnRevivedPacket, void()); // The constructor set this up to return true by default. MOCK_METHOD1(OnPacketHeader, bool(const QuicPacketHeader& header)); @@ -70,6 +71,8 @@ class NoOpFramerVisitor : public QuicFramerVisitorInterface { virtual void OnError(QuicFramer* framer) OVERRIDE {} virtual void OnPacket(const IPEndPoint& self_address, const IPEndPoint& peer_address) OVERRIDE {} + virtual void OnPublicResetPacket( + const QuicPublicResetPacket& packet) OVERRIDE {} virtual void OnRevivedPacket() OVERRIDE {} virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE; virtual void OnFecProtectedPayload(base::StringPiece payload) OVERRIDE {} |