diff options
author | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-31 07:35:31 +0000 |
---|---|---|
committer | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-31 07:35:31 +0000 |
commit | 8e01c06dc909bf509e3cddaa6d1daa5712834d5c (patch) | |
tree | 6f12e762feaff9f4866479297fefae7aa3b53003 /net | |
parent | e13748443e37885265c38a3d2bfad0a0389074d0 (diff) | |
download | chromium_src-8e01c06dc909bf509e3cddaa6d1daa5712834d5c.zip chromium_src-8e01c06dc909bf509e3cddaa6d1daa5712834d5c.tar.gz chromium_src-8e01c06dc909bf509e3cddaa6d1daa5712834d5c.tar.bz2 |
Land Recent QUIC changes.
Fix a bug in the QuicFramer where the entropy genererator was not
being consulted when truncated acks were sent, causing an incorrect
entropy to be sent for truncated acks.
Merge internal change: 55879563
Add an optional initial RTT to the negotiation parameters to allow the
QUIC client to suggest an expected RTT to the server.
Merge internal change: 55792505
Changing DCHECKs to LOG(DFATAL) so we'll notice if they're occurring in
the running server/client.
Merge internal change: 55790448
Added a blank line per the change in the following CL which deleted
default-true flags.
Merge internal change: 55738366
Allow a REJ message to be twice as large as a CHLO message that doesn't
contain a valid source-address token.
message that doesn't contain a valid source-address token could be
twice as large as before.
Merge internal change: 55736193
Log OpenSSL errors when QUIC encryption fails.
Merge internal change: 55718465
Second version with improved tests and keeping the client and server
packet sizes identical to ensure the truncated ack detection works
correctly.
Merge internal change: 55651642
Hidden bug that uses ack_frame instead of stream_frame.
Merge internal change: 55648483
Move QuicCryptoServerConfig from crypto_server_config.{h,cc} to
quic_crypto_server_config.{h,cc} and move QuicCryptoClientConfig from
crypt_handshake.{h,cc} to quic_crypto_client_config.{h,cc} to be
consistent.
Merge internal change: 55644306
Ported IOVector unit tests (as part of porting the following CL).
Makes GFE's IOVector class copy-able. I am working on using IOVector
through QUIC's read and write paths, and it would be tremendously
helpful to have value-semantics for an IOVector object. Making it
copy-able was noted in joechan@'s TODO in the original IOVector code.
Merge internal change: 55509822
Cancel all pending alarms when the QUIC connection is closed.
Merge internal change: 55640997
Removed version 10 from supported versions.
Adding QUIC_VERSION_12 to optimize the Quic ack framing format to
reduce the size and better handle ranges of nacks, which should make
truncated acks virtually impossible. Also adding an explicit flag for
truncated acks and moving the ack outside of the connection close frame.
Merge internal change: 55594574
R=rch@chromium.org
Review URL: https://codereview.chromium.org/51313002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@232035 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
65 files changed, 2808 insertions, 1117 deletions
diff --git a/net/net.gyp b/net/net.gyp index 114f8f1..9d511d1 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -762,8 +762,6 @@ 'quic/crypto/crypto_protocol.h', 'quic/crypto/crypto_secret_boxer.cc', 'quic/crypto/crypto_secret_boxer.h', - 'quic/crypto/crypto_server_config.cc', - 'quic/crypto/crypto_server_config.h', 'quic/crypto/crypto_server_config_protobuf.cc', 'quic/crypto/crypto_server_config_protobuf.h', 'quic/crypto/crypto_utils.cc', @@ -785,6 +783,10 @@ 'quic/crypto/proof_verifier.cc', 'quic/crypto/proof_verifier_chromium.cc', 'quic/crypto/proof_verifier_chromium.h', + 'quic/crypto/quic_crypto_client_config.cc', + 'quic/crypto/quic_crypto_client_config.h', + 'quic/crypto/quic_crypto_server_config.cc', + 'quic/crypto/quic_crypto_server_config.h', 'quic/crypto/quic_decrypter.cc', 'quic/crypto/quic_decrypter.h', 'quic/crypto/quic_encrypter.cc', @@ -1736,7 +1738,6 @@ 'quic/crypto/channel_id_test.cc', 'quic/crypto/common_cert_set_test.cc', 'quic/crypto/crypto_framer_test.cc', - 'quic/crypto/crypto_handshake_test.cc', 'quic/crypto/crypto_secret_boxer_test.cc', 'quic/crypto/crypto_server_test.cc', 'quic/crypto/crypto_utils_test.cc', @@ -1745,8 +1746,10 @@ 'quic/crypto/null_encrypter_test.cc', 'quic/crypto/p256_key_exchange_test.cc', 'quic/crypto/proof_test.cc', + 'quic/crypto/quic_crypto_server_config_test.cc', 'quic/crypto/quic_random_test.cc', 'quic/crypto/strike_register_test.cc', + 'quic/iovector_test.cc', 'quic/test_tools/crypto_test_utils.cc', 'quic/test_tools/crypto_test_utils.h', 'quic/test_tools/crypto_test_utils_chromium.cc', diff --git a/net/quic/congestion_control/fix_rate_sender.cc b/net/quic/congestion_control/fix_rate_sender.cc index 05fcb35..7aab1e5 100644 --- a/net/quic/congestion_control/fix_rate_sender.cc +++ b/net/quic/congestion_control/fix_rate_sender.cc @@ -20,8 +20,9 @@ namespace net { FixRateSender::FixRateSender(const QuicClock* clock) : bitrate_(QuicBandwidth::FromBytesPerSecond(kInitialBitrate)), + max_segment_size_(kDefaultMaxPacketSize), fix_rate_leaky_bucket_(bitrate_), - paced_sender_(bitrate_), + paced_sender_(bitrate_, max_segment_size_), data_in_flight_(0), latest_rtt_(QuicTime::Delta::Zero()) { DLOG(INFO) << "FixRateSender"; @@ -30,12 +31,18 @@ FixRateSender::FixRateSender(const QuicClock* clock) FixRateSender::~FixRateSender() { } +void FixRateSender::SetFromConfig(const QuicConfig& config, bool is_server) { + max_segment_size_ = config.server_max_packet_size(); + paced_sender_.set_max_segment_size(max_segment_size_); +} + void FixRateSender::OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, QuicTime feedback_receive_time, const SentPacketsMap& /*sent_packets*/) { - DCHECK(feedback.type == kFixRate) << - "Invalid incoming CongestionFeedbackType:" << feedback.type; + if (feedback.type != kFixRate) { + LOG(DFATAL) << "Invalid incoming CongestionFeedbackType:" << feedback.type; + } if (feedback.type == kFixRate) { bitrate_ = feedback.fix_rate.bitrate; fix_rate_leaky_bucket_.SetDrainingRate(feedback_receive_time, bitrate_); @@ -105,7 +112,7 @@ QuicByteCount FixRateSender::CongestionWindow() { QuicByteCount window_size_bytes = bitrate_.ToBytesPerPeriod( QuicTime::Delta::FromMicroseconds(kWindowSizeUs)); // Make sure window size is not less than a packet. - return std::max(kMaxPacketSize, window_size_bytes); + return std::max(kDefaultMaxPacketSize, window_size_bytes); } QuicBandwidth FixRateSender::BandwidthEstimate() { diff --git a/net/quic/congestion_control/fix_rate_sender.h b/net/quic/congestion_control/fix_rate_sender.h index bbbee68..f69fd08 100644 --- a/net/quic/congestion_control/fix_rate_sender.h +++ b/net/quic/congestion_control/fix_rate_sender.h @@ -23,6 +23,8 @@ class NET_EXPORT_PRIVATE FixRateSender : public SendAlgorithmInterface { explicit FixRateSender(const QuicClock* clock); virtual ~FixRateSender(); + virtual void SetFromConfig(const QuicConfig& config, bool is_server) OVERRIDE; + // Start implementation of SendAlgorithmInterface. virtual void OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, @@ -56,6 +58,7 @@ class NET_EXPORT_PRIVATE FixRateSender : public SendAlgorithmInterface { QuicByteCount CongestionWindow(); QuicBandwidth bitrate_; + QuicByteCount max_segment_size_; LeakyBucket fix_rate_leaky_bucket_; PacedSender paced_sender_; QuicByteCount data_in_flight_; diff --git a/net/quic/congestion_control/fix_rate_test.cc b/net/quic/congestion_control/fix_rate_test.cc index 6cb8c9c..7f186d1 100644 --- a/net/quic/congestion_control/fix_rate_test.cc +++ b/net/quic/congestion_control/fix_rate_test.cc @@ -63,12 +63,12 @@ TEST_F(FixRateTest, SenderAPI) { EXPECT_EQ(300000, sender_->BandwidthEstimate().ToBytesPerSecond()); EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - sender_->OnPacketSent(clock_.Now(), 1, kMaxPacketSize, NOT_RETRANSMISSION, - HAS_RETRANSMITTABLE_DATA); + sender_->OnPacketSent(clock_.Now(), 1, kDefaultMaxPacketSize, + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - sender_->OnPacketSent(clock_.Now(), 2, kMaxPacketSize, NOT_RETRANSMISSION, - HAS_RETRANSMITTABLE_DATA); + sender_->OnPacketSent(clock_.Now(), 2, kDefaultMaxPacketSize, + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); sender_->OnPacketSent(clock_.Now(), 3, 600, NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), @@ -78,8 +78,8 @@ TEST_F(FixRateTest, SenderAPI) { EXPECT_EQ(QuicTime::Delta::Infinite(), sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(8)); - sender_->OnIncomingAck(1, kMaxPacketSize, rtt_); - sender_->OnIncomingAck(2, kMaxPacketSize, rtt_); + sender_->OnIncomingAck(1, kDefaultMaxPacketSize, rtt_); + sender_->OnIncomingAck(2, kDefaultMaxPacketSize, rtt_); sender_->OnIncomingAck(3, 600, rtt_); EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); diff --git a/net/quic/congestion_control/inter_arrival_probe.cc b/net/quic/congestion_control/inter_arrival_probe.cc index 7d6abdc..d8fa2db 100644 --- a/net/quic/congestion_control/inter_arrival_probe.cc +++ b/net/quic/congestion_control/inter_arrival_probe.cc @@ -16,8 +16,9 @@ const float kUncertainScaleFactor = 0.5; // TODO(pwestin): revisit this factor. namespace net { -InterArrivalProbe::InterArrivalProbe() - : estimate_available_(false), +InterArrivalProbe::InterArrivalProbe(QuicByteCount max_segment_size) + : max_segment_size_(max_segment_size), + estimate_available_(false), available_channel_estimate_(QuicBandwidth::Zero()), unacked_data_(0) { } @@ -25,6 +26,10 @@ InterArrivalProbe::InterArrivalProbe() InterArrivalProbe::~InterArrivalProbe() { } +void InterArrivalProbe::set_max_segment_size(QuicByteCount max_segment_size) { + max_segment_size_ = max_segment_size; +} + bool InterArrivalProbe::GetEstimate(QuicBandwidth* available_channel_estimate) { if (!estimate_available_) { return false; @@ -50,7 +55,7 @@ QuicByteCount InterArrivalProbe::GetAvailableCongestionWindow() { if (estimate_available_) { return 0; } - return (kProbeSizePackets * kMaxPacketSize) - unacked_data_; + return (kProbeSizePackets * max_segment_size_) - unacked_data_; } void InterArrivalProbe::OnIncomingFeedback( diff --git a/net/quic/congestion_control/inter_arrival_probe.h b/net/quic/congestion_control/inter_arrival_probe.h index e4c0331..0cba7ee 100644 --- a/net/quic/congestion_control/inter_arrival_probe.h +++ b/net/quic/congestion_control/inter_arrival_probe.h @@ -17,9 +17,11 @@ namespace net { class NET_EXPORT_PRIVATE InterArrivalProbe { public: - InterArrivalProbe(); + explicit InterArrivalProbe(QuicByteCount max_segment_size); ~InterArrivalProbe(); + void set_max_segment_size(QuicByteCount max_segment_size); + // Call every time a packet is sent to the network. void OnPacketSent(QuicByteCount bytes); @@ -45,6 +47,7 @@ class NET_EXPORT_PRIVATE InterArrivalProbe { bool GetEstimate(QuicBandwidth* available_channel_estimate); private: + QuicByteCount max_segment_size_; scoped_ptr<AvailableChannelEstimator> available_channel_estimator_; QuicPacketSequenceNumber first_sequence_number_; bool estimate_available_; diff --git a/net/quic/congestion_control/inter_arrival_probe_test.cc b/net/quic/congestion_control/inter_arrival_probe_test.cc index c311da0..d242a6c 100644 --- a/net/quic/congestion_control/inter_arrival_probe_test.cc +++ b/net/quic/congestion_control/inter_arrival_probe_test.cc @@ -13,7 +13,9 @@ namespace test { class InterArrivalProbeTest : public ::testing::Test { protected: - InterArrivalProbeTest() : start_(QuicTime::Zero()) { + InterArrivalProbeTest() + : probe_(kDefaultMaxPacketSize), + start_(QuicTime::Zero()) { } InterArrivalProbe probe_; @@ -22,19 +24,20 @@ class InterArrivalProbeTest : public ::testing::Test { TEST_F(InterArrivalProbeTest, CongestionWindow) { for (size_t i = 0; i < 10; i++) { - probe_.OnPacketSent(kMaxPacketSize); - EXPECT_EQ((9 - i) * kMaxPacketSize, probe_.GetAvailableCongestionWindow()); + probe_.OnPacketSent(kDefaultMaxPacketSize); + EXPECT_EQ((9 - i) * kDefaultMaxPacketSize, + probe_.GetAvailableCongestionWindow()); } - probe_.OnAcknowledgedPacket(kMaxPacketSize); - EXPECT_EQ(kMaxPacketSize, probe_.GetAvailableCongestionWindow()); + probe_.OnAcknowledgedPacket(kDefaultMaxPacketSize); + EXPECT_EQ(kDefaultMaxPacketSize, probe_.GetAvailableCongestionWindow()); - probe_.OnPacketSent(kMaxPacketSize); + probe_.OnPacketSent(kDefaultMaxPacketSize); EXPECT_EQ(0u, probe_.GetAvailableCongestionWindow()); } TEST_F(InterArrivalProbeTest, Estimate) { QuicPacketSequenceNumber sequence_number = 1; - QuicByteCount bytes_sent = kMaxPacketSize; + QuicByteCount bytes_sent = kDefaultMaxPacketSize; QuicTime time_received = start_.Add(QuicTime::Delta::FromMilliseconds(10)); QuicTime time_sent = start_.Add(QuicTime::Delta::FromMilliseconds(1)); QuicBandwidth available_channel_estimate = QuicBandwidth::Zero(); @@ -50,13 +53,13 @@ TEST_F(InterArrivalProbeTest, Estimate) { time_received = time_received.Add(QuicTime::Delta::FromMilliseconds(10)); } EXPECT_TRUE(probe_.GetEstimate(&available_channel_estimate)); - EXPECT_EQ(kMaxPacketSize * 100, + EXPECT_EQ(kDefaultMaxPacketSize * 100, static_cast<uint64>(available_channel_estimate.ToBytesPerSecond())); } TEST_F(InterArrivalProbeTest, EstimateWithLoss) { QuicPacketSequenceNumber sequence_number = 1; - QuicByteCount bytes_sent = kMaxPacketSize; + QuicByteCount bytes_sent = kDefaultMaxPacketSize; QuicTime time_received = start_.Add(QuicTime::Delta::FromMilliseconds(10)); QuicTime time_sent = start_.Add(QuicTime::Delta::FromMilliseconds(1)); QuicBandwidth available_channel_estimate = QuicBandwidth::Zero(); @@ -73,7 +76,7 @@ TEST_F(InterArrivalProbeTest, EstimateWithLoss) { time_received = time_received.Add(QuicTime::Delta::FromMilliseconds(10)); } EXPECT_TRUE(probe_.GetEstimate(&available_channel_estimate)); - EXPECT_EQ(kMaxPacketSize * 50, + EXPECT_EQ(kDefaultMaxPacketSize * 50, static_cast<uint64>(available_channel_estimate.ToBytesPerSecond())); } diff --git a/net/quic/congestion_control/inter_arrival_sender.cc b/net/quic/congestion_control/inter_arrival_sender.cc index 2740f1e..94df198 100644 --- a/net/quic/congestion_control/inter_arrival_sender.cc +++ b/net/quic/congestion_control/inter_arrival_sender.cc @@ -24,15 +24,16 @@ static const int kMinBitrateSmoothingPeriodMs = 500; InterArrivalSender::InterArrivalSender(const QuicClock* clock) : probing_(true), + max_segment_size_(kDefaultMaxPacketSize), current_bandwidth_(QuicBandwidth::Zero()), smoothed_rtt_(QuicTime::Delta::Zero()), channel_estimator_(new ChannelEstimator()), bitrate_ramp_up_(new InterArrivalBitrateRampUp(clock)), overuse_detector_(new InterArrivalOveruseDetector()), - probe_(new InterArrivalProbe()), + probe_(new InterArrivalProbe(max_segment_size_)), state_machine_(new InterArrivalStateMachine(clock)), paced_sender_(new PacedSender(QuicBandwidth::FromKBytesPerSecond( - kProbeBitrateKBytesPerSecond))), + kProbeBitrateKBytesPerSecond), max_segment_size_)), accumulated_number_of_lost_packets_(0), bandwidth_usage_state_(kBandwidthSteady), back_down_time_(QuicTime::Zero()), @@ -43,6 +44,13 @@ InterArrivalSender::InterArrivalSender(const QuicClock* clock) InterArrivalSender::~InterArrivalSender() { } +void InterArrivalSender::SetFromConfig(const QuicConfig& config, + bool is_server) { + max_segment_size_ = config.server_max_packet_size(); + paced_sender_->set_max_segment_size(max_segment_size_); + probe_->set_max_segment_size(max_segment_size_); +} + // TODO(pwestin): this is really inefficient (4% CPU on the GFE loadtest). // static QuicBandwidth InterArrivalSender::CalculateSentBandwidth( @@ -459,7 +467,7 @@ void InterArrivalSender::EstimateBandwidthAfterDelayEvent( // While in delay sensing mode send at least one packet per RTT. QuicBandwidth min_delay_bitrate = - QuicBandwidth::FromBytesAndTimeDelta(kMaxPacketSize, SmoothedRtt()); + QuicBandwidth::FromBytesAndTimeDelta(max_segment_size_, SmoothedRtt()); new_target_bitrate = std::max(new_target_bitrate, min_delay_bitrate); ResetCurrentBandwidth(feedback_receive_time, new_target_bitrate); diff --git a/net/quic/congestion_control/inter_arrival_sender.h b/net/quic/congestion_control/inter_arrival_sender.h index 3961a2b..2b70e74 100644 --- a/net/quic/congestion_control/inter_arrival_sender.h +++ b/net/quic/congestion_control/inter_arrival_sender.h @@ -27,6 +27,8 @@ class NET_EXPORT_PRIVATE InterArrivalSender : public SendAlgorithmInterface { explicit InterArrivalSender(const QuicClock* clock); virtual ~InterArrivalSender(); + virtual void SetFromConfig(const QuicConfig& config, bool is_server) OVERRIDE; + static QuicBandwidth CalculateSentBandwidth( const SendAlgorithmInterface::SentPacketsMap& sent_packets_map, QuicTime feedback_receive_time); @@ -83,6 +85,7 @@ class NET_EXPORT_PRIVATE InterArrivalSender : public SendAlgorithmInterface { bool ProbingPhase(QuicTime feedback_receive_time); bool probing_; // Are we currently in the probing phase? + QuicByteCount max_segment_size_; QuicBandwidth current_bandwidth_; QuicTime::Delta smoothed_rtt_; scoped_ptr<ChannelEstimator> channel_estimator_; diff --git a/net/quic/congestion_control/inter_arrival_sender_test.cc b/net/quic/congestion_control/inter_arrival_sender_test.cc index c1c848bb..65264ee6 100644 --- a/net/quic/congestion_control/inter_arrival_sender_test.cc +++ b/net/quic/congestion_control/inter_arrival_sender_test.cc @@ -37,7 +37,7 @@ class InterArrivalSenderTest : public ::testing::Test { void SendAvailableCongestionWindow() { while (sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()) { - QuicByteCount bytes_in_packet = kMaxPacketSize; + QuicByteCount bytes_in_packet = kDefaultMaxPacketSize; sent_packets_[sequence_number_] = new class SendAlgorithmInterface::SentPacket( bytes_in_packet, send_clock_.Now()); @@ -52,7 +52,8 @@ class InterArrivalSenderTest : public ::testing::Test { void AckNPackets(int n) { for (int i = 0; i < n; ++i) { - sender_.OnIncomingAck(acked_sequence_number_++, kMaxPacketSize, rtt_); + sender_.OnIncomingAck( + acked_sequence_number_++, kDefaultMaxPacketSize, rtt_); } } @@ -147,7 +148,7 @@ TEST_F(InterArrivalSenderTest, ProbeFollowedByFullRampUpCycle) { // We should now have our probe rate. QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(41); - int64 probe_rate = kMaxPacketSize * 9 * kNumMicrosPerSecond / + int64 probe_rate = kDefaultMaxPacketSize * 9 * kNumMicrosPerSecond / acc_arrival_time.ToMicroseconds(); EXPECT_NEAR(0.7f * probe_rate, sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); @@ -259,7 +260,7 @@ TEST_F(InterArrivalSenderTest, ProbeFollowedByFullRampUpCycle) { sender_.BandwidthEstimate().ToBytesPerSecond(), 2000); EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 3400, 100); - int64 max_rate = kMaxPacketSize * kNumMicrosPerSecond / + int64 max_rate = kDefaultMaxPacketSize * kNumMicrosPerSecond / one_ms_.ToMicroseconds(); int64 halfway_rate = probe_rate + (max_rate - probe_rate) / 2; @@ -325,7 +326,7 @@ TEST_F(InterArrivalSenderTest, DelaySpikeFollowedBySlowDrain) { // We should now have our probe rate. QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(41); - int64 probe_rate = kMaxPacketSize * 9 * kNumMicrosPerSecond / + int64 probe_rate = kDefaultMaxPacketSize * 9 * kNumMicrosPerSecond / acc_arrival_time.ToMicroseconds(); EXPECT_NEAR(0.7f * probe_rate, sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); @@ -422,7 +423,7 @@ TEST_F(InterArrivalSenderTest, DelaySpikeFollowedByImmediateDrain) { // We should now have our probe rate. QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(41); - int64 probe_rate = kMaxPacketSize * 9 * kNumMicrosPerSecond / + int64 probe_rate = kDefaultMaxPacketSize * 9 * kNumMicrosPerSecond / acc_arrival_time.ToMicroseconds(); EXPECT_NEAR(0.7f * probe_rate, sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); @@ -526,7 +527,7 @@ TEST_F(InterArrivalSenderTest, MinBitrateDueToLoss) { NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(81); - int64 probe_rate = kMaxPacketSize * 9 * kNumMicrosPerSecond / + int64 probe_rate = kDefaultMaxPacketSize * 9 * kNumMicrosPerSecond / acc_arrival_time.ToMicroseconds(); EXPECT_NEAR(0.7f * probe_rate, sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); @@ -539,7 +540,7 @@ TEST_F(InterArrivalSenderTest, MinBitrateDueToLoss) { EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); sender_.OnIncomingLoss(send_clock_.Now()); - sender_.OnIncomingAck(acked_sequence_number_, kMaxPacketSize, rtt_); + sender_.OnIncomingAck(acked_sequence_number_, kDefaultMaxPacketSize, rtt_); acked_sequence_number_ += 2; // Create a loss by not acking both packets. SendFeedbackMessageNPackets(2, nine_ms_, nine_ms_); } @@ -554,7 +555,7 @@ TEST_F(InterArrivalSenderTest, MinBitrateDueToLoss) { EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); sender_.OnIncomingLoss(send_clock_.Now()); - sender_.OnIncomingAck(acked_sequence_number_, kMaxPacketSize, rtt_); + sender_.OnIncomingAck(acked_sequence_number_, kDefaultMaxPacketSize, rtt_); acked_sequence_number_ += 2; // Create a loss by not acking both packets. SendFeedbackMessageNPackets(2, nine_ms_, nine_ms_); diff --git a/net/quic/congestion_control/paced_sender.cc b/net/quic/congestion_control/paced_sender.cc index 8de5437..9a5cc44 100644 --- a/net/quic/congestion_control/paced_sender.cc +++ b/net/quic/congestion_control/paced_sender.cc @@ -16,9 +16,14 @@ const int64 kMinPacketBurstSize = 2; // AvailableCongestionWindow. const int64 kMaxSchedulingDelayUs = 2000; -PacedSender::PacedSender(QuicBandwidth estimate) +PacedSender::PacedSender(QuicBandwidth estimate, QuicByteCount max_segment_size) : leaky_bucket_(estimate), - pace_(estimate) { + pace_(estimate), + max_segment_size_(kDefaultMaxPacketSize) { +} + +void PacedSender::set_max_segment_size(QuicByteCount max_segment_size) { + max_segment_size_ = max_segment_size; } void PacedSender::UpdateBandwidthEstimate(QuicTime now, @@ -39,7 +44,7 @@ QuicTime::Delta PacedSender::TimeUntilSend(QuicTime now, // Pace the data. QuicByteCount pacing_window = pace_.ToBytesPerPeriod( QuicTime::Delta::FromMicroseconds(kMaxSchedulingDelayUs)); - QuicByteCount min_window_size = kMinPacketBurstSize * kMaxPacketSize; + QuicByteCount min_window_size = kMinPacketBurstSize * max_segment_size_; pacing_window = std::max(pacing_window, min_window_size); if (pacing_window > leaky_bucket_.BytesPending(now)) { diff --git a/net/quic/congestion_control/paced_sender.h b/net/quic/congestion_control/paced_sender.h index 1f61585..6d7c429 100644 --- a/net/quic/congestion_control/paced_sender.h +++ b/net/quic/congestion_control/paced_sender.h @@ -17,7 +17,9 @@ namespace net { class NET_EXPORT_PRIVATE PacedSender { public: - explicit PacedSender(QuicBandwidth bandwidth_estimate); + PacedSender(QuicBandwidth bandwidth_estimate, QuicByteCount max_segment_size); + + void set_max_segment_size(QuicByteCount max_segment_size); // The estimated bandidth from the congestion algorithm changed. void UpdateBandwidthEstimate(QuicTime now, QuicBandwidth bandwidth_estimate); @@ -32,6 +34,7 @@ class NET_EXPORT_PRIVATE PacedSender { // Helper object to track the rate data can leave the buffer for pacing. LeakyBucket leaky_bucket_; QuicBandwidth pace_; + QuicByteCount max_segment_size_; DISALLOW_COPY_AND_ASSIGN(PacedSender); }; diff --git a/net/quic/congestion_control/paced_sender_test.cc b/net/quic/congestion_control/paced_sender_test.cc index b583315..fa42c2c 100644 --- a/net/quic/congestion_control/paced_sender_test.cc +++ b/net/quic/congestion_control/paced_sender_test.cc @@ -20,7 +20,8 @@ class PacedSenderTest : public ::testing::Test { PacedSenderTest() : zero_time_(QuicTime::Delta::Zero()), paced_sender_(new PacedSender( - QuicBandwidth::FromKBytesPerSecond(kHundredKBytesPerS))) { + QuicBandwidth::FromKBytesPerSecond(kHundredKBytesPerS), + kDefaultMaxPacketSize)) { } const QuicTime::Delta zero_time_; @@ -32,10 +33,10 @@ TEST_F(PacedSenderTest, Basic) { paced_sender_->UpdateBandwidthEstimate(clock_.Now(), QuicBandwidth::FromKBytesPerSecond(kHundredKBytesPerS * 10)); EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero()); - paced_sender_->OnPacketSent(clock_.Now(), kMaxPacketSize); + paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize); EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero()); - paced_sender_->OnPacketSent(clock_.Now(), kMaxPacketSize); - EXPECT_EQ(static_cast<int64>(kMaxPacketSize * 2), + paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize); + EXPECT_EQ(static_cast<int64>(kDefaultMaxPacketSize * 2), paced_sender_->TimeUntilSend( clock_.Now(), zero_time_).ToMicroseconds()); clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(24)); @@ -46,10 +47,10 @@ TEST_F(PacedSenderTest, LowRate) { paced_sender_->UpdateBandwidthEstimate(clock_.Now(), QuicBandwidth::FromKBytesPerSecond(kHundredKBytesPerS)); EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero()); - paced_sender_->OnPacketSent(clock_.Now(), kMaxPacketSize); + paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize); EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero()); - paced_sender_->OnPacketSent(clock_.Now(), kMaxPacketSize); - EXPECT_EQ(static_cast<int64>(kMaxPacketSize * 20), + paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize); + EXPECT_EQ(static_cast<int64>(kDefaultMaxPacketSize * 20), paced_sender_->TimeUntilSend( clock_.Now(), zero_time_).ToMicroseconds()); clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(24)); @@ -62,11 +63,11 @@ TEST_F(PacedSenderTest, HighRate) { paced_sender_->UpdateBandwidthEstimate(clock_.Now(), bandwidth_estimate); EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero()); for (int i = 0; i < 16; ++i) { - paced_sender_->OnPacketSent(clock_.Now(), kMaxPacketSize); + paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize); EXPECT_TRUE(paced_sender_->TimeUntilSend( clock_.Now(), zero_time_).IsZero()); } - paced_sender_->OnPacketSent(clock_.Now(), kMaxPacketSize); + paced_sender_->OnPacketSent(clock_.Now(), kDefaultMaxPacketSize); EXPECT_EQ(2040, paced_sender_->TimeUntilSend( clock_.Now(), zero_time_).ToMicroseconds()); clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(20400)); diff --git a/net/quic/congestion_control/quic_congestion_control_test.cc b/net/quic/congestion_control/quic_congestion_control_test.cc index b4f33d2..a93263a 100644 --- a/net/quic/congestion_control/quic_congestion_control_test.cc +++ b/net/quic/congestion_control/quic_congestion_control_test.cc @@ -16,15 +16,6 @@ using std::max; namespace net { namespace test { -class QuicCongestionManagerPeer : public QuicCongestionManager { - public: - explicit QuicCongestionManagerPeer(const QuicClock* clock, - CongestionFeedbackType congestion_type) - : QuicCongestionManager(clock, congestion_type) { - } - using QuicCongestionManager::BandwidthEstimate; -}; - class QuicCongestionControlTest : public ::testing::Test { protected: QuicCongestionControlTest() @@ -32,12 +23,12 @@ class QuicCongestionControlTest : public ::testing::Test { } void SetUpCongestionType(CongestionFeedbackType congestion_type) { - manager_.reset(new QuicCongestionManagerPeer(&clock_, congestion_type)); + manager_.reset(new QuicCongestionManager(&clock_, congestion_type)); } MockClock clock_; QuicTime start_; - scoped_ptr<QuicCongestionManagerPeer> manager_; + scoped_ptr<QuicCongestionManager> manager_; }; TEST_F(QuicCongestionControlTest, FixedRateSenderAPI) { @@ -49,8 +40,8 @@ TEST_F(QuicCongestionControlTest, FixedRateSenderAPI) { clock_.Now()); EXPECT_TRUE(manager_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - manager_->OnPacketSent(1, clock_.Now(), kMaxPacketSize, NOT_RETRANSMISSION, - HAS_RETRANSMITTABLE_DATA); + manager_->OnPacketSent(1, clock_.Now(), kDefaultMaxPacketSize, + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); EXPECT_EQ(QuicTime::Delta::FromMilliseconds(40), manager_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); @@ -79,8 +70,8 @@ TEST_F(QuicCongestionControlTest, FixedRatePacing) { for (QuicPacketSequenceNumber i = 1; i <= 100; ++i) { EXPECT_TRUE(manager_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - manager_->OnPacketSent(i, clock_.Now(), kMaxPacketSize, NOT_RETRANSMISSION, - HAS_RETRANSMITTABLE_DATA); + manager_->OnPacketSent(i, clock_.Now(), kDefaultMaxPacketSize, + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); QuicTime::Delta advance_time = manager_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); clock_.AdvanceTime(advance_time); @@ -110,11 +101,11 @@ TEST_F(QuicCongestionControlTest, Pacing) { for (QuicPacketSequenceNumber i = 1; i <= 100;) { EXPECT_TRUE(manager_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - manager_->OnPacketSent(i++, clock_.Now(), kMaxPacketSize, + manager_->OnPacketSent(i++, clock_.Now(), kDefaultMaxPacketSize, NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); EXPECT_TRUE(manager_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); - manager_->OnPacketSent(i++, clock_.Now(), kMaxPacketSize, + manager_->OnPacketSent(i++, clock_.Now(), kDefaultMaxPacketSize, NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA); QuicTime::Delta advance_time = manager_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); diff --git a/net/quic/congestion_control/quic_congestion_manager.cc b/net/quic/congestion_control/quic_congestion_manager.cc index ed133fc..d93d1ac 100644 --- a/net/quic/congestion_control/quic_congestion_manager.cc +++ b/net/quic/congestion_control/quic_congestion_manager.cc @@ -48,6 +48,19 @@ QuicCongestionManager::~QuicCongestionManager() { STLDeleteValues(&packet_history_map_); } +void QuicCongestionManager::SetFromConfig(const QuicConfig& config, + bool is_server) { + if (config.initial_round_trip_time_us() > 0 && + current_rtt_.IsInfinite()) { + // The initial rtt should already be set on the client side. + DLOG_IF(INFO, !is_server) + << "Client did not set an initial RTT, but did negotiate one."; + current_rtt_ = + QuicTime::Delta::FromMicroseconds(config.initial_round_trip_time_us()); + } + send_algorithm_->SetFromConfig(config, is_server); +} + void QuicCongestionManager::OnPacketSent( QuicPacketSequenceNumber sequence_number, QuicTime sent_time, diff --git a/net/quic/congestion_control/quic_congestion_manager.h b/net/quic/congestion_control/quic_congestion_manager.h index 6d15719..27d6392 100644 --- a/net/quic/congestion_control/quic_congestion_manager.h +++ b/net/quic/congestion_control/quic_congestion_manager.h @@ -32,6 +32,8 @@ class NET_EXPORT_PRIVATE QuicCongestionManager { CongestionFeedbackType congestion_type); virtual ~QuicCongestionManager(); + virtual void SetFromConfig(const QuicConfig& config, bool is_server); + // Called when we have received an ack frame from peer. virtual void OnIncomingAckFrame(const QuicAckFrame& frame, QuicTime ack_receive_time); diff --git a/net/quic/congestion_control/send_algorithm_interface.h b/net/quic/congestion_control/send_algorithm_interface.h index 8ff3961..59f6ad0 100644 --- a/net/quic/congestion_control/send_algorithm_interface.h +++ b/net/quic/congestion_control/send_algorithm_interface.h @@ -13,6 +13,7 @@ #include "net/base/net_export.h" #include "net/quic/quic_bandwidth.h" #include "net/quic/quic_clock.h" +#include "net/quic/quic_config.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_time.h" @@ -41,6 +42,8 @@ class NET_EXPORT_PRIVATE SendAlgorithmInterface { virtual ~SendAlgorithmInterface() {} + virtual void SetFromConfig(const QuicConfig& config, bool is_server) = 0; + // Called when we receive congestion feedback from remote peer. virtual void OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, diff --git a/net/quic/congestion_control/tcp_cubic_sender.cc b/net/quic/congestion_control/tcp_cubic_sender.cc index 0a7a897..6ff93a7 100644 --- a/net/quic/congestion_control/tcp_cubic_sender.cc +++ b/net/quic/congestion_control/tcp_cubic_sender.cc @@ -51,6 +51,14 @@ TcpCubicSender::~TcpCubicSender() { UMA_HISTOGRAM_COUNTS("Net.QuicSession.FinalTcpCwnd", congestion_window_); } +void TcpCubicSender::SetFromConfig(const QuicConfig& config, bool is_server) { + if (is_server) { + // Set the initial window size. + // Ignoring the max packet size and always using TCP's default MSS. + congestion_window_ = config.server_initial_congestion_window(); + } +} + void TcpCubicSender::OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, QuicTime feedback_receive_time, @@ -206,7 +214,7 @@ bool TcpCubicSender::IsCwndLimited() const { return left <= tcp_max_burst; } -// Called when we receive and ack. Normal TCP tracks how many packets one ack +// Called when we receive an ack. Normal TCP tracks how many packets one ack // represents, but quic has a separate ack for each packet. void TcpCubicSender::CongestionAvoidance(QuicPacketSequenceNumber ack) { if (!IsCwndLimited()) { @@ -254,6 +262,8 @@ void TcpCubicSender::OnTimeOut() { void TcpCubicSender::AckAccounting(QuicTime::Delta rtt) { if (rtt.IsInfinite() || rtt.IsZero()) { + DLOG(INFO) << "Ignoring rtt, because it's " + << (rtt.IsZero() ? "Zero" : "Infinite"); return; } // RTT can't be negative. @@ -278,7 +288,8 @@ void TcpCubicSender::AckAccounting(QuicTime::Delta rtt) { smoothed_rtt_ = QuicTime::Delta::FromMicroseconds( kOneMinusAlpha * smoothed_rtt_.ToMicroseconds() + kAlpha * rtt.ToMicroseconds()); - DLOG(INFO) << "Cubic; mean_deviation_:" << mean_deviation_.ToMicroseconds(); + DLOG(INFO) << "Cubic; smoothed_rtt_:" << smoothed_rtt_.ToMicroseconds() + << " mean_deviation_:" << mean_deviation_.ToMicroseconds(); } // Hybrid start triggers when cwnd is larger than some threshold. diff --git a/net/quic/congestion_control/tcp_cubic_sender.h b/net/quic/congestion_control/tcp_cubic_sender.h index cf5f2ff..2c2e698 100644 --- a/net/quic/congestion_control/tcp_cubic_sender.h +++ b/net/quic/congestion_control/tcp_cubic_sender.h @@ -35,6 +35,8 @@ class NET_EXPORT_PRIVATE TcpCubicSender : public SendAlgorithmInterface { QuicTcpCongestionWindow max_tcp_congestion_window); virtual ~TcpCubicSender(); + virtual void SetFromConfig(const QuicConfig& config, bool is_server) OVERRIDE; + // Start implementation of SendAlgorithmInterface. virtual void OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, diff --git a/net/quic/congestion_control/tcp_cubic_sender_test.cc b/net/quic/congestion_control/tcp_cubic_sender_test.cc index 15845cd..2d495d1 100644 --- a/net/quic/congestion_control/tcp_cubic_sender_test.cc +++ b/net/quic/congestion_control/tcp_cubic_sender_test.cc @@ -13,6 +13,7 @@ namespace net { namespace test { const uint32 kDefaultWindowTCP = 10 * kDefaultTCPMSS; + // TODO(ianswett): Remove 10000 once b/10075719 is fixed. const QuicTcpCongestionWindow kDefaultMaxCongestionWindowTCP = 10000; @@ -23,6 +24,11 @@ class TcpCubicSenderPeer : public TcpCubicSender { QuicTcpCongestionWindow max_tcp_congestion_window) : TcpCubicSender(clock, reno, max_tcp_congestion_window) { } + + QuicTcpCongestionWindow congestion_window() { + return congestion_window_; + } + using TcpCubicSender::AvailableSendWindow; using TcpCubicSender::SendWindow; using TcpCubicSender::AckAccounting; @@ -371,5 +377,16 @@ TEST_F(TcpCubicSenderTest, SendWindowNotAffectedByAcks) { EXPECT_GT(send_window, sender_->AvailableSendWindow()); } +TEST_F(TcpCubicSenderTest, ConfigureMaxInitialWindow) { + QuicTcpCongestionWindow congestion_window = sender_->congestion_window(); + QuicConfig config; + config.set_server_initial_congestion_window(2 * congestion_window, + 2 * congestion_window); + EXPECT_EQ(2 * congestion_window, config.server_initial_congestion_window()); + + sender_->SetFromConfig(config, true); + EXPECT_EQ(2 * congestion_window, sender_->congestion_window()); +} + } // namespace test } // namespace net diff --git a/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc b/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc index cc9bf35..394971a7 100644 --- a/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc +++ b/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc @@ -4,6 +4,7 @@ #include "net/quic/crypto/aes_128_gcm_12_encrypter.h" +#include <openssl/err.h> #include <openssl/evp.h> #include <string.h> @@ -19,6 +20,18 @@ const size_t kKeySize = 16; const size_t kNoncePrefixSize = 4; const size_t kAESNonceSize = 12; +void ClearOpenSslErrors() { +#ifdef NDEBUG + while (ERR_get_error()) {} +#else + while (long error = ERR_get_error()) { + char buf[120]; + ERR_error_string_n(error, buf, arraysize(buf)); + DLOG(ERROR) << "OpenSSL error: " << buf; + } +#endif +} + } // namespace Aes128Gcm12Encrypter::Aes128Gcm12Encrypter() {} @@ -35,12 +48,14 @@ bool Aes128Gcm12Encrypter::SetKey(StringPiece key) { // Set the cipher type and the key. if (EVP_EncryptInit_ex(ctx_.get(), EVP_aes_128_gcm(), NULL, key_, NULL) == 0) { + ClearOpenSslErrors(); return false; } // Set the IV (nonce) length. if (EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_GCM_SET_IVLEN, kAESNonceSize, NULL) == 0) { + ClearOpenSslErrors(); return false; } @@ -69,6 +84,7 @@ bool Aes128Gcm12Encrypter::Encrypt(StringPiece nonce, if (EVP_EncryptInit_ex( ctx_.get(), NULL, NULL, NULL, reinterpret_cast<const unsigned char*>(nonce.data())) == 0) { + ClearOpenSslErrors(); return false; } @@ -82,6 +98,7 @@ bool Aes128Gcm12Encrypter::Encrypt(StringPiece nonce, ctx_.get(), NULL, &unused_len, reinterpret_cast<const unsigned char*>(associated_data.data()), associated_data.size()) == 0) { + ClearOpenSslErrors(); return false; } } @@ -91,17 +108,20 @@ bool Aes128Gcm12Encrypter::Encrypt(StringPiece nonce, ctx_.get(), output, &len, reinterpret_cast<const unsigned char*>(plaintext.data()), plaintext.size()) == 0) { + ClearOpenSslErrors(); return false; } output += len; if (EVP_EncryptFinal_ex(ctx_.get(), output, &len) == 0) { + ClearOpenSslErrors(); return false; } output += len; if (EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_GCM_GET_TAG, kAuthTagSize, output) == 0) { + ClearOpenSslErrors(); return false; } diff --git a/net/quic/crypto/crypto_handshake.cc b/net/quic/crypto/crypto_handshake.cc index 0f3bb78..c869557 100644 --- a/net/quic/crypto/crypto_handshake.cc +++ b/net/quic/crypto/crypto_handshake.cc @@ -7,34 +7,22 @@ #include <ctype.h> #include "base/memory/scoped_ptr.h" -#include "base/stl_util.h" #include "base/strings/stringprintf.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "crypto/secure_hash.h" #include "net/base/net_util.h" -#include "net/quic/crypto/cert_compressor.h" -#include "net/quic/crypto/channel_id.h" #include "net/quic/crypto/common_cert_set.h" #include "net/quic/crypto/crypto_framer.h" -#include "net/quic/crypto/crypto_utils.h" -#include "net/quic/crypto/curve25519_key_exchange.h" #include "net/quic/crypto/key_exchange.h" -#include "net/quic/crypto/p256_key_exchange.h" -#include "net/quic/crypto/proof_verifier.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" #include "net/quic/crypto/quic_random.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_utils.h" -#if defined(OS_WIN) -#include "base/win/windows_version.h" -#endif - using base::StringPiece; using base::StringPrintf; -using std::map; using std::string; using std::vector; @@ -348,606 +336,4 @@ QuicCryptoConfig::QuicCryptoConfig() QuicCryptoConfig::~QuicCryptoConfig() {} -QuicCryptoClientConfig::QuicCryptoClientConfig() {} - -QuicCryptoClientConfig::~QuicCryptoClientConfig() { - STLDeleteValues(&cached_states_); -} - -QuicCryptoClientConfig::CachedState::CachedState() - : server_config_valid_(false), - generation_counter_(0) {} - -QuicCryptoClientConfig::CachedState::~CachedState() {} - -bool QuicCryptoClientConfig::CachedState::IsComplete(QuicWallTime now) const { - if (server_config_.empty() || !server_config_valid_) { - return false; - } - - const CryptoHandshakeMessage* scfg = GetServerConfig(); - if (!scfg) { - // Should be impossible short of cache corruption. - DCHECK(false); - return false; - } - - uint64 expiry_seconds; - if (scfg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR || - now.ToUNIXSeconds() >= expiry_seconds) { - return false; - } - - return true; -} - -const CryptoHandshakeMessage* -QuicCryptoClientConfig::CachedState::GetServerConfig() const { - if (server_config_.empty()) { - return NULL; - } - - if (!scfg_.get()) { - scfg_.reset(CryptoFramer::ParseMessage(server_config_)); - DCHECK(scfg_.get()); - } - return scfg_.get(); -} - -QuicErrorCode QuicCryptoClientConfig::CachedState::SetServerConfig( - StringPiece server_config, QuicWallTime now, string* error_details) { - const bool matches_existing = server_config == server_config_; - - // Even if the new server config matches the existing one, we still wish to - // reject it if it has expired. - scoped_ptr<CryptoHandshakeMessage> new_scfg_storage; - const CryptoHandshakeMessage* new_scfg; - - if (!matches_existing) { - new_scfg_storage.reset(CryptoFramer::ParseMessage(server_config)); - new_scfg = new_scfg_storage.get(); - } else { - new_scfg = GetServerConfig(); - } - - if (!new_scfg) { - *error_details = "SCFG invalid"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - uint64 expiry_seconds; - if (new_scfg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR) { - *error_details = "SCFG missing EXPY"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - if (now.ToUNIXSeconds() >= expiry_seconds) { - *error_details = "SCFG has expired"; - return QUIC_CRYPTO_SERVER_CONFIG_EXPIRED; - } - - if (!matches_existing) { - server_config_ = server_config.as_string(); - SetProofInvalid(); - scfg_.reset(new_scfg_storage.release()); - } - return QUIC_NO_ERROR; -} - -void QuicCryptoClientConfig::CachedState::InvalidateServerConfig() { - server_config_.clear(); - scfg_.reset(); - SetProofInvalid(); -} - -void QuicCryptoClientConfig::CachedState::SetProof(const vector<string>& certs, - StringPiece signature) { - bool has_changed = - signature != server_config_sig_ || certs_.size() != certs.size(); - - if (!has_changed) { - for (size_t i = 0; i < certs_.size(); i++) { - if (certs_[i] != certs[i]) { - has_changed = true; - break; - } - } - } - - if (!has_changed) { - return; - } - - // If the proof has changed then it needs to be revalidated. - SetProofInvalid(); - certs_ = certs; - server_config_sig_ = signature.as_string(); -} - -void QuicCryptoClientConfig::CachedState::ClearProof() { - SetProofInvalid(); - certs_.clear(); - server_config_sig_.clear(); -} - -void QuicCryptoClientConfig::CachedState::SetProofValid() { - server_config_valid_ = true; -} - -void QuicCryptoClientConfig::CachedState::SetProofInvalid() { - server_config_valid_ = false; - ++generation_counter_; -} - -const string& QuicCryptoClientConfig::CachedState::server_config() const { - return server_config_; -} - -const string& -QuicCryptoClientConfig::CachedState::source_address_token() const { - return source_address_token_; -} - -const vector<string>& QuicCryptoClientConfig::CachedState::certs() const { - return certs_; -} - -const string& QuicCryptoClientConfig::CachedState::signature() const { - return server_config_sig_; -} - -bool QuicCryptoClientConfig::CachedState::proof_valid() const { - return server_config_valid_; -} - -uint64 QuicCryptoClientConfig::CachedState::generation_counter() const { - return generation_counter_; -} - -const ProofVerifyDetails* -QuicCryptoClientConfig::CachedState::proof_verify_details() const { - return proof_verify_details_.get(); -} - -void QuicCryptoClientConfig::CachedState::set_source_address_token( - StringPiece token) { - source_address_token_ = token.as_string(); -} - -void QuicCryptoClientConfig::CachedState::SetProofVerifyDetails( - ProofVerifyDetails* details) { - proof_verify_details_.reset(details); -} - -void QuicCryptoClientConfig::CachedState::InitializeFrom( - const QuicCryptoClientConfig::CachedState& other) { - DCHECK(server_config_.empty()); - DCHECK(!server_config_valid_); - server_config_ = other.server_config_; - source_address_token_ = other.source_address_token_; - certs_ = other.certs_; - server_config_sig_ = other.server_config_sig_; - server_config_valid_ = other.server_config_valid_; -} - -void QuicCryptoClientConfig::SetDefaults() { - // Version must be 0. - // TODO(agl): this version stuff is obsolete now. - version = QuicCryptoConfig::CONFIG_VERSION; - - // Key exchange methods. - kexs.resize(2); - kexs[0] = kC255; - kexs[1] = kP256; - - // Authenticated encryption algorithms. - aead.resize(1); - aead[0] = kAESG; -} - -QuicCryptoClientConfig::CachedState* QuicCryptoClientConfig::LookupOrCreate( - const string& server_hostname) { - map<string, CachedState*>::const_iterator it = - cached_states_.find(server_hostname); - if (it != cached_states_.end()) { - return it->second; - } - - CachedState* cached = new CachedState; - cached_states_.insert(make_pair(server_hostname, cached)); - return cached; -} - -void QuicCryptoClientConfig::FillInchoateClientHello( - const string& server_hostname, - const CachedState* cached, - QuicCryptoNegotiatedParameters* out_params, - CryptoHandshakeMessage* out) const { - out->set_tag(kCHLO); - out->set_minimum_size(kClientHelloMinimumSize); - - // Server name indication. We only send SNI if it's a valid domain name, as - // per the spec. - if (CryptoUtils::IsValidSNI(server_hostname)) { - out->SetStringPiece(kSNI, server_hostname); - } - out->SetValue(kVERS, version); - - if (!cached->source_address_token().empty()) { - out->SetStringPiece(kSourceAddressTokenTag, cached->source_address_token()); - } - - if (proof_verifier_.get()) { - // Don't request ECDSA proofs on platforms that do not support ECDSA - // certificates. - bool disableECDSA = false; -#if defined(OS_WIN) - if (base::win::GetVersion() < base::win::VERSION_VISTA) - disableECDSA = true; -#endif - if (disableECDSA) { - out->SetTaglist(kPDMD, kX59R, 0); - } else { - out->SetTaglist(kPDMD, kX509, 0); - } - - if (!cached->proof_valid()) { - // If we are expecting a certificate chain, double the size of the client - // hello so that the response from the server can be larger - hopefully - // including the whole certificate chain. - out->set_minimum_size(kClientHelloMinimumSize * 2); - } - } - - if (common_cert_sets) { - out->SetStringPiece(kCCS, common_cert_sets->GetCommonHashes()); - } - - const vector<string>& certs = cached->certs(); - // We save |certs| in the QuicCryptoNegotiatedParameters so that, if the - // client config is being used for multiple connections, another connection - // doesn't update the cached certificates and cause us to be unable to - // process the server's compressed certificate chain. - out_params->cached_certs = certs; - if (!certs.empty()) { - vector<uint64> hashes; - hashes.reserve(certs.size()); - for (vector<string>::const_iterator i = certs.begin(); - i != certs.end(); ++i) { - hashes.push_back(QuicUtils::FNV1a_64_Hash(i->data(), i->size())); - } - out->SetVector(kCCRT, hashes); - } -} - -QuicErrorCode QuicCryptoClientConfig::FillClientHello( - const string& server_hostname, - QuicGuid guid, - const CachedState* cached, - QuicWallTime now, - QuicRandom* rand, - QuicCryptoNegotiatedParameters* out_params, - CryptoHandshakeMessage* out, - string* error_details) const { - DCHECK(error_details != NULL); - - FillInchoateClientHello(server_hostname, cached, out_params, out); - - const CryptoHandshakeMessage* scfg = cached->GetServerConfig(); - if (!scfg) { - // This should never happen as our caller should have checked - // cached->IsComplete() before calling this function. - *error_details = "Handshake not ready"; - return QUIC_CRYPTO_INTERNAL_ERROR; - } - - StringPiece scid; - if (!scfg->GetStringPiece(kSCID, &scid)) { - *error_details = "SCFG missing SCID"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - out->SetStringPiece(kSCID, scid); - - const QuicTag* their_aeads; - const QuicTag* their_key_exchanges; - size_t num_their_aeads, num_their_key_exchanges; - if (scfg->GetTaglist(kAEAD, &their_aeads, - &num_their_aeads) != QUIC_NO_ERROR || - scfg->GetTaglist(kKEXS, &their_key_exchanges, - &num_their_key_exchanges) != QUIC_NO_ERROR) { - *error_details = "Missing AEAD or KEXS"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - size_t key_exchange_index; - if (!QuicUtils::FindMutualTag( - aead, their_aeads, num_their_aeads, QuicUtils::PEER_PRIORITY, - &out_params->aead, NULL) || - !QuicUtils::FindMutualTag( - kexs, their_key_exchanges, num_their_key_exchanges, - QuicUtils::PEER_PRIORITY, &out_params->key_exchange, - &key_exchange_index)) { - *error_details = "Unsupported AEAD or KEXS"; - return QUIC_CRYPTO_NO_SUPPORT; - } - out->SetTaglist(kAEAD, out_params->aead, 0); - out->SetTaglist(kKEXS, out_params->key_exchange, 0); - - StringPiece public_value; - if (scfg->GetNthValue24(kPUBS, key_exchange_index, &public_value) != - QUIC_NO_ERROR) { - *error_details = "Missing public value"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - StringPiece orbit; - if (!scfg->GetStringPiece(kORBT, &orbit) || orbit.size() != kOrbitSize) { - *error_details = "SCFG missing OBIT"; - return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; - } - - CryptoUtils::GenerateNonce(now, rand, orbit, &out_params->client_nonce); - out->SetStringPiece(kNONC, out_params->client_nonce); - if (!out_params->server_nonce.empty()) { - out->SetStringPiece(kServerNonceTag, out_params->server_nonce); - } - - switch (out_params->key_exchange) { - case kC255: - out_params->client_key_exchange.reset(Curve25519KeyExchange::New( - Curve25519KeyExchange::NewPrivateKey(rand))); - break; - case kP256: - out_params->client_key_exchange.reset(P256KeyExchange::New( - P256KeyExchange::NewPrivateKey())); - break; - default: - DCHECK(false); - *error_details = "Configured to support an unknown key exchange"; - return QUIC_CRYPTO_INTERNAL_ERROR; - } - - if (!out_params->client_key_exchange->CalculateSharedKey( - public_value, &out_params->initial_premaster_secret)) { - *error_details = "Key exchange failure"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - out->SetStringPiece(kPUBS, out_params->client_key_exchange->public_value()); - - bool do_channel_id = false; - if (channel_id_signer_.get()) { - const QuicTag* their_proof_demands; - size_t num_their_proof_demands; - if (scfg->GetTaglist(kPDMD, &their_proof_demands, - &num_their_proof_demands) == QUIC_NO_ERROR) { - for (size_t i = 0; i < num_their_proof_demands; i++) { - if (their_proof_demands[i] == kCHID) { - do_channel_id = true; - break; - } - } - } - } - - if (do_channel_id) { - // In order to calculate the encryption key for the CETV block we need to - // serialise the client hello as it currently is (i.e. without the CETV - // block). For this, the client hello is serialized without padding. - const size_t orig_min_size = out->minimum_size(); - out->set_minimum_size(0); - - CryptoHandshakeMessage cetv; - cetv.set_tag(kCETV); - - string hkdf_input; - const QuicData& client_hello_serialized = out->GetSerialized(); - hkdf_input.append(QuicCryptoConfig::kCETVLabel, - strlen(QuicCryptoConfig::kCETVLabel) + 1); - hkdf_input.append(reinterpret_cast<char*>(&guid), sizeof(guid)); - hkdf_input.append(client_hello_serialized.data(), - client_hello_serialized.length()); - hkdf_input.append(cached->server_config()); - - string key, signature; - if (!channel_id_signer_->Sign(server_hostname, hkdf_input, - &key, &signature)) { - *error_details = "Channel ID signature failed"; - return QUIC_INVALID_CHANNEL_ID_SIGNATURE; - } - - cetv.SetStringPiece(kCIDK, key); - cetv.SetStringPiece(kCIDS, signature); - - CrypterPair crypters; - if (!CryptoUtils::DeriveKeys(out_params->initial_premaster_secret, - out_params->aead, out_params->client_nonce, - out_params->server_nonce, hkdf_input, - CryptoUtils::CLIENT, &crypters)) { - *error_details = "Symmetric key setup failed"; - return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED; - } - - const QuicData& cetv_plaintext = cetv.GetSerialized(); - scoped_ptr<QuicData> cetv_ciphertext(crypters.encrypter->EncryptPacket( - 0 /* sequence number */, - StringPiece() /* associated data */, - cetv_plaintext.AsStringPiece())); - if (!cetv_ciphertext.get()) { - *error_details = "Packet encryption failed"; - return QUIC_ENCRYPTION_FAILURE; - } - - out->SetStringPiece(kCETV, cetv_ciphertext->AsStringPiece()); - out->MarkDirty(); - - out->set_minimum_size(orig_min_size); - } - - out_params->hkdf_input_suffix.clear(); - out_params->hkdf_input_suffix.append(reinterpret_cast<char*>(&guid), - sizeof(guid)); - const QuicData& client_hello_serialized = out->GetSerialized(); - out_params->hkdf_input_suffix.append(client_hello_serialized.data(), - client_hello_serialized.length()); - out_params->hkdf_input_suffix.append(cached->server_config()); - - string hkdf_input; - const size_t label_len = strlen(QuicCryptoConfig::kInitialLabel) + 1; - hkdf_input.reserve(label_len + out_params->hkdf_input_suffix.size()); - hkdf_input.append(QuicCryptoConfig::kInitialLabel, label_len); - hkdf_input.append(out_params->hkdf_input_suffix); - - if (!CryptoUtils::DeriveKeys( - out_params->initial_premaster_secret, out_params->aead, - out_params->client_nonce, out_params->server_nonce, hkdf_input, - CryptoUtils::CLIENT, &out_params->initial_crypters)) { - *error_details = "Symmetric key setup failed"; - return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED; - } - - return QUIC_NO_ERROR; -} - -QuicErrorCode QuicCryptoClientConfig::ProcessRejection( - const CryptoHandshakeMessage& rej, - QuicWallTime now, - CachedState* cached, - QuicCryptoNegotiatedParameters* out_params, - string* error_details) { - DCHECK(error_details != NULL); - - if (rej.tag() != kREJ) { - *error_details = "Message is not REJ"; - return QUIC_CRYPTO_INTERNAL_ERROR; - } - - StringPiece scfg; - if (!rej.GetStringPiece(kSCFG, &scfg)) { - *error_details = "Missing SCFG"; - return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; - } - - QuicErrorCode error = cached->SetServerConfig(scfg, now, error_details); - if (error != QUIC_NO_ERROR) { - return error; - } - - StringPiece token; - if (rej.GetStringPiece(kSourceAddressTokenTag, &token)) { - cached->set_source_address_token(token); - } - - StringPiece nonce; - if (rej.GetStringPiece(kServerNonceTag, &nonce)) { - out_params->server_nonce = nonce.as_string(); - } - - StringPiece proof, cert_bytes; - bool has_proof = rej.GetStringPiece(kPROF, &proof); - bool has_cert = rej.GetStringPiece(kCertificateTag, &cert_bytes); - if (has_proof && has_cert) { - vector<string> certs; - if (!CertCompressor::DecompressChain(cert_bytes, out_params->cached_certs, - common_cert_sets, &certs)) { - *error_details = "Certificate data invalid"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - cached->SetProof(certs, proof); - } else { - cached->ClearProof(); - if (has_proof && !has_cert) { - *error_details = "Certificate missing"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - if (!has_proof && has_cert) { - *error_details = "Proof missing"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - } - - return QUIC_NO_ERROR; -} - -QuicErrorCode QuicCryptoClientConfig::ProcessServerHello( - const CryptoHandshakeMessage& server_hello, - QuicGuid guid, - CachedState* cached, - QuicCryptoNegotiatedParameters* out_params, - string* error_details) { - DCHECK(error_details != NULL); - - if (server_hello.tag() != kSHLO) { - *error_details = "Bad tag"; - return QUIC_INVALID_CRYPTO_MESSAGE_TYPE; - } - - // Learn about updated source address tokens. - StringPiece token; - if (server_hello.GetStringPiece(kSourceAddressTokenTag, &token)) { - cached->set_source_address_token(token); - } - - // TODO(agl): - // learn about updated SCFGs. - - StringPiece public_value; - if (!server_hello.GetStringPiece(kPUBS, &public_value)) { - *error_details = "server hello missing forward secure public value"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - if (!out_params->client_key_exchange->CalculateSharedKey( - public_value, &out_params->forward_secure_premaster_secret)) { - *error_details = "Key exchange failure"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - string hkdf_input; - const size_t label_len = strlen(QuicCryptoConfig::kForwardSecureLabel) + 1; - hkdf_input.reserve(label_len + out_params->hkdf_input_suffix.size()); - hkdf_input.append(QuicCryptoConfig::kForwardSecureLabel, label_len); - hkdf_input.append(out_params->hkdf_input_suffix); - - if (!CryptoUtils::DeriveKeys( - out_params->forward_secure_premaster_secret, out_params->aead, - out_params->client_nonce, out_params->server_nonce, hkdf_input, - CryptoUtils::CLIENT, &out_params->forward_secure_crypters)) { - *error_details = "Symmetric key setup failed"; - return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED; - } - - return QUIC_NO_ERROR; -} - -ProofVerifier* QuicCryptoClientConfig::proof_verifier() const { - return proof_verifier_.get(); -} - -void QuicCryptoClientConfig::SetProofVerifier(ProofVerifier* verifier) { - proof_verifier_.reset(verifier); -} - -ChannelIDSigner* QuicCryptoClientConfig::channel_id_signer() const { - return channel_id_signer_.get(); -} - -void QuicCryptoClientConfig::SetChannelIDSigner(ChannelIDSigner* signer) { - channel_id_signer_.reset(signer); -} - -void QuicCryptoClientConfig::InitializeFrom( - const std::string& server_hostname, - const std::string& canonical_server_hostname, - QuicCryptoClientConfig* canonical_crypto_config) { - CachedState* canonical_cached = - canonical_crypto_config->LookupOrCreate(canonical_server_hostname); - if (!canonical_cached->proof_valid()) { - return; - } - CachedState* cached = LookupOrCreate(server_hostname); - cached->InitializeFrom(*canonical_cached); -} - } // namespace net diff --git a/net/quic/crypto/crypto_handshake.h b/net/quic/crypto/crypto_handshake.h index 4fefba5..c520294 100644 --- a/net/quic/crypto/crypto_handshake.h +++ b/net/quic/crypto/crypto_handshake.h @@ -225,192 +225,6 @@ class NET_EXPORT_PRIVATE QuicCryptoConfig { DISALLOW_COPY_AND_ASSIGN(QuicCryptoConfig); }; -// QuicCryptoClientConfig contains crypto-related configuration settings for a -// client. Note that this object isn't thread-safe. It's designed to be used on -// a single thread at a time. -class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { - public: - // A CachedState contains the information that the client needs in order to - // perform a 0-RTT handshake with a server. This information can be reused - // over several connections to the same server. - class NET_EXPORT_PRIVATE CachedState { - public: - CachedState(); - ~CachedState(); - - // IsComplete returns true if this object contains enough information to - // perform a handshake with the server. |now| is used to judge whether any - // cached server config has expired. - bool IsComplete(QuicWallTime now) const; - - // GetServerConfig returns the parsed contents of |server_config|, or NULL - // if |server_config| is empty. The return value is owned by this object - // and is destroyed when this object is. - const CryptoHandshakeMessage* GetServerConfig() const; - - // SetServerConfig checks that |server_config| parses correctly and stores - // it in |server_config_|. |now| is used to judge whether |server_config| - // has expired. - QuicErrorCode SetServerConfig(base::StringPiece server_config, - QuicWallTime now, - std::string* error_details); - - // InvalidateServerConfig clears the cached server config (if any). - void InvalidateServerConfig(); - - // SetProof stores a certificate chain and signature. - void SetProof(const std::vector<std::string>& certs, - base::StringPiece signature); - - // Clears the certificate chain and signature and invalidates the proof. - void ClearProof(); - - // SetProofValid records that the certificate chain and signature have been - // validated and that it's safe to assume that the server is legitimate. - // (Note: this does not check the chain or signature.) - void SetProofValid(); - - // If the server config or the proof has changed then it needs to be - // revalidated. Helper function to keep server_config_valid_ and - // generation_counter_ in sync. - void SetProofInvalid(); - - const std::string& server_config() const; - const std::string& source_address_token() const; - const std::vector<std::string>& certs() const; - const std::string& signature() const; - bool proof_valid() const; - uint64 generation_counter() const; - const ProofVerifyDetails* proof_verify_details() const; - - void set_source_address_token(base::StringPiece token); - - // SetProofVerifyDetails takes ownership of |details|. - void SetProofVerifyDetails(ProofVerifyDetails* details); - - // Copy the |server_config_|, |source_address_token_|, |certs_| and - // |server_config_sig_| from the |other|. The remaining fields, - // |generation_counter_|, |proof_verify_details_|, and |scfg_| remain - // unchanged. - void InitializeFrom(const CachedState& other); - - private: - std::string server_config_id_; // An opaque id from the server. - std::string server_config_; // A serialized handshake message. - std::string source_address_token_; // An opaque proof of IP ownership. - std::vector<std::string> certs_; // A list of certificates in leaf-first - // order. - std::string server_config_sig_; // A signature of |server_config_|. - bool server_config_valid_; // True if |server_config_| is correctly - // signed and |certs_| has been - // validated. - // Generation counter associated with the |server_config_|, |certs_| and - // |server_config_sig_| combination. It is incremented whenever we set - // server_config_valid_ to false. - uint64 generation_counter_; - - scoped_ptr<ProofVerifyDetails> proof_verify_details_; - - // scfg contains the cached, parsed value of |server_config|. - mutable scoped_ptr<CryptoHandshakeMessage> scfg_; - - DISALLOW_COPY_AND_ASSIGN(CachedState); - }; - - QuicCryptoClientConfig(); - ~QuicCryptoClientConfig(); - - // Sets the members to reasonable, default values. - void SetDefaults(); - - // LookupOrCreate returns a CachedState for the given hostname. If no such - // CachedState currently exists, it will be created and cached. - CachedState* LookupOrCreate(const std::string& server_hostname); - - // FillInchoateClientHello sets |out| to be a CHLO message that elicits a - // source-address token or SCFG from a server. If |cached| is non-NULL, the - // source-address token will be taken from it. |out_params| is used in order - // to store the cached certs that were sent as hints to the server in - // |out_params->cached_certs|. - void FillInchoateClientHello(const std::string& server_hostname, - const CachedState* cached, - QuicCryptoNegotiatedParameters* out_params, - CryptoHandshakeMessage* out) const; - - // FillClientHello sets |out| to be a CHLO message based on the configuration - // of this object. This object must have cached enough information about - // |server_hostname| in order to perform a handshake. This can be checked - // with the |IsComplete| member of |CachedState|. - // - // |clock| and |rand| are used to generate the nonce and |out_params| is - // filled with the results of the handshake that the server is expected to - // accept. - QuicErrorCode FillClientHello(const std::string& server_hostname, - QuicGuid guid, - const CachedState* cached, - QuicWallTime now, - QuicRandom* rand, - QuicCryptoNegotiatedParameters* out_params, - CryptoHandshakeMessage* out, - std::string* error_details) const; - - // ProcessRejection processes a REJ message from a server and updates the - // cached information about that server. After this, |IsComplete| may return - // true for that server's CachedState. If the rejection message contains - // state about a future handshake (i.e. an nonce value from the server), then - // it will be saved in |out_params|. |now| is used to judge whether the - // server config in the rejection message has expired. - QuicErrorCode ProcessRejection(const CryptoHandshakeMessage& rej, - QuicWallTime now, - CachedState* cached, - QuicCryptoNegotiatedParameters* out_params, - std::string* error_details); - - // ProcessServerHello processes the message in |server_hello|, updates the - // cached information about that server, writes the negotiated parameters to - // |out_params| and returns QUIC_NO_ERROR. If |server_hello| is unacceptable - // then it puts an error message in |error_details| and returns an error - // code. - QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello, - QuicGuid guid, - CachedState* cached, - QuicCryptoNegotiatedParameters* out_params, - std::string* error_details); - - ProofVerifier* proof_verifier() const; - - // SetProofVerifier takes ownership of a |ProofVerifier| that clients are - // free to use in order to verify certificate chains from servers. If a - // ProofVerifier is set then the client will request a certificate chain from - // the server. - void SetProofVerifier(ProofVerifier* verifier); - - ChannelIDSigner* channel_id_signer() const; - - // SetChannelIDSigner sets a ChannelIDSigner that will be called when the - // server supports channel IDs to sign a message proving possession of the - // given ChannelID. This object takes ownership of |signer|. - void SetChannelIDSigner(ChannelIDSigner* signer); - - // Initialize the CachedState from |canonical_crypto_config| for the - // |canonical_server_hostname| as the initial CachedState for - // |server_hostname|. We will copy config data only if - // |canonical_crypto_config| has valid proof. - void InitializeFrom(const std::string& server_hostname, - const std::string& canonical_server_hostname, - QuicCryptoClientConfig* canonical_crypto_config); - - private: - // cached_states_ maps from the server hostname to the cached information - // about that server. - std::map<std::string, CachedState*> cached_states_; - - scoped_ptr<ProofVerifier> proof_verifier_; - scoped_ptr<ChannelIDSigner> channel_id_signer_; - - DISALLOW_COPY_AND_ASSIGN(QuicCryptoClientConfig); -}; - } // namespace net #endif // NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_H_ diff --git a/net/quic/crypto/crypto_protocol.h b/net/quic/crypto/crypto_protocol.h index 0dcf016..42a056a 100644 --- a/net/quic/crypto/crypto_protocol.h +++ b/net/quic/crypto/crypto_protocol.h @@ -69,6 +69,10 @@ const QuicTag kICSL = TAG('I', 'C', 'S', 'L'); // Idle connection state // lifetime const QuicTag kKATO = TAG('K', 'A', 'T', 'O'); // Keepalive timeout const QuicTag kMSPC = TAG('M', 'S', 'P', 'C'); // Max streams per connection. +const QuicTag kIRTT = TAG('I', 'R', 'T', 'T'); // Estimated initial RTT in us. +const QuicTag kSWND = TAG('S', 'W', 'N', 'D'); // Server's Initial congestion + // window. +const QuicTag kSMSS = TAG('S', 'M', 'S', 'S'); // Server's maximum packet size. const QuicTag kSNI = TAG('S', 'N', 'I', '\0'); // Server name // indication const QuicTag kPUBS = TAG('P', 'U', 'B', 'S'); // Public key values diff --git a/net/quic/crypto/crypto_server_test.cc b/net/quic/crypto/crypto_server_test.cc index 258afd5..1d9664d 100644 --- a/net/quic/crypto/crypto_server_test.cc +++ b/net/quic/crypto/crypto_server_test.cc @@ -4,8 +4,8 @@ #include "base/strings/string_number_conversions.h" #include "crypto/secure_hash.h" -#include "net/quic/crypto/crypto_server_config.h" #include "net/quic/crypto/crypto_utils.h" +#include "net/quic/crypto/quic_crypto_server_config.h" #include "net/quic/crypto/quic_random.h" #include "net/quic/test_tools/crypto_test_utils.h" #include "net/quic/test_tools/mock_clock.h" diff --git a/net/quic/crypto/quic_crypto_client_config.cc b/net/quic/crypto/quic_crypto_client_config.cc new file mode 100644 index 0000000..3dafdb8 --- /dev/null +++ b/net/quic/crypto/quic_crypto_client_config.cc @@ -0,0 +1,633 @@ +// 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/crypto/quic_crypto_client_config.h" + +#include "base/stl_util.h" +#include "net/quic/crypto/cert_compressor.h" +#include "net/quic/crypto/channel_id.h" +#include "net/quic/crypto/common_cert_set.h" +#include "net/quic/crypto/crypto_framer.h" +#include "net/quic/crypto/crypto_utils.h" +#include "net/quic/crypto/curve25519_key_exchange.h" +#include "net/quic/crypto/key_exchange.h" +#include "net/quic/crypto/p256_key_exchange.h" +#include "net/quic/crypto/proof_verifier.h" +#include "net/quic/crypto/quic_encrypter.h" +#include "net/quic/quic_utils.h" + +#if defined(OS_WIN) +#include "base/win/windows_version.h" +#endif + +using base::StringPiece; +using std::map; +using std::string; +using std::vector; + +namespace net { + +QuicCryptoClientConfig::QuicCryptoClientConfig() {} + +QuicCryptoClientConfig::~QuicCryptoClientConfig() { + STLDeleteValues(&cached_states_); +} + +QuicCryptoClientConfig::CachedState::CachedState() + : server_config_valid_(false), + generation_counter_(0) {} + +QuicCryptoClientConfig::CachedState::~CachedState() {} + +bool QuicCryptoClientConfig::CachedState::IsComplete(QuicWallTime now) const { + if (server_config_.empty() || !server_config_valid_) { + return false; + } + + const CryptoHandshakeMessage* scfg = GetServerConfig(); + if (!scfg) { + // Should be impossible short of cache corruption. + DCHECK(false); + return false; + } + + uint64 expiry_seconds; + if (scfg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR || + now.ToUNIXSeconds() >= expiry_seconds) { + return false; + } + + return true; +} + +const CryptoHandshakeMessage* +QuicCryptoClientConfig::CachedState::GetServerConfig() const { + if (server_config_.empty()) { + return NULL; + } + + if (!scfg_.get()) { + scfg_.reset(CryptoFramer::ParseMessage(server_config_)); + DCHECK(scfg_.get()); + } + return scfg_.get(); +} + +QuicErrorCode QuicCryptoClientConfig::CachedState::SetServerConfig( + StringPiece server_config, QuicWallTime now, string* error_details) { + const bool matches_existing = server_config == server_config_; + + // Even if the new server config matches the existing one, we still wish to + // reject it if it has expired. + scoped_ptr<CryptoHandshakeMessage> new_scfg_storage; + const CryptoHandshakeMessage* new_scfg; + + if (!matches_existing) { + new_scfg_storage.reset(CryptoFramer::ParseMessage(server_config)); + new_scfg = new_scfg_storage.get(); + } else { + new_scfg = GetServerConfig(); + } + + if (!new_scfg) { + *error_details = "SCFG invalid"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + uint64 expiry_seconds; + if (new_scfg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR) { + *error_details = "SCFG missing EXPY"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + if (now.ToUNIXSeconds() >= expiry_seconds) { + *error_details = "SCFG has expired"; + return QUIC_CRYPTO_SERVER_CONFIG_EXPIRED; + } + + if (!matches_existing) { + server_config_ = server_config.as_string(); + SetProofInvalid(); + scfg_.reset(new_scfg_storage.release()); + } + return QUIC_NO_ERROR; +} + +void QuicCryptoClientConfig::CachedState::InvalidateServerConfig() { + server_config_.clear(); + scfg_.reset(); + SetProofInvalid(); +} + +void QuicCryptoClientConfig::CachedState::SetProof(const vector<string>& certs, + StringPiece signature) { + bool has_changed = + signature != server_config_sig_ || certs_.size() != certs.size(); + + if (!has_changed) { + for (size_t i = 0; i < certs_.size(); i++) { + if (certs_[i] != certs[i]) { + has_changed = true; + break; + } + } + } + + if (!has_changed) { + return; + } + + // If the proof has changed then it needs to be revalidated. + SetProofInvalid(); + certs_ = certs; + server_config_sig_ = signature.as_string(); +} + +void QuicCryptoClientConfig::CachedState::ClearProof() { + SetProofInvalid(); + certs_.clear(); + server_config_sig_.clear(); +} + +void QuicCryptoClientConfig::CachedState::SetProofValid() { + server_config_valid_ = true; +} + +void QuicCryptoClientConfig::CachedState::SetProofInvalid() { + server_config_valid_ = false; + ++generation_counter_; +} + +const string& QuicCryptoClientConfig::CachedState::server_config() const { + return server_config_; +} + +const string& +QuicCryptoClientConfig::CachedState::source_address_token() const { + return source_address_token_; +} + +const vector<string>& QuicCryptoClientConfig::CachedState::certs() const { + return certs_; +} + +const string& QuicCryptoClientConfig::CachedState::signature() const { + return server_config_sig_; +} + +bool QuicCryptoClientConfig::CachedState::proof_valid() const { + return server_config_valid_; +} + +uint64 QuicCryptoClientConfig::CachedState::generation_counter() const { + return generation_counter_; +} + +const ProofVerifyDetails* +QuicCryptoClientConfig::CachedState::proof_verify_details() const { + return proof_verify_details_.get(); +} + +void QuicCryptoClientConfig::CachedState::set_source_address_token( + StringPiece token) { + source_address_token_ = token.as_string(); +} + +void QuicCryptoClientConfig::CachedState::SetProofVerifyDetails( + ProofVerifyDetails* details) { + proof_verify_details_.reset(details); +} + +void QuicCryptoClientConfig::CachedState::InitializeFrom( + const QuicCryptoClientConfig::CachedState& other) { + DCHECK(server_config_.empty()); + DCHECK(!server_config_valid_); + server_config_ = other.server_config_; + source_address_token_ = other.source_address_token_; + certs_ = other.certs_; + server_config_sig_ = other.server_config_sig_; + server_config_valid_ = other.server_config_valid_; +} + +void QuicCryptoClientConfig::SetDefaults() { + // Version must be 0. + // TODO(agl): this version stuff is obsolete now. + version = QuicCryptoConfig::CONFIG_VERSION; + + // Key exchange methods. + kexs.resize(2); + kexs[0] = kC255; + kexs[1] = kP256; + + // Authenticated encryption algorithms. + aead.resize(1); + aead[0] = kAESG; +} + +QuicCryptoClientConfig::CachedState* QuicCryptoClientConfig::LookupOrCreate( + const string& server_hostname) { + map<string, CachedState*>::const_iterator it = + cached_states_.find(server_hostname); + if (it != cached_states_.end()) { + return it->second; + } + + CachedState* cached = new CachedState; + cached_states_.insert(make_pair(server_hostname, cached)); + return cached; +} + +void QuicCryptoClientConfig::FillInchoateClientHello( + const string& server_hostname, + const CachedState* cached, + QuicCryptoNegotiatedParameters* out_params, + CryptoHandshakeMessage* out) const { + out->set_tag(kCHLO); + out->set_minimum_size(kClientHelloMinimumSize); + + // Server name indication. We only send SNI if it's a valid domain name, as + // per the spec. + if (CryptoUtils::IsValidSNI(server_hostname)) { + out->SetStringPiece(kSNI, server_hostname); + } + out->SetValue(kVERS, version); + + if (!cached->source_address_token().empty()) { + out->SetStringPiece(kSourceAddressTokenTag, cached->source_address_token()); + } + + if (proof_verifier_.get()) { + // Don't request ECDSA proofs on platforms that do not support ECDSA + // certificates. + bool disableECDSA = false; +#if defined(OS_WIN) + if (base::win::GetVersion() < base::win::VERSION_VISTA) + disableECDSA = true; +#endif + if (disableECDSA) { + out->SetTaglist(kPDMD, kX59R, 0); + } else { + out->SetTaglist(kPDMD, kX509, 0); + } + + if (!cached->proof_valid()) { + // If we are expecting a certificate chain, double the size of the client + // hello so that the response from the server can be larger - hopefully + // including the whole certificate chain. + out->set_minimum_size(kClientHelloMinimumSize * 2); + } + } + + if (common_cert_sets) { + out->SetStringPiece(kCCS, common_cert_sets->GetCommonHashes()); + } + + const vector<string>& certs = cached->certs(); + // We save |certs| in the QuicCryptoNegotiatedParameters so that, if the + // client config is being used for multiple connections, another connection + // doesn't update the cached certificates and cause us to be unable to + // process the server's compressed certificate chain. + out_params->cached_certs = certs; + if (!certs.empty()) { + vector<uint64> hashes; + hashes.reserve(certs.size()); + for (vector<string>::const_iterator i = certs.begin(); + i != certs.end(); ++i) { + hashes.push_back(QuicUtils::FNV1a_64_Hash(i->data(), i->size())); + } + out->SetVector(kCCRT, hashes); + } +} + +QuicErrorCode QuicCryptoClientConfig::FillClientHello( + const string& server_hostname, + QuicGuid guid, + const CachedState* cached, + QuicWallTime now, + QuicRandom* rand, + QuicCryptoNegotiatedParameters* out_params, + CryptoHandshakeMessage* out, + string* error_details) const { + DCHECK(error_details != NULL); + + FillInchoateClientHello(server_hostname, cached, out_params, out); + + const CryptoHandshakeMessage* scfg = cached->GetServerConfig(); + if (!scfg) { + // This should never happen as our caller should have checked + // cached->IsComplete() before calling this function. + *error_details = "Handshake not ready"; + return QUIC_CRYPTO_INTERNAL_ERROR; + } + + StringPiece scid; + if (!scfg->GetStringPiece(kSCID, &scid)) { + *error_details = "SCFG missing SCID"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + out->SetStringPiece(kSCID, scid); + + const QuicTag* their_aeads; + const QuicTag* their_key_exchanges; + size_t num_their_aeads, num_their_key_exchanges; + if (scfg->GetTaglist(kAEAD, &their_aeads, + &num_their_aeads) != QUIC_NO_ERROR || + scfg->GetTaglist(kKEXS, &their_key_exchanges, + &num_their_key_exchanges) != QUIC_NO_ERROR) { + *error_details = "Missing AEAD or KEXS"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + size_t key_exchange_index; + if (!QuicUtils::FindMutualTag( + aead, their_aeads, num_their_aeads, QuicUtils::PEER_PRIORITY, + &out_params->aead, NULL) || + !QuicUtils::FindMutualTag( + kexs, their_key_exchanges, num_their_key_exchanges, + QuicUtils::PEER_PRIORITY, &out_params->key_exchange, + &key_exchange_index)) { + *error_details = "Unsupported AEAD or KEXS"; + return QUIC_CRYPTO_NO_SUPPORT; + } + out->SetTaglist(kAEAD, out_params->aead, 0); + out->SetTaglist(kKEXS, out_params->key_exchange, 0); + + StringPiece public_value; + if (scfg->GetNthValue24(kPUBS, key_exchange_index, &public_value) != + QUIC_NO_ERROR) { + *error_details = "Missing public value"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + StringPiece orbit; + if (!scfg->GetStringPiece(kORBT, &orbit) || orbit.size() != kOrbitSize) { + *error_details = "SCFG missing OBIT"; + return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; + } + + CryptoUtils::GenerateNonce(now, rand, orbit, &out_params->client_nonce); + out->SetStringPiece(kNONC, out_params->client_nonce); + if (!out_params->server_nonce.empty()) { + out->SetStringPiece(kServerNonceTag, out_params->server_nonce); + } + + switch (out_params->key_exchange) { + case kC255: + out_params->client_key_exchange.reset(Curve25519KeyExchange::New( + Curve25519KeyExchange::NewPrivateKey(rand))); + break; + case kP256: + out_params->client_key_exchange.reset(P256KeyExchange::New( + P256KeyExchange::NewPrivateKey())); + break; + default: + DCHECK(false); + *error_details = "Configured to support an unknown key exchange"; + return QUIC_CRYPTO_INTERNAL_ERROR; + } + + if (!out_params->client_key_exchange->CalculateSharedKey( + public_value, &out_params->initial_premaster_secret)) { + *error_details = "Key exchange failure"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + out->SetStringPiece(kPUBS, out_params->client_key_exchange->public_value()); + + bool do_channel_id = false; + if (channel_id_signer_.get()) { + const QuicTag* their_proof_demands; + size_t num_their_proof_demands; + if (scfg->GetTaglist(kPDMD, &their_proof_demands, + &num_their_proof_demands) == QUIC_NO_ERROR) { + for (size_t i = 0; i < num_their_proof_demands; i++) { + if (their_proof_demands[i] == kCHID) { + do_channel_id = true; + break; + } + } + } + } + + if (do_channel_id) { + // In order to calculate the encryption key for the CETV block we need to + // serialise the client hello as it currently is (i.e. without the CETV + // block). For this, the client hello is serialized without padding. + const size_t orig_min_size = out->minimum_size(); + out->set_minimum_size(0); + + CryptoHandshakeMessage cetv; + cetv.set_tag(kCETV); + + string hkdf_input; + const QuicData& client_hello_serialized = out->GetSerialized(); + hkdf_input.append(QuicCryptoConfig::kCETVLabel, + strlen(QuicCryptoConfig::kCETVLabel) + 1); + hkdf_input.append(reinterpret_cast<char*>(&guid), sizeof(guid)); + hkdf_input.append(client_hello_serialized.data(), + client_hello_serialized.length()); + hkdf_input.append(cached->server_config()); + + string key, signature; + if (!channel_id_signer_->Sign(server_hostname, hkdf_input, + &key, &signature)) { + *error_details = "Channel ID signature failed"; + return QUIC_INVALID_CHANNEL_ID_SIGNATURE; + } + + cetv.SetStringPiece(kCIDK, key); + cetv.SetStringPiece(kCIDS, signature); + + CrypterPair crypters; + if (!CryptoUtils::DeriveKeys(out_params->initial_premaster_secret, + out_params->aead, out_params->client_nonce, + out_params->server_nonce, hkdf_input, + CryptoUtils::CLIENT, &crypters)) { + *error_details = "Symmetric key setup failed"; + return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED; + } + + const QuicData& cetv_plaintext = cetv.GetSerialized(); + scoped_ptr<QuicData> cetv_ciphertext(crypters.encrypter->EncryptPacket( + 0 /* sequence number */, + StringPiece() /* associated data */, + cetv_plaintext.AsStringPiece())); + if (!cetv_ciphertext.get()) { + *error_details = "Packet encryption failed"; + return QUIC_ENCRYPTION_FAILURE; + } + + out->SetStringPiece(kCETV, cetv_ciphertext->AsStringPiece()); + out->MarkDirty(); + + out->set_minimum_size(orig_min_size); + } + + out_params->hkdf_input_suffix.clear(); + out_params->hkdf_input_suffix.append(reinterpret_cast<char*>(&guid), + sizeof(guid)); + const QuicData& client_hello_serialized = out->GetSerialized(); + out_params->hkdf_input_suffix.append(client_hello_serialized.data(), + client_hello_serialized.length()); + out_params->hkdf_input_suffix.append(cached->server_config()); + + string hkdf_input; + const size_t label_len = strlen(QuicCryptoConfig::kInitialLabel) + 1; + hkdf_input.reserve(label_len + out_params->hkdf_input_suffix.size()); + hkdf_input.append(QuicCryptoConfig::kInitialLabel, label_len); + hkdf_input.append(out_params->hkdf_input_suffix); + + if (!CryptoUtils::DeriveKeys( + out_params->initial_premaster_secret, out_params->aead, + out_params->client_nonce, out_params->server_nonce, hkdf_input, + CryptoUtils::CLIENT, &out_params->initial_crypters)) { + *error_details = "Symmetric key setup failed"; + return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED; + } + + return QUIC_NO_ERROR; +} + +QuicErrorCode QuicCryptoClientConfig::ProcessRejection( + const CryptoHandshakeMessage& rej, + QuicWallTime now, + CachedState* cached, + QuicCryptoNegotiatedParameters* out_params, + string* error_details) { + DCHECK(error_details != NULL); + + if (rej.tag() != kREJ) { + *error_details = "Message is not REJ"; + return QUIC_CRYPTO_INTERNAL_ERROR; + } + + StringPiece scfg; + if (!rej.GetStringPiece(kSCFG, &scfg)) { + *error_details = "Missing SCFG"; + return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND; + } + + QuicErrorCode error = cached->SetServerConfig(scfg, now, error_details); + if (error != QUIC_NO_ERROR) { + return error; + } + + StringPiece token; + if (rej.GetStringPiece(kSourceAddressTokenTag, &token)) { + cached->set_source_address_token(token); + } + + StringPiece nonce; + if (rej.GetStringPiece(kServerNonceTag, &nonce)) { + out_params->server_nonce = nonce.as_string(); + } + + StringPiece proof, cert_bytes; + bool has_proof = rej.GetStringPiece(kPROF, &proof); + bool has_cert = rej.GetStringPiece(kCertificateTag, &cert_bytes); + if (has_proof && has_cert) { + vector<string> certs; + if (!CertCompressor::DecompressChain(cert_bytes, out_params->cached_certs, + common_cert_sets, &certs)) { + *error_details = "Certificate data invalid"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + cached->SetProof(certs, proof); + } else { + cached->ClearProof(); + if (has_proof && !has_cert) { + *error_details = "Certificate missing"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + if (!has_proof && has_cert) { + *error_details = "Proof missing"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + } + + return QUIC_NO_ERROR; +} + +QuicErrorCode QuicCryptoClientConfig::ProcessServerHello( + const CryptoHandshakeMessage& server_hello, + QuicGuid guid, + CachedState* cached, + QuicCryptoNegotiatedParameters* out_params, + string* error_details) { + DCHECK(error_details != NULL); + + if (server_hello.tag() != kSHLO) { + *error_details = "Bad tag"; + return QUIC_INVALID_CRYPTO_MESSAGE_TYPE; + } + + // Learn about updated source address tokens. + StringPiece token; + if (server_hello.GetStringPiece(kSourceAddressTokenTag, &token)) { + cached->set_source_address_token(token); + } + + // TODO(agl): + // learn about updated SCFGs. + + StringPiece public_value; + if (!server_hello.GetStringPiece(kPUBS, &public_value)) { + *error_details = "server hello missing forward secure public value"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + if (!out_params->client_key_exchange->CalculateSharedKey( + public_value, &out_params->forward_secure_premaster_secret)) { + *error_details = "Key exchange failure"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + string hkdf_input; + const size_t label_len = strlen(QuicCryptoConfig::kForwardSecureLabel) + 1; + hkdf_input.reserve(label_len + out_params->hkdf_input_suffix.size()); + hkdf_input.append(QuicCryptoConfig::kForwardSecureLabel, label_len); + hkdf_input.append(out_params->hkdf_input_suffix); + + if (!CryptoUtils::DeriveKeys( + out_params->forward_secure_premaster_secret, out_params->aead, + out_params->client_nonce, out_params->server_nonce, hkdf_input, + CryptoUtils::CLIENT, &out_params->forward_secure_crypters)) { + *error_details = "Symmetric key setup failed"; + return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED; + } + + return QUIC_NO_ERROR; +} + +ProofVerifier* QuicCryptoClientConfig::proof_verifier() const { + return proof_verifier_.get(); +} + +void QuicCryptoClientConfig::SetProofVerifier(ProofVerifier* verifier) { + proof_verifier_.reset(verifier); +} + +ChannelIDSigner* QuicCryptoClientConfig::channel_id_signer() const { + return channel_id_signer_.get(); +} + +void QuicCryptoClientConfig::SetChannelIDSigner(ChannelIDSigner* signer) { + channel_id_signer_.reset(signer); +} + +void QuicCryptoClientConfig::InitializeFrom( + const std::string& server_hostname, + const std::string& canonical_server_hostname, + QuicCryptoClientConfig* canonical_crypto_config) { + CachedState* canonical_cached = + canonical_crypto_config->LookupOrCreate(canonical_server_hostname); + if (!canonical_cached->proof_valid()) { + return; + } + CachedState* cached = LookupOrCreate(server_hostname); + cached->InitializeFrom(*canonical_cached); +} + +} // namespace net diff --git a/net/quic/crypto/quic_crypto_client_config.h b/net/quic/crypto/quic_crypto_client_config.h new file mode 100644 index 0000000..8164b20 --- /dev/null +++ b/net/quic/crypto/quic_crypto_client_config.h @@ -0,0 +1,208 @@ +// 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. + +#ifndef NET_QUIC_CRYPTO_QUIC_CRYPTO_CLIENT_CONFIG_H_ +#define NET_QUIC_CRYPTO_QUIC_CRYPTO_CLIENT_CONFIG_H_ + +#include <map> +#include <string> +#include <vector> + +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_piece.h" +#include "net/base/net_export.h" +#include "net/quic/crypto/crypto_handshake.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +// QuicCryptoClientConfig contains crypto-related configuration settings for a +// client. Note that this object isn't thread-safe. It's designed to be used on +// a single thread at a time. +class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { + public: + // A CachedState contains the information that the client needs in order to + // perform a 0-RTT handshake with a server. This information can be reused + // over several connections to the same server. + class NET_EXPORT_PRIVATE CachedState { + public: + CachedState(); + ~CachedState(); + + // IsComplete returns true if this object contains enough information to + // perform a handshake with the server. |now| is used to judge whether any + // cached server config has expired. + bool IsComplete(QuicWallTime now) const; + + // GetServerConfig returns the parsed contents of |server_config|, or NULL + // if |server_config| is empty. The return value is owned by this object + // and is destroyed when this object is. + const CryptoHandshakeMessage* GetServerConfig() const; + + // SetServerConfig checks that |server_config| parses correctly and stores + // it in |server_config_|. |now| is used to judge whether |server_config| + // has expired. + QuicErrorCode SetServerConfig(base::StringPiece server_config, + QuicWallTime now, + std::string* error_details); + + // InvalidateServerConfig clears the cached server config (if any). + void InvalidateServerConfig(); + + // SetProof stores a certificate chain and signature. + void SetProof(const std::vector<std::string>& certs, + base::StringPiece signature); + + // Clears the certificate chain and signature and invalidates the proof. + void ClearProof(); + + // SetProofValid records that the certificate chain and signature have been + // validated and that it's safe to assume that the server is legitimate. + // (Note: this does not check the chain or signature.) + void SetProofValid(); + + // If the server config or the proof has changed then it needs to be + // revalidated. Helper function to keep server_config_valid_ and + // generation_counter_ in sync. + void SetProofInvalid(); + + const std::string& server_config() const; + const std::string& source_address_token() const; + const std::vector<std::string>& certs() const; + const std::string& signature() const; + bool proof_valid() const; + uint64 generation_counter() const; + const ProofVerifyDetails* proof_verify_details() const; + + void set_source_address_token(base::StringPiece token); + + // SetProofVerifyDetails takes ownership of |details|. + void SetProofVerifyDetails(ProofVerifyDetails* details); + + // Copy the |server_config_|, |source_address_token_|, |certs_| and + // |server_config_sig_| from the |other|. The remaining fields, + // |generation_counter_|, |proof_verify_details_|, and |scfg_| remain + // unchanged. + void InitializeFrom(const CachedState& other); + + private: + std::string server_config_id_; // An opaque id from the server. + std::string server_config_; // A serialized handshake message. + std::string source_address_token_; // An opaque proof of IP ownership. + std::vector<std::string> certs_; // A list of certificates in leaf-first + // order. + std::string server_config_sig_; // A signature of |server_config_|. + bool server_config_valid_; // True if |server_config_| is correctly + // signed and |certs_| has been + // validated. + // Generation counter associated with the |server_config_|, |certs_| and + // |server_config_sig_| combination. It is incremented whenever we set + // server_config_valid_ to false. + uint64 generation_counter_; + + scoped_ptr<ProofVerifyDetails> proof_verify_details_; + + // scfg contains the cached, parsed value of |server_config|. + mutable scoped_ptr<CryptoHandshakeMessage> scfg_; + + DISALLOW_COPY_AND_ASSIGN(CachedState); + }; + + QuicCryptoClientConfig(); + ~QuicCryptoClientConfig(); + + // Sets the members to reasonable, default values. + void SetDefaults(); + + // LookupOrCreate returns a CachedState for the given hostname. If no such + // CachedState currently exists, it will be created and cached. + CachedState* LookupOrCreate(const std::string& server_hostname); + + // FillInchoateClientHello sets |out| to be a CHLO message that elicits a + // source-address token or SCFG from a server. If |cached| is non-NULL, the + // source-address token will be taken from it. |out_params| is used in order + // to store the cached certs that were sent as hints to the server in + // |out_params->cached_certs|. + void FillInchoateClientHello(const std::string& server_hostname, + const CachedState* cached, + QuicCryptoNegotiatedParameters* out_params, + CryptoHandshakeMessage* out) const; + + // FillClientHello sets |out| to be a CHLO message based on the configuration + // of this object. This object must have cached enough information about + // |server_hostname| in order to perform a handshake. This can be checked + // with the |IsComplete| member of |CachedState|. + // + // |clock| and |rand| are used to generate the nonce and |out_params| is + // filled with the results of the handshake that the server is expected to + // accept. + QuicErrorCode FillClientHello(const std::string& server_hostname, + QuicGuid guid, + const CachedState* cached, + QuicWallTime now, + QuicRandom* rand, + QuicCryptoNegotiatedParameters* out_params, + CryptoHandshakeMessage* out, + std::string* error_details) const; + + // ProcessRejection processes a REJ message from a server and updates the + // cached information about that server. After this, |IsComplete| may return + // true for that server's CachedState. If the rejection message contains + // state about a future handshake (i.e. an nonce value from the server), then + // it will be saved in |out_params|. |now| is used to judge whether the + // server config in the rejection message has expired. + QuicErrorCode ProcessRejection(const CryptoHandshakeMessage& rej, + QuicWallTime now, + CachedState* cached, + QuicCryptoNegotiatedParameters* out_params, + std::string* error_details); + + // ProcessServerHello processes the message in |server_hello|, updates the + // cached information about that server, writes the negotiated parameters to + // |out_params| and returns QUIC_NO_ERROR. If |server_hello| is unacceptable + // then it puts an error message in |error_details| and returns an error + // code. + QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello, + QuicGuid guid, + CachedState* cached, + QuicCryptoNegotiatedParameters* out_params, + std::string* error_details); + + ProofVerifier* proof_verifier() const; + + // SetProofVerifier takes ownership of a |ProofVerifier| that clients are + // free to use in order to verify certificate chains from servers. If a + // ProofVerifier is set then the client will request a certificate chain from + // the server. + void SetProofVerifier(ProofVerifier* verifier); + + ChannelIDSigner* channel_id_signer() const; + + // SetChannelIDSigner sets a ChannelIDSigner that will be called when the + // server supports channel IDs to sign a message proving possession of the + // given ChannelID. This object takes ownership of |signer|. + void SetChannelIDSigner(ChannelIDSigner* signer); + + // Initialize the CachedState from |canonical_crypto_config| for the + // |canonical_server_hostname| as the initial CachedState for + // |server_hostname|. We will copy config data only if + // |canonical_crypto_config| has valid proof. + void InitializeFrom(const std::string& server_hostname, + const std::string& canonical_server_hostname, + QuicCryptoClientConfig* canonical_crypto_config); + + private: + // cached_states_ maps from the server hostname to the cached information + // about that server. + std::map<std::string, CachedState*> cached_states_; + + scoped_ptr<ProofVerifier> proof_verifier_; + scoped_ptr<ChannelIDSigner> channel_id_signer_; + + DISALLOW_COPY_AND_ASSIGN(QuicCryptoClientConfig); +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_QUIC_CRYPTO_CLIENT_CONFIG_H_ diff --git a/net/quic/crypto/crypto_server_config.cc b/net/quic/crypto/quic_crypto_server_config.cc index 7fb4fa9..2380058 100644 --- a/net/quic/crypto/crypto_server_config.cc +++ b/net/quic/crypto/quic_crypto_server_config.cc @@ -1,8 +1,8 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// 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/crypto/crypto_server_config.h" +#include "net/quic/crypto/quic_crypto_server_config.h" #include <stdlib.h> #include <algorithm> @@ -757,12 +757,22 @@ void QuicCryptoServerConfig::BuildRejection( // kREJOverheadBytes is a very rough estimate of how much of a REJ // message is taken up by things other than the certificates. - const size_t kREJOverheadBytes = 112; + // STK: 56 bytes + // SNO: 56 bytes + // SCFG + // SCID: 16 bytes + // PUBS: 38 bytes + const size_t kREJOverheadBytes = 166; + // kMultiplier is the multiple of the CHLO message size that a REJ message + // must stay under when the client doesn't present a valid source-address + // token. + const size_t kMultiplier = 2; // max_unverified_size is the number of bytes that the certificate chain // and signature can consume before we will demand a valid source-address // token. - const size_t max_unverified_size = client_hello.size() - kREJOverheadBytes; - COMPILE_ASSERT(kClientHelloMinimumSize >= kREJOverheadBytes, + const size_t max_unverified_size = + client_hello.size() * kMultiplier - kREJOverheadBytes; + COMPILE_ASSERT(kClientHelloMinimumSize * kMultiplier >= kREJOverheadBytes, overhead_calculation_may_underflow); if (info.valid_source_address_token || signature.size() + compressed.size() < max_unverified_size) { diff --git a/net/quic/crypto/crypto_server_config.h b/net/quic/crypto/quic_crypto_server_config.h index 4551425..80d9311 100644 --- a/net/quic/crypto/crypto_server_config.h +++ b/net/quic/crypto/quic_crypto_server_config.h @@ -1,9 +1,9 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// 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. -#ifndef NET_QUIC_CRYPTO_CRYPTO_SERVER_CONFIG_H_ -#define NET_QUIC_CRYPTO_CRYPTO_SERVER_CONFIG_H_ +#ifndef NET_QUIC_CRYPTO_QUIC_CRYPTO_SERVER_CONFIG_H_ +#define NET_QUIC_CRYPTO_QUIC_CRYPTO_SERVER_CONFIG_H_ #include <map> #include <string> @@ -367,4 +367,4 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { } // namespace net -#endif // NET_QUIC_CRYPTO_CRYPTO_SERVER_CONFIG_H_ +#endif // NET_QUIC_CRYPTO_QUIC_CRYPTO_SERVER_CONFIG_H_ diff --git a/net/quic/crypto/crypto_handshake_test.cc b/net/quic/crypto/quic_crypto_server_config_test.cc index dcde964..fc8882c 100644 --- a/net/quic/crypto/crypto_handshake_test.cc +++ b/net/quic/crypto/quic_crypto_server_config_test.cc @@ -1,14 +1,14 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// 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/crypto/crypto_handshake.h" +#include "net/quic/crypto/quic_crypto_server_config.h" #include <stdarg.h> #include "base/stl_util.h" #include "net/quic/crypto/aes_128_gcm_12_encrypter.h" -#include "net/quic/crypto/crypto_server_config.h" +#include "net/quic/crypto/crypto_handshake.h" #include "net/quic/crypto/crypto_server_config_protobuf.h" #include "net/quic/crypto/quic_random.h" #include "net/quic/quic_time.h" diff --git a/net/quic/iovector_test.cc b/net/quic/iovector_test.cc new file mode 100644 index 0000000..b25f29c --- /dev/null +++ b/net/quic/iovector_test.cc @@ -0,0 +1,283 @@ +// 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/iovector.h" + +#include <string.h> + +#include "base/logging.h" +#include "testing/gtest/include/gtest/gtest.h" + +using std::string; + +namespace net { +namespace test { +namespace { + +const char* const test_data[] = { + "test string 1, a medium size one.", + "test string2", + "test string 3, a looooooooooooong loooooooooooooooong string" +}; + +TEST(IOVectorTest, CopyConstructor) { + IOVector iov1; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { + iov1.Append(const_cast<char*>(test_data[i]), strlen(test_data[i])); + } + IOVector iov2 = iov1; + EXPECT_EQ(iov2.Size(), iov1.Size()); + for (size_t i = 0; i < iov2.Size(); ++i) { + EXPECT_TRUE(iov2.iovec()[i].iov_base == iov1.iovec()[i].iov_base); + EXPECT_EQ(iov2.iovec()[i].iov_len, iov1.iovec()[i].iov_len); + } + EXPECT_EQ(iov2.TotalBufferSize(), iov1.TotalBufferSize()); +} + +TEST(IOVectorTest, AssignmentOperator) { + IOVector iov1; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { + iov1.Append(const_cast<char*>(test_data[i]), strlen(test_data[i])); + } + IOVector iov2; + iov2.Append(const_cast<char*>("ephemeral string"), 16); + // The following assignment results in a shallow copy; + // both IOVectors point to the same underlying data. + iov2 = iov1; + EXPECT_EQ(iov2.Size(), iov1.Size()); + for (size_t i = 0; i < iov2.Size(); ++i) { + EXPECT_TRUE(iov2.iovec()[i].iov_base == iov1.iovec()[i].iov_base); + EXPECT_EQ(iov2.iovec()[i].iov_len, iov1.iovec()[i].iov_len); + } + EXPECT_EQ(iov2.TotalBufferSize(), iov1.TotalBufferSize()); +} + +TEST(IOVectorTest, Append) { + IOVector iov; + int length = 0; + const struct iovec* iov2 = iov.iovec(); + + ASSERT_EQ(0u, iov.Size()); + ASSERT_TRUE(iov2 == NULL); + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { + const int str_len = strlen(test_data[i]); + const int append_len = str_len / 2; + // This should append a new block + iov.Append(const_cast<char*>(test_data[i]), append_len); + length += append_len; + ASSERT_EQ(i + 1, static_cast<size_t>(iov.Size())); + ASSERT_TRUE(iov.LastBlockEnd() == test_data[i] + append_len); + // This should just lengthen the existing block. + iov.Append(const_cast<char*>(test_data[i] + append_len), + str_len - append_len); + length += (str_len - append_len); + ASSERT_EQ(i + 1, static_cast<size_t>(iov.Size())); + ASSERT_TRUE(iov.LastBlockEnd() == test_data[i] + str_len); + } + + iov2 = iov.iovec(); + ASSERT_TRUE(iov2 != NULL); + for (size_t i = 0; i < iov.Size(); ++i) { + ASSERT_TRUE(test_data[i] == iov2[i].iov_base); + ASSERT_EQ(strlen(test_data[i]), iov2[i].iov_len); + } +} + +TEST(IOVectorTest, AppendIovec) { + IOVector iov; + const struct iovec test_iov[] = { + {const_cast<char*>("foo"), 3}, + {const_cast<char*>("bar"), 3}, + {const_cast<char*>("buzzzz"), 6} + }; + iov.AppendIovec(test_iov, ARRAYSIZE_UNSAFE(test_iov)); + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_iov); ++i) { + EXPECT_EQ(test_iov[i].iov_base, iov.iovec()[i].iov_base); + EXPECT_EQ(test_iov[i].iov_len, iov.iovec()[i].iov_len); + } + + // Test AppendIovecAtMostBytes. + iov.Clear(); + // Stop in the middle of a block. + EXPECT_EQ(5u, iov.AppendIovecAtMostBytes(test_iov, ARRAYSIZE_UNSAFE(test_iov), + 5)); + EXPECT_EQ(5u, iov.TotalBufferSize()); + iov.Append(static_cast<char*>(test_iov[1].iov_base) + 2, 1); + // Make sure the boundary case, where max_bytes == size of block also works. + EXPECT_EQ(6u, iov.AppendIovecAtMostBytes(&test_iov[2], 1, 6)); + ASSERT_LE(ARRAYSIZE_UNSAFE(test_iov), static_cast<size_t>(iov.Size())); + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_iov); ++i) { + EXPECT_EQ(test_iov[i].iov_base, iov.iovec()[i].iov_base); + EXPECT_EQ(test_iov[i].iov_len, iov.iovec()[i].iov_len); + } +} + +TEST(IOVectorTest, ConsumeHalfBlocks) { + IOVector iov; + int length = 0; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { + const int str_len = strlen(test_data[i]); + iov.Append(const_cast<char*>(test_data[i]), str_len); + length += str_len; + } + const char* endp = iov.LastBlockEnd(); + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { + const struct iovec* iov2 = iov.iovec(); + const size_t str_len = strlen(test_data[i]); + size_t tmp = str_len / 2; + + ASSERT_TRUE(iov2 != NULL); + ASSERT_TRUE(iov2[0].iov_base == test_data[i]); + ASSERT_EQ(str_len, iov2[0].iov_len); + + // Consume half of the first block. + size_t consumed = iov.Consume(tmp); + ASSERT_EQ(tmp, consumed); + ASSERT_EQ(ARRAYSIZE_UNSAFE(test_data) - i, static_cast<size_t>(iov.Size())); + iov2 = iov.iovec(); + ASSERT_TRUE(iov2 != NULL); + ASSERT_TRUE(iov2[0].iov_base == test_data[i] + tmp); + ASSERT_EQ(iov2[0].iov_len, str_len - tmp); + + // Consume the rest of the first block + consumed = iov.Consume(str_len - tmp); + ASSERT_EQ(str_len - tmp, consumed); + ASSERT_EQ(ARRAYSIZE_UNSAFE(test_data) - i - 1, + static_cast<size_t>(iov.Size())); + iov2 = iov.iovec(); + if (iov.Size() > 0) { + ASSERT_TRUE(iov2 != NULL); + ASSERT_TRUE(iov.LastBlockEnd() == endp); + } else { + ASSERT_TRUE(iov2 == NULL); + ASSERT_TRUE(iov.LastBlockEnd() == NULL); + } + } +} + +TEST(IOVectorTest, ConsumeTwoAndHalfBlocks) { + IOVector iov; + int length = 0; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { + const int str_len = strlen(test_data[i]); + iov.Append(const_cast<char*>(test_data[i]), str_len); + length += str_len; + } + const size_t last_len = strlen(test_data[ARRAYSIZE_UNSAFE(test_data) - 1]); + const size_t half_len = last_len / 2; + + const char* endp = iov.LastBlockEnd(); + size_t consumed = iov.Consume(length - half_len); + ASSERT_EQ(length - half_len, consumed); + const struct iovec* iov2 = iov.iovec(); + ASSERT_TRUE(iov2 != NULL); + ASSERT_EQ(1u, iov.Size()); + ASSERT_TRUE(iov2[0].iov_base == + test_data[ARRAYSIZE_UNSAFE(test_data) - 1] + last_len - half_len); + ASSERT_EQ(half_len, iov2[0].iov_len); + ASSERT_TRUE(iov.LastBlockEnd() == endp); + + consumed = iov.Consume(half_len); + ASSERT_EQ(half_len, consumed); + iov2 = iov.iovec(); + ASSERT_EQ(0u, iov.Size()); + ASSERT_TRUE(iov2 == NULL); + ASSERT_TRUE(iov.LastBlockEnd() == NULL); +} + +TEST(IOVectorTest, ConsumeTooMuch) { + IOVector iov; + int length = 0; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { + const int str_len = strlen(test_data[i]); + iov.Append(const_cast<char*>(test_data[i]), str_len); + length += str_len; + } + + int consumed = 0; + consumed = iov.Consume(length); + // TODO(rtenneti): enable when chromium supports EXPECT_DFATAL. + /* + EXPECT_DFATAL( + {consumed = iov.Consume(length + 1);}, + "Attempting to consume 1 non-existent bytes."); + */ + ASSERT_EQ(length, consumed); + const struct iovec* iov2 = iov.iovec(); + ASSERT_EQ(0u, iov.Size()); + ASSERT_TRUE(iov2 == NULL); + ASSERT_TRUE(iov.LastBlockEnd() == NULL); +} + +TEST(IOVectorTest, Clear) { + IOVector iov; + int length = 0; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { + const int str_len = strlen(test_data[i]); + iov.Append(const_cast<char*>(test_data[i]), str_len); + length += str_len; + } + const struct iovec* iov2 = iov.iovec(); + ASSERT_TRUE(iov2 != NULL); + ASSERT_EQ(ARRAYSIZE_UNSAFE(test_data), static_cast<size_t>(iov.Size())); + + iov.Clear(); + iov2 = iov.iovec(); + ASSERT_EQ(0u, iov.Size()); + ASSERT_TRUE(iov2 == NULL); +} + +TEST(IOVectorTest, Capacity) { + IOVector iov; + // Note: IOVector merges adjacent Appends() into a single iov. + // Therefore, if we expect final size of iov to be 3, we must insure + // that the items we are appending are not adjacent. To achieve that + // we use use an array (a[1] provides a buffer between a[0] and b[0], + // and makes them non-adjacent). + char a[2], b[2], c[2]; + iov.Append(&a[0], 1); + iov.Append(&b[0], 1); + iov.Append(&c[0], 1); + ASSERT_EQ(3u, iov.Size()); + size_t capacity = iov.Capacity(); + EXPECT_LE(iov.Size(), capacity); + iov.Consume(2); + // The capacity should not have changed. + EXPECT_EQ(capacity, iov.Capacity()); +} + +TEST(IOVectorTest, Swap) { + IOVector iov1, iov2; + // See IOVector merge comment above. + char a[2], b[2], c[2], d[2], e[2]; + iov1.Append(&a[0], 1); + iov1.Append(&b[0], 1); + + iov2.Append(&c[0], 1); + iov2.Append(&d[0], 1); + iov2.Append(&e[0], 1); + iov1.Swap(&iov2); + + ASSERT_EQ(3u, iov1.Size()); + EXPECT_EQ(&c[0], iov1.iovec()[0].iov_base); + EXPECT_EQ(1u, iov1.iovec()[0].iov_len); + EXPECT_EQ(&d[0], iov1.iovec()[1].iov_base); + EXPECT_EQ(1u, iov1.iovec()[1].iov_len); + EXPECT_EQ(&e[0], iov1.iovec()[2].iov_base); + EXPECT_EQ(1u, iov1.iovec()[2].iov_len); + + ASSERT_EQ(2u, iov2.Size()); + EXPECT_EQ(&a[0], iov2.iovec()[0].iov_base); + EXPECT_EQ(1u, iov2.iovec()[0].iov_len); + EXPECT_EQ(&b[0], iov2.iovec()[1].iov_base); + EXPECT_EQ(1u, iov2.iovec()[1].iov_len); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/net/quic/quic_config.cc b/net/quic/quic_config.cc index 545d4c1..3fa8211 100644 --- a/net/quic/quic_config.cc +++ b/net/quic/quic_config.cc @@ -19,7 +19,9 @@ QuicNegotiableValue::QuicNegotiableValue(QuicTag tag, Presence presence) } QuicNegotiableUint32::QuicNegotiableUint32(QuicTag tag, Presence presence) - : QuicNegotiableValue(tag, presence) { + : QuicNegotiableValue(tag, presence), + max_value_(0), + default_value_(0) { } void QuicNegotiableUint32::set(uint32 max, uint32 default_value) { @@ -224,9 +226,15 @@ QuicConfig::QuicConfig() : kICSL, QuicNegotiableValue::PRESENCE_REQUIRED), keepalive_timeout_seconds_(kKATO, QuicNegotiableValue::PRESENCE_OPTIONAL), max_streams_per_connection_(kMSPC, QuicNegotiableValue::PRESENCE_REQUIRED), - max_time_before_crypto_handshake_(QuicTime::Delta::Zero()) { - idle_connection_state_lifetime_seconds_.set(0, 0); - keepalive_timeout_seconds_.set(0, 0); + max_time_before_crypto_handshake_(QuicTime::Delta::Zero()), + server_initial_congestion_window_( + kSWND, QuicNegotiableValue::PRESENCE_OPTIONAL), + server_max_packet_size_(kSMSS, QuicNegotiableValue::PRESENCE_OPTIONAL), + initial_round_trip_time_us_(kIRTT, QuicNegotiableValue::PRESENCE_OPTIONAL) { + // All optional non-zero parameters should be initialized here. + server_initial_congestion_window_.set(kMaxInitialWindow, + kDefaultInitialWindow); + server_max_packet_size_.set(kMaxPacketSize, kDefaultMaxPacketSize); } QuicConfig::~QuicConfig() {} @@ -277,11 +285,45 @@ QuicTime::Delta QuicConfig::max_time_before_crypto_handshake() const { return max_time_before_crypto_handshake_; } +void QuicConfig::set_server_initial_congestion_window(size_t max_initial_window, + size_t default_initial_window) { + server_initial_congestion_window_.set(max_initial_window, + default_initial_window); +} + +uint32 QuicConfig::server_initial_congestion_window() const { + return server_initial_congestion_window_.GetUint32(); +} + +void QuicConfig::set_server_max_packet_size(size_t max_bytes, + size_t default_bytes) { + server_max_packet_size_.set(max_bytes, default_bytes); +} + +uint32 QuicConfig::server_max_packet_size() const { + return server_max_packet_size_.GetUint32(); +} + +void QuicConfig::set_initial_round_trip_time_us(size_t max_rtt, + size_t default_rtt) { + initial_round_trip_time_us_.set(max_rtt, default_rtt); +} + +uint32 QuicConfig::initial_round_trip_time_us() const { + return initial_round_trip_time_us_.GetUint32(); +} + bool QuicConfig::negotiated() { + // TODO(ianswett): Add the negotiated parameters once and iterate over all + // of them in negotiated, ToHandshakeMessage, ProcessClientHello, and + // ProcessServerHello. return congestion_control_.negotiated() && idle_connection_state_lifetime_seconds_.negotiated() && keepalive_timeout_seconds_.negotiated() && - max_streams_per_connection_.negotiated(); + max_streams_per_connection_.negotiated() && + server_initial_congestion_window_.negotiated() && + server_max_packet_size_.negotiated() && + initial_round_trip_time_us_.negotiated(); } void QuicConfig::SetDefaults() { @@ -294,6 +336,9 @@ void QuicConfig::SetDefaults() { kDefaultMaxStreamsPerConnection); max_time_before_crypto_handshake_ = QuicTime::Delta::FromSeconds( kDefaultMaxTimeForCryptoHandshakeSecs); + server_initial_congestion_window_.set(kMaxInitialWindow, + kDefaultInitialWindow); + server_max_packet_size_.set(kMaxPacketSize, kDefaultMaxPacketSize); } void QuicConfig::ToHandshakeMessage(CryptoHandshakeMessage* out) const { @@ -301,6 +346,10 @@ void QuicConfig::ToHandshakeMessage(CryptoHandshakeMessage* out) const { idle_connection_state_lifetime_seconds_.ToHandshakeMessage(out); keepalive_timeout_seconds_.ToHandshakeMessage(out); max_streams_per_connection_.ToHandshakeMessage(out); + server_initial_congestion_window_.ToHandshakeMessage(out); + server_max_packet_size_.ToHandshakeMessage(out); + // TODO(ianswett): Don't transmit parameters which are optional and not set. + initial_round_trip_time_us_.ToHandshakeMessage(out); } QuicErrorCode QuicConfig::ProcessClientHello( @@ -324,6 +373,18 @@ QuicErrorCode QuicConfig::ProcessClientHello( error = max_streams_per_connection_.ProcessClientHello( client_hello, error_details); } + if (error == QUIC_NO_ERROR) { + error = server_initial_congestion_window_.ProcessClientHello( + client_hello, error_details); + } + if (error == QUIC_NO_ERROR) { + error = server_max_packet_size_.ProcessClientHello( + client_hello, error_details); + } + if (error == QUIC_NO_ERROR) { + error = initial_round_trip_time_us_.ProcessClientHello( + client_hello, error_details); + } return error; } @@ -348,6 +409,18 @@ QuicErrorCode QuicConfig::ProcessServerHello( error = max_streams_per_connection_.ProcessServerHello( server_hello, error_details); } + if (error == QUIC_NO_ERROR) { + error = server_initial_congestion_window_.ProcessServerHello( + server_hello, error_details); + } + if (error == QUIC_NO_ERROR) { + error = server_max_packet_size_.ProcessServerHello( + server_hello, error_details); + } + if (error == QUIC_NO_ERROR) { + error = initial_round_trip_time_us_.ProcessServerHello( + server_hello, error_details); + } return error; } diff --git a/net/quic/quic_config.h b/net/quic/quic_config.h index c07f332..9328c67 100644 --- a/net/quic/quic_config.h +++ b/net/quic/quic_config.h @@ -41,6 +41,7 @@ class NET_EXPORT_PRIVATE QuicNegotiableValue { class NET_EXPORT_PRIVATE QuicNegotiableUint32 : public QuicNegotiableValue { public: + // Default and max values default to 0. QuicNegotiableUint32(QuicTag name, Presence presence); // Sets the maximum possible value that can be achieved after negotiation and @@ -159,6 +160,23 @@ class NET_EXPORT_PRIVATE QuicConfig { QuicTime::Delta max_time_before_crypto_handshake() const; + // Sets the server's TCP sender's max and default initial congestion window + // in packets. + void set_server_initial_congestion_window(size_t max_initial_window, + size_t default_initial_window); + + uint32 server_initial_congestion_window() const; + + // Sets the server's max packet size and default max packet size in bytes. + void set_server_max_packet_size(size_t max_bytes, size_t default_bytes); + + uint32 server_max_packet_size() const; + + // Sets an estimated initial round trip time in us. + void set_initial_round_trip_time_us(size_t max_rtt, size_t default_rtt); + + uint32 initial_round_trip_time_us() const; + bool negotiated(); // SetDefaults sets the members to sensible, default values. @@ -190,6 +208,12 @@ class NET_EXPORT_PRIVATE QuicConfig { // Maximum time till the session can be alive before crypto handshake is // finished. (Not negotiated). QuicTime::Delta max_time_before_crypto_handshake_; + // Initial congestion window in packets. + QuicNegotiableUint32 server_initial_congestion_window_; + // Maximum packet size for the server to send in bytes. + QuicNegotiableUint32 server_max_packet_size_; + // Initial round trip time estimate in microseconds. + QuicNegotiableUint32 initial_round_trip_time_us_; }; } // namespace net diff --git a/net/quic/quic_config_test.cc b/net/quic/quic_config_test.cc index eeaa97d..c7ed364 100644 --- a/net/quic/quic_config_test.cc +++ b/net/quic/quic_config_test.cc @@ -20,6 +20,7 @@ class QuicConfigTest : public ::testing::Test { protected: QuicConfigTest() { config_.SetDefaults(); + config_.set_initial_round_trip_time_us(kMaxInitialRoundTripTimeUs, 0); } QuicConfig config_; @@ -59,6 +60,9 @@ TEST_F(QuicConfigTest, ProcessClientHello) { QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs)); client_config.set_max_streams_per_connection( 2 * kDefaultMaxStreamsPerConnection, kDefaultMaxStreamsPerConnection); + client_config.set_initial_round_trip_time_us( + 10 * base::Time::kMicrosecondsPerMillisecond, + 10 * base::Time::kMicrosecondsPerMillisecond); CryptoHandshakeMessage msg; client_config.ToHandshakeMessage(&msg); @@ -72,6 +76,8 @@ TEST_F(QuicConfigTest, ProcessClientHello) { EXPECT_EQ(kDefaultMaxStreamsPerConnection, config_.max_streams_per_connection()); EXPECT_EQ(QuicTime::Delta::FromSeconds(0), config_.keepalive_timeout()); + EXPECT_EQ(10 * base::Time::kMicrosecondsPerMillisecond, + config_.initial_round_trip_time_us()); } TEST_F(QuicConfigTest, ProcessServerHello) { @@ -85,6 +91,13 @@ TEST_F(QuicConfigTest, ProcessServerHello) { server_config.set_max_streams_per_connection( kDefaultMaxStreamsPerConnection / 2, kDefaultMaxStreamsPerConnection / 2); + server_config.set_server_max_packet_size(kDefaultMaxPacketSize / 2, + kDefaultMaxPacketSize / 2); + server_config.set_server_initial_congestion_window(kDefaultInitialWindow / 2, + kDefaultInitialWindow / 2); + server_config.set_initial_round_trip_time_us( + 10 * base::Time::kMicrosecondsPerMillisecond, + 10 * base::Time::kMicrosecondsPerMillisecond); CryptoHandshakeMessage msg; server_config.ToHandshakeMessage(&msg); @@ -97,7 +110,12 @@ TEST_F(QuicConfigTest, ProcessServerHello) { config_.idle_connection_state_lifetime()); EXPECT_EQ(kDefaultMaxStreamsPerConnection / 2, config_.max_streams_per_connection()); + EXPECT_EQ(kDefaultMaxPacketSize / 2, config_.server_max_packet_size()); + EXPECT_EQ(kDefaultInitialWindow / 2, + config_.server_initial_congestion_window()); EXPECT_EQ(QuicTime::Delta::FromSeconds(0), config_.keepalive_timeout()); + EXPECT_EQ(10 * base::Time::kMicrosecondsPerMillisecond, + config_.initial_round_trip_time_us()); } TEST_F(QuicConfigTest, MissingValueInCHLO) { diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc index 4c9a6bc..c426868 100644 --- a/net/quic/quic_connection.cc +++ b/net/quic/quic_connection.cc @@ -19,6 +19,7 @@ #include "net/quic/crypto/quic_encrypter.h" #include "net/quic/iovector.h" #include "net/quic/quic_bandwidth.h" +#include "net/quic/quic_config.h" #include "net/quic/quic_utils.h" using base::hash_map; @@ -54,7 +55,7 @@ const QuicPacketSequenceNumber kMaxPacketGap = 5000; // We want to make sure if we get a nack packet which triggers several // retransmissions, we don't queue up too many packets. 10 is TCP's default // initial congestion window(RFC 6928). -const size_t kMaxRetransmissionsPerAck = 10; +const size_t kMaxRetransmissionsPerAck = kDefaultInitialWindow; // TCP retransmits after 3 nacks. // TODO(ianswett): Change to match TCP's rule of retransmitting once an ack @@ -252,6 +253,19 @@ QuicConnection::~QuicConnection() { } } +void QuicConnection::SetFromConfig(const QuicConfig& config) { + DCHECK_LT(0u, config.server_max_packet_size()); + DCHECK_LT(0u, config.server_initial_congestion_window()); + SetIdleNetworkTimeout(config.idle_connection_state_lifetime()); + // Set the max packet length only when QUIC_VERSION_12 or later is supported, + // with explicitly truncated acks. + if (version() > QUIC_VERSION_11) { + options()->max_packet_length = config.server_max_packet_size(); + } + congestion_manager_.SetFromConfig(config, is_server_); + // TODO(satyamshekhar): Set congestion control and ICSL also. +} + bool QuicConnection::SelectMutualVersion( const QuicVersionVector& available_versions) { // Try to find the highest mutual version by iterating over supported @@ -506,8 +520,10 @@ void QuicConnection::ProcessAckFrame(const QuicAckFrame& incoming_ack) { largest_seen_packet_with_ack_ = last_header_.packet_sequence_number; - received_truncated_ack_ = incoming_ack.received_info.missing_packets.size() >= - QuicFramer::GetMaxUnackedPackets(last_header_); + received_truncated_ack_ = version() <= QUIC_VERSION_11 ? + incoming_ack.received_info.missing_packets.size() >= + QuicFramer::GetMaxUnackedPackets(last_header_) : + incoming_ack.received_info.is_truncated; received_packet_manager_.UpdatePacketInformationReceivedByPeer(incoming_ack); received_packet_manager_.UpdatePacketInformationSentByPeer(incoming_ack); @@ -590,8 +606,10 @@ bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) { // We can't have too many unacked packets, or our ack frames go over // kMaxPacketSize. - DCHECK_LE(incoming_ack.received_info.missing_packets.size(), - QuicFramer::GetMaxUnackedPackets(last_header_)); + if (version() <= QUIC_VERSION_11) { + DCHECK_LE(incoming_ack.received_info.missing_packets.size(), + QuicFramer::GetMaxUnackedPackets(last_header_)); + } if (incoming_ack.sent_info.least_unacked < received_packet_manager_.peer_least_packet_awaiting_ack()) { @@ -672,9 +690,8 @@ bool QuicConnection::OnConnectionCloseFrame( DLOG(INFO) << ENDPOINT << "Connection " << guid() << " closed with error " << QuicUtils::ErrorToString(frame.error_code) << " " << frame.error_details; - CloseConnection(frame.error_code, true); - DCHECK(!connected_); - return false; + last_close_frames_.push_back(frame); + return connected_; } bool QuicConnection::OnGoAwayFrame(const QuicGoAwayFrame& frame) { @@ -743,6 +760,10 @@ void QuicConnection::OnPacketComplete() { congestion_manager_.OnIncomingQuicCongestionFeedbackFrame( last_congestion_frames_[i], time_of_last_received_packet_); } + if (!last_close_frames_.empty()) { + CloseConnection(last_close_frames_[0].error_code, true); + DCHECK(!connected_); + } if (received_packet_manager_.GetNumMissingPackets() != 0) { send_ack_immediately = true; @@ -1286,13 +1307,20 @@ bool QuicConnection::WritePacket(EncryptionLevel level, CloseConnection(QUIC_ENCRYPTION_FAILURE, false); return false; } + + if (encrypted->length() > options()->max_packet_length) { + LOG(DFATAL) << ENDPOINT + << "Writing an encrypted packet larger than max_packet_length:" + << options()->max_packet_length; + } DLOG(INFO) << ENDPOINT << "Sending packet number " << sequence_number << " : " << (packet->is_fec_packet() ? "FEC " : (retransmittable == HAS_RETRANSMITTABLE_DATA ? "data bearing " : " ack only ")) << ", encryption level: " << QuicUtils::EncryptionLevelToString(level) - << ", length:" << packet->length(); + << ", length:" << packet->length() << ", encrypted length:" + << encrypted->length(); DVLOG(2) << ENDPOINT << "packet(" << sequence_number << "): " << std::endl << QuicUtils::StringToHexASCIIDump(packet->AsStringPiece()); @@ -1344,7 +1372,7 @@ bool QuicConnection::ShouldDiscardPacket( HasRetransmittableData retransmittable) { if (!connected_) { DLOG(INFO) << ENDPOINT - << "Not sending packet as connection is disconnected."; + << "Not sending packet as connection is disconnected."; return true; } @@ -1353,7 +1381,7 @@ bool QuicConnection::ShouldDiscardPacket( // Drop packets that are NULL encrypted since the peer won't accept them // anymore. DLOG(INFO) << ENDPOINT << "Dropping packet: " << sequence_number - << " since the packet is NULL encrypted."; + << " since the packet is NULL encrypted."; sent_packet_manager_.DiscardUnackedPacket(sequence_number); return true; } @@ -1367,7 +1395,7 @@ bool QuicConnection::ShouldDiscardPacket( // high water mark, all before we're able to send the packet // then we can simply drop it. DLOG(INFO) << ENDPOINT << "Dropping packet: " << sequence_number - << " since it has already been acked."; + << " since it has already been acked."; return true; } @@ -1385,7 +1413,7 @@ bool QuicConnection::ShouldDiscardPacket( if (!sent_packet_manager_.HasRetransmittableFrames(sequence_number)) { DLOG(INFO) << ENDPOINT << "Dropping packet: " << sequence_number - << " since a previous transmission has been acked."; + << " since a previous transmission has been acked."; sent_packet_manager_.DiscardUnackedPacket(sequence_number); return true; } @@ -1737,6 +1765,7 @@ void QuicConnection::SendConnectionClosePacket(QuicErrorCode error, DLOG(INFO) << ENDPOINT << "Force closing " << guid() << " with error " << QuicUtils::ErrorToString(error) << " (" << error << ") " << details; + ScopedPacketBundler ack_bundler(this, version() > QUIC_VERSION_11); QuicConnectionCloseFrame* frame = new QuicConnectionCloseFrame(); frame->error_code = error; frame->error_details = details; @@ -1751,6 +1780,13 @@ void QuicConnection::CloseConnection(QuicErrorCode error, bool from_peer) { DCHECK(connected_); connected_ = false; visitor_->OnConnectionClosed(error, from_peer); + // Cancel the alarms so they don't trigger any action now that the + // connection is closed. + ack_alarm_->Cancel(); + resume_writes_alarm_->Cancel(); + retransmission_alarm_->Cancel(); + send_alarm_->Cancel(); + timeout_alarm_->Cancel(); } void QuicConnection::SendGoAway(QuicErrorCode error, diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h index c5ad5c4..84dba1d 100644 --- a/net/quic/quic_connection.h +++ b/net/quic/quic_connection.h @@ -34,7 +34,6 @@ #include "net/quic/quic_alarm.h" #include "net/quic/quic_blocked_writer_interface.h" #include "net/quic/quic_connection_stats.h" -#include "net/quic/quic_framer.h" #include "net/quic/quic_packet_creator.h" #include "net/quic/quic_packet_generator.h" #include "net/quic/quic_packet_writer.h" @@ -49,6 +48,7 @@ NET_EXPORT_PRIVATE extern bool FLAGS_bundle_ack_with_outgoing_packet; namespace net { class QuicClock; +class QuicConfig; class QuicConnection; class QuicDecrypter; class QuicEncrypter; @@ -85,6 +85,9 @@ class NET_EXPORT_PRIVATE QuicConnectionVisitorInterface { // Called once a specific QUIC version is agreed by both endpoints. virtual void OnSuccessfulVersionNegotiation(const QuicVersion& version) = 0; + // Indicates a new QuicConfig has been negotiated. + virtual void OnConfigNegotiated() = 0; + // Called when a blocked socket becomes writable. If all pending bytes for // this visitor are consumed by the connection successfully this should // return true, otherwise it should return false. @@ -199,6 +202,9 @@ class NET_EXPORT_PRIVATE QuicConnection const QuicVersionVector& supported_versions); virtual ~QuicConnection(); + // Sets connection parameters from the supplied |config|. + void SetFromConfig(const QuicConfig& config); + // Send the data in |data| to the peer in as few packets as possible. // Returns a pair with the number of bytes consumed from data, and a boolean // indicating if the fin bit was consumed. This does not indicate the data @@ -672,6 +678,7 @@ class NET_EXPORT_PRIVATE QuicConnection std::vector<QuicCongestionFeedbackFrame> last_congestion_frames_; std::vector<QuicRstStreamFrame> last_rst_frames_; std::vector<QuicGoAwayFrame> last_goaway_frames_; + std::vector<QuicConnectionCloseFrame> last_close_frames_; // Then number of packets retransmitted because of nacks // while processed the current ack frame. size_t retransmitted_nacked_packet_count_; diff --git a/net/quic/quic_connection_test.cc b/net/quic/quic_connection_test.cc index 2433648..73706f1 100644 --- a/net/quic/quic_connection_test.cc +++ b/net/quic/quic_connection_test.cc @@ -546,6 +546,7 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> { FLAGS_track_retransmission_history = true; connection_.set_visitor(&visitor_); connection_.SetSendAlgorithm(send_algorithm_); + framer_.set_received_entropy_calculator(&entropy_calculator_); // Simplify tests by not sending feedback unless specifically configured. SetFeedback(NULL); EXPECT_CALL( @@ -810,6 +811,7 @@ class QuicConnectionTest : public ::testing::TestWithParam<bool> { QuicGuid guid_; QuicFramer framer_; QuicPacketCreator creator_; + MockEntropyCalculator entropy_calculator_; MockSendAlgorithm* send_algorithm_; TestReceiveAlgorithm* receive_algorithm_; @@ -936,7 +938,11 @@ TEST_P(QuicConnectionTest, RejectPacketTooFarOut) { ProcessDataPacket(6000, 0, !kEntropyFlag); } -TEST_P(QuicConnectionTest, TruncatedAck) { +// TODO(rtenneti): Delete this when QUIC_VERSION_11 is deprecated. +TEST_P(QuicConnectionTest, TruncatedAck11) { + if (QuicVersionMax() > QUIC_VERSION_11) { + return; + } EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(2); EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1); @@ -964,6 +970,38 @@ TEST_P(QuicConnectionTest, TruncatedAck) { EXPECT_FALSE(QuicConnectionPeer::GetReceivedTruncatedAck(&connection_)); } +TEST_P(QuicConnectionTest, TruncatedAck) { + // TODO(rtenneti): Delete this when QUIC_VERSION_11 is deprecated. + if (QuicVersionMax() <= QUIC_VERSION_11) { + return; + } + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(256); + EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(2); + int num_packets = 256 * 2 + 1; + for (int i = 0; i < num_packets; ++i) { + SendStreamDataToPeer(1, "foo", i * 3, !kFin, NULL); + } + + QuicAckFrame frame(num_packets, QuicTime::Zero(), 1); + // Create an ack with 256 nacks, none adjacent to one another. + for (QuicPacketSequenceNumber i = 1; i <= 256; ++i) { + frame.received_info.missing_packets.insert(i * 2); + } + EXPECT_CALL(entropy_calculator_, + EntropyHash(511)).WillOnce(testing::Return(0)); + ProcessAckPacket(&frame); + + EXPECT_TRUE(QuicConnectionPeer::GetReceivedTruncatedAck(&connection_)); + + frame.received_info.missing_packets.erase(192); + + // Removing one missing packet allows us to ack 192 and one more range. + EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(2); + ProcessAckPacket(&frame); + EXPECT_FALSE(QuicConnectionPeer::GetReceivedTruncatedAck(&connection_)); +} + TEST_P(QuicConnectionTest, AckReceiptCausesAckSendBadEntropy) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); @@ -1099,33 +1137,6 @@ TEST_P(QuicConnectionTest, LargestObservedLower) { ProcessAckPacket(&frame2); } -TEST_P(QuicConnectionTest, LeastUnackedGreaterThanPacketSequenceNumber) { - EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, false)); - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); - // Create an ack with least_unacked is 2 in packet number 1. - creator_.set_sequence_number(0); - QuicAckFrame frame(0, QuicTime::Zero(), 2); - EXPECT_CALL(visitor_, OnCanWrite()).Times(0); - ProcessAckPacket(&frame); -} - -TEST_P(QuicConnectionTest, - NackSequenceNumberGreaterThanLargestReceived) { - EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - - SendStreamDataToPeer(1, "foo", 0, !kFin, NULL); - SendStreamDataToPeer(1, "bar", 3, !kFin, NULL); - SendStreamDataToPeer(1, "eep", 6, !kFin, NULL); - - EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, false)); - EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)); - QuicAckFrame frame(0, QuicTime::Zero(), 1); - frame.received_info.missing_packets.insert(3); - EXPECT_CALL(visitor_, OnCanWrite()).Times(0); - ProcessAckPacket(&frame); -} - TEST_P(QuicConnectionTest, AckUnsentData) { // Ack a packet which has not been sent. EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, false)); @@ -2329,6 +2340,12 @@ TEST_P(QuicConnectionTest, InitialTimeout) { connection_.GetTimeoutAlarm()->Fire(); EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); EXPECT_FALSE(connection_.connected()); + + EXPECT_FALSE(connection_.GetAckAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetResumeWritesAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetSendAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); } TEST_P(QuicConnectionTest, TimeoutAfterSend) { @@ -3063,7 +3080,7 @@ TEST_P(QuicConnectionTest, TestFecGroupLimits) { ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 3) == NULL); } -TEST_P(QuicConnectionTest, DontProcessFramesIfPacketClosedConnection) { +TEST_P(QuicConnectionTest, ProcessFramesIfPacketClosedConnection) { // Construct a packet with stream frame and connection close frame. header_.public_header.guid = guid_; header_.packet_sequence_number = 1; @@ -3089,7 +3106,7 @@ TEST_P(QuicConnectionTest, DontProcessFramesIfPacketClosedConnection) { ENCRYPTION_NONE, 1, *packet)); EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, true)); - EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(0); + EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); @@ -3277,7 +3294,7 @@ TEST_P(QuicConnectionTest, AckNotifierCallbackAfterFECRecovery) { ack_header.public_header.version_flag = false; ack_header.entropy_flag = !kEntropyFlag; ack_header.fec_flag = true; - ack_header.packet_sequence_number = 42; + ack_header.packet_sequence_number = 1; ack_header.is_in_fec_group = IN_FEC_GROUP; ack_header.fec_group = 1; diff --git a/net/quic/quic_crypto_client_stream.cc b/net/quic/quic_crypto_client_stream.cc index b0f8117..4451fb6 100644 --- a/net/quic/quic_crypto_client_stream.cc +++ b/net/quic/quic_crypto_client_stream.cc @@ -346,6 +346,8 @@ void QuicCryptoClientStream::DoHandshakeLoop( error, "Server hello invalid: " + error_details); return; } + session()->OnConfigNegotiated(); + CrypterPair* crypters = &crypto_negotiated_params_.forward_secure_crypters; // TODO(agl): we don't currently latch this decrypter because the idea diff --git a/net/quic/quic_crypto_client_stream.h b/net/quic/quic_crypto_client_stream.h index 5a9042b..ca2fea7 100644 --- a/net/quic/quic_crypto_client_stream.h +++ b/net/quic/quic_crypto_client_stream.h @@ -9,8 +9,8 @@ #include "net/cert/cert_verify_result.h" #include "net/cert/x509_certificate.h" -#include "net/quic/crypto/crypto_handshake.h" #include "net/quic/crypto/proof_verifier.h" +#include "net/quic/crypto/quic_crypto_client_config.h" #include "net/quic/quic_config.h" #include "net/quic/quic_crypto_stream.h" diff --git a/net/quic/quic_crypto_server_stream.cc b/net/quic/quic_crypto_server_stream.cc index 63637f5..1234483 100644 --- a/net/quic/quic_crypto_server_stream.cc +++ b/net/quic/quic_crypto_server_stream.cc @@ -7,8 +7,8 @@ #include "base/base64.h" #include "crypto/secure_hash.h" #include "net/quic/crypto/crypto_protocol.h" -#include "net/quic/crypto/crypto_server_config.h" #include "net/quic/crypto/crypto_utils.h" +#include "net/quic/crypto/quic_crypto_server_config.h" #include "net/quic/quic_config.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_session.h" @@ -62,6 +62,7 @@ void QuicCryptoServerStream::OnHandshakeMessage( CloseConnectionWithDetails(error, error_details); return; } + session()->OnConfigNegotiated(); config->ToHandshakeMessage(&reply); diff --git a/net/quic/quic_crypto_server_stream_test.cc b/net/quic/quic_crypto_server_stream_test.cc index d622342..2b15853 100644 --- a/net/quic/quic_crypto_server_stream_test.cc +++ b/net/quic/quic_crypto_server_stream_test.cc @@ -12,8 +12,8 @@ #include "net/quic/crypto/crypto_framer.h" #include "net/quic/crypto/crypto_handshake.h" #include "net/quic/crypto/crypto_protocol.h" -#include "net/quic/crypto/crypto_server_config.h" #include "net/quic/crypto/crypto_utils.h" +#include "net/quic/crypto/quic_crypto_server_config.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" #include "net/quic/crypto/quic_random.h" diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc index 493cf80..4d7c99f 100644 --- a/net/quic/quic_framer.cc +++ b/net/quic/quic_framer.cc @@ -13,6 +13,8 @@ using base::StringPiece; using std::make_pair; using std::map; +using std::max; +using std::min; using std::numeric_limits; using std::string; @@ -20,19 +22,6 @@ namespace net { namespace { -// TODO(jri): Remove uses of QuicFrameTypeOld when -// QUIC versions < 10 are no longer supported. -enum QuicFrameTypeOld { - PADDING_FRAME_OLD = 0, - STREAM_FRAME_OLD, - ACK_FRAME_OLD, - CONGESTION_FEEDBACK_FRAME_OLD, - RST_STREAM_FRAME_OLD, - CONNECTION_CLOSE_FRAME_OLD, - GOAWAY_FRAME_OLD, - NUM_FRAME_TYPES_OLD -}; - // Mask to select the lowest 48 bits of a sequence number. const QuicPacketSequenceNumber k6ByteSequenceNumberMask = GG_UINT64_C(0x0000FFFFFFFFFFFF); @@ -46,6 +35,10 @@ const QuicPacketSequenceNumber k1ByteSequenceNumberMask = const QuicGuid k1ByteGuidMask = GG_UINT64_C(0x00000000000000FF); const QuicGuid k4ByteGuidMask = GG_UINT64_C(0x00000000FFFFFFFF); +// Number of bits the sequence number length bits are shifted from the right +// edge of the public header. +const uint8 kPublicHeaderSequenceNumberShift = 4; + // New Frame Types, QUIC v. >= 10: // There are two interpretations for the Frame Type byte in the QUIC protocol, // resulting in two Frame Types: Special Frame Types and Regular Frame Types. @@ -89,6 +82,15 @@ const uint8 kQuicStreamDataLengthMask = 0x01; const uint8 kQuicStreamFinShift = 1; const uint8 kQuicStreamFinMask = 0x01; +// Sequence number size shift used in AckFrames. +const uint8 kQuicSequenceNumberLengthShift = 2; + +// Acks may be truncated. +const uint8 kQuicAckTruncatedShift = 1; +const uint8 kQuicAckTruncatedMask = 0x01; + +// Acks may not have any nacks. +const uint8 kQuicHasNacksMask = 0x01; const uint32 kInvalidDeltaTime = 0xffffffff; @@ -119,6 +121,7 @@ QuicFramer::QuicFramer(const QuicVersionVector& supported_versions, bool is_server) : visitor_(NULL), fec_builder_(NULL), + entropy_calculator_(NULL), error_(QUIC_NO_ERROR), last_sequence_number_(0), last_serialized_guid_(0), @@ -146,14 +149,28 @@ size_t QuicFramer::GetMinStreamFrameSize(QuicVersion version, } // static -size_t QuicFramer::GetMinAckFrameSize() { +size_t QuicFramer::GetMinAckFrameSizev11() { return kQuicFrameTypeSize + kQuicEntropyHashSize + PACKET_6BYTE_SEQUENCE_NUMBER + kQuicEntropyHashSize + - PACKET_6BYTE_SEQUENCE_NUMBER + kQuicDeltaTimeLargestObservedSize + + PACKET_6BYTE_SEQUENCE_NUMBER + kQuicv11DeltaTimeLargestObservedSize + kNumberOfMissingPacketsSize; } // static +size_t QuicFramer::GetMinAckFrameSize( + QuicVersion version, + QuicSequenceNumberLength sequence_number_length, + QuicSequenceNumberLength largest_observed_length) { + if (version <= QUIC_VERSION_11) { + return GetMinAckFrameSizev11(); + } else { + return kQuicFrameTypeSize + kQuicEntropyHashSize + + sequence_number_length + kQuicEntropyHashSize + + largest_observed_length + kQuicDeltaTimeLargestObservedSize; + } +} + +// static size_t QuicFramer::GetMinRstStreamFrameSize() { return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicErrorCodeSize + kQuicErrorDetailsLengthSize; @@ -161,8 +178,7 @@ size_t QuicFramer::GetMinRstStreamFrameSize() { // static size_t QuicFramer::GetMinConnectionCloseFrameSize() { - return kQuicFrameTypeSize + kQuicErrorCodeSize + kQuicErrorDetailsLengthSize + - GetMinAckFrameSize() - 1; // Don't include the frame type again. + return kQuicFrameTypeSize + kQuicErrorCodeSize + kQuicErrorDetailsLengthSize; } // static @@ -176,9 +192,10 @@ size_t QuicFramer::GetMinGoAwayFrameSize() { // QuicEncrypter::GetMaxPlaintextSize. // 16 is a conservative estimate in the case of AEAD_AES_128_GCM_12, which uses // 12-byte tags. +// TODO(ianswett): Deprecate this with QUIC_VERSION_11. size_t QuicFramer::GetMaxUnackedPackets(QuicPacketHeader header) { - return (kMaxPacketSize - GetPacketHeaderSize(header) - - GetMinAckFrameSize() - 16) / PACKET_6BYTE_SEQUENCE_NUMBER; + return (kDefaultMaxPacketSize - GetPacketHeaderSize(header) - + GetMinAckFrameSizev11() - 16) / PACKET_6BYTE_SEQUENCE_NUMBER; } // static @@ -219,12 +236,15 @@ size_t QuicFramer::GetVersionNegotiationPacketSize(size_t number_versions) { } // static -bool QuicFramer::CanTruncate(const QuicFrame& frame, size_t free_bytes) { +bool QuicFramer::CanTruncate( + QuicVersion version, const QuicFrame& frame, size_t free_bytes) { // TODO(ianswett): GetMinConnectionCloseFrameSize may be incorrect, because // checking for it here results in frames not being added, but the resulting // frames do actually fit. if ((frame.type == ACK_FRAME || frame.type == CONNECTION_CLOSE_FRAME) && - free_bytes >= GetMinAckFrameSize()) { + free_bytes >= GetMinAckFrameSize(version, + PACKET_6BYTE_SEQUENCE_NUMBER, + PACKET_6BYTE_SEQUENCE_NUMBER)) { return true; } return false; @@ -239,22 +259,25 @@ bool QuicFramer::IsSupportedVersion(const QuicVersion version) const { return false; } -size_t QuicFramer::GetSerializedFrameLength(const QuicFrame& frame, - size_t free_bytes, - bool first_frame, - bool last_frame) { +size_t QuicFramer::GetSerializedFrameLength( + const QuicFrame& frame, + size_t free_bytes, + bool first_frame, + bool last_frame, + QuicSequenceNumberLength sequence_number_length) { if (frame.type == PADDING_FRAME) { // PADDING implies end of packet. return free_bytes; } - size_t frame_len = ComputeFrameLength(frame, last_frame); + size_t frame_len = + ComputeFrameLength(frame, last_frame, sequence_number_length); if (frame_len > free_bytes) { // Only truncate the first frame in a packet, so if subsequent ones go // over, stop including more frames. if (!first_frame) { return 0; } - if (CanTruncate(frame, free_bytes)) { + if (CanTruncate(quic_version_, frame, free_bytes)) { // Truncate the frame so the packet will not exceed kMaxPacketSize. // Note that we may not use every byte of the writer in this case. DLOG(INFO) << "Truncating large frame"; @@ -264,6 +287,10 @@ size_t QuicFramer::GetSerializedFrameLength(const QuicFrame& frame, return frame_len; } +QuicFramer::AckFrameInfo::AckFrameInfo() : max_delta(0) { } + +QuicFramer::AckFrameInfo::~AckFrameInfo() { } + QuicPacketEntropyHash QuicFramer::GetPacketEntropyHash( const QuicPacketHeader& header) const { if (!header.entropy_flag) { @@ -274,6 +301,7 @@ QuicPacketEntropyHash QuicFramer::GetPacketEntropyHash( return 1 << (header.packet_sequence_number % 8); } +// Test only. SerializedPacket QuicFramer::BuildUnsizedDataPacket( const QuicPacketHeader& header, const QuicFrames& frames) { @@ -284,7 +312,8 @@ SerializedPacket QuicFramer::BuildUnsizedDataPacket( bool first_frame = i == 0; bool last_frame = i == frames.size() - 1; const size_t frame_size = GetSerializedFrameLength( - frames[i], max_plaintext_size - packet_size, first_frame, last_frame); + frames[i], max_plaintext_size - packet_size, first_frame, last_frame, + header.public_header.sequence_number_length); DCHECK(frame_size); packet_size += frame_size; } @@ -298,7 +327,7 @@ SerializedPacket QuicFramer::BuildDataPacket( QuicDataWriter writer(packet_size); const SerializedPacket kNoPacket( 0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL); - if (!WritePacketHeader(header, &writer)) { + if (!AppendPacketHeader(header, &writer)) { return kNoPacket; } @@ -321,8 +350,15 @@ SerializedPacket QuicFramer::BuildDataPacket( } break; case ACK_FRAME: - if (!AppendAckFramePayload(*frame.ack_frame, &writer)) { - return kNoPacket; + if (quic_version_ <= QUIC_VERSION_11) { + if (!AppendAckFramePayloadV11(*frame.ack_frame, &writer)) { + return kNoPacket; + } + } else { + if (!AppendAckFramePayloadAndTypeByte( + header, *frame.ack_frame, &writer)) { + return kNoPacket; + } } break; case CONGESTION_FEEDBACK_FRAME: @@ -383,7 +419,7 @@ SerializedPacket QuicFramer::BuildFecPacket(const QuicPacketHeader& header, QuicDataWriter writer(len); const SerializedPacket kNoPacket( 0, PACKET_1BYTE_SEQUENCE_NUMBER, NULL, 0, NULL); - if (!WritePacketHeader(header, &writer)) { + if (!AppendPacketHeader(header, &writer)) { return kNoPacket; } @@ -538,7 +574,7 @@ bool QuicFramer::ProcessDataPacket( StringPiece payload = reader_->PeekRemainingPayload(); visitor_->OnFecProtectedPayload(payload); } - if (!ProcessFrameData()) { + if (!ProcessFrameData(header)) { DCHECK_NE(QUIC_NO_ERROR, error_); // ProcessFrameData sets the error. DLOG(WARNING) << "Unable to process frame data."; return false; @@ -589,7 +625,7 @@ bool QuicFramer::ProcessRevivedPacket(QuicPacketHeader* header, } reader_.reset(new QuicDataReader(payload.data(), payload.length())); - if (!ProcessFrameData()) { + if (!ProcessFrameData(*header)) { DCHECK_NE(QUIC_NO_ERROR, error_); // ProcessFrameData sets the error. DLOG(WARNING) << "Unable to process frame data."; return false; @@ -600,8 +636,8 @@ bool QuicFramer::ProcessRevivedPacket(QuicPacketHeader* header, return true; } -bool QuicFramer::WritePacketHeader(const QuicPacketHeader& header, - QuicDataWriter* writer) { +bool QuicFramer::AppendPacketHeader(const QuicPacketHeader& header, + QuicDataWriter* writer) { DCHECK(header.fec_group > 0 || header.is_in_fec_group == NOT_IN_FEC_GROUP); uint8 public_flags = 0; if (header.public_header.reset_flag) { @@ -610,20 +646,10 @@ bool QuicFramer::WritePacketHeader(const QuicPacketHeader& header, if (header.public_header.version_flag) { public_flags |= PACKET_PUBLIC_FLAGS_VERSION; } - switch (header.public_header.sequence_number_length) { - case PACKET_1BYTE_SEQUENCE_NUMBER: - public_flags |= PACKET_PUBLIC_FLAGS_1BYTE_SEQUENCE; - break; - case PACKET_2BYTE_SEQUENCE_NUMBER: - public_flags |= PACKET_PUBLIC_FLAGS_2BYTE_SEQUENCE; - break; - case PACKET_4BYTE_SEQUENCE_NUMBER: - public_flags |= PACKET_PUBLIC_FLAGS_4BYTE_SEQUENCE; - break; - case PACKET_6BYTE_SEQUENCE_NUMBER: - public_flags |= PACKET_PUBLIC_FLAGS_6BYTE_SEQUENCE; - break; - } + + public_flags |= + GetSequenceNumberFlags(header.public_header.sequence_number_length) + << kPublicHeaderSequenceNumberShift; switch (header.public_header.guid_length) { case PACKET_0BYTE_GUID: @@ -636,7 +662,7 @@ bool QuicFramer::WritePacketHeader(const QuicPacketHeader& header, return false; } if (!writer->WriteUInt8(header.public_header.guid & k1ByteGuidMask)) { - return false; + return false; } break; case PACKET_4BYTE_GUID: @@ -789,20 +815,9 @@ bool QuicFramer::ProcessPublicHeader( break; } - switch (public_flags & PACKET_PUBLIC_FLAGS_6BYTE_SEQUENCE) { - case PACKET_PUBLIC_FLAGS_6BYTE_SEQUENCE: - public_header->sequence_number_length = PACKET_6BYTE_SEQUENCE_NUMBER; - break; - case PACKET_PUBLIC_FLAGS_4BYTE_SEQUENCE: - public_header->sequence_number_length = PACKET_4BYTE_SEQUENCE_NUMBER; - break; - case PACKET_PUBLIC_FLAGS_2BYTE_SEQUENCE: - public_header->sequence_number_length = PACKET_2BYTE_SEQUENCE_NUMBER; - break; - case PACKET_PUBLIC_FLAGS_1BYTE_SEQUENCE: - public_header->sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER; - break; - } + public_header->sequence_number_length = + ReadSequenceNumberLength( + public_flags >> kPublicHeaderSequenceNumberShift); // Read the version only if the packet is from the client. // version flag from the server means version negotiation packet. @@ -843,6 +858,91 @@ bool QuicFramer::ReadGuidFromPacket(const QuicEncryptedPacket& packet, return reader.ReadUInt64(guid); } +// static +QuicSequenceNumberLength QuicFramer::ReadSequenceNumberLength(uint8 flags) { + switch (flags & PACKET_FLAGS_6BYTE_SEQUENCE) { + case PACKET_FLAGS_6BYTE_SEQUENCE: + return PACKET_6BYTE_SEQUENCE_NUMBER; + case PACKET_FLAGS_4BYTE_SEQUENCE: + return PACKET_4BYTE_SEQUENCE_NUMBER; + case PACKET_FLAGS_2BYTE_SEQUENCE: + return PACKET_2BYTE_SEQUENCE_NUMBER; + case PACKET_FLAGS_1BYTE_SEQUENCE: + return PACKET_1BYTE_SEQUENCE_NUMBER; + default: + LOG(DFATAL) << "Unreachable case statement."; + return PACKET_6BYTE_SEQUENCE_NUMBER; + } +} + +// static +QuicSequenceNumberLength QuicFramer::GetMinSequenceNumberLength( + QuicPacketSequenceNumber sequence_number) { + if (sequence_number < 1 << (PACKET_1BYTE_SEQUENCE_NUMBER * 8)) { + return PACKET_1BYTE_SEQUENCE_NUMBER; + } else if (sequence_number < 1 << (PACKET_2BYTE_SEQUENCE_NUMBER * 8)) { + return PACKET_2BYTE_SEQUENCE_NUMBER; + } else if (sequence_number < + GG_UINT64_C(1) << (PACKET_4BYTE_SEQUENCE_NUMBER * 8)) { + return PACKET_4BYTE_SEQUENCE_NUMBER; + } else { + return PACKET_6BYTE_SEQUENCE_NUMBER; + } +} + +// static +uint8 QuicFramer::GetSequenceNumberFlags( + QuicSequenceNumberLength sequence_number_length) { + switch (sequence_number_length) { + case PACKET_1BYTE_SEQUENCE_NUMBER: + return PACKET_FLAGS_1BYTE_SEQUENCE; + case PACKET_2BYTE_SEQUENCE_NUMBER: + return PACKET_FLAGS_2BYTE_SEQUENCE; + case PACKET_4BYTE_SEQUENCE_NUMBER: + return PACKET_FLAGS_4BYTE_SEQUENCE; + case PACKET_6BYTE_SEQUENCE_NUMBER: + return PACKET_FLAGS_6BYTE_SEQUENCE; + default: + LOG(DFATAL) << "Unreachable case statement."; + return PACKET_FLAGS_6BYTE_SEQUENCE; + } +} + +// static +QuicFramer::AckFrameInfo QuicFramer::GetAckFrameInfo( + const QuicAckFrame& frame) { + const ReceivedPacketInfo& received_info = frame.received_info; + + AckFrameInfo ack_info; + if (!received_info.missing_packets.empty()) { + DCHECK_GE(frame.received_info.largest_observed, + *frame.received_info.missing_packets.rend()); + size_t cur_range_length = 0; + SequenceNumberSet::const_iterator iter = + received_info.missing_packets.begin(); + QuicPacketSequenceNumber last_missing = *iter; + ++iter; + for (; iter != received_info.missing_packets.end(); ++iter) { + if (cur_range_length != numeric_limits<uint8>::max() && + *iter == (last_missing + 1)) { + ++cur_range_length; + } else { + ack_info.nack_ranges[last_missing - cur_range_length] + = cur_range_length; + cur_range_length = 0; + } + ack_info.max_delta = max(ack_info.max_delta, *iter - last_missing); + last_missing = *iter; + } + // Include the last nack range. + ack_info.nack_ranges[last_missing - cur_range_length] = cur_range_length; + // Include the range to the largest observed. + ack_info.max_delta = max(ack_info.max_delta, + received_info.largest_observed - last_missing); + } + return ack_info; +} + bool QuicFramer::ProcessPacketHeader( QuicPacketHeader* header, const QuicEncryptedPacket& packet) { @@ -915,7 +1015,7 @@ bool QuicFramer::ProcessPacketSequenceNumber( return true; } -bool QuicFramer::ProcessFrameData() { +bool QuicFramer::ProcessFrameData(const QuicPacketHeader& header) { if (reader_->IsDoneReading()) { set_detailed_error("Packet has no frames."); return RaiseError(QUIC_MISSING_PAYLOAD); @@ -945,7 +1045,7 @@ bool QuicFramer::ProcessFrameData() { // Ack Frame if (frame_type & kQuicFrameTypeAckMask) { QuicAckFrame frame; - if (!ProcessAckFrame(&frame)) { + if (!ProcessAckFrame(header, frame_type, &frame)) { return RaiseError(QUIC_INVALID_ACK_DATA); } if (!visitor_->OnAckFrame(frame)) { @@ -1002,10 +1102,12 @@ bool QuicFramer::ProcessFrameData() { return RaiseError(QUIC_INVALID_CONNECTION_CLOSE_DATA); } - if (!visitor_->OnAckFrame(frame.ack_frame)) { - DLOG(INFO) << "Visitor asked to stop further processing."; - // Returning true since there was no parsing error. - return true; + if (version() <= QUIC_VERSION_11) { + if (!visitor_->OnAckFrame(frame.ack_frame)) { + DLOG(INFO) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } } if (!visitor_->OnConnectionCloseFrame(frame)) { @@ -1090,17 +1192,25 @@ bool QuicFramer::ProcessStreamFrame(uint8 frame_type, return true; } -bool QuicFramer::ProcessAckFrame(QuicAckFrame* frame) { - if (!ProcessSentInfo(&frame->sent_info)) { +bool QuicFramer::ProcessAckFrame(const QuicPacketHeader& header, + uint8 frame_type, + QuicAckFrame* frame) { + if (!ProcessSentInfo(header, &frame->sent_info)) { return false; } - if (!ProcessReceivedInfo(&frame->received_info)) { - return false; + if (quic_version_ <= QUIC_VERSION_11) { + if (!ProcessReceivedInfoV11(&frame->received_info)) { + return false; + } + } else { + if (!ProcessReceivedInfo(frame_type, &frame->received_info)) { + return false; + } } return true; } -bool QuicFramer::ProcessReceivedInfo(ReceivedPacketInfo* received_info) { +bool QuicFramer::ProcessReceivedInfoV11(ReceivedPacketInfo* received_info) { if (!reader_->ReadBytes(&received_info->entropy_hash, 1)) { set_detailed_error("Unable to read entropy hash for received packets."); return false; @@ -1144,16 +1254,103 @@ bool QuicFramer::ProcessReceivedInfo(ReceivedPacketInfo* received_info) { return true; } -bool QuicFramer::ProcessSentInfo(SentPacketInfo* sent_info) { +bool QuicFramer::ProcessReceivedInfo(uint8 frame_type, + ReceivedPacketInfo* received_info) { + // Determine the three lengths from the frame type: largest observed length, + // missing sequence number length, and missing range length. + const QuicSequenceNumberLength missing_sequence_number_length = + ReadSequenceNumberLength(frame_type); + frame_type >>= kQuicSequenceNumberLengthShift; + const QuicSequenceNumberLength largest_observed_sequence_number_length = + ReadSequenceNumberLength(frame_type); + frame_type >>= kQuicSequenceNumberLengthShift; + received_info->is_truncated = frame_type & kQuicAckTruncatedMask; + frame_type >>= kQuicAckTruncatedShift; + bool has_nacks = frame_type & kQuicHasNacksMask; + + if (!reader_->ReadBytes(&received_info->entropy_hash, 1)) { + set_detailed_error("Unable to read entropy hash for received packets."); + return false; + } + + if (!reader_->ReadBytes(&received_info->largest_observed, + largest_observed_sequence_number_length)) { + set_detailed_error("Unable to read largest observed."); + return false; + } + + uint64 delta_time_largest_observed_us; + if (!reader_->ReadUFloat16(&delta_time_largest_observed_us)) { + set_detailed_error("Unable to read delta time largest observed."); + return false; + } + + if (delta_time_largest_observed_us == kUFloat16MaxValue) { + received_info->delta_time_largest_observed = QuicTime::Delta::Infinite(); + } else { + received_info->delta_time_largest_observed = + QuicTime::Delta::FromMicroseconds(delta_time_largest_observed_us); + } + + if (!has_nacks) { + return true; + } + + uint8 num_missing_ranges; + if (!reader_->ReadBytes(&num_missing_ranges, 1)) { + set_detailed_error("Unable to read num missing packet ranges."); + return false; + } + + QuicPacketSequenceNumber last_sequence_number = + received_info->largest_observed; + for (size_t i = 0; i < num_missing_ranges; ++i) { + QuicPacketSequenceNumber missing_delta = 0; + if (!reader_->ReadBytes(&missing_delta, missing_sequence_number_length)) { + set_detailed_error("Unable to read missing sequence number delta."); + return false; + } + last_sequence_number -= missing_delta; + QuicPacketSequenceNumber range_length = 0; + if (!reader_->ReadBytes(&range_length, PACKET_1BYTE_SEQUENCE_NUMBER)) { + set_detailed_error("Unable to read missing sequence number range."); + return false; + } + for (size_t i = 0; i <= range_length; ++i) { + received_info->missing_packets.insert(last_sequence_number - i); + } + // Subtract an extra 1 to ensure ranges are represented efficiently and + // can't overlap by 1 sequence number. This allows a missing_delta of 0 + // to represent an adjacent nack range. + last_sequence_number -= (range_length + 1); + } + + return true; +} + +bool QuicFramer::ProcessSentInfo(const QuicPacketHeader& header, + SentPacketInfo* sent_info) { if (!reader_->ReadBytes(&sent_info->entropy_hash, 1)) { set_detailed_error("Unable to read entropy hash for sent packets."); return false; } - if (!ProcessPacketSequenceNumber(PACKET_6BYTE_SEQUENCE_NUMBER, - &sent_info->least_unacked)) { - set_detailed_error("Unable to read least unacked."); - return false; + if (quic_version_ <= QUIC_VERSION_11) { + if (!ProcessPacketSequenceNumber(PACKET_6BYTE_SEQUENCE_NUMBER, + &sent_info->least_unacked)) { + set_detailed_error("Unable to read least unacked."); + return false; + } + } else { + QuicPacketSequenceNumber least_unacked_delta = 0; + if (!reader_->ReadBytes(&least_unacked_delta, + header.public_header.sequence_number_length)) { + set_detailed_error("Unable to read least unacked delta."); + return false; + } + DCHECK_GE(header.packet_sequence_number, least_unacked_delta); + sent_info->least_unacked = + header.packet_sequence_number - least_unacked_delta; } return true; @@ -1204,7 +1401,7 @@ bool QuicFramer::ProcessQuicCongestionFeedbackFrame( inter_arrival->received_packet_times.insert( make_pair(smallest_received, time_received)); - for (int i = 0; i < num_received_packets - 1; ++i) { + for (uint8 i = 0; i < num_received_packets - 1; ++i) { uint16 sequence_delta; if (!reader_->ReadUInt16(&sequence_delta)) { set_detailed_error( @@ -1242,6 +1439,7 @@ bool QuicFramer::ProcessQuicCongestionFeedbackFrame( "Unable to read accumulated number of lost packets."); return false; } + // TODO(ianswett): Remove receive window, since it's constant. uint16 receive_window = 0; if (!reader_->ReadUInt16(&receive_window)) { set_detailed_error("Unable to read receive window."); @@ -1313,9 +1511,11 @@ bool QuicFramer::ProcessConnectionCloseFrame(QuicConnectionCloseFrame* frame) { } frame->error_details = error_details.as_string(); - if (!ProcessAckFrame(&frame->ack_frame)) { - DLOG(WARNING) << "Unable to process ack frame."; - return false; + if (quic_version_ <= QUIC_VERSION_11) { + if (!ProcessAckFrame(QuicPacketHeader(), 0, &frame->ack_frame)) { + DLOG(WARNING) << "Unable to process ack frame."; + return false; + } } return true; @@ -1494,8 +1694,32 @@ bool QuicFramer::DecryptPayload(const QuicPacketHeader& header, return true; } -size_t QuicFramer::ComputeFrameLength(const QuicFrame& frame, - bool last_frame_in_packet) { +size_t QuicFramer::GetAckFrameSize( + const QuicAckFrame& ack, + QuicSequenceNumberLength sequence_number_length) { + if (quic_version_ <= QUIC_VERSION_11) { + return GetMinAckFrameSizev11() + PACKET_6BYTE_SEQUENCE_NUMBER * + ack.received_info.missing_packets.size(); + } else { + AckFrameInfo ack_info = GetAckFrameInfo(ack); + QuicSequenceNumberLength largest_observed_length = + GetMinSequenceNumberLength(ack.received_info.largest_observed); + QuicSequenceNumberLength missing_sequence_number_length = + GetMinSequenceNumberLength(ack_info.max_delta); + + return GetMinAckFrameSize(quic_version_, + sequence_number_length, + largest_observed_length) + + (ack_info.nack_ranges.empty() ? 0 : kNumberOfMissingPacketsSize) + + ack_info.nack_ranges.size() * + (missing_sequence_number_length + PACKET_1BYTE_SEQUENCE_NUMBER); + } +} + +size_t QuicFramer::ComputeFrameLength( + const QuicFrame& frame, + bool last_frame_in_packet, + QuicSequenceNumberLength sequence_number_length) { switch (frame.type) { case STREAM_FRAME: return GetMinStreamFrameSize(quic_version_, @@ -1504,9 +1728,7 @@ size_t QuicFramer::ComputeFrameLength(const QuicFrame& frame, last_frame_in_packet) + frame.stream_frame->data.size(); case ACK_FRAME: { - const QuicAckFrame& ack = *frame.ack_frame; - return GetMinAckFrameSize() + PACKET_6BYTE_SEQUENCE_NUMBER * - ack.received_info.missing_packets.size(); + return GetAckFrameSize(*frame.ack_frame, sequence_number_length); } case CONGESTION_FEEDBACK_FRAME: { size_t len = kQuicFrameTypeSize; @@ -1546,11 +1768,17 @@ size_t QuicFramer::ComputeFrameLength(const QuicFrame& frame, return GetMinRstStreamFrameSize() + frame.rst_stream_frame->error_details.size(); case CONNECTION_CLOSE_FRAME: { - const QuicAckFrame& ack = frame.connection_close_frame->ack_frame; - return GetMinConnectionCloseFrameSize() + - frame.connection_close_frame->error_details.size() + - PACKET_6BYTE_SEQUENCE_NUMBER * - ack.received_info.missing_packets.size(); + if (quic_version_ <= QUIC_VERSION_11) { + const QuicAckFrame& ack = frame.connection_close_frame->ack_frame; + // Don't include the frame type again. + return GetMinConnectionCloseFrameSize() + GetMinAckFrameSizev11() - 1 + + frame.connection_close_frame->error_details.size() + + PACKET_6BYTE_SEQUENCE_NUMBER * + ack.received_info.missing_packets.size(); + } else { + return GetMinConnectionCloseFrameSize() + + frame.connection_close_frame->error_details.size(); + } } case GOAWAY_FRAME: return GetMinGoAwayFrameSize() + frame.goaway_frame->reason_phrase.size(); @@ -1597,7 +1825,10 @@ bool QuicFramer::AppendTypeByte(const QuicFrame& frame, break; } case ACK_FRAME: { - // TODO(ianswett): Use extra 5 bits in the ack framing. + // Quic Version 12 and above append the type byte later. + if (quic_version_ >= QUIC_VERSION_12) { + return true; + } type_byte = kQuicFrameTypeAckMask; break; } @@ -1701,8 +1932,7 @@ void QuicFramer::set_version(const QuicVersion version) { quic_version_ = version; } -// TODO(ianswett): Use varints or another more compact approach for all deltas. -bool QuicFramer::AppendAckFramePayload( +bool QuicFramer::AppendAckFramePayloadV11( const QuicAckFrame& frame, QuicDataWriter* writer) { // TODO(satyamshekhar): Decide how often we really should send this @@ -1775,6 +2005,139 @@ bool QuicFramer::AppendAckFramePayload( return true; } +bool QuicFramer::AppendAckFramePayloadAndTypeByte( + const QuicPacketHeader& header, + const QuicAckFrame& frame, + QuicDataWriter* writer) { + AckFrameInfo ack_info = GetAckFrameInfo(frame); + QuicPacketSequenceNumber ack_largest_observed = + frame.received_info.largest_observed; + QuicSequenceNumberLength largest_observed_length = + GetMinSequenceNumberLength(ack_largest_observed); + QuicSequenceNumberLength missing_sequence_number_length = + GetMinSequenceNumberLength(ack_info.max_delta); + // Determine whether we need to truncate ranges. + size_t available_range_bytes = writer->capacity() - writer->length() - + GetMinAckFrameSize(quic_version_, + header.public_header.sequence_number_length, + largest_observed_length); + size_t max_num_ranges = available_range_bytes / + (missing_sequence_number_length + PACKET_1BYTE_SEQUENCE_NUMBER); + max_num_ranges = + min(static_cast<size_t>(numeric_limits<uint8>::max()), max_num_ranges); + bool truncated = ack_info.nack_ranges.size() > max_num_ranges; + DLOG_IF(INFO, truncated) << "Truncating ack from " + << ack_info.nack_ranges.size() << " ranges to " + << max_num_ranges; + + // Write out the type byte by setting the low order bits and doing shifts + // to make room for the next bit flags to be set. + // Whether there are any nacks. + uint8 type_byte = ack_info.nack_ranges.empty() ? 0 : kQuicHasNacksMask; + + // truncating bit. + type_byte <<= kQuicAckTruncatedShift; + type_byte |= truncated ? kQuicAckTruncatedMask : 0; + + // Largest observed sequence number length. + type_byte <<= kQuicSequenceNumberLengthShift; + type_byte |= GetSequenceNumberFlags(largest_observed_length); + + // Missing sequence number length. + type_byte <<= kQuicSequenceNumberLengthShift; + type_byte |= GetSequenceNumberFlags(missing_sequence_number_length); + + type_byte |= kQuicFrameTypeAckMask; + + if (!writer->WriteUInt8(type_byte)) { + return false; + } + + // TODO(satyamshekhar): Decide how often we really should send this + // entropy_hash update. + if (!writer->WriteUInt8(frame.sent_info.entropy_hash)) { + return false; + } + + DCHECK_GE(header.packet_sequence_number, frame.sent_info.least_unacked); + const QuicPacketSequenceNumber least_unacked_delta = + header.packet_sequence_number - frame.sent_info.least_unacked; + if (!AppendPacketSequenceNumber(header.public_header.sequence_number_length, + least_unacked_delta, writer)) { + return false; + } + + const ReceivedPacketInfo& received_info = frame.received_info; + QuicPacketEntropyHash ack_entropy_hash = received_info.entropy_hash; + NackRangeMap::reverse_iterator ack_iter = ack_info.nack_ranges.rbegin(); + if (truncated) { + // Skip the nack ranges which the truncated ack won't include and set + // a correct largest observed for the truncated ack. + for (size_t i = 1; i < (ack_info.nack_ranges.size() - max_num_ranges); + ++i) { + ++ack_iter; + } + // If the last range is followed by acks, include them. + // If the last range is followed by another range, specify the end of the + // range as the largest_observed. + ack_largest_observed = ack_iter->first - 1; + // Also update the entropy so it matches the largest observed. + ack_entropy_hash = entropy_calculator_->EntropyHash(ack_largest_observed); + ++ack_iter; + } + + if (!writer->WriteUInt8(ack_entropy_hash)) { + return false; + } + + if (!AppendPacketSequenceNumber(largest_observed_length, + ack_largest_observed, writer)) { + return false; + } + + uint64 delta_time_largest_observed_us = kUFloat16MaxValue; + if (!received_info.delta_time_largest_observed.IsInfinite()) { + delta_time_largest_observed_us = + received_info.delta_time_largest_observed.ToMicroseconds(); + } + + if (!writer->WriteUFloat16(delta_time_largest_observed_us)) { + return false; + } + + if (ack_info.nack_ranges.empty()) { + return true; + } + + const uint8 num_missing_ranges = + min(ack_info.nack_ranges.size(), max_num_ranges); + if (!writer->WriteBytes(&num_missing_ranges, 1)) { + return false; + } + + int num_ranges_written = 0; + QuicPacketSequenceNumber last_sequence_written = ack_largest_observed; + for (; ack_iter != ack_info.nack_ranges.rend(); ++ack_iter) { + // Calculate the delta to the last number in the range. + QuicPacketSequenceNumber missing_delta = + last_sequence_written - (ack_iter->first + ack_iter->second); + if (!AppendPacketSequenceNumber(missing_sequence_number_length, + missing_delta, writer)) { + return false; + } + if (!AppendPacketSequenceNumber(PACKET_1BYTE_SEQUENCE_NUMBER, + ack_iter->second, writer)) { + return false; + } + // Subtract 1 so a missing_delta of 0 means an adjacent range. + last_sequence_written = ack_iter->first - 1; + ++num_ranges_written; + } + + DCHECK_EQ(num_missing_ranges, num_ranges_written); + return true; +} + bool QuicFramer::AppendQuicCongestionFeedbackFramePayload( const QuicCongestionFeedbackFrame& frame, QuicDataWriter* writer) { @@ -1893,7 +2256,9 @@ bool QuicFramer::AppendConnectionCloseFramePayload( if (!writer->WriteStringPiece16(frame.error_details)) { return false; } - AppendAckFramePayload(frame.ack_frame, writer); + if (quic_version_ <= QUIC_VERSION_11) { + return AppendAckFramePayloadV11(frame.ack_frame, writer); + } return true; } diff --git a/net/quic/quic_framer.h b/net/quic/quic_framer.h index 5d55138..5b07091 100644 --- a/net/quic/quic_framer.h +++ b/net/quic/quic_framer.h @@ -44,7 +44,9 @@ const size_t kQuicStreamPayloadLengthSize = 2; const size_t kQuicEntropyHashSize = 1; // Size in bytes reserved for the delta time of the largest observed // sequence number in ack frames. -const size_t kQuicDeltaTimeLargestObservedSize = 4; +// TODO(ianswett): Remove this once QUIC_VERSION_11 is removed. +const size_t kQuicv11DeltaTimeLargestObservedSize = 4; +const size_t kQuicDeltaTimeLargestObservedSize = 2; // Size in bytes reserved for the number of missing packets in ack frames. const size_t kNumberOfMissingPacketsSize = 1; @@ -233,7 +235,12 @@ class NET_EXPORT_PRIVATE QuicFramer { QuicStreamOffset offset, bool last_frame_in_packet); // Size in bytes of all ack frame fields without the missing packets. - static size_t GetMinAckFrameSize(); + // TODO(ianswett): Remove this once QUIC_VERSION_11 is removed. + static size_t GetMinAckFrameSizev11(); + static size_t GetMinAckFrameSize( + QuicVersion version, + QuicSequenceNumberLength sequence_number_length, + QuicSequenceNumberLength largest_observed_length); // Size in bytes of all reset stream frame without the error details. static size_t GetMinRstStreamFrameSize(); // Size in bytes of all connection close frame fields without the error @@ -242,7 +249,7 @@ class NET_EXPORT_PRIVATE QuicFramer { // Size in bytes of all GoAway frame fields without the reason phrase. static size_t GetMinGoAwayFrameSize(); // The maximum number of nacks which can be transmitted in a single ack packet - // without exceeding kMaxPacketSize. + // without exceeding kDefaultMaxPacketSize. static size_t GetMaxUnackedPackets(QuicPacketHeader header); // Size in bytes required to serialize the stream id. static size_t GetStreamIdSize(QuicStreamId stream_id); @@ -252,15 +259,18 @@ class NET_EXPORT_PRIVATE QuicFramer { static size_t GetVersionNegotiationPacketSize(size_t number_versions); - static bool CanTruncate(const QuicFrame& frame, size_t free_bytes); + static bool CanTruncate( + QuicVersion version, const QuicFrame& frame, size_t free_bytes); // Returns the number of bytes added to the packet for the specified frame, // and 0 if the frame doesn't fit. Includes the header size for the first // frame. - size_t GetSerializedFrameLength(const QuicFrame& frame, - size_t free_bytes, - bool first_frame, - bool last_frame); + size_t GetSerializedFrameLength( + const QuicFrame& frame, + size_t free_bytes, + bool first_frame, + bool last_frame, + QuicSequenceNumberLength sequence_number_length); // Returns the associated data from the encrypted packet |encrypted| as a // stringpiece. @@ -341,9 +351,28 @@ class NET_EXPORT_PRIVATE QuicFramer { static bool ReadGuidFromPacket(const QuicEncryptedPacket& packet, QuicGuid* guid); + static QuicSequenceNumberLength ReadSequenceNumberLength(uint8 flags); + + // The minimum sequence number length required to represent |sequence_number|. + static QuicSequenceNumberLength GetMinSequenceNumberLength( + QuicPacketSequenceNumber sequence_number); + private: friend class test::QuicFramerPeer; + typedef std::map<QuicPacketSequenceNumber, uint8> NackRangeMap; + + class AckFrameInfo { + public: + AckFrameInfo(); + virtual ~AckFrameInfo(); + + // The maximum delta between ranges. + QuicPacketSequenceNumber max_delta; + // Nack ranges starting with start sequence numbers and lengths. + NackRangeMap nack_ranges; + }; + QuicPacketEntropyHash GetPacketEntropyHash( const QuicPacketHeader& header) const; @@ -354,9 +383,6 @@ class NET_EXPORT_PRIVATE QuicFramer { bool ProcessVersionNegotiationPacket(QuicPacketPublicHeader* public_header); - bool WritePacketHeader(const QuicPacketHeader& header, - QuicDataWriter* writer); - bool ProcessPublicHeader(QuicPacketPublicHeader* header); bool ProcessPacketHeader(QuicPacketHeader* header, @@ -365,11 +391,16 @@ class NET_EXPORT_PRIVATE QuicFramer { bool ProcessPacketSequenceNumber( QuicSequenceNumberLength sequence_number_length, QuicPacketSequenceNumber* sequence_number); - bool ProcessFrameData(); + bool ProcessFrameData(const QuicPacketHeader& header); bool ProcessStreamFrame(uint8 frame_type, QuicStreamFrame* frame); - bool ProcessAckFrame(QuicAckFrame* frame); - bool ProcessReceivedInfo(ReceivedPacketInfo* received_info); - bool ProcessSentInfo(SentPacketInfo* sent_info); + bool ProcessAckFrame(const QuicPacketHeader& header, + uint8 frame_type, + QuicAckFrame* frame); + // TODO(ianswett): Remove this once QUIC_VERSION_11 is removed. + bool ProcessReceivedInfoV11(ReceivedPacketInfo* received_info); + bool ProcessReceivedInfo(uint8 frame_type, ReceivedPacketInfo* received_info); + bool ProcessSentInfo(const QuicPacketHeader& public_header, + SentPacketInfo* sent_info); bool ProcessQuicCongestionFeedbackFrame( QuicCongestionFeedbackFrame* congestion_feedback); bool ProcessRstStreamFrame(QuicRstStreamFrame* frame); @@ -385,22 +416,39 @@ class NET_EXPORT_PRIVATE QuicFramer { QuicSequenceNumberLength sequence_number_length, QuicPacketSequenceNumber packet_sequence_number) const; + // Computes the wire size in bytes of the |ack| frame, assuming no truncation. + size_t GetAckFrameSize(const QuicAckFrame& ack, + QuicSequenceNumberLength sequence_number_length); + // Computes the wire size in bytes of the payload of |frame|. - size_t ComputeFrameLength(const QuicFrame& frame, bool last_frame_in_packet); + size_t ComputeFrameLength(const QuicFrame& frame, + bool last_frame_in_packet, + QuicSequenceNumberLength sequence_number_length); static bool AppendPacketSequenceNumber( QuicSequenceNumberLength sequence_number_length, QuicPacketSequenceNumber packet_sequence_number, QuicDataWriter* writer); + static uint8 GetSequenceNumberFlags( + QuicSequenceNumberLength sequence_number_length); + + static AckFrameInfo GetAckFrameInfo(const QuicAckFrame& frame); + + bool AppendPacketHeader(const QuicPacketHeader& header, + QuicDataWriter* writer); bool AppendTypeByte(const QuicFrame& frame, bool last_frame_in_packet, QuicDataWriter* writer); bool AppendStreamFramePayload(const QuicStreamFrame& frame, bool last_frame_in_packet, QuicDataWriter* builder); - bool AppendAckFramePayload(const QuicAckFrame& frame, - QuicDataWriter* builder); + bool AppendAckFramePayloadAndTypeByte(const QuicPacketHeader& header, + const QuicAckFrame& frame, + QuicDataWriter* builder); + // TODO(ianswett): Remove this once QUIC_VERSION_11 is removed. + bool AppendAckFramePayloadV11(const QuicAckFrame& frame, + QuicDataWriter* builder); bool AppendQuicCongestionFeedbackFramePayload( const QuicCongestionFeedbackFrame& frame, QuicDataWriter* builder); diff --git a/net/quic/quic_framer_test.cc b/net/quic/quic_framer_test.cc index 31c7c45..c599f66 100644 --- a/net/quic/quic_framer_test.cc +++ b/net/quic/quic_framer_test.cc @@ -1589,7 +1589,10 @@ TEST_P(QuicFramerTest, StreamFrameInFecGroup) { EXPECT_EQ("hello world!", visitor_.stream_frames_[0]->data); } -TEST_P(QuicFramerTest, AckFrame) { +TEST_P(QuicFramerTest, AckFramev11) { + if (GetParam() > QUIC_VERSION_11) { + return; + } unsigned char packet[] = { // public flags (8 byte guid) 0x3C, @@ -1651,13 +1654,13 @@ TEST_P(QuicFramerTest, AckFrame) { const size_t kMissingDeltaTimeOffset = kLargestObservedOffset + PACKET_6BYTE_SEQUENCE_NUMBER; const size_t kNumMissingPacketOffset = kMissingDeltaTimeOffset + - kQuicDeltaTimeLargestObservedSize; + kQuicv11DeltaTimeLargestObservedSize; const size_t kMissingPacketsOffset = kNumMissingPacketOffset + kNumberOfMissingPacketsSize; // Now test framing boundaries const size_t missing_packets_size = 1 * PACKET_6BYTE_SEQUENCE_NUMBER; for (size_t i = kQuicFrameTypeSize; - i < QuicFramer::GetMinAckFrameSize() + missing_packets_size; ++i) { + i < QuicFramer::GetMinAckFrameSizev11() + missing_packets_size; ++i) { string expected_error; if (i < kLeastUnackedOffset) { expected_error = "Unable to read entropy hash for sent packets."; @@ -1682,6 +1685,249 @@ TEST_P(QuicFramerTest, AckFrame) { } } +TEST_P(QuicFramerTest, AckFrame) { + if (GetParam() <= QUIC_VERSION_11) { + return; + } + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xA8, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags (entropy) + 0x01, + + // frame type (ack frame) + // (has nacks, not truncated, 6 byte largest observed, 1 byte delta) + 0x6C, + // entropy hash of sent packets till least awaiting - 1. + 0xAB, + // least packet sequence number awaiting an ack, delta from sequence number. + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, + // entropy hash of all received packets. + 0xBA, + // largest observed packet sequence number + 0xBF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // Zero delta time. + 0x0, 0x0, + // num missing packets + 0x01, + // missing packet delta + 0x01, + // 0 more missing packets in range. + 0x00, + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + ASSERT_EQ(1u, visitor_.ack_frames_.size()); + const QuicAckFrame& frame = *visitor_.ack_frames_[0]; + EXPECT_EQ(0xAB, frame.sent_info.entropy_hash); + EXPECT_EQ(0xBA, frame.received_info.entropy_hash); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.received_info.largest_observed); + ASSERT_EQ(1u, frame.received_info.missing_packets.size()); + SequenceNumberSet::const_iterator missing_iter = + frame.received_info.missing_packets.begin(); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter); + EXPECT_EQ(GG_UINT64_C(0x0123456789AA0), frame.sent_info.least_unacked); + + const size_t kSentEntropyOffset = kQuicFrameTypeSize; + const size_t kLeastUnackedOffset = kSentEntropyOffset + kQuicEntropyHashSize; + const size_t kReceivedEntropyOffset = kLeastUnackedOffset + + PACKET_6BYTE_SEQUENCE_NUMBER; + const size_t kLargestObservedOffset = kReceivedEntropyOffset + + kQuicEntropyHashSize; + const size_t kMissingDeltaTimeOffset = kLargestObservedOffset + + PACKET_6BYTE_SEQUENCE_NUMBER; + const size_t kNumMissingPacketOffset = kMissingDeltaTimeOffset + + kQuicDeltaTimeLargestObservedSize; + const size_t kMissingPacketsOffset = kNumMissingPacketOffset + + kNumberOfMissingPacketsSize; + const size_t kMissingPacketsRange = kMissingPacketsOffset + + PACKET_1BYTE_SEQUENCE_NUMBER; + // Now test framing boundaries + const size_t ack_frame_size = kMissingPacketsRange + + PACKET_1BYTE_SEQUENCE_NUMBER; + for (size_t i = kQuicFrameTypeSize; i < ack_frame_size; ++i) { + string expected_error; + if (i < kLeastUnackedOffset) { + expected_error = "Unable to read entropy hash for sent packets."; + } else if (i < kReceivedEntropyOffset) { + expected_error = "Unable to read least unacked delta."; + } else if (i < kLargestObservedOffset) { + expected_error = "Unable to read entropy hash for received packets."; + } else if (i < kMissingDeltaTimeOffset) { + expected_error = "Unable to read largest observed."; + } else if (i < kNumMissingPacketOffset) { + expected_error = "Unable to read delta time largest observed."; + } else if (i < kMissingPacketsOffset) { + expected_error = "Unable to read num missing packet ranges."; + } else if (i < kMissingPacketsRange) { + expected_error = "Unable to read missing sequence number delta."; + } else { + expected_error = "Unable to read missing sequence number range."; + } + CheckProcessingFails( + packet, + i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_ACK_DATA); + } +} + +TEST_P(QuicFramerTest, AckFrameNoNacks) { + if (GetParam() <= QUIC_VERSION_11) { + return; + } + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xA8, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags (entropy) + 0x01, + + // frame type (ack frame) + // (no nacks, not truncated, 6 byte largest observed, 1 byte delta) + 0x4C, + // entropy hash of sent packets till least awaiting - 1. + 0xAB, + // least packet sequence number awaiting an ack, delta from sequence number. + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, + // entropy hash of all received packets. + 0xBA, + // largest observed packet sequence number + 0xBF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // Zero delta time. + 0x0, 0x0, + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + ASSERT_EQ(1u, visitor_.ack_frames_.size()); + QuicAckFrame* frame = visitor_.ack_frames_[0]; + EXPECT_EQ(0xAB, frame->sent_info.entropy_hash); + EXPECT_EQ(0xBA, frame->received_info.entropy_hash); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), + frame->received_info.largest_observed); + ASSERT_EQ(0u, frame->received_info.missing_packets.size()); + EXPECT_EQ(GG_UINT64_C(0x0123456789AA0), frame->sent_info.least_unacked); + + // Verify that the packet re-serializes identically. + QuicFrames frames; + frames.push_back(QuicFrame(frame)); + scoped_ptr<QuicPacket> data( + framer_.BuildUnsizedDataPacket(*visitor_.header_, frames).packet); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + +TEST_P(QuicFramerTest, AckFrame500Nacks) { + if (GetParam() <= QUIC_VERSION_11) { + return; + } + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xA8, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags (entropy) + 0x01, + + // frame type (ack frame) + // (has nacks, not truncated, 6 byte largest observed, 1 byte delta) + 0x6C, + // entropy hash of sent packets till least awaiting - 1. + 0xAB, + // least packet sequence number awaiting an ack, delta from sequence number. + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, + // entropy hash of all received packets. + 0xBA, + // largest observed packet sequence number + 0xBF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // Zero delta time. + 0x0, 0x0, + // num missing packet ranges + 0x02, + // missing packet delta + 0x01, + // 243 more missing packets in range. + // The ranges are listed in this order so the re-constructed packet matches. + 0xF3, + // No gap between ranges + 0x00, + // 255 more missing packets in range. + 0xFF, + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + ASSERT_EQ(1u, visitor_.ack_frames_.size()); + QuicAckFrame* frame = visitor_.ack_frames_[0]; + EXPECT_EQ(0xAB, frame->sent_info.entropy_hash); + EXPECT_EQ(0xBA, frame->received_info.entropy_hash); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), + frame->received_info.largest_observed); + ASSERT_EQ(500u, frame->received_info.missing_packets.size()); + SequenceNumberSet::const_iterator first_missing_iter = + frame->received_info.missing_packets.begin(); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABE) - 499, *first_missing_iter); + SequenceNumberSet::const_reverse_iterator last_missing_iter = + frame->received_info.missing_packets.rbegin(); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *last_missing_iter); + EXPECT_EQ(GG_UINT64_C(0x0123456789AA0), frame->sent_info.least_unacked); + + // Verify that the packet re-serializes identically. + QuicFrames frames; + frames.push_back(QuicFrame(frame)); + scoped_ptr<QuicPacket> data( + framer_.BuildUnsizedDataPacket(*visitor_.header_, frames).packet); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + TEST_P(QuicFramerTest, CongestionFeedbackFrameTCP) { unsigned char packet[] = { // public flags (8 byte guid) @@ -1971,7 +2217,10 @@ TEST_P(QuicFramerTest, RstStreamFrame) { } } -TEST_P(QuicFramerTest, ConnectionCloseFrame) { +TEST_P(QuicFramerTest, ConnectionCloseFramev11) { + if (GetParam() > QUIC_VERSION_11) { + return; + } unsigned char packet[] = { // public flags (8 byte guid) 0x3C, @@ -2042,8 +2291,68 @@ TEST_P(QuicFramerTest, ConnectionCloseFrame) { // Now test framing boundaries for (size_t i = kQuicFrameTypeSize; - i < QuicFramer::GetMinConnectionCloseFrameSize() - - QuicFramer::GetMinAckFrameSize(); ++i) { + i < QuicFramer::GetMinConnectionCloseFrameSize(); ++i) { + string expected_error; + if (i < kQuicFrameTypeSize + kQuicErrorCodeSize) { + expected_error = "Unable to read connection close error code."; + } else { + expected_error = "Unable to read connection close error details."; + } + CheckProcessingFails( + packet, + i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_CONNECTION_CLOSE_DATA); + } +} + +TEST_P(QuicFramerTest, ConnectionCloseFrame) { + if (GetParam() <= QUIC_VERSION_11) { + return; + } + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags + 0x00, + + // frame type (connection close frame) + 0x02, + // error code + 0x11, 0x00, 0x00, 0x00, + + // error details length + 0x0d, 0x00, + // error details + 'b', 'e', 'c', 'a', + 'u', 's', 'e', ' ', + 'I', ' ', 'c', 'a', + 'n', + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); + + EXPECT_EQ(0u, visitor_.stream_frames_.size()); + + EXPECT_EQ(0x11, visitor_.connection_close_frame_.error_code); + EXPECT_EQ("because I can", visitor_.connection_close_frame_.error_details); + + ASSERT_EQ(0u, visitor_.ack_frames_.size()); + + // Now test framing boundaries + for (size_t i = kQuicFrameTypeSize; + i < QuicFramer::GetMinConnectionCloseFrameSize(); ++i) { string expected_error; if (i < kQuicFrameTypeSize + kQuicErrorCodeSize) { expected_error = "Unable to read connection close error code."; @@ -2571,7 +2880,10 @@ TEST_P(QuicFramerTest, BuildVersionNegotiationPacket) { AsChars(packet), arraysize(packet)); } -TEST_P(QuicFramerTest, BuildAckFramePacket) { +TEST_P(QuicFramerTest, BuildAckFramePacketv11) { + if (GetParam() > QUIC_VERSION_11) { + return; + } QuicPacketHeader header; header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; @@ -2635,6 +2947,75 @@ TEST_P(QuicFramerTest, BuildAckFramePacket) { AsChars(packet), arraysize(packet)); } +TEST_P(QuicFramerTest, BuildAckFramePacket) { + if (GetParam() <= QUIC_VERSION_11) { + return; + } + QuicPacketHeader header; + header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.reset_flag = false; + header.public_header.version_flag = false; + header.fec_flag = false; + header.entropy_flag = true; + header.packet_sequence_number = GG_UINT64_C(0x770123456789AA8); + header.fec_group = 0; + + QuicAckFrame ack_frame; + ack_frame.received_info.entropy_hash = 0x43; + ack_frame.received_info.largest_observed = GG_UINT64_C(0x770123456789ABF); + ack_frame.received_info.delta_time_largest_observed = QuicTime::Delta::Zero(); + ack_frame.received_info.missing_packets.insert( + GG_UINT64_C(0x770123456789ABE)); + ack_frame.sent_info.entropy_hash = 0x14; + ack_frame.sent_info.least_unacked = GG_UINT64_C(0x770123456789AA0); + + QuicFrames frames; + frames.push_back(QuicFrame(&ack_frame)); + + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xA8, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags (entropy) + 0x01, + + // frame type (ack frame) + // (has nacks, not truncated, 6 byte largest observed, 1 byte delta) + 0x6C, + // entropy hash of sent packets till least awaiting - 1. + 0x14, + // least packet sequence number awaiting an ack, delta from sequence number. + 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, + // entropy hash of all received packets. + 0x43, + // largest observed packet sequence number + 0xBF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // Zero delta time. + 0x0, 0x0, + // num missing packet ranges + 0x01, + // missing packet delta + 0x01, + // 0 more missing packets in range. + 0x00, + }; + + scoped_ptr<QuicPacket> data( + framer_.BuildUnsizedDataPacket(header, frames).packet); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketTCP) { QuicPacketHeader header; header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); @@ -2880,7 +3261,10 @@ TEST_P(QuicFramerTest, BuildRstFramePacket) { AsChars(packet), arraysize(packet)); } -TEST_P(QuicFramerTest, BuildCloseFramePacket) { +TEST_P(QuicFramerTest, BuildCloseFramePacketv11) { + if (GetParam() > QUIC_VERSION_11) { + return; + } QuicPacketHeader header; header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.public_header.reset_flag = false; @@ -2957,6 +3341,60 @@ TEST_P(QuicFramerTest, BuildCloseFramePacket) { AsChars(packet), arraysize(packet)); } +TEST_P(QuicFramerTest, BuildCloseFramePacket) { + if (GetParam() <= QUIC_VERSION_11) { + return; + } + QuicPacketHeader header; + header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.public_header.reset_flag = false; + header.public_header.version_flag = false; + header.fec_flag = false; + header.entropy_flag = true; + header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); + header.fec_group = 0; + + QuicConnectionCloseFrame close_frame; + close_frame.error_code = static_cast<QuicErrorCode>(0x05060708); + close_frame.error_details = "because I can"; + + QuicFrames frames; + frames.push_back(QuicFrame(&close_frame)); + + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags (entropy) + 0x01, + + // frame type (connection close frame) + 0x02, + // error code + 0x08, 0x07, 0x06, 0x05, + // error details length + 0x0d, 0x00, + // error details + 'b', 'e', 'c', 'a', + 'u', 's', 'e', ' ', + 'I', ' ', 'c', 'a', + 'n', + }; + + scoped_ptr<QuicPacket> data( + framer_.BuildUnsizedDataPacket(header, frames).packet); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + TEST_P(QuicFramerTest, BuildGoAwayPacket) { QuicPacketHeader header; header.public_header.guid = GG_UINT64_C(0xFEDCBA9876543210); @@ -3193,9 +3631,9 @@ TEST_P(QuicFramerTest, DISABLED_Truncation) { QuicAckFrame* ack_frame = &close_frame.ack_frame; close_frame.error_code = static_cast<QuicErrorCode>(0x05); close_frame.error_details = "because I can"; - ack_frame->received_info.largest_observed = 201; + ack_frame->received_info.largest_observed = 601; ack_frame->sent_info.least_unacked = 0; - for (uint64 i = 1; i < ack_frame->received_info.largest_observed; ++i) { + for (uint64 i = 1; i < ack_frame->received_info.largest_observed; i += 2) { ack_frame->received_info.missing_packets.insert(i); } @@ -3215,6 +3653,8 @@ TEST_P(QuicFramerTest, DISABLED_Truncation) { *raw_ack_packet)); // Create a packet with just connection close. + // TODO(ianswett): Remove this section when v11 is retired because v12 no + // longer embeds an ack frame in the connection close frame. frames.clear(); frame.type = CONNECTION_CLOSE_FRAME; frame.connection_close_frame = &close_frame; @@ -3230,6 +3670,20 @@ TEST_P(QuicFramerTest, DISABLED_Truncation) { // Now make sure we can turn our ack packet back into an ack frame ASSERT_TRUE(framer_.ProcessPacket(*ack_packet)); + ASSERT_EQ(1u, visitor_.ack_frames_.size()); + const QuicAckFrame& processed_ack_frame = *visitor_.ack_frames_[0]; + EXPECT_EQ(0u, processed_ack_frame.sent_info.least_unacked); + if (GetParam() > QUIC_VERSION_11) { + EXPECT_TRUE(processed_ack_frame.received_info.is_truncated); + EXPECT_EQ(510u, processed_ack_frame.received_info.largest_observed); + ASSERT_EQ(255u, processed_ack_frame.received_info.missing_packets.size()); + SequenceNumberSet::const_iterator missing_iter = + processed_ack_frame.received_info.missing_packets.begin(); + EXPECT_EQ(1u, *missing_iter); + SequenceNumberSet::const_reverse_iterator last_missing_iter = + processed_ack_frame.received_info.missing_packets.rbegin(); + EXPECT_EQ(509u, *last_missing_iter); + } // And do the same for the close frame. ASSERT_TRUE(framer_.ProcessPacket(*close_packet)); @@ -3447,6 +3901,9 @@ TEST_P(QuicFramerTest, StopPacketProcessing) { } TEST_P(QuicFramerTest, ConnectionCloseWithInvalidAck) { + if (GetParam() > QUIC_VERSION_11) { + return; + } unsigned char packet[] = { // public flags (8 byte guid) 0x3C, diff --git a/net/quic/quic_packet_creator.cc b/net/quic/quic_packet_creator.cc index 17839af..fe5a712 100644 --- a/net/quic/quic_packet_creator.cc +++ b/net/quic/quic_packet_creator.cc @@ -90,15 +90,8 @@ void QuicPacketCreator::UpdateSequenceNumberLength( bytes_per_second / options_.max_packet_length; const uint64 delta = max(current_delta, congestion_window); - if (delta < 1 << ((PACKET_1BYTE_SEQUENCE_NUMBER * 8) - 2)) { - options_.send_sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER; - } else if (delta < 1 << ((PACKET_2BYTE_SEQUENCE_NUMBER * 8) - 2)) { - options_.send_sequence_number_length = PACKET_2BYTE_SEQUENCE_NUMBER; - } else if (delta < 1 << ((PACKET_4BYTE_SEQUENCE_NUMBER * 8) - 2)) { - options_.send_sequence_number_length = PACKET_4BYTE_SEQUENCE_NUMBER; - } else { - options_.send_sequence_number_length = PACKET_6BYTE_SEQUENCE_NUMBER; - } + options_.send_sequence_number_length = + QuicFramer::GetMinSequenceNumberLength(delta * 4); } bool QuicPacketCreator::HasRoomForStreamFrame(QuicStreamId id, @@ -137,7 +130,9 @@ size_t QuicPacketCreator::CreateStreamFrame(QuicStreamId id, } if (data.size() == 0) { - DCHECK(fin); + if (!fin) { + LOG(DFATAL) << "Creating a stream frame with no data or fin."; + } // Create a new packet for the fin, if necessary. *frame = QuicFrame(new QuicStreamFrame(id, true, offset, "")); return 0; @@ -294,8 +289,8 @@ SerializedPacket QuicPacketCreator::SerializePacket() { size_t max_plaintext_size = framer_->GetMaxPlaintextSize(options_.max_packet_length); DCHECK_GE(max_plaintext_size, packet_size_); - // ACK and CONNECTION_CLOSE Frames will only be truncated only if they're - // the first frame in the packet. If truncation is to occure, then + // ACK and CONNECTION_CLOSE Frames will be truncated only if they're + // the first frame in the packet. If truncation is to occur, then // GetSerializedFrameLength will have returned all bytes free. bool possibly_truncated = packet_size_ != max_plaintext_size || @@ -304,7 +299,10 @@ SerializedPacket QuicPacketCreator::SerializePacket() { queued_frames_.back().type == CONNECTION_CLOSE_FRAME); SerializedPacket serialized = framer_->BuildDataPacket(header, queued_frames_, packet_size_); - DCHECK(serialized.packet); + if (!serialized.packet) { + LOG(DFATAL) << "Failed to serialize " << queued_frames_.size() + << " frames."; + } // Because of possible truncation, we can't be confident that our // packet size calculation worked correctly. if (!possibly_truncated) @@ -329,7 +327,10 @@ SerializedPacket QuicPacketCreator::SerializeFec() { fec_group_.reset(NULL); fec_group_number_ = 0; packet_size_ = 0; - DCHECK(serialized.packet); + if (!serialized.packet) { + LOG(DFATAL) << "Failed to serialize fec packet for group:" + << fec_data.fec_group; + } DCHECK_GE(options_.max_packet_length, serialized.packet->length()); return serialized; } @@ -393,7 +394,8 @@ bool QuicPacketCreator::ShouldRetransmit(const QuicFrame& frame) { bool QuicPacketCreator::AddFrame(const QuicFrame& frame, bool save_retransmittable_frames) { size_t frame_len = framer_->GetSerializedFrameLength( - frame, BytesFree(), queued_frames_.empty(), true); + frame, BytesFree(), queued_frames_.empty(), true, + options()->send_sequence_number_length); if (frame_len == 0) { return false; } diff --git a/net/quic/quic_packet_creator.h b/net/quic/quic_packet_creator.h index 614ebbc..1d76d87 100644 --- a/net/quic/quic_packet_creator.h +++ b/net/quic/quic_packet_creator.h @@ -33,7 +33,7 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { // Options for controlling how packets are created. struct Options { Options() - : max_packet_length(kMaxPacketSize), + : max_packet_length(kDefaultMaxPacketSize), max_packets_per_fec_group(0), send_guid_length(PACKET_8BYTE_GUID), send_sequence_number_length(PACKET_1BYTE_SEQUENCE_NUMBER) {} @@ -128,6 +128,9 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { // in BytesFree. size_t PacketSize() const; + // TODO(jri): AddSavedFrame calls AddFrame, which only saves the frame + // if it is a stream frame, not other types of frames. Fix this API; + // add a AddNonSavedFrame method. // Adds |frame| to the packet creator's list of frames to be serialized. // Returns false if the frame doesn't fit into the current packet. bool AddSavedFrame(const QuicFrame& frame); diff --git a/net/quic/quic_packet_creator_test.cc b/net/quic/quic_packet_creator_test.cc index 841d5d1..393de10 100644 --- a/net/quic/quic_packet_creator_test.cc +++ b/net/quic/quic_packet_creator_test.cc @@ -258,7 +258,7 @@ TEST_F(QuicPacketCreatorTest, SerializeWithFECChangingSequenceNumberLength) { // Ensure the next FEC group starts using the new sequence number length. serialized = creator_.SerializeAllFrames(frames_); EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length); - delete frames_[0].stream_frame; + delete frames_[0].ack_frame; delete serialized.packet; } @@ -292,7 +292,10 @@ TEST_F(QuicPacketCreatorTest, ReserializeFramesWithSequenceNumberLength) { delete serialized.packet; } -TEST_F(QuicPacketCreatorTest, SerializeConnectionClose) { +// TODO(rtenneti): Delete this when QUIC_VERSION_11 is deprecated. +TEST_F(QuicPacketCreatorTest, SerializeConnectionClosev11) { + client_framer_.set_version(QUIC_VERSION_11); + server_framer_.set_version(QUIC_VERSION_11); QuicConnectionCloseFrame frame; frame.error_code = QUIC_NO_ERROR; frame.error_details = "error"; @@ -313,6 +316,30 @@ TEST_F(QuicPacketCreatorTest, SerializeConnectionClose) { delete serialized.packet; } +TEST_F(QuicPacketCreatorTest, SerializeConnectionClose) { + // TODO(rtenneti): Delete this when QUIC_VERSION_11 is deprecated. + if (QuicVersionMax() <= QUIC_VERSION_11) { + return; + } + QuicConnectionCloseFrame frame; + frame.error_code = QUIC_NO_ERROR; + frame.error_details = "error"; + frame.ack_frame = QuicAckFrame(0u, QuicTime::Zero(), 0u); + + SerializedPacket serialized = creator_.SerializeConnectionClose(&frame); + ASSERT_EQ(1u, serialized.sequence_number); + ASSERT_EQ(1u, creator_.sequence_number()); + + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnConnectionCloseFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + + ProcessPacket(serialized.packet); + delete serialized.packet; +} + TEST_F(QuicPacketCreatorTest, CreateStreamFrame) { QuicFrame frame; size_t consumed = creator_.CreateStreamFrame(1u, "test", 0u, false, &frame); @@ -362,7 +389,7 @@ TEST_F(QuicPacketCreatorTest, StreamFrameConsumption) { // Compute the total overhead for a single frame in packet. const size_t overhead = GetPacketHeaderOverhead() + GetEncryptionOverhead() + GetStreamFrameOverhead(); - size_t capacity = kMaxPacketSize - overhead; + size_t capacity = kDefaultMaxPacketSize - overhead; // Now, test various sizes around this size. for (int delta = -5; delta <= 5; ++delta) { string data(capacity + delta, 'A'); @@ -391,7 +418,7 @@ TEST_F(QuicPacketCreatorTest, CryptoStreamFramePacketPadding) { const size_t overhead = GetPacketHeaderOverhead() + GetEncryptionOverhead() + GetStreamFrameOverhead(); ASSERT_GT(kMaxPacketSize, overhead); - size_t capacity = kMaxPacketSize - overhead; + size_t capacity = kDefaultMaxPacketSize - overhead; // Now, test various sizes around this size. for (int delta = -5; delta <= 5; ++delta) { string data(capacity + delta, 'A'); @@ -408,10 +435,10 @@ TEST_F(QuicPacketCreatorTest, CryptoStreamFramePacketPadding) { // (1 byte) and to expand the stream frame (another 2 bytes) the packet // will not be padded. if (bytes_free < 3) { - EXPECT_EQ(client_framer_.GetMaxPlaintextSize(kMaxPacketSize) - bytes_free, - serialized_packet.packet->length()); + EXPECT_EQ(client_framer_.GetMaxPlaintextSize(kDefaultMaxPacketSize) + - bytes_free, serialized_packet.packet->length()); } else { - EXPECT_EQ(client_framer_.GetMaxPlaintextSize(kMaxPacketSize), + EXPECT_EQ(client_framer_.GetMaxPlaintextSize(kDefaultMaxPacketSize), serialized_packet.packet->length()); } delete serialized_packet.packet; @@ -426,8 +453,8 @@ TEST_F(QuicPacketCreatorTest, NonCryptoStreamFramePacketNonPadding) { // Compute the total overhead for a single frame in packet. const size_t overhead = GetPacketHeaderOverhead() + GetEncryptionOverhead() + GetStreamFrameOverhead(); - ASSERT_GT(kMaxPacketSize, overhead); - size_t capacity = kMaxPacketSize - overhead; + ASSERT_GT(kDefaultMaxPacketSize, overhead); + size_t capacity = kDefaultMaxPacketSize - overhead; // Now, test various sizes around this size. for (int delta = -5; delta <= 5; ++delta) { string data(capacity + delta, 'A'); @@ -441,10 +468,10 @@ TEST_F(QuicPacketCreatorTest, NonCryptoStreamFramePacketNonPadding) { SerializedPacket serialized_packet = creator_.SerializePacket(); ASSERT_TRUE(serialized_packet.packet); if (bytes_free > 0) { - EXPECT_EQ(client_framer_.GetMaxPlaintextSize(kMaxPacketSize) - bytes_free, - serialized_packet.packet->length()); + EXPECT_EQ(client_framer_.GetMaxPlaintextSize(kDefaultMaxPacketSize) + - bytes_free, serialized_packet.packet->length()); } else { - EXPECT_EQ(client_framer_.GetMaxPlaintextSize(kMaxPacketSize), + EXPECT_EQ(client_framer_.GetMaxPlaintextSize(kDefaultMaxPacketSize), serialized_packet.packet->length()); } delete serialized_packet.packet; @@ -576,7 +603,7 @@ TEST_P(QuicPacketCreatorTest, AddFrameAndSerialize) { creator_.BytesFree()); // Add a variety of frame types and then a padding frame. - QuicAckFrame ack_frame; + QuicAckFrame ack_frame(0u, QuicTime::Zero(), 0u); EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&ack_frame))); EXPECT_TRUE(creator_.HasPendingFrames()); diff --git a/net/quic/quic_packet_generator.cc b/net/quic/quic_packet_generator.cc index 01275e2..d2bf7fa 100644 --- a/net/quic/quic_packet_generator.cc +++ b/net/quic/quic_packet_generator.cc @@ -216,7 +216,9 @@ bool QuicPacketGenerator::AddNextPendingFrame() { return !should_send_feedback_; } - DCHECK(!queued_control_frames_.empty()); + if (queued_control_frames_.empty()) { + LOG(DFATAL) << "AddNextPendingFrame called with no queued control frames."; + } if (!AddFrame(queued_control_frames_.back())) { // Packet was full. return false; diff --git a/net/quic/quic_packet_generator_test.cc b/net/quic/quic_packet_generator_test.cc index a1e30aa..71b35e7 100644 --- a/net/quic/quic_packet_generator_test.cc +++ b/net/quic/quic_packet_generator_test.cc @@ -434,7 +434,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataFEC) { } // Send enough data to create 3 packets: two full and one partial. - size_t data_len = 2 * kMaxPacketSize + 100; + size_t data_len = 2 * kDefaultMaxPacketSize + 100; QuicConsumedData consumed = generator_.ConsumeData(3, CreateData(data_len), 0, true, NULL); EXPECT_EQ(data_len, consumed.bytes_consumed); @@ -466,7 +466,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataSendsFecAtEnd) { } // Send enough data to create 2 packets: one full and one partial. - size_t data_len = 1 * kMaxPacketSize + 100; + size_t data_len = 1 * kDefaultMaxPacketSize + 100; QuicConsumedData consumed = generator_.ConsumeData(3, CreateData(data_len), 0, true, NULL); EXPECT_EQ(data_len, consumed.bytes_consumed); @@ -584,7 +584,7 @@ TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations2) { } // Send enough data to exceed one packet - size_t data_len = kMaxPacketSize + 100; + size_t data_len = kDefaultMaxPacketSize + 100; QuicConsumedData consumed = generator_.ConsumeData(3, CreateData(data_len), 0, true, NULL); EXPECT_EQ(data_len, consumed.bytes_consumed); diff --git a/net/quic/quic_protocol.cc b/net/quic/quic_protocol.cc index 23c3b4f..acb0edc 100644 --- a/net/quic/quic_protocol.cc +++ b/net/quic/quic_protocol.cc @@ -127,6 +127,8 @@ QuicTag QuicVersionToQuicTag(const QuicVersion version) { return MakeQuicTag('Q', '0', '1', '0'); case QUIC_VERSION_11: return MakeQuicTag('Q', '0', '1', '1'); + case QUIC_VERSION_12: + return MakeQuicTag('Q', '0', '1', '2'); default: // This shold be an ERROR because we should never attempt to convert an // invalid QuicVersion to be written to the wire. @@ -155,6 +157,7 @@ string QuicVersionToString(const QuicVersion version) { switch (version) { RETURN_STRING_LITERAL(QUIC_VERSION_10); RETURN_STRING_LITERAL(QUIC_VERSION_11); + RETURN_STRING_LITERAL(QUIC_VERSION_12); default: return "QUIC_VERSION_UNSUPPORTED"; } @@ -196,7 +199,8 @@ ostream& operator<<(ostream& os, const QuicPacketHeader& header) { // TODO(ianswett): Initializing largest_observed to 0 should not be necessary. ReceivedPacketInfo::ReceivedPacketInfo() : largest_observed(0), - delta_time_largest_observed(QuicTime::Delta::Infinite()) { + delta_time_largest_observed(QuicTime::Delta::Infinite()), + is_truncated(false) { } ReceivedPacketInfo::~ReceivedPacketInfo() {} diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h index 6c152e8..aff081c 100644 --- a/net/quic/quic_protocol.h +++ b/net/quic/quic_protocol.h @@ -45,8 +45,18 @@ typedef std::vector<QuicTag> QuicTagVector; typedef uint32 QuicPriority; // TODO(rch): Consider Quic specific names for these constants. -// Maximum size in bytes of a QUIC packet. -const QuicByteCount kMaxPacketSize = 1200; +// Default and initial maximum size in bytes of a QUIC packet. +const QuicByteCount kDefaultMaxPacketSize = 1200; +// The maximum packet size of any QUIC packet, based on ethernet's max size, +// minus the IP and UDP headers. +const QuicByteCount kMaxPacketSize = 1472; + +// Maximum size of the initial congestion window in packets. +const size_t kDefaultInitialWindow = 10; +const size_t kMaxInitialWindow = 100; + +// Don't allow a client to suggest an RTT longer than 15 seconds. +const uint32 kMaxInitialRoundTripTimeUs = 15 * kNumMicrosPerSecond; // Maximum number of open streams per connection. const size_t kDefaultMaxStreamsPerConnection = 100; @@ -146,6 +156,14 @@ enum QuicSequenceNumberLength { PACKET_6BYTE_SEQUENCE_NUMBER = 6 }; +// Used to indicate a QuicSequenceNumberLength using two flag bits. +enum QuicSequenceNumberLengthFlags { + PACKET_FLAGS_1BYTE_SEQUENCE = 0, // 00 + PACKET_FLAGS_2BYTE_SEQUENCE = 1, // 01 + PACKET_FLAGS_4BYTE_SEQUENCE = 1 << 1, // 10 + PACKET_FLAGS_6BYTE_SEQUENCE = 1 << 1 | 1, // 11 +}; + // The public flags are specified in one byte. enum QuicPacketPublicFlags { PACKET_PUBLIC_FLAGS_NONE = 0, @@ -171,10 +189,10 @@ enum QuicPacketPublicFlags { // --01----: 2 bytes // --10----: 4 bytes // --11----: 6 bytes - PACKET_PUBLIC_FLAGS_1BYTE_SEQUENCE = 0, - PACKET_PUBLIC_FLAGS_2BYTE_SEQUENCE = 1 << 4, - PACKET_PUBLIC_FLAGS_4BYTE_SEQUENCE = 1 << 5, - PACKET_PUBLIC_FLAGS_6BYTE_SEQUENCE = 1 << 5 | 1 << 4, + PACKET_PUBLIC_FLAGS_1BYTE_SEQUENCE = PACKET_FLAGS_1BYTE_SEQUENCE << 4, + PACKET_PUBLIC_FLAGS_2BYTE_SEQUENCE = PACKET_FLAGS_2BYTE_SEQUENCE << 4, + PACKET_PUBLIC_FLAGS_4BYTE_SEQUENCE = PACKET_FLAGS_4BYTE_SEQUENCE << 4, + PACKET_PUBLIC_FLAGS_6BYTE_SEQUENCE = PACKET_FLAGS_6BYTE_SEQUENCE << 4, // All bits set (bits 6 and 7 are not currently used): 00111111 PACKET_PUBLIC_FLAGS_MAX = (1 << 6) - 1 @@ -208,7 +226,8 @@ enum QuicVersion { QUIC_VERSION_UNSUPPORTED = 0, QUIC_VERSION_10 = 10, - QUIC_VERSION_11 = 11, // Current version. + QUIC_VERSION_11 = 11, + QUIC_VERSION_12 = 12, // Current version. }; // This vector contains QUIC versions which we currently support. @@ -533,6 +552,9 @@ struct NET_EXPORT_PRIVATE ReceivedPacketInfo { // structure. // The set of packets which we're expecting and have not received. SequenceNumberSet missing_packets; + + // Whether the ack had to be truncated when sent. + bool is_truncated; }; // True if the sequence number is greater than largest_observed or is listed @@ -634,6 +656,7 @@ struct NET_EXPORT_PRIVATE QuicRstStreamFrame { struct NET_EXPORT_PRIVATE QuicConnectionCloseFrame { QuicErrorCode error_code; std::string error_details; + // TODO(ianswett): Remove this once QUIC_VERSION_11 is removed. QuicAckFrame ack_frame; }; diff --git a/net/quic/quic_session.cc b/net/quic/quic_session.cc index 77494b5..6f28dc5 100644 --- a/net/quic/quic_session.cc +++ b/net/quic/quic_session.cc @@ -59,6 +59,10 @@ class VisitorShim : public QuicConnectionVisitorInterface { session_->OnSuccessfulVersionNegotiation(version); } + virtual void OnConfigNegotiated() OVERRIDE { + session_->OnConfigNegotiated(); + } + virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) OVERRIDE { session_->OnConnectionClosed(error, from_peer); @@ -89,12 +93,11 @@ QuicSession::QuicSession(QuicConnection* connection, has_pending_handshake_(false) { connection_->set_visitor(visitor_shim_.get()); - connection_->SetIdleNetworkTimeout(config_.idle_connection_state_lifetime()); + connection_->SetFromConfig(config_); if (connection_->connected()) { connection_->SetOverallConnectionTimeout( config_.max_time_before_crypto_handshake()); } - // TODO(satyamshekhar): Set congestion control and ICSL also. } QuicSession::~QuicSession() { @@ -339,6 +342,10 @@ bool QuicSession::IsCryptoHandshakeConfirmed() { return GetCryptoStream()->handshake_confirmed(); } +void QuicSession::OnConfigNegotiated() { + connection_->SetFromConfig(config_); +} + void QuicSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { switch (event) { // TODO(satyamshekhar): Move the logic of setting the encrypter/decrypter @@ -356,8 +363,6 @@ void QuicSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { case HANDSHAKE_CONFIRMED: LOG_IF(DFATAL, !config_.negotiated()) << ENDPOINT << "Handshake confirmed without parameter negotiation."; - connection_->SetIdleNetworkTimeout( - config_.idle_connection_state_lifetime()); connection_->SetOverallConnectionTimeout(QuicTime::Delta::Infinite()); max_open_streams_ = config_.max_streams_per_connection(); break; diff --git a/net/quic/quic_session.h b/net/quic/quic_session.h index 3867ff6..0254086 100644 --- a/net/quic/quic_session.h +++ b/net/quic/quic_session.h @@ -66,6 +66,7 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) OVERRIDE; virtual void OnSuccessfulVersionNegotiation( const QuicVersion& version) OVERRIDE{} + virtual void OnConfigNegotiated() OVERRIDE; // Not needed for HTTP. virtual bool OnCanWrite() OVERRIDE; virtual bool HasPendingHandshake() const OVERRIDE; diff --git a/net/quic/quic_session_test.cc b/net/quic/quic_session_test.cc index e90b20f..ef1eb38 100644 --- a/net/quic/quic_session_test.cc +++ b/net/quic/quic_session_test.cc @@ -48,6 +48,7 @@ class TestCryptoStream : public QuicCryptoStream { const QuicErrorCode error = session()->config()->ProcessClientHello( msg, &error_details); EXPECT_EQ(QUIC_NO_ERROR, error); + session()->OnConfigNegotiated(); session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); } diff --git a/net/quic/reliable_quic_stream.cc b/net/quic/reliable_quic_stream.cc index afba1f0..b17086b 100644 --- a/net/quic/reliable_quic_stream.cc +++ b/net/quic/reliable_quic_stream.cc @@ -502,6 +502,7 @@ uint32 ReliableQuicStream::StripPriorityAndHeaderId( data, data_len, &headers_id_and_priority_buffer_, &temporary_priority); if (total_bytes_parsed > 0 && headers_id_and_priority_buffer_.size() == 0) { priority_parsed_ = true; + // Spdy priorities are inverted, so the highest numerical value is the // lowest legal priority. if (temporary_priority > static_cast<QuicPriority>(kLowestPriority)) { diff --git a/net/quic/test_tools/crypto_test_utils.cc b/net/quic/test_tools/crypto_test_utils.cc index 76de7fa..0de17f9 100644 --- a/net/quic/test_tools/crypto_test_utils.cc +++ b/net/quic/test_tools/crypto_test_utils.cc @@ -7,7 +7,7 @@ #include "net/quic/crypto/channel_id.h" #include "net/quic/crypto/common_cert_set.h" #include "net/quic/crypto/crypto_handshake.h" -#include "net/quic/crypto/crypto_server_config.h" +#include "net/quic/crypto/quic_crypto_server_config.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" #include "net/quic/crypto/quic_random.h" diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc index e1d148f..dd41526 100644 --- a/net/quic/test_tools/quic_test_utils.cc +++ b/net/quic/test_tools/quic_test_utils.cc @@ -432,7 +432,8 @@ size_t GetPacketLengthForOneStream( version, PACKET_8BYTE_GUID, include_version, sequence_number_length, is_in_fec_group); const size_t ack_length = NullEncrypter(false).GetCiphertextSize( - QuicFramer::GetMinAckFrameSize()) + + QuicFramer::GetMinAckFrameSize( + version, sequence_number_length, PACKET_1BYTE_SEQUENCE_NUMBER)) + GetPacketHeaderSize(PACKET_8BYTE_GUID, include_version, sequence_number_length, is_in_fec_group); if (stream_length < ack_length) { @@ -451,11 +452,19 @@ size_t GetMinStreamFrameSize(QuicVersion version) { return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicMaxStreamOffsetSize; } +TestEntropyCalculator::TestEntropyCalculator() { } + +TestEntropyCalculator::~TestEntropyCalculator() { } + QuicPacketEntropyHash TestEntropyCalculator::EntropyHash( QuicPacketSequenceNumber sequence_number) const { return 1u; } +MockEntropyCalculator::MockEntropyCalculator() { } + +MockEntropyCalculator::~MockEntropyCalculator() { } + QuicConfig DefaultQuicConfig() { QuicConfig config; config.SetDefaults(); diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h index 3ad8e3d..86c5caa 100644 --- a/net/quic/test_tools/quic_test_utils.h +++ b/net/quic/test_tools/quic_test_utils.h @@ -193,6 +193,7 @@ class MockConnectionVisitor : public QuicConnectionVisitorInterface { MOCK_CONST_METHOD0(HasPendingHandshake, bool()); MOCK_METHOD1(OnSuccessfulVersionNegotiation, void(const QuicVersion& version)); + MOCK_METHOD0(OnConfigNegotiated, void()); private: DISALLOW_COPY_AND_ASSIGN(MockConnectionVisitor); @@ -348,6 +349,7 @@ class MockSendAlgorithm : public SendAlgorithmInterface { MockSendAlgorithm(); virtual ~MockSendAlgorithm(); + MOCK_METHOD2(SetFromConfig, void(const QuicConfig& config, bool is_server)); MOCK_METHOD3(OnIncomingQuicCongestionFeedbackFrame, void(const QuicCongestionFeedbackFrame&, QuicTime feedback_receive_time, @@ -376,13 +378,23 @@ class MockSendAlgorithm : public SendAlgorithmInterface { class TestEntropyCalculator : public QuicReceivedEntropyHashCalculatorInterface { public: - TestEntropyCalculator() { } - virtual ~TestEntropyCalculator() { } + TestEntropyCalculator(); + virtual ~TestEntropyCalculator(); virtual QuicPacketEntropyHash EntropyHash( QuicPacketSequenceNumber sequence_number) const OVERRIDE; }; +class MockEntropyCalculator : public TestEntropyCalculator { + public: + MockEntropyCalculator(); + virtual ~MockEntropyCalculator(); + + MOCK_CONST_METHOD1( + EntropyHash, + QuicPacketEntropyHash(QuicPacketSequenceNumber sequence_number)); +}; + class TestDecompressorVisitor : public QuicSpdyDecompressor::Visitor { public: virtual ~TestDecompressorVisitor() {} diff --git a/net/tools/quic/end_to_end_test.cc b/net/tools/quic/end_to_end_test.cc index b0a963f..220e4f7 100644 --- a/net/tools/quic/end_to_end_test.cc +++ b/net/tools/quic/end_to_end_test.cc @@ -11,6 +11,7 @@ #include "base/strings/string_number_conversions.h" #include "base/synchronization/waitable_event.h" #include "net/base/ip_endpoint.h" +#include "net/quic/congestion_control/tcp_cubic_sender.h" #include "net/quic/crypto/aes_128_gcm_12_encrypter.h" #include "net/quic/crypto/null_encrypter.h" #include "net/quic/quic_framer.h" @@ -145,6 +146,8 @@ class EndToEndTest : public ::testing::TestWithParam<TestParams> { FLAGS_track_retransmission_history = true; client_config_.SetDefaults(); server_config_.SetDefaults(); + server_config_.set_initial_round_trip_time_us(kMaxInitialRoundTripTimeUs, + 0); QuicInMemoryCachePeer::ResetForTests(); AddToCache("GET", kLargeRequest, "HTTP/1.1", "200", "OK", kFooResponseBody); @@ -536,6 +539,7 @@ TEST_P(EndToEndTest, DISABLED_LargePostZeroRTTFailure) { // The 0-RTT handshake should succeed. client_->Connect(); + client_->WaitForResponseForMs(-1); ASSERT_TRUE(client_->client()->connected()); EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request)); EXPECT_EQ(1, client_->client()->session()->GetNumSentClientHellos()); @@ -677,6 +681,106 @@ TEST_P(EndToEndTest, LimitMaxOpenStreams) { EXPECT_EQ(2u, client_negotiated_config->max_streams_per_connection()); } +TEST_P(EndToEndTest, LimitMaxPacketSizeAndCongestionWindowAndRTT) { + // Client tries to negotiate twice the server's max and negotiation settles + // on the max. + client_config_.set_server_max_packet_size(2 * kMaxPacketSize, + kDefaultMaxPacketSize); + client_config_.set_server_initial_congestion_window(2 * kMaxInitialWindow, + kDefaultInitialWindow); + client_config_.set_initial_round_trip_time_us(1, 1); + + ASSERT_TRUE(Initialize()); + client_->client()->WaitForCryptoHandshakeConfirmed(); + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + ASSERT_EQ(1u, dispatcher->session_map().size()); + QuicSession* session = dispatcher->session_map().begin()->second; + while (!session->IsCryptoHandshakeConfirmed()) { + server_thread_->server()->WaitForEvents(); + } + + QuicConfig* client_negotiated_config = client_->client()->session()->config(); + QuicConfig* server_negotiated_config = session->config(); + QuicCongestionManager* client_congestion_manager = + QuicConnectionPeer::GetCongestionManager( + client_->client()->session()->connection()); + QuicCongestionManager* server_congestion_manager = + QuicConnectionPeer::GetCongestionManager(session->connection()); + + EXPECT_EQ(kMaxPacketSize, client_negotiated_config->server_max_packet_size()); + if (negotiated_version_ > QUIC_VERSION_11) { + EXPECT_EQ(kMaxPacketSize, client_->client()->options()->max_packet_length); + EXPECT_EQ(kMaxPacketSize, session->options()->max_packet_length); + } else { + EXPECT_EQ(kDefaultMaxPacketSize, + client_->client()->options()->max_packet_length); + EXPECT_EQ(kDefaultMaxPacketSize, + session->options()->max_packet_length); + } + + EXPECT_EQ(kMaxInitialWindow, + client_negotiated_config->server_initial_congestion_window()); + EXPECT_EQ(kMaxInitialWindow, + server_negotiated_config->server_initial_congestion_window()); + // The client shouldn't set it's initial window based on the negotiated value. + EXPECT_EQ(kDefaultInitialWindow * kDefaultTCPMSS, + client_congestion_manager->GetCongestionWindow()); + EXPECT_EQ(kMaxInitialWindow * kDefaultTCPMSS, + server_congestion_manager->GetCongestionWindow()); + + EXPECT_EQ(1u, client_negotiated_config->initial_round_trip_time_us()); + EXPECT_EQ(1u, server_negotiated_config->initial_round_trip_time_us()); + + // Now use the negotiated limits with packet loss. + SetPacketLossPercentage(30); + + // 10 Kb body. + string body; + GenerateBody(&body, 1024 * 10); + + HTTPMessage request(HttpConstants::HTTP_1_1, + HttpConstants::POST, "/foo"); + request.AddBody(body, true); + + EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request)); +} + +TEST_P(EndToEndTest, InitialRTT) { + // Client tries to negotiate twice the server's max and negotiation settles + // on the max. + client_config_.set_initial_round_trip_time_us(2 * kMaxInitialRoundTripTimeUs, + 0); + + ASSERT_TRUE(Initialize()); + client_->client()->WaitForCryptoHandshakeConfirmed(); + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + ASSERT_EQ(1u, dispatcher->session_map().size()); + QuicSession* session = dispatcher->session_map().begin()->second; + while (!session->IsCryptoHandshakeConfirmed()) { + server_thread_->server()->WaitForEvents(); + } + + QuicConfig* client_negotiated_config = client_->client()->session()->config(); + QuicConfig* server_negotiated_config = session->config(); + QuicCongestionManager* client_congestion_manager = + QuicConnectionPeer::GetCongestionManager( + client_->client()->session()->connection()); + QuicCongestionManager* server_congestion_manager = + QuicConnectionPeer::GetCongestionManager(session->connection()); + + EXPECT_EQ(kMaxInitialRoundTripTimeUs, + client_negotiated_config->initial_round_trip_time_us()); + EXPECT_EQ(kMaxInitialRoundTripTimeUs, + server_negotiated_config->initial_round_trip_time_us()); + // Now that acks have been exchanged, the RTT estimate has decreased on the + // server and is not infinite on the client. + EXPECT_FALSE(client_congestion_manager->SmoothedRtt().IsInfinite()); + EXPECT_GE(kMaxInitialRoundTripTimeUs, + server_congestion_manager->SmoothedRtt().ToMicroseconds()); +} + TEST_P(EndToEndTest, ResetConnection) { ASSERT_TRUE(Initialize()); diff --git a/net/tools/quic/quic_client.cc b/net/tools/quic/quic_client.cc index b1fa935..50e2a7c 100644 --- a/net/tools/quic/quic_client.cc +++ b/net/tools/quic/quic_client.cc @@ -45,6 +45,12 @@ QuicClient::QuicClient(IPEndPoint server_address, supported_versions_(supported_versions), print_response_(print_response) { config_.SetDefaults(); + // TODO(ianswett): Allow the client to change the server's max packet size and + // initial congestion window. + config_.set_server_max_packet_size(kDefaultMaxPacketSize, + kDefaultMaxPacketSize); + config_.set_server_initial_congestion_window(kDefaultInitialWindow, + kDefaultInitialWindow); } QuicClient::QuicClient(IPEndPoint server_address, diff --git a/net/tools/quic/quic_dispatcher_test.cc b/net/tools/quic/quic_dispatcher_test.cc index 90b3074..f15b18d 100644 --- a/net/tools/quic/quic_dispatcher_test.cc +++ b/net/tools/quic/quic_dispatcher_test.cc @@ -8,7 +8,7 @@ #include "base/strings/string_piece.h" #include "net/quic/crypto/crypto_handshake.h" -#include "net/quic/crypto/crypto_server_config.h" +#include "net/quic/crypto/quic_crypto_server_config.h" #include "net/quic/crypto/quic_random.h" #include "net/quic/quic_crypto_stream.h" #include "net/quic/test_tools/quic_test_utils.h" diff --git a/net/tools/quic/quic_server.cc b/net/tools/quic/quic_server.cc index 66a9aa9..0d87d1c 100644 --- a/net/tools/quic/quic_server.cc +++ b/net/tools/quic/quic_server.cc @@ -43,6 +43,7 @@ QuicServer::QuicServer() supported_versions_(QuicSupportedVersions()) { // Use hardcoded crypto parameters for now. config_.SetDefaults(); + config_.set_initial_round_trip_time_us(kMaxInitialRoundTripTimeUs, 0); Initialize(); } diff --git a/net/tools/quic/quic_server.h b/net/tools/quic/quic_server.h index f166555..7c8e405 100644 --- a/net/tools/quic/quic_server.h +++ b/net/tools/quic/quic_server.h @@ -10,7 +10,7 @@ #include "base/memory/scoped_ptr.h" #include "net/base/ip_endpoint.h" -#include "net/quic/crypto/crypto_server_config.h" +#include "net/quic/crypto/quic_crypto_server_config.h" #include "net/quic/quic_config.h" #include "net/quic/quic_framer.h" #include "net/tools/epoll_server/epoll_server.h" diff --git a/net/tools/quic/quic_server_session_test.cc b/net/tools/quic/quic_server_session_test.cc index 1412f38..08c25ca 100644 --- a/net/tools/quic/quic_server_session_test.cc +++ b/net/tools/quic/quic_server_session_test.cc @@ -5,7 +5,7 @@ #include "net/tools/quic/quic_server_session.h" -#include "net/quic/crypto/crypto_server_config.h" +#include "net/quic/crypto/quic_crypto_server_config.h" #include "net/quic/crypto/quic_random.h" #include "net/quic/quic_connection.h" #include "net/quic/test_tools/quic_connection_peer.h" diff --git a/net/tools/quic/test_tools/mock_quic_dispatcher.h b/net/tools/quic/test_tools/mock_quic_dispatcher.h index ac198bc..505025c 100644 --- a/net/tools/quic/test_tools/mock_quic_dispatcher.h +++ b/net/tools/quic/test_tools/mock_quic_dispatcher.h @@ -6,7 +6,7 @@ #define NET_TOOLS_QUIC_TEST_TOOLS_MOCK_QUIC_DISPATCHER_H_ #include "net/base/ip_endpoint.h" -#include "net/quic/crypto/crypto_server_config.h" +#include "net/quic/crypto/quic_crypto_server_config.h" #include "net/quic/quic_config.h" #include "net/quic/quic_protocol.h" #include "net/tools/epoll_server/epoll_server.h" |