diff options
author | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-30 21:12:17 +0000 |
---|---|---|
committer | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-30 21:12:17 +0000 |
commit | b0643107853022d9925317352099d525b9eaa359 (patch) | |
tree | 0f6bca29f5fead59feb960ee5ceb426228ed9a33 /net/quic | |
parent | f7e27dde9cecc7faeba4fe6bf2663cce96dac448 (diff) | |
download | chromium_src-b0643107853022d9925317352099d525b9eaa359.zip chromium_src-b0643107853022d9925317352099d525b9eaa359.tar.gz chromium_src-b0643107853022d9925317352099d525b9eaa359.tar.bz2 |
Land Recent QUIC changes.
Make the FEC group optional by adding a flag to the private headers.
Merge internal change: 46979143
Merging changes from chromium CL - 15385004
Merge internal change: 46949614
Removing debug logging from RecordPacketReceived. Seems redundant to
log both when we actually receive and when we record it.
Merge internal change: 46934210
Logging crypto handshake as a DVLOG rather than DLOG as it hasn't
recently been needed to debug test failures.
Merge internal change: 46932247
Changing the quic test client to simply not return a stream if not connected.
This will hopefully turn server test check-failures into server test
failures.
Merge internal change: 46932163
QUIC: redo server nonces.
Previously, in order to cope with strike-register failures and client
clock-sync issues, the server could issue a server nonce to a client. This
meant that the server had to remember rejected handshakes so that the server
nonce could be matched up. With this change, QUIC servers no longer need to
keep track of rejected handshakes.
Instead of issuing and remembering nonces, a server will now encrypt them and
forget about them. When a server nonce is used to establish freshness for a
connection, it will be stored in a per-GFE strike-register. (This
strike-register is separate from the one used to process client nonces.)
Merge internal change: 46889484
Remove FEC_ENTROPY_FLAG from private flags. Now, FEC packet's entropy
flag contain the xor of entropies of the protected packets.
Merge internal change: 46889094
Limit the number of times we'll fast-retransmit a given packet using taildrop.
Merge internal change: 46754530
Added CommonCertSetsQUIC to anonymous namespace.
QUIC: cleanups round two.
* Make CommonCertSetsQUIC a Singleton to save on every Config having its own
copy.
* Rework server config expiry: previously it caused an error at client hello
send time. Now it will cause an error at REJ processing time but, if the
config expired after we cached it, we will act as if we didn't have a cached
server config.
* Invalidate the server config cache in the event of a client hello sending
failure. This will prevent a bad server config from being cached and poisoning
connection attempts for the lifetime of the cache.
* Fix a bug in the test code which failed to parse hex chunks in debugging
messages correctly. (Thanks to wtc for noticing.)
Merge internal change: 46742937
Merging changes from chromium - CL 15074007
Merge internal change: 46710932
Fix a bug in QuicSession's header compression behavior which could lead
to infinite loops.
Merge internal change: 46694681
Getting 5% our CPU usage back by not calculating SentBandwidth for the
tcp congestion control algorithm.
Added a TODO to improve that function since it's pretty abysmal: the
ToLargerUnits and Subtract overhead alone accounted for 4.5% of the cpu
in initial loadtest runs.
Merge internal change: 46608880
Adding support for truncated guids in QuicFramer.
Merge internal change: 46575819
using our latched write_blocked status to spare us useless system calls.
Merge internal change: 46573462
Fixing some crashing issues in the QUIC loadtest, where if a client ever
disconnects it never recovers, either crashing trying to create a stream
or crashing waiting for a response on a non-existant stream.
I'm not sure if we have the same problem for the http/https simple clients
but we definitely do for QUIC.
Merge internal change: 46562890
Merging changes from chromium - CL 14614006
Merge internal change: 46460427
Merging cleanup changes from chromium CL - 14651009
Merge internal change: 46457093
Fixing a test framework bug for quic: we were munging headers to do
https:// for insecure quic resulting in a 404 in the http-only service
map. Then disalbing the test since we don't advertise secure SPDY on
insecure QUIC.
Merge internal change: 46408400
Move QuicConfig from ssl_global_data to quic_dispatcher.cc. Initialize
using values from QuicConfigProto and use the max_time_before_crypto_handshake
to set the overall connection timeout before crypto handshake finishes.
Merge internal change: 46400649
QUIC: implement ChannelIDs.
We'll need this for HTTPS.
Merge internal change: 46396357
Deleted usage of scoped_ptr_openssl. Added TODO comments for porting
ChannelIDSigner and Verifier.
R=rch@chromium.org
Review URL: https://chromiumcodereview.appspot.com/15937012
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@203220 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/quic')
84 files changed, 2138 insertions, 772 deletions
diff --git a/net/quic/congestion_control/fix_rate_sender.cc b/net/quic/congestion_control/fix_rate_sender.cc index 4e3f635..5398cc6 100644 --- a/net/quic/congestion_control/fix_rate_sender.cc +++ b/net/quic/congestion_control/fix_rate_sender.cc @@ -31,7 +31,6 @@ FixRateSender::~FixRateSender() { void FixRateSender::OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, QuicTime feedback_receive_time, - QuicBandwidth /*sent_bandwidth*/, const SentPacketsMap& /*sent_packets*/) { DCHECK(feedback.type == kFixRate) << "Invalid incoming CongestionFeedbackType:" << feedback.type; diff --git a/net/quic/congestion_control/fix_rate_sender.h b/net/quic/congestion_control/fix_rate_sender.h index 42915e1..545f2d3 100644 --- a/net/quic/congestion_control/fix_rate_sender.h +++ b/net/quic/congestion_control/fix_rate_sender.h @@ -27,7 +27,6 @@ class NET_EXPORT_PRIVATE FixRateSender : public SendAlgorithmInterface { virtual void OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, QuicTime feedback_receive_time, - QuicBandwidth sent_bandwidth, const SentPacketsMap& sent_packets) OVERRIDE; virtual void OnIncomingAck(QuicPacketSequenceNumber acked_sequence_number, QuicByteCount acked_bytes, diff --git a/net/quic/congestion_control/fix_rate_test.cc b/net/quic/congestion_control/fix_rate_test.cc index f48dc45..b772d01 100644 --- a/net/quic/congestion_control/fix_rate_test.cc +++ b/net/quic/congestion_control/fix_rate_test.cc @@ -30,7 +30,6 @@ class FixRateTest : public ::testing::Test { protected: FixRateTest() : rtt_(QuicTime::Delta::FromMilliseconds(30)), - unused_bandwidth_(QuicBandwidth::Zero()), sender_(new FixRateSender(&clock_)), receiver_(new FixRateReceiverPeer()), start_(clock_.Now()) { @@ -38,7 +37,6 @@ class FixRateTest : public ::testing::Test { clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2)); } const QuicTime::Delta rtt_; - const QuicBandwidth unused_bandwidth_; MockClock clock_; SendAlgorithmInterface::SentPacketsMap unused_packet_map_; scoped_ptr<FixRateSender> sender_; @@ -61,7 +59,7 @@ TEST_F(FixRateTest, SenderAPI) { feedback.type = kFixRate; feedback.fix_rate.bitrate = QuicBandwidth::FromKBytesPerSecond(300); sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), - unused_bandwidth_, unused_packet_map_); + unused_packet_map_); EXPECT_EQ(300000, sender_->BandwidthEstimate().ToBytesPerSecond()); EXPECT_TRUE(sender_->TimeUntilSend( clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA).IsZero()); @@ -91,7 +89,7 @@ TEST_F(FixRateTest, FixRatePacing) { receiver_->SetBitrate(QuicBandwidth::FromKBytesPerSecond(240)); ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), - unused_bandwidth_, unused_packet_map_); + unused_packet_map_); QuicTime acc_advance_time(QuicTime::Zero()); QuicPacketSequenceNumber sequence_number = 0; for (int i = 0; i < num_packets; i += 2) { diff --git a/net/quic/congestion_control/inter_arrival_sender.cc b/net/quic/congestion_control/inter_arrival_sender.cc index 77b9af0..c8d2c86 100644 --- a/net/quic/congestion_control/inter_arrival_sender.cc +++ b/net/quic/congestion_control/inter_arrival_sender.cc @@ -4,6 +4,8 @@ #include "net/quic/congestion_control/inter_arrival_sender.h" +namespace net { + namespace { const int64 kProbeBitrateKBytesPerSecond = 1200; // 9.6 Mbit/s const float kPacketLossBitrateReduction = 0.7f; @@ -12,9 +14,11 @@ const float kMaxBitrateReduction = 0.9f; const float kMinBitrateReduction = 0.05f; const uint64 kMinBitrateKbit = 10; const int kInitialRttMs = 60; // At a typical RTT 60 ms. -} -namespace net { +static const int kBitrateSmoothingPeriodMs = 1000; +static const int kMinBitrateSmoothingPeriodMs = 500; + +} // namespace InterArrivalSender::InterArrivalSender(const QuicClock* clock) : probing_(true), @@ -37,16 +41,52 @@ InterArrivalSender::InterArrivalSender(const QuicClock* clock) InterArrivalSender::~InterArrivalSender() { } +// TODO(pwestin): this is really inefficient (4% CPU on the GFE loadtest). +// static +QuicBandwidth InterArrivalSender::CalculateSentBandwidth( + const SendAlgorithmInterface::SentPacketsMap& sent_packets_map, + QuicTime feedback_receive_time) { + const QuicTime::Delta kBitrateSmoothingPeriod = + QuicTime::Delta::FromMilliseconds(kBitrateSmoothingPeriodMs); + const QuicTime::Delta kMinBitrateSmoothingPeriod = + QuicTime::Delta::FromMilliseconds(kMinBitrateSmoothingPeriodMs); + + QuicByteCount sum_bytes_sent = 0; + + // Sum packet from new until they are kBitrateSmoothingPeriod old. + SendAlgorithmInterface::SentPacketsMap::const_reverse_iterator history_rit = + sent_packets_map.rbegin(); + + QuicTime::Delta max_diff = QuicTime::Delta::Zero(); + for (; history_rit != sent_packets_map.rend(); ++history_rit) { + QuicTime::Delta diff = + feedback_receive_time.Subtract(history_rit->second->SendTimestamp()); + if (diff > kBitrateSmoothingPeriod) { + break; + } + sum_bytes_sent += history_rit->second->BytesSent(); + max_diff = diff; + } + if (max_diff < kMinBitrateSmoothingPeriod) { + // No estimate. + return QuicBandwidth::Zero(); + } + return QuicBandwidth::FromBytesAndTimeDelta(sum_bytes_sent, max_diff); +} + void InterArrivalSender::OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, QuicTime feedback_receive_time, - QuicBandwidth sent_bandwidth, const SentPacketsMap& sent_packets) { DCHECK(feedback.type == kInterArrival); if (feedback.type != kInterArrival) { return; } + + QuicBandwidth sent_bandwidth = CalculateSentBandwidth(sent_packets, + feedback_receive_time); + TimeMap::const_iterator received_it; for (received_it = feedback.inter_arrival.received_packet_times.begin(); received_it != feedback.inter_arrival.received_packet_times.end(); diff --git a/net/quic/congestion_control/inter_arrival_sender.h b/net/quic/congestion_control/inter_arrival_sender.h index afcafe8..7997d0d 100644 --- a/net/quic/congestion_control/inter_arrival_sender.h +++ b/net/quic/congestion_control/inter_arrival_sender.h @@ -27,11 +27,14 @@ class NET_EXPORT_PRIVATE InterArrivalSender : public SendAlgorithmInterface { explicit InterArrivalSender(const QuicClock* clock); virtual ~InterArrivalSender(); + static QuicBandwidth CalculateSentBandwidth( + const SendAlgorithmInterface::SentPacketsMap& sent_packets_map, + QuicTime feedback_receive_time); + // Start implementation of SendAlgorithmInterface. virtual void OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, QuicTime feedback_receive_time, - QuicBandwidth sent_bandwidth, const SentPacketsMap& sent_packets) OVERRIDE; virtual void OnIncomingAck(QuicPacketSequenceNumber acked_sequence_number, diff --git a/net/quic/congestion_control/inter_arrival_sender_test.cc b/net/quic/congestion_control/inter_arrival_sender_test.cc index 9594780..e44be09 100644 --- a/net/quic/congestion_control/inter_arrival_sender_test.cc +++ b/net/quic/congestion_control/inter_arrival_sender_test.cc @@ -72,8 +72,8 @@ class InterArrivalSenderTest : public ::testing::Test { feedback_sequence_number_, receive_time)); feedback_sequence_number_++; - sender_.OnIncomingQuicCongestionFeedbackFrame( - feedback, send_clock_.Now(), QuicBandwidth::Zero(), sent_packets_); + sender_.OnIncomingQuicCongestionFeedbackFrame(feedback, send_clock_.Now(), + sent_packets_); } void SendFeedbackMessageNPackets(int n, @@ -95,7 +95,7 @@ class InterArrivalSenderTest : public ::testing::Test { feedback_sequence_number_++; } sender_.OnIncomingQuicCongestionFeedbackFrame(feedback, send_clock_.Now(), - QuicBandwidth::Zero(), sent_packets_); + sent_packets_); } QuicTime::Delta SenderDeltaSinceStart() { diff --git a/net/quic/congestion_control/quic_congestion_control_test.cc b/net/quic/congestion_control/quic_congestion_control_test.cc index 0a581d0..53e2db8 100644 --- a/net/quic/congestion_control/quic_congestion_control_test.cc +++ b/net/quic/congestion_control/quic_congestion_control_test.cc @@ -22,7 +22,6 @@ class QuicCongestionManagerPeer : public QuicCongestionManager { CongestionFeedbackType congestion_type) : QuicCongestionManager(clock, congestion_type) { } - using QuicCongestionManager::SentBandwidth; using QuicCongestionManager::BandwidthEstimate; }; @@ -48,7 +47,6 @@ TEST_F(QuicCongestionControlTest, FixedRateSenderAPI) { congestion_feedback.fix_rate.bitrate = QuicBandwidth::FromKBytesPerSecond(30); manager_->OnIncomingQuicCongestionFeedbackFrame(congestion_feedback, clock_.Now()); - EXPECT_TRUE(manager_->SentBandwidth(clock_.Now()).IsZero()); EXPECT_TRUE(manager_->TimeUntilSend( clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA).IsZero()); manager_->SentPacket(1, clock_.Now(), kMaxPacketSize, NOT_RETRANSMISSION); diff --git a/net/quic/congestion_control/quic_congestion_manager.cc b/net/quic/congestion_control/quic_congestion_manager.cc index 48091c3..d40293c 100644 --- a/net/quic/congestion_control/quic_congestion_manager.cc +++ b/net/quic/congestion_control/quic_congestion_manager.cc @@ -13,12 +13,12 @@ namespace { static const int kBitrateSmoothingPeriodMs = 1000; -static const int kMinBitrateSmoothingPeriodMs = 500; static const int kHistoryPeriodMs = 5000; static const int kDefaultRetransmissionTimeMs = 500; static const size_t kMaxRetransmissions = 10; static const size_t kTailDropWindowSize = 5; +static const size_t kTailDropMaxRetransmissions = 4; COMPILE_ASSERT(kHistoryPeriodMs >= kBitrateSmoothingPeriodMs, history_must_be_longer_or_equal_to_the_smoothing_period); @@ -69,9 +69,8 @@ void QuicCongestionManager::AbandoningPacket( void QuicCongestionManager::OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& frame, QuicTime feedback_receive_time) { - QuicBandwidth sent_bandwidth = SentBandwidth(feedback_receive_time); send_algorithm_->OnIncomingQuicCongestionFeedbackFrame( - frame, feedback_receive_time, sent_bandwidth, packet_history_map_); + frame, feedback_receive_time, packet_history_map_); } void QuicCongestionManager::OnIncomingAckFrame(const QuicAckFrame& frame, @@ -155,7 +154,10 @@ const QuicTime::Delta QuicCongestionManager::GetRetransmissionDelay( // TODO(pwestin): This should take the RTT into account instead of a hard // coded kDefaultRetransmissionTimeMs. Ideally the variance of the RTT too. if (unacked_packets_count <= kTailDropWindowSize) { - return QuicTime::Delta::FromMilliseconds(kDefaultRetransmissionTimeMs); + if (number_retransmissions <= kTailDropMaxRetransmissions) { + return QuicTime::Delta::FromMilliseconds(kDefaultRetransmissionTimeMs); + } + number_retransmissions -= kTailDropMaxRetransmissions; } return QuicTime::Delta::FromMilliseconds( @@ -167,36 +169,6 @@ const QuicTime::Delta QuicCongestionManager::SmoothedRtt() { return send_algorithm_->SmoothedRtt(); } -QuicBandwidth QuicCongestionManager::SentBandwidth( - QuicTime feedback_receive_time) const { - const QuicTime::Delta kBitrateSmoothingPeriod = - QuicTime::Delta::FromMilliseconds(kBitrateSmoothingPeriodMs); - const QuicTime::Delta kMinBitrateSmoothingPeriod = - QuicTime::Delta::FromMilliseconds(kMinBitrateSmoothingPeriodMs); - - QuicByteCount sum_bytes_sent = 0; - - // Sum packet from new until they are kBitrateSmoothingPeriod old. - SendAlgorithmInterface::SentPacketsMap::const_reverse_iterator history_rit = - packet_history_map_.rbegin(); - - QuicTime::Delta max_diff = QuicTime::Delta::Zero(); - for (; history_rit != packet_history_map_.rend(); ++history_rit) { - QuicTime::Delta diff = - feedback_receive_time.Subtract(history_rit->second->SendTimestamp()); - if (diff > kBitrateSmoothingPeriod) { - break; - } - sum_bytes_sent += history_rit->second->BytesSent(); - max_diff = diff; - } - if (max_diff < kMinBitrateSmoothingPeriod) { - // No estimate. - return QuicBandwidth::Zero(); - } - return QuicBandwidth::FromBytesAndTimeDelta(sum_bytes_sent, max_diff); -} - QuicBandwidth QuicCongestionManager::BandwidthEstimate() { return send_algorithm_->BandwidthEstimate(); } diff --git a/net/quic/congestion_control/quic_congestion_manager.h b/net/quic/congestion_control/quic_congestion_manager.h index 1ffe12a..204209a 100644 --- a/net/quic/congestion_control/quic_congestion_manager.h +++ b/net/quic/congestion_control/quic_congestion_manager.h @@ -102,7 +102,6 @@ class NET_EXPORT_PRIVATE QuicCongestionManager { // Get the current(last) rtt. Infinite is returned if invalid. const QuicTime::Delta rtt(); - QuicBandwidth SentBandwidth(QuicTime feedback_receive_time) const; void CleanupPacketHistory(); const QuicClock* clock_; diff --git a/net/quic/congestion_control/quic_congestion_manager_test.cc b/net/quic/congestion_control/quic_congestion_manager_test.cc index 8b8f473..64aab9a 100644 --- a/net/quic/congestion_control/quic_congestion_manager_test.cc +++ b/net/quic/congestion_control/quic_congestion_manager_test.cc @@ -4,6 +4,7 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" +#include "net/quic/congestion_control/inter_arrival_sender.h" #include "net/quic/congestion_control/quic_congestion_manager.h" #include "net/quic/quic_protocol.h" #include "net/quic/test_tools/mock_clock.h" @@ -28,8 +29,9 @@ class QuicCongestionManagerPeer : public QuicCongestionManager { } using QuicCongestionManager::rtt; - using QuicCongestionManager::SentBandwidth; - + const SendAlgorithmInterface::SentPacketsMap& packet_history_map() { + return packet_history_map_; + } private: DISALLOW_COPY_AND_ASSIGN(QuicCongestionManagerPeer); }; @@ -69,7 +71,10 @@ TEST_F(QuicCongestionManagerTest, Bandwidth) { } EXPECT_EQ(100, manager_->BandwidthEstimate().ToKBytesPerSecond()); EXPECT_NEAR(100, - manager_->SentBandwidth(clock_.Now()).ToKBytesPerSecond(), 4); + InterArrivalSender::CalculateSentBandwidth( + manager_->packet_history_map(), + clock_.Now()).ToKBytesPerSecond(), + 4); } TEST_F(QuicCongestionManagerTest, BandwidthWith1SecondGap) { @@ -95,13 +100,21 @@ TEST_F(QuicCongestionManagerTest, BandwidthWith1SecondGap) { } EXPECT_EQ(100000, manager_->BandwidthEstimate().ToBytesPerSecond()); EXPECT_NEAR(100000, - manager_->SentBandwidth(clock_.Now()).ToBytesPerSecond(), 2000); + InterArrivalSender::CalculateSentBandwidth( + manager_->packet_history_map(), + clock_.Now()).ToBytesPerSecond(), + 2000); clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(500)); EXPECT_NEAR(50000, - manager_->SentBandwidth(clock_.Now()).ToBytesPerSecond(), 1000); + InterArrivalSender::CalculateSentBandwidth( + manager_->packet_history_map(), + clock_.Now()).ToBytesPerSecond(), + 1000); clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(501)); EXPECT_NEAR(100000, manager_->BandwidthEstimate().ToBytesPerSecond(), 2000); - EXPECT_TRUE(manager_->SentBandwidth(clock_.Now()).IsZero()); + EXPECT_TRUE(InterArrivalSender::CalculateSentBandwidth( + manager_->packet_history_map(), + clock_.Now()).IsZero()); for (int i = 1; i <= 150; ++i) { EXPECT_TRUE(manager_->TimeUntilSend( clock_.Now(), NOT_RETRANSMISSION, kIgnored).IsZero()); @@ -113,7 +126,10 @@ TEST_F(QuicCongestionManagerTest, BandwidthWith1SecondGap) { } EXPECT_EQ(100, manager_->BandwidthEstimate().ToKBytesPerSecond()); EXPECT_NEAR(100, - manager_->SentBandwidth(clock_.Now()).ToKBytesPerSecond(), 2); + InterArrivalSender::CalculateSentBandwidth( + manager_->packet_history_map(), + clock_.Now()).ToKBytesPerSecond(), + 2); } TEST_F(QuicCongestionManagerTest, Rtt) { diff --git a/net/quic/congestion_control/send_algorithm_interface.h b/net/quic/congestion_control/send_algorithm_interface.h index fd8ec3e..f2c4e7b 100644 --- a/net/quic/congestion_control/send_algorithm_interface.h +++ b/net/quic/congestion_control/send_algorithm_interface.h @@ -45,7 +45,6 @@ class NET_EXPORT_PRIVATE SendAlgorithmInterface { virtual void OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, QuicTime feedback_receive_time, - QuicBandwidth sent_bandwidth, const SentPacketsMap& sent_packets) = 0; // Called for each received ACK, with sequence number from remote peer. diff --git a/net/quic/congestion_control/tcp_cubic_sender.cc b/net/quic/congestion_control/tcp_cubic_sender.cc index 78bd7d9..e71b8c2 100644 --- a/net/quic/congestion_control/tcp_cubic_sender.cc +++ b/net/quic/congestion_control/tcp_cubic_sender.cc @@ -35,7 +35,6 @@ TcpCubicSender::TcpCubicSender(const QuicClock* clock, bool reno) void TcpCubicSender::OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, QuicTime feedback_receive_time, - QuicBandwidth /*sent_bandwidth*/, const SentPacketsMap& /*sent_packets*/) { if (last_received_accumulated_number_of_lost_packets_ != feedback.tcp.accumulated_number_of_lost_packets) { diff --git a/net/quic/congestion_control/tcp_cubic_sender.h b/net/quic/congestion_control/tcp_cubic_sender.h index 2a1066d..0b7e948 100644 --- a/net/quic/congestion_control/tcp_cubic_sender.h +++ b/net/quic/congestion_control/tcp_cubic_sender.h @@ -33,7 +33,6 @@ class NET_EXPORT_PRIVATE TcpCubicSender : public SendAlgorithmInterface { virtual void OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, QuicTime feedback_receive_time, - QuicBandwidth sent_bandwidth, const SentPacketsMap& sent_packets) OVERRIDE; virtual void OnIncomingAck(QuicPacketSequenceNumber acked_sequence_number, QuicByteCount acked_bytes, diff --git a/net/quic/congestion_control/tcp_cubic_sender_test.cc b/net/quic/congestion_control/tcp_cubic_sender_test.cc index 71d53c6..a9e468f 100644 --- a/net/quic/congestion_control/tcp_cubic_sender_test.cc +++ b/net/quic/congestion_control/tcp_cubic_sender_test.cc @@ -29,7 +29,6 @@ class TcpCubicSenderTest : public ::testing::Test { TcpCubicSenderTest() : rtt_(QuicTime::Delta::FromMilliseconds(60)), one_ms_(QuicTime::Delta::FromMilliseconds(1)), - fake_bandwidth_(QuicBandwidth::Zero()), sender_(new TcpCubicSenderPeer(&clock_, true)), receiver_(new TcpReceiver()), sequence_number_(1), @@ -59,7 +58,6 @@ class TcpCubicSenderTest : public ::testing::Test { const QuicTime::Delta rtt_; const QuicTime::Delta one_ms_; - const QuicBandwidth fake_bandwidth_; MockClock clock_; SendAlgorithmInterface::SentPacketsMap not_used_; scoped_ptr<TcpCubicSenderPeer> sender_; @@ -79,7 +77,7 @@ TEST_F(TcpCubicSenderTest, SimpleSender) { // Get default QuicCongestionFeedbackFrame from receiver. ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), - fake_bandwidth_, not_used_); + not_used_); // Make sure we can send. EXPECT_TRUE(sender_->TimeUntilSend( clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA).IsZero()); @@ -100,7 +98,7 @@ TEST_F(TcpCubicSenderTest, ExponentialSlowStart) { // Get default QuicCongestionFeedbackFrame from receiver. ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), - fake_bandwidth_, not_used_); + not_used_); // Make sure we can send. EXPECT_TRUE(sender_->TimeUntilSend( clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA).IsZero()); @@ -129,7 +127,7 @@ TEST_F(TcpCubicSenderTest, SlowStartAckTrain) { // Get default QuicCongestionFeedbackFrame from receiver. ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), - fake_bandwidth_, not_used_); + not_used_); // Make sure we can send. EXPECT_TRUE(sender_->TimeUntilSend( clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA).IsZero()); @@ -172,7 +170,7 @@ TEST_F(TcpCubicSenderTest, SlowStartPacketLoss) { // Get default QuicCongestionFeedbackFrame from receiver. ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), - fake_bandwidth_, not_used_); + not_used_); // Make sure we can send. EXPECT_TRUE(sender_->TimeUntilSend( clock_.Now(), NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA).IsZero()); diff --git a/net/quic/crypto/cert_compressor.h b/net/quic/crypto/cert_compressor.h index 612bc529..7b7e2f0 100644 --- a/net/quic/crypto/cert_compressor.h +++ b/net/quic/crypto/cert_compressor.h @@ -22,9 +22,9 @@ namespace net { // that they already have. In the event that one of them is to be // compressed, it can be replaced with just the hash. // 2) The peer may provide a number of hashes that represent sets of -// pre-shared certificates. If one of those certificates is to be -// compressed, and it's known to the given CommonCertSets, then it can be -// replaced with a set hash and certificate index. +// pre-shared certificates (CommonCertSets). If one of those certificates +// is to be compressed, and it's known to the given CommonCertSets, then it +// can be replaced with a set hash and certificate index. // 3) Otherwise the certificates are compressed with zlib using a pre-shared // dictionary that consists of the certificates handled with the above // methods and a small chunk of common substrings. diff --git a/net/quic/crypto/channel_id.cc b/net/quic/crypto/channel_id.cc new file mode 100644 index 0000000..e707bf0 --- /dev/null +++ b/net/quic/crypto/channel_id.cc @@ -0,0 +1,14 @@ +// 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/channel_id.h" + +namespace net { + +// static +const char ChannelIDVerifier::kContextStr[] = "QUIC ChannelID"; +// static +const char ChannelIDVerifier::kClientToServerStr[] = "client -> server"; + +} // namespace net diff --git a/net/quic/crypto/channel_id.h b/net/quic/crypto/channel_id.h new file mode 100644 index 0000000..b114d9b --- /dev/null +++ b/net/quic/crypto/channel_id.h @@ -0,0 +1,51 @@ +// 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_CHANNEL_ID_H_ +#define NET_QUIC_CRYPTO_CHANNEL_ID_H_ + +#include <string> + +#include "base/strings/string_piece.h" +#include "net/base/net_export.h" + +namespace net { + +// ChannelIDSigner is an abstract interface that implements signing by +// ChannelID keys. +class NET_EXPORT_PRIVATE ChannelIDSigner { + public: + virtual ~ChannelIDSigner() { } + + // Sign signs |signed_data| using the ChannelID key for |hostname| and puts + // the serialized public key into |out_key| and the signature into + // |out_signature|. It returns true on success. + virtual bool Sign(const std::string& hostname, + base::StringPiece signed_data, + std::string* out_key, + std::string* out_signature) = 0; +}; + +// ChannelIDVerifier verifies ChannelID signatures. +class NET_EXPORT_PRIVATE ChannelIDVerifier { + public: + // kContextStr is prepended to the data to be signed in order to ensure that + // a ChannelID signature cannot be used in a different context. (The + // terminating NUL byte is inclued.) + static const char kContextStr[]; + // kClientToServerStr follows kContextStr to specify that the ChannelID is + // being used in the client to server direction. (The terminating NUL byte is + // included.) + static const char kClientToServerStr[]; + + // Verify returns true iff |signature| is a valid signature of |signed_data| + // by |key|. + static bool Verify(base::StringPiece key, + base::StringPiece signed_data, + base::StringPiece signature); +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_CHANNEL_ID_H_ diff --git a/net/quic/crypto/channel_id_nss.cc b/net/quic/crypto/channel_id_nss.cc new file mode 100644 index 0000000..0fd885c --- /dev/null +++ b/net/quic/crypto/channel_id_nss.cc @@ -0,0 +1,20 @@ +// 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/channel_id.h" + +using base::StringPiece; + +namespace net { + +// TODO(rtenneti): Implement NSS support for ChannelIDVerifier::Verify and +// real ChannelIDSigner (using the SpdyCredentialBuilder as a model). +// static +bool ChannelIDVerifier::Verify(StringPiece key, + StringPiece signed_data, + StringPiece signature) { + return true; +} + +} // namespace net diff --git a/net/quic/crypto/channel_id_openssl.cc b/net/quic/crypto/channel_id_openssl.cc new file mode 100644 index 0000000..04c7931 --- /dev/null +++ b/net/quic/crypto/channel_id_openssl.cc @@ -0,0 +1,80 @@ +// 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/channel_id.h" + +#include <openssl/bn.h> +#include <openssl/ec.h> +#include <openssl/ecdsa.h> +#include <openssl/obj_mac.h> +#include <openssl/sha.h> + +#include "crypto/openssl_util.h" + +using base::StringPiece; + +namespace net { + +// static +bool ChannelIDVerifier::Verify(StringPiece key, + StringPiece signed_data, + StringPiece signature) { + if (key.size() != 32 * 2 || + signature.size() != 32 * 2) { + return false; + } + + crypto::ScopedOpenSSL<EC_GROUP, EC_GROUP_free> p256( + EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); + if (p256.get() == NULL) { + return false; + } + + crypto::ScopedOpenSSL<BIGNUM, BN_free> x(BN_new()), y(BN_new()), + r(BN_new()), s(BN_new()); + + ECDSA_SIG sig; + sig.r = r.get(); + sig.s = s.get(); + + const uint8* key_bytes = reinterpret_cast<const uint8*>(key.data()); + const uint8* proof_bytes = reinterpret_cast<const uint8*>(signature.data()); + + if (BN_bin2bn(key_bytes + 0, 32, x.get()) == NULL || + BN_bin2bn(key_bytes + 32, 32, y.get()) == NULL || + BN_bin2bn(proof_bytes + 0, 32, sig.r) == NULL || + BN_bin2bn(proof_bytes + 32, 32, sig.s) == NULL) { + return false; + } + + crypto::ScopedOpenSSL<EC_POINT, EC_POINT_free> point( + EC_POINT_new(p256.get())); + if (point.get() == NULL || + !EC_POINT_set_affine_coordinates_GFp(p256.get(), point.get(), x.get(), + y.get(), NULL)) { + return false; + } + + crypto::ScopedOpenSSL<EC_KEY, EC_KEY_free> ecdsa_key(EC_KEY_new()); + if (ecdsa_key.get() == NULL || + !EC_KEY_set_group(ecdsa_key.get(), p256.get()) || + !EC_KEY_set_public_key(ecdsa_key.get(), point.get())) { + return false; + } + + SHA256_CTX sha256; + SHA256_Init(&sha256); + SHA256_Update(&sha256, ChannelIDVerifier::kContextStr, + strlen(ChannelIDVerifier::kContextStr) + 1); + SHA256_Update(&sha256, ChannelIDVerifier::kClientToServerStr, + strlen(ChannelIDVerifier::kClientToServerStr) + 1); + SHA256_Update(&sha256, signed_data.data(), signed_data.size()); + + unsigned char digest[SHA256_DIGEST_LENGTH]; + SHA256_Final(digest, &sha256); + + return ECDSA_do_verify(digest, sizeof(digest), &sig, ecdsa_key.get()) == 1; +} + +} // namespace net diff --git a/net/quic/crypto/channel_id_test.cc b/net/quic/crypto/channel_id_test.cc new file mode 100644 index 0000000..33595f8 --- /dev/null +++ b/net/quic/crypto/channel_id_test.cc @@ -0,0 +1,49 @@ +// 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/channel_id.h" + +#include "net/quic/test_tools/crypto_test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +using std::string; + +namespace net { +namespace test { + +// TODO(rtenneti): Enable testing of ChannelID. +TEST(ChannelIDTest, DISABLED_SignAndVerify) { + scoped_ptr<ChannelIDSigner> signer( + CryptoTestUtils::ChannelIDSignerForTesting()); + + const string signed_data = "signed data"; + const string hostname = "foo.example.com"; + string key, signature; + ASSERT_TRUE(signer->Sign(hostname, signed_data, &key, &signature)); + + EXPECT_EQ(key, CryptoTestUtils::ChannelIDKeyForHostname(hostname)); + + EXPECT_TRUE(ChannelIDVerifier::Verify(key, signed_data, signature)); + + EXPECT_FALSE(ChannelIDVerifier::Verify("a" + key, signed_data, signature)); + EXPECT_FALSE(ChannelIDVerifier::Verify(key, "a" + signed_data, signature)); + + scoped_ptr<char[]> bad_key(new char[key.size()]); + memcpy(bad_key.get(), key.data(), key.size()); + bad_key[1] ^= 0x80; + EXPECT_FALSE(ChannelIDVerifier::Verify( + string(bad_key.get(), key.size()), signed_data, signature)); + + scoped_ptr<char[]> bad_signature(new char[signature.size()]); + memcpy(bad_signature.get(), signature.data(), signature.size()); + bad_signature[1] ^= 0x80; + EXPECT_FALSE(ChannelIDVerifier::Verify( + key, signed_data, string(bad_signature.get(), signature.size()))); + + EXPECT_FALSE(ChannelIDVerifier::Verify( + key, "wrong signed data", signature)); +} + +} // namespace test +} // namespace net diff --git a/net/quic/crypto/common_cert_set.cc b/net/quic/crypto/common_cert_set.cc index 01a54ce..f631cd6 100644 --- a/net/quic/crypto/common_cert_set.cc +++ b/net/quic/crypto/common_cert_set.cc @@ -6,6 +6,7 @@ #include "base/basictypes.h" #include "base/logging.h" +#include "base/memory/singleton.h" #include "net/quic/quic_utils.h" using base::StringPiece; @@ -16,6 +17,8 @@ namespace common_cert_set_0 { #include "net/quic/crypto/common_cert_set_0.c" } +namespace { + struct CertSet { // num_certs contains the number of certificates in this set. size_t num_certs; @@ -28,7 +31,7 @@ struct CertSet { uint64 hash; }; -static const CertSet kSets[] = { +const CertSet kSets[] = { { common_cert_set_0::kNumCerts, common_cert_set_0::kCerts, @@ -37,38 +40,13 @@ static const CertSet kSets[] = { }, }; -static const uint64 kSetHashes[] = { +const uint64 kSetHashes[] = { common_cert_set_0::kHash, }; -CommonCertSets::~CommonCertSets() { -} - -CommonCertSetsQUIC::CommonCertSetsQUIC() { -} - -StringPiece CommonCertSetsQUIC::GetCommonHashes() const { - return StringPiece(reinterpret_cast<const char*>(kSetHashes), - sizeof(uint64) * arraysize(kSetHashes)); -} - -StringPiece CommonCertSetsQUIC::GetCert(uint64 hash, uint32 index) const { - for (size_t i = 0; i < arraysize(kSets); i++) { - if (kSets[i].hash == hash) { - if (index < kSets[i].num_certs) { - return StringPiece(reinterpret_cast<const char*>(kSets[i].certs[index]), - kSets[i].lens[index]); - } - break; - } - } - - return StringPiece(); -} - // Compare returns a value less than, equal to or greater than zero if |a| is // lexicographically less than, equal to or greater than |b|, respectively. -static int Compare(StringPiece a, const unsigned char* b, size_t b_len) { +int Compare(StringPiece a, const unsigned char* b, size_t b_len) { size_t len = a.size(); if (len > b_len) { len = b_len; @@ -86,50 +64,95 @@ static int Compare(StringPiece a, const unsigned char* b, size_t b_len) { return 0; } -bool CommonCertSetsQUIC::MatchCert(StringPiece cert, - StringPiece common_set_hashes, - uint64* out_hash, - uint32* out_index) const { - if (common_set_hashes.size() % sizeof(uint64) != 0) { - return false; +// CommonCertSetsQUIC implements the CommonCertSets interface using the default +// certificate sets. +class CommonCertSetsQUIC : public CommonCertSets { + public: + // CommonCertSets interface. + virtual StringPiece GetCommonHashes() const OVERRIDE { + return StringPiece(reinterpret_cast<const char*>(kSetHashes), + sizeof(uint64) * arraysize(kSetHashes)); } - for (size_t i = 0; i < common_set_hashes.size() / sizeof(uint64); i++) { - uint64 hash; - memcpy(&hash, common_set_hashes.data() + i*sizeof(uint64), sizeof(uint64)); - - for (size_t j = 0; j < arraysize(kSets); j++) { - if (kSets[j].hash != hash) { - continue; + virtual StringPiece GetCert(uint64 hash, uint32 index) const OVERRIDE { + for (size_t i = 0; i < arraysize(kSets); i++) { + if (kSets[i].hash == hash) { + if (index < kSets[i].num_certs) { + return StringPiece( + reinterpret_cast<const char*>(kSets[i].certs[index]), + kSets[i].lens[index]); + } + break; } + } - if (kSets[j].num_certs == 0) { - continue; - } + return StringPiece(); + } + + virtual bool MatchCert(StringPiece cert, StringPiece common_set_hashes, + uint64* out_hash, uint32* out_index) const OVERRIDE { + if (common_set_hashes.size() % sizeof(uint64) != 0) { + return false; + } - // Binary search for a matching certificate. - size_t min = 0; - size_t max = kSets[j].num_certs - 1; - while (max >= min) { - size_t mid = min + ((max - min) / 2); - int n = Compare(cert, kSets[j].certs[mid], kSets[j].lens[mid]); - if (n < 0) { - if (mid == 0) { - break; + for (size_t i = 0; i < common_set_hashes.size() / sizeof(uint64); i++) { + uint64 hash; + memcpy(&hash, common_set_hashes.data() + i * sizeof(uint64), + sizeof(uint64)); + + for (size_t j = 0; j < arraysize(kSets); j++) { + if (kSets[j].hash != hash) { + continue; + } + + if (kSets[j].num_certs == 0) { + continue; + } + + // Binary search for a matching certificate. + size_t min = 0; + size_t max = kSets[j].num_certs - 1; + while (max >= min) { + size_t mid = min + ((max - min) / 2); + int n = Compare(cert, kSets[j].certs[mid], kSets[j].lens[mid]); + if (n < 0) { + if (mid == 0) { + break; + } + max = mid - 1; + } else if (n > 0) { + min = mid + 1; + } else { + *out_hash = hash; + *out_index = mid; + return true; } - max = mid - 1; - } else if (n > 0) { - min = mid + 1; - } else { - *out_hash = hash; - *out_index = mid; - return true; } } } + + return false; + } + + static CommonCertSetsQUIC* GetInstance() { + return Singleton<CommonCertSetsQUIC>::get(); } - return false; + private: + CommonCertSetsQUIC() {} + virtual ~CommonCertSetsQUIC() {} + + friend struct DefaultSingletonTraits<CommonCertSetsQUIC>; + DISALLOW_COPY_AND_ASSIGN(CommonCertSetsQUIC); +}; + +} // anonymous namespace + +CommonCertSets::~CommonCertSets() {} + +// static +const CommonCertSets* CommonCertSets::GetInstanceQUIC() { + return CommonCertSetsQUIC::GetInstance(); } } // namespace net diff --git a/net/quic/crypto/common_cert_set.h b/net/quic/crypto/common_cert_set.h index 4f5d330..a9e9304 100644 --- a/net/quic/crypto/common_cert_set.h +++ b/net/quic/crypto/common_cert_set.h @@ -19,6 +19,9 @@ class NET_EXPORT_PRIVATE CommonCertSets { public: virtual ~CommonCertSets(); + // GetInstanceQUIC returns the standard QUIC common certificate sets. + static const CommonCertSets* GetInstanceQUIC(); + // GetCommonHashes returns a StringPiece containing the hashes of common sets // supported by this object. The 64-bit hashes are concatenated in the // StringPiece. @@ -39,26 +42,6 @@ class NET_EXPORT_PRIVATE CommonCertSets { uint32* out_index) const = 0; }; -// CommonCertSetsQUIC implements the CommonCertSets interface using the default -// certificate sets. -class NET_EXPORT_PRIVATE CommonCertSetsQUIC : public CommonCertSets { - public: - CommonCertSetsQUIC(); - - // CommonCertSets interface. - virtual base::StringPiece GetCommonHashes() const OVERRIDE; - - virtual base::StringPiece GetCert(uint64 hash, uint32 index) const OVERRIDE; - - virtual bool MatchCert(base::StringPiece cert, - base::StringPiece common_set_hashes, - uint64* out_hash, - uint32* out_index) const OVERRIDE; - - private: - DISALLOW_COPY_AND_ASSIGN(CommonCertSetsQUIC); -}; - } // namespace net #endif // NET_QUIC_CRYPTO_COMMON_CERT_SET_H_ diff --git a/net/quic/crypto/common_cert_set_test.cc b/net/quic/crypto/common_cert_set_test.cc index 57615c9..6ce020d 100644 --- a/net/quic/crypto/common_cert_set_test.cc +++ b/net/quic/crypto/common_cert_set_test.cc @@ -76,30 +76,30 @@ TEST(CommonCertSets, FindGIA) { StringPiece gia(reinterpret_cast<const char*>(kGIACertificate), sizeof(kGIACertificate)); - CommonCertSetsQUIC sets; + const CommonCertSets* sets(CommonCertSets::GetInstanceQUIC()); const uint64 in_hash = GG_UINT64_C(0xde8086f914a3af54); uint64 hash; uint32 index; - ASSERT_TRUE(sets.MatchCert( + ASSERT_TRUE(sets->MatchCert( gia, StringPiece(reinterpret_cast<const char*>(&in_hash), sizeof(in_hash)), &hash, &index)); EXPECT_EQ(in_hash, hash); - StringPiece gia_copy = sets.GetCert(hash, index); + StringPiece gia_copy = sets->GetCert(hash, index); EXPECT_FALSE(gia_copy.empty()); ASSERT_EQ(gia.size(), gia_copy.size()); EXPECT_TRUE(0 == memcmp(gia.data(), gia_copy.data(), gia.size())); } TEST(CommonCertSets, NonMatch) { - CommonCertSetsQUIC sets; + const CommonCertSets* sets(CommonCertSets::GetInstanceQUIC()); StringPiece not_a_cert("hello"); const uint64 in_hash = GG_UINT64_C(0xde8086f914a3af54); uint64 hash; uint32 index; - EXPECT_FALSE(sets.MatchCert( + EXPECT_FALSE(sets->MatchCert( not_a_cert, StringPiece(reinterpret_cast<const char*>(&in_hash), sizeof(in_hash)), &hash, &index)); diff --git a/net/quic/crypto/crypto_handshake.cc b/net/quic/crypto/crypto_handshake.cc index 445361a..acc1504 100644 --- a/net/quic/crypto/crypto_handshake.cc +++ b/net/quic/crypto/crypto_handshake.cc @@ -14,6 +14,7 @@ #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" @@ -45,7 +46,7 @@ CryptoHandshakeMessage::CryptoHandshakeMessage( tag_value_map_(other.tag_value_map_), minimum_size_(other.minimum_size_) { // Don't copy serialized_. scoped_ptr doesn't have a copy constructor. - // The new object can reconstruct serialized_ lazily. + // The new object can lazily reconstruct serialized_. } CryptoHandshakeMessage::~CryptoHandshakeMessage() {} @@ -75,6 +76,10 @@ const QuicData& CryptoHandshakeMessage::GetSerialized() const { return *serialized_.get(); } +void CryptoHandshakeMessage::MarkDirty() { + serialized_.reset(); +} + void CryptoHandshakeMessage::Insert(QuicTagValueMap::const_iterator begin, QuicTagValueMap::const_iterator end) { tag_value_map_.insert(begin, end); @@ -84,7 +89,7 @@ void CryptoHandshakeMessage::SetTaglist(QuicTag tag, ...) { // Warning, if sizeof(QuicTag) > sizeof(int) then this function will break // because the terminating 0 will only be promoted to int. COMPILE_ASSERT(sizeof(QuicTag) <= sizeof(int), - crypto_tag_not_be_larger_than_int_or_varargs_will_break); + crypto_tag_may_not_be_larger_than_int_or_varargs_will_break); vector<QuicTag> tags; va_list ap; @@ -110,6 +115,10 @@ void CryptoHandshakeMessage::SetStringPiece(QuicTag tag, StringPiece value) { tag_value_map_[tag] = value.as_string(); } +void CryptoHandshakeMessage::Erase(QuicTag tag) { + tag_value_map_.erase(tag); +} + QuicErrorCode CryptoHandshakeMessage::GetTaglist(QuicTag tag, const QuicTag** out_tags, size_t* out_len) const { @@ -179,15 +188,6 @@ QuicErrorCode CryptoHandshakeMessage::GetNthValue24(QuicTag tag, } } -bool CryptoHandshakeMessage::GetString(QuicTag tag, string* out) const { - QuicTagValueMap::const_iterator it = tag_value_map_.find(tag); - if (it == tag_value_map_.end()) { - return false; - } - *out = it->second; - return true; -} - QuicErrorCode CryptoHandshakeMessage::GetUint16(QuicTag tag, uint16* out) const { return GetPOD(tag, out, sizeof(uint16)); @@ -218,6 +218,9 @@ size_t CryptoHandshakeMessage::size() const { } void CryptoHandshakeMessage::set_minimum_size(size_t min_bytes) { + if (min_bytes == minimum_size_) { + return; + } serialized_.reset(); minimum_size_ = min_bytes; } @@ -327,17 +330,22 @@ QuicCryptoNegotiatedParameters::QuicCryptoNegotiatedParameters() QuicCryptoNegotiatedParameters::~QuicCryptoNegotiatedParameters() {} CrypterPair::CrypterPair() {} + CrypterPair::~CrypterPair() {} // static const char QuicCryptoConfig::kInitialLabel[] = "QUIC key expansion"; +// static +const char QuicCryptoConfig::kCETVLabel[] = "QUIC CETV block"; + +// static const char QuicCryptoConfig::kForwardSecureLabel[] = "QUIC forward secure key expansion"; QuicCryptoConfig::QuicCryptoConfig() : version(0), - common_cert_sets(new CommonCertSetsQUIC) { + common_cert_sets(CommonCertSets::GetInstanceQUIC()) { } QuicCryptoConfig::~QuicCryptoConfig() {} @@ -353,8 +361,25 @@ QuicCryptoClientConfig::CachedState::CachedState() QuicCryptoClientConfig::CachedState::~CachedState() {} -bool QuicCryptoClientConfig::CachedState::is_complete() const { - return !server_config_.empty() && server_config_valid_; +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* @@ -370,27 +395,57 @@ QuicCryptoClientConfig::CachedState::GetServerConfig() const { return scfg_.get(); } -bool QuicCryptoClientConfig::CachedState::SetServerConfig(StringPiece scfg) { - if (scfg == server_config_) { - return true; +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(); } - scfg_.reset(CryptoFramer::ParseMessage(scfg)); - if (!scfg_.get()) { - return false; + 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; } - server_config_ = scfg.as_string(); + + 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(); + server_config_valid_ = false; + scfg_.reset(new_scfg_storage.release()); + } + return QUIC_NO_ERROR; +} + +void QuicCryptoClientConfig::CachedState::InvalidateServerConfig() { + server_config_.clear(); + scfg_.reset(); server_config_valid_ = false; - return true; } void QuicCryptoClientConfig::CachedState::SetProof(const vector<string>& certs, StringPiece signature) { - bool has_changed = signature != server_config_sig_; + bool has_changed = + signature != server_config_sig_ || certs_.size() != certs.size(); - if (certs_.size() != certs.size()) { - has_changed = true; - } if (!has_changed) { for (size_t i = 0; i < certs_.size(); i++) { if (certs_[i] != certs[i]) { @@ -442,6 +497,7 @@ void QuicCryptoClientConfig::CachedState::set_source_address_token( void QuicCryptoClientConfig::SetDefaults() { // Version must be 0. + // TODO(agl): this version stuff is obsolete now. version = QuicCryptoConfig::CONFIG_VERSION; // Key exchange methods. @@ -490,7 +546,7 @@ void QuicCryptoClientConfig::FillInchoateClientHello( out->SetTaglist(kPDMD, kX509, 0); } - if (common_cert_sets.get()) { + if (common_cert_sets) { out->SetStringPiece(kCCS, common_cert_sets->GetCommonHashes()); } @@ -527,22 +583,11 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( const CryptoHandshakeMessage* scfg = cached->GetServerConfig(); if (!scfg) { // This should never happen as our caller should have checked - // cached->is_complete() before calling this function. + // cached->IsComplete() before calling this function. *error_details = "Handshake not ready"; return QUIC_CRYPTO_INTERNAL_ERROR; } - uint64 expiry_seconds; - if (scfg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR) { - *error_details = "SCFG missing EXPY"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; - } - - if (static_cast<uint64>(now.ToUNIXSeconds()) >= expiry_seconds) { - *error_details = "SCFG expired"; - return QUIC_CRYPTO_SERVER_CONFIG_EXPIRED; - } - StringPiece scid; if (!scfg->GetStringPiece(kSCID, &scid)) { *error_details = "SCFG missing SCID"; @@ -550,13 +595,6 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( } out->SetStringPiece(kSCID, scid); - // Calculate the mutual algorithms that the connection is going to use. - if (scfg->GetUint16(kVERS, &out_params->version) != QUIC_NO_ERROR || - out_params->version != QuicCryptoConfig::CONFIG_VERSION) { - *error_details = "Bad version"; - return QUIC_CRYPTO_VERSION_NOT_SUPPORTED; - } - const QuicTag* their_aeads; const QuicTag* their_key_exchanges; size_t num_their_aeads, num_their_key_exchanges; @@ -569,9 +607,9 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( } size_t key_exchange_index; - if (!QuicUtils::FindMutualTag(aead, their_aeads, num_their_aeads, - QuicUtils::PEER_PRIORITY, &out_params->aead, - NULL) || + 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, @@ -607,8 +645,8 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( Curve25519KeyExchange::NewPrivateKey(rand))); break; case kP256: - out_params->client_key_exchange - .reset(P256KeyExchange::New(P256KeyExchange::NewPrivateKey())); + out_params->client_key_exchange.reset(P256KeyExchange::New( + P256KeyExchange::NewPrivateKey())); break; default: DCHECK(false); @@ -623,6 +661,68 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( } 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_INTERNAL_ERROR; + } + + cetv.SetStringPiece(kCIDK, key); + cetv.SetStringPiece(kCIDS, signature); + + CrypterPair crypters; + CryptoUtils::DeriveKeys(out_params->initial_premaster_secret, + out_params->aead, out_params->client_nonce, + out_params->server_nonce, hkdf_input, + CryptoUtils::CLIENT, &crypters); + + const QuicData& cetv_plaintext = cetv.GetSerialized(); + scoped_ptr<QuicData> cetv_ciphertext(crypters.encrypter->EncryptPacket( + 0 /* sequence number */, + StringPiece() /* associated data */, + cetv_plaintext.AsStringPiece())); + + 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)); @@ -648,19 +748,25 @@ QuicErrorCode QuicCryptoClientConfig::FillClientHello( QuicErrorCode QuicCryptoClientConfig::ProcessRejection( CachedState* cached, const CryptoHandshakeMessage& rej, + QuicWallTime now, 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; } - if (!cached->SetServerConfig(scfg)) { - *error_details = "Invalid SCFG"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + QuicErrorCode error = cached->SetServerConfig(scfg, now, error_details); + if (error != QUIC_NO_ERROR) { + return error; } StringPiece token; @@ -669,8 +775,7 @@ QuicErrorCode QuicCryptoClientConfig::ProcessRejection( } StringPiece nonce; - if (rej.GetStringPiece(kServerNonceTag, &nonce) && nonce.size() == - kNonceSize) { + if (rej.GetStringPiece(kServerNonceTag, &nonce)) { out_params->server_nonce = nonce.as_string(); } @@ -679,7 +784,7 @@ QuicErrorCode QuicCryptoClientConfig::ProcessRejection( rej.GetStringPiece(kCertificateTag, &cert_bytes)) { vector<string> certs; if (!CertCompressor::DecompressChain(cert_bytes, out_params->cached_certs, - common_cert_sets.get(), &certs)) { + common_cert_sets, &certs)) { *error_details = "Certificate data invalid"; return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; } @@ -739,4 +844,8 @@ void QuicCryptoClientConfig::SetProofVerifier(ProofVerifier* verifier) { proof_verifier_.reset(verifier); } +void QuicCryptoClientConfig::SetChannelIDSigner(ChannelIDSigner* signer) { + channel_id_signer_.reset(signer); +} + } // namespace net diff --git a/net/quic/crypto/crypto_handshake.h b/net/quic/crypto/crypto_handshake.h index 96a78a2..ac24ef9 100644 --- a/net/quic/crypto/crypto_handshake.h +++ b/net/quic/crypto/crypto_handshake.h @@ -17,6 +17,7 @@ namespace net { +class ChannelIDSigner; class CommonCertSets; class KeyExchange; class ProofVerifier; @@ -41,6 +42,9 @@ class NET_EXPORT_PRIVATE CryptoHandshakeMessage { // result. Subsequently altering the message does not invalidate the cache. const QuicData& GetSerialized() const; + // MarkDirty invalidates the cache created by |GetSerialized|. + void MarkDirty(); + // SetValue sets an element with the given tag to the raw, memory contents of // |v|. template<class T> void SetValue(QuicTag tag, const T& v) { @@ -75,6 +79,9 @@ class NET_EXPORT_PRIVATE CryptoHandshakeMessage { void SetStringPiece(QuicTag tag, base::StringPiece value); + // Erase removes a tag/value, if present, from the message. + void Erase(QuicTag tag); + // GetTaglist finds an element with the given tag containing zero or more // tags. If such a tag doesn't exist, it returns false. Otherwise it sets // |out_tags| and |out_len| to point to the array of tags and returns true. @@ -91,7 +98,6 @@ class NET_EXPORT_PRIVATE CryptoHandshakeMessage { QuicErrorCode GetNthValue24(QuicTag tag, unsigned index, base::StringPiece* out) const; - bool GetString(QuicTag tag, std::string* out) const; QuicErrorCode GetUint16(QuicTag tag, uint16* out) const; QuicErrorCode GetUint32(QuicTag tag, uint32* out) const; QuicErrorCode GetUint64(QuicTag tag, uint64* out) const; @@ -174,6 +180,11 @@ struct NET_EXPORT_PRIVATE QuicCryptoNegotiatedParameters { // client_key_exchange is used by clients to store the ephemeral KeyExchange // for the connection. scoped_ptr<KeyExchange> client_key_exchange; + // channel_id is set by servers to a ChannelID key when the client correctly + // proves possession of the corresponding private key. It consists of 32 + // bytes of x coordinate, followed by 32 bytes of y coordinate. Both values + // are big-endian and the pair is a P-256 public key. + std::string channel_id; }; // QuicCryptoConfig contains common configuration between clients and servers. @@ -190,6 +201,10 @@ class NET_EXPORT_PRIVATE QuicCryptoConfig { // key to this protocol. static const char kInitialLabel[]; + // kCETVLabel is a constant that is used when deriving the keys for the + // encrypted tag/value block in the client hello. + static const char kCETVLabel[]; + // kForwardSecureLabel is a constant that is used when deriving the forward // secure keys for the connection in order to tie the resulting key to this // protocol. @@ -206,7 +221,7 @@ class NET_EXPORT_PRIVATE QuicCryptoConfig { // Authenticated encryption with associated data (AEAD) algorithms. QuicTagVector aead; - scoped_ptr<CommonCertSets> common_cert_sets; + const CommonCertSets* common_cert_sets; private: DISALLOW_COPY_AND_ASSIGN(QuicCryptoConfig); @@ -225,19 +240,25 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { CachedState(); ~CachedState(); - // is_complete returns true if this object contains enough information to - // perform a handshake with the server. - bool is_complete() const; + // 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 |scfg| parses correctly and stores it in - // |server_config|. It returns true if the parsing succeeds and false - // otherwise. - bool SetServerConfig(base::StringPiece scfg); + // 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, @@ -293,7 +314,7 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { // 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 |is_complete| member of |CachedState|. + // 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 @@ -308,12 +329,14 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { std::string* error_details) const; // ProcessRejection processes a REJ message from a server and updates the - // cached information about that server. After this, |is_complete| may return + // 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|. + // it will be saved in |out_params|. |now| is used to judge whether the + // server config in the rejection message has expired. QuicErrorCode ProcessRejection(CachedState* cached, const CryptoHandshakeMessage& rej, + QuicWallTime now, QuicCryptoNegotiatedParameters* out_params, std::string* error_details); @@ -334,12 +357,18 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { // the server. void SetProofVerifier(ProofVerifier* verifier); + // 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); + 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); }; diff --git a/net/quic/crypto/crypto_handshake_test.cc b/net/quic/crypto/crypto_handshake_test.cc index b637db5..829599a 100644 --- a/net/quic/crypto/crypto_handshake_test.cc +++ b/net/quic/crypto/crypto_handshake_test.cc @@ -40,12 +40,13 @@ class QuicCryptoServerConfigPeer { }; TEST(QuicCryptoServerConfigTest, ServerConfig) { - QuicCryptoServerConfig server("source address token secret"); + QuicRandom* rand = QuicRandom::GetInstance(); + QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand); MockClock clock; scoped_ptr<CryptoHandshakeMessage>( - server.AddDefaultConfig(QuicRandom::GetInstance(), &clock, - QuicCryptoServerConfig::kDefaultExpiry)); + server.AddDefaultConfig(rand, &clock, + QuicCryptoServerConfig::ConfigOptions())); } TEST(QuicCryptoServerConfigTest, SourceAddressTokens) { @@ -54,13 +55,13 @@ TEST(QuicCryptoServerConfigTest, SourceAddressTokens) { return; } - QuicCryptoServerConfig server("source address token secret"); + QuicRandom* rand = QuicRandom::GetInstance(); + QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand); IPAddressNumber ip; CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip)); IPEndPoint ip4 = IPEndPoint(ip, 1); CHECK(ParseIPLiteralToNumber("2001:db8:0::42", &ip)); IPEndPoint ip6 = IPEndPoint(ip, 2); - QuicRandom* rand = QuicRandom::GetInstance(); MockClock clock; clock.AdvanceTime(QuicTime::Delta::FromSeconds(1000000)); QuicCryptoServerConfigPeer peer(&server); diff --git a/net/quic/crypto/crypto_protocol.h b/net/quic/crypto/crypto_protocol.h index 52f0dde..d877ce3 100644 --- a/net/quic/crypto/crypto_protocol.h +++ b/net/quic/crypto/crypto_protocol.h @@ -32,6 +32,8 @@ const QuicTag kCHLO = TAG('C', 'H', 'L', 'O'); // Client hello const QuicTag kSHLO = TAG('S', 'H', 'L', 'O'); // Server hello const QuicTag kSCFG = TAG('S', 'C', 'F', 'G'); // Server config const QuicTag kREJ = TAG('R', 'E', 'J', '\0'); // Reject +const QuicTag kCETV = TAG('C', 'E', 'T', 'V'); // Client encrypted tag-value + // pairs // Key exchange methods const QuicTag kP256 = TAG('P', '2', '5', '6'); // ECDH, Curve P-256 @@ -47,6 +49,7 @@ const QuicTag kINAR = TAG('I', 'N', 'A', 'R'); // Inter arrival // Proof types (i.e. certificate types) const QuicTag kX509 = TAG('X', '5', '0', '9'); // X.509 certificate +const QuicTag kCHID = TAG('C', 'H', 'I', 'D'); // Channel ID. // Client hello tags const QuicTag kVERS = TAG('V', 'E', 'R', 'S'); // Version @@ -72,6 +75,10 @@ const QuicTag kCCS = TAG('C', 'C', 'S', 0); // Common certificate set const QuicTag kCCRT = TAG('C', 'C', 'R', 'T'); // Cached certificate const QuicTag kEXPY = TAG('E', 'X', 'P', 'Y'); // Expiry +// CETV tags +const QuicTag kCIDK = TAG('C', 'I', 'D', 'K'); // ChannelID key +const QuicTag kCIDS = TAG('C', 'I', 'D', 'S'); // ChannelID signature + // Universal tags const QuicTag kPAD = TAG('P', 'A', 'D', '\0'); // Padding diff --git a/net/quic/crypto/crypto_server_config.cc b/net/quic/crypto/crypto_server_config.cc index 1b5ce76..aa30e67 100644 --- a/net/quic/crypto/crypto_server_config.cc +++ b/net/quic/crypto/crypto_server_config.cc @@ -12,6 +12,7 @@ #include "net/quic/crypto/aes_128_gcm_12_decrypter.h" #include "net/quic/crypto/aes_128_gcm_12_encrypter.h" #include "net/quic/crypto/cert_compressor.h" +#include "net/quic/crypto/channel_id.h" #include "net/quic/crypto/crypto_framer.h" #include "net/quic/crypto/crypto_server_config_protobuf.h" #include "net/quic/crypto/crypto_utils.h" @@ -40,18 +41,35 @@ namespace net { // static const char QuicCryptoServerConfig::TESTING[] = "secret string for testing"; +QuicCryptoServerConfig::ConfigOptions::ConfigOptions() + : expiry_time(QuicWallTime::Zero()), + channel_id_enabled(false) { } + QuicCryptoServerConfig::QuicCryptoServerConfig( - StringPiece source_address_token_secret) + StringPiece source_address_token_secret, + QuicRandom* rand) : strike_register_lock_(), + server_nonce_strike_register_lock_(), strike_register_max_entries_(1 << 10), strike_register_window_secs_(600), source_address_token_future_secs_(3600), - source_address_token_lifetime_secs_(86400) { + source_address_token_lifetime_secs_(86400), + server_nonce_strike_register_max_entries_(1 << 10), + server_nonce_strike_register_window_secs_(120) { crypto::HKDF hkdf(source_address_token_secret, StringPiece() /* no salt */, "QUIC source address token key", CryptoSecretBoxer::GetKeySize(), 0 /* no fixed IV needed */); source_address_token_boxer_.SetKey(hkdf.server_write_key()); + + // Generate a random key and orbit for server nonces. + rand->RandBytes(server_nonce_orbit_, sizeof(server_nonce_orbit_)); + const size_t key_size = server_nonce_boxer_.GetKeySize(); + scoped_ptr<uint8[]> key_bytes(new uint8[key_size]); + rand->RandBytes(key_bytes.get(), key_size); + + server_nonce_boxer_.SetKey( + StringPiece(reinterpret_cast<char*>(key_bytes.get()), key_size)); } QuicCryptoServerConfig::~QuicCryptoServerConfig() { @@ -62,7 +80,7 @@ QuicCryptoServerConfig::~QuicCryptoServerConfig() { QuicServerConfigProtobuf* QuicCryptoServerConfig::DefaultConfig( QuicRandom* rand, const QuicClock* clock, - uint64 expiry_time) { + const ConfigOptions& options) { CryptoHandshakeMessage msg; const string curve25519_private_key = @@ -94,14 +112,14 @@ QuicServerConfigProtobuf* QuicCryptoServerConfig::DefaultConfig( msg.SetValue(kVERS, static_cast<uint16>(0)); msg.SetStringPiece(kPUBS, encoded_public_values); - if (expiry_time == 0) { + if (options.expiry_time.IsZero()) { const QuicWallTime now = clock->WallNow(); const QuicWallTime expiry = now.Add(QuicTime::Delta::FromSeconds( 60 * 60 * 24 * 180 /* 180 days, ~six months */)); const uint64 expiry_seconds = expiry.ToUNIXSeconds(); msg.SetValue(kEXPY, expiry_seconds); } else { - msg.SetValue(kEXPY, expiry_time); + msg.SetValue(kEXPY, options.expiry_time.ToUNIXSeconds()); } char scid_bytes[16]; @@ -112,6 +130,10 @@ QuicServerConfigProtobuf* QuicCryptoServerConfig::DefaultConfig( rand->RandBytes(orbit_bytes, sizeof(orbit_bytes)); msg.SetStringPiece(kORBT, StringPiece(orbit_bytes, sizeof(orbit_bytes))); + if (options.channel_id_enabled) { + msg.SetTaglist(kPDMD, kCHID, 0); + } + scoped_ptr<QuicData> serialized(CryptoFramer::ConstructHandshakeMessage(msg)); scoped_ptr<QuicServerConfigProtobuf> config(new QuicServerConfigProtobuf); @@ -187,6 +209,18 @@ CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig( return NULL; } + const QuicTag* proof_demand_tags; + size_t num_proof_demand_tags; + if (msg->GetTaglist(kPDMD, &proof_demand_tags, &num_proof_demand_tags) == + QUIC_NO_ERROR) { + for (size_t i = 0; i < num_proof_demand_tags; i++) { + if (proof_demand_tags[i] == kCHID) { + config->channel_id_enabled = true; + break; + } + } + } + for (size_t i = 0; i < kexs_len; i++) { const QuicTag tag = kexs_tags[i]; string private_key; @@ -252,6 +286,8 @@ CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig( return NULL; } + // FIXME(agl): this is mismatched with |DefaultConfig|, which generates a + // random id. scoped_ptr<SecureHash> sha256(SecureHash::Create(SecureHash::SHA256)); sha256->Update(protobuf->config().data(), protobuf->config().size()); char id_bytes[16]; @@ -267,9 +303,9 @@ CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig( CryptoHandshakeMessage* QuicCryptoServerConfig::AddDefaultConfig( QuicRandom* rand, const QuicClock* clock, - uint64 expiry_time) { + const ConfigOptions& options) { scoped_ptr<QuicServerConfigProtobuf> config( - DefaultConfig(rand, clock, expiry_time)); + DefaultConfig(rand, clock, options)); return AddConfig(config.get()); } @@ -311,15 +347,14 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( valid_source_address_token = true; } - const string fresh_source_address_token = - NewSourceAddressToken(client_ip, rand, now); + StringPiece sni; + if (client_hello.GetStringPiece(kSNI, &sni) && + !CryptoUtils::IsValidSNI(sni)) { + *error_details = "Invalid SNI name"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } - // If we previously sent a REJ to this client then we may have stored a - // server nonce in |params|. In which case, we know that the connection - // is unique because the server nonce will be mixed into the key generation. - bool unique_by_server_nonce = !params->server_nonce.empty(); - // If we can't ensure uniqueness by a server nonce, then we will try and use - // the strike register. + // The client nonce is used first to try and establish uniqueness. bool unique_by_strike_register = false; StringPiece client_nonce; @@ -334,7 +369,8 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( strike_register_max_entries_, static_cast<uint32>(now.ToUNIXSeconds()), strike_register_window_secs_, - config->orbit)); + config->orbit, + StrikeRegister::DENY_REQUESTS_AT_STARTUP)); } unique_by_strike_register = strike_register_->Insert( reinterpret_cast<const uint8*>(client_nonce.data()), @@ -343,23 +379,21 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( StringPiece server_nonce; client_hello.GetStringPiece(kServerNonceTag, &server_nonce); - const bool server_nonce_matches = server_nonce == params->server_nonce; - - out->Clear(); - StringPiece sni; - if (client_hello.GetStringPiece(kSNI, &sni) && - !CryptoUtils::IsValidSNI(sni)) { - *error_details = "Invalid SNI name"; - return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + // If the client nonce didn't establish uniqueness then an echoed server + // nonce may. + bool unique_by_server_nonce = false; + if (!unique_by_strike_register && !server_nonce.empty()) { + unique_by_server_nonce = ValidateServerNonce(server_nonce, now); } + out->Clear(); + StringPiece scid; if (!client_hello.GetStringPiece(kSCID, &scid) || scid.as_string() != config->id || !valid_source_address_token || !client_nonce_well_formed || - !server_nonce_matches || (!unique_by_strike_register && !unique_by_server_nonce)) { // If the client didn't provide a server config ID, or gave the wrong one, @@ -367,14 +401,9 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( // give the client enough information to do better next time. out->set_tag(kREJ); out->SetStringPiece(kSCFG, config->serialized); - out->SetStringPiece(kSourceAddressTokenTag, fresh_source_address_token); - if (params->server_nonce.empty()) { - CryptoUtils::GenerateNonce( - now, rand, StringPiece(reinterpret_cast<const char*>(config->orbit), - sizeof(config->orbit)), - ¶ms->server_nonce); - } - out->SetStringPiece(kServerNonceTag, params->server_nonce); + out->SetStringPiece(kSourceAddressTokenTag, + NewSourceAddressToken(client_ip, rand, now)); + out->SetStringPiece(kServerNonceTag, NewServerNonce(rand, now)); // The client may have requested a certificate chain. const QuicTag* their_proof_demands; @@ -402,11 +431,12 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( const string compressed = CertCompressor::CompressChain( *certs, their_common_set_hashes, their_cached_cert_hashes, - config->common_cert_sets.get()); + config->common_cert_sets); // kMaxUnverifiedSize is the number of bytes that the certificate chain // and signature can consume before we will demand a valid // source-address token. + // TODO(agl): make this configurable. static const size_t kMaxUnverifiedSize = 400; if (valid_source_address_token || signature.size() + compressed.size() < kMaxUnverifiedSize) { @@ -475,6 +505,54 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( client_hello_serialized.length()); hkdf_suffix.append(config->serialized); + StringPiece cetv_ciphertext; + if (config->channel_id_enabled && + client_hello.GetStringPiece(kCETV, &cetv_ciphertext)) { + CryptoHandshakeMessage client_hello_copy(client_hello); + client_hello_copy.Erase(kCETV); + client_hello_copy.Erase(kPAD); + + const QuicData& client_hello_serialized = client_hello_copy.GetSerialized(); + string hkdf_input; + 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(config->serialized); + + CrypterPair crypters; + CryptoUtils::DeriveKeys(params->initial_premaster_secret, params->aead, + client_nonce, server_nonce, hkdf_input, + CryptoUtils::SERVER, &crypters); + + scoped_ptr<QuicData> cetv_plaintext(crypters.decrypter->DecryptPacket( + 0 /* sequence number */, StringPiece() /* associated data */, + cetv_ciphertext)); + if (!cetv_plaintext.get()) { + *error_details = "CETV decryption failure"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + scoped_ptr<CryptoHandshakeMessage> cetv(CryptoFramer::ParseMessage( + cetv_plaintext->AsStringPiece())); + if (!cetv.get()) { + *error_details = "CETV parse error"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + StringPiece key, signature; + if (cetv->GetStringPiece(kCIDK, &key) && + cetv->GetStringPiece(kCIDS, &signature)) { + if (!ChannelIDVerifier::Verify(key, hkdf_input, signature)) { + *error_details = "ChannelID signature failure"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + params->channel_id = key.as_string(); + } + } + string hkdf_input; size_t label_len = strlen(QuicCryptoConfig::kInitialLabel) + 1; hkdf_input.reserve(label_len + hkdf_suffix.size()); @@ -482,7 +560,7 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( hkdf_input.append(hkdf_suffix); CryptoUtils::DeriveKeys(params->initial_premaster_secret, params->aead, - client_nonce, params->server_nonce, hkdf_input, + client_nonce, server_nonce, hkdf_input, CryptoUtils::SERVER, ¶ms->initial_crypters); string forward_secure_public_value; @@ -511,12 +589,13 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( forward_secure_hkdf_input.append(hkdf_suffix); CryptoUtils::DeriveKeys(params->forward_secure_premaster_secret, params->aead, - client_nonce, params->server_nonce, - forward_secure_hkdf_input, CryptoUtils::SERVER, + client_nonce, server_nonce, forward_secure_hkdf_input, + CryptoUtils::SERVER, ¶ms->forward_secure_crypters); out->set_tag(kSHLO); - out->SetStringPiece(kSourceAddressTokenTag, fresh_source_address_token); + out->SetStringPiece(kSourceAddressTokenTag, + NewSourceAddressToken(client_ip, rand, now)); out->SetStringPiece(kPUBS, forward_secure_public_value); return QUIC_NO_ERROR; } @@ -602,7 +681,73 @@ bool QuicCryptoServerConfig::ValidateSourceAddressToken( return true; } -QuicCryptoServerConfig::Config::Config() {} +// kServerNoncePlaintextSize is the number of bytes in an unencrypted server +// nonce. +static const size_t kServerNoncePlaintextSize = + 4 /* timestamp */ + 20 /* random bytes */; + +string QuicCryptoServerConfig::NewServerNonce(QuicRandom* rand, + QuicWallTime now) const { + const uint32 timestamp = static_cast<uint32>(now.ToUNIXSeconds()); + + uint8 server_nonce[kServerNoncePlaintextSize]; + COMPILE_ASSERT(sizeof(server_nonce) > sizeof(timestamp), nonce_too_small); + server_nonce[0] = static_cast<uint8>(timestamp >> 24); + server_nonce[1] = static_cast<uint8>(timestamp >> 16); + server_nonce[2] = static_cast<uint8>(timestamp >> 8); + server_nonce[3] = static_cast<uint8>(timestamp); + rand->RandBytes(&server_nonce[sizeof(timestamp)], + sizeof(server_nonce) - sizeof(timestamp)); + + return server_nonce_boxer_.Box( + rand, + StringPiece(reinterpret_cast<char*>(server_nonce), sizeof(server_nonce))); +} + +bool QuicCryptoServerConfig::ValidateServerNonce(StringPiece token, + QuicWallTime now) const { + string storage; + StringPiece plaintext; + if (!server_nonce_boxer_.Unbox(token, &storage, &plaintext)) { + return false; + } + + // plaintext contains: + // uint32 timestamp + // uint8[20] random bytes + + if (plaintext.size() != kServerNoncePlaintextSize) { + // This should never happen because the value decrypted correctly. + LOG(DFATAL) << "Seemingly valid server nonce had incorrect length."; + return false; + } + + uint8 server_nonce[32]; + memcpy(server_nonce, plaintext.data(), 4); + memcpy(server_nonce + 4, server_nonce_orbit_, sizeof(server_nonce_orbit_)); + memcpy(server_nonce + 4 + sizeof(server_nonce_orbit_), plaintext.data() + 4, + 20); + COMPILE_ASSERT(4 + sizeof(server_nonce_orbit_) + 20 == sizeof(server_nonce), + bad_nonce_buffer_length); + + bool is_unique; + { + base::AutoLock auto_lock(server_nonce_strike_register_lock_); + if (server_nonce_strike_register_.get() == NULL) { + server_nonce_strike_register_.reset(new StrikeRegister( + server_nonce_strike_register_max_entries_, + static_cast<uint32>(now.ToUNIXSeconds()), + server_nonce_strike_register_window_secs_, server_nonce_orbit_, + StrikeRegister::NO_STARTUP_PERIOD_NEEDED)); + } + is_unique = server_nonce_strike_register_->Insert( + server_nonce, static_cast<uint32>(now.ToUNIXSeconds())); + } + + return is_unique; +} + +QuicCryptoServerConfig::Config::Config() : channel_id_enabled(false) { } QuicCryptoServerConfig::Config::~Config() { STLDeleteElements(&key_exchanges); } diff --git a/net/quic/crypto/crypto_server_config.h b/net/quic/crypto/crypto_server_config.h index ed64ee3..7799fdd 100644 --- a/net/quic/crypto/crypto_server_config.h +++ b/net/quic/crypto/crypto_server_config.h @@ -38,30 +38,37 @@ class QuicCryptoServerConfigPeer; // need to consider locking. class NET_EXPORT_PRIVATE QuicCryptoServerConfig { public: - enum { - // kDefaultExpiry can be passed to DefaultConfig to select the default - // expiry time. - kDefaultExpiry = 0, + // ConfigOptions contains options for generating server configs. + struct NET_EXPORT_PRIVATE ConfigOptions { + ConfigOptions(); + + // expiry_time is the time, in UNIX seconds, when the server config will + // expire. If unset, it defaults to the current time plus six months. + QuicWallTime expiry_time; + // channel_id_enabled controls whether the server config will indicate + // support for ChannelIDs. + bool channel_id_enabled; }; // |source_address_token_secret|: secret key material used for encrypting and // decrypting source address tokens. It can be of any length as it is fed // into a KDF before use. In tests, use TESTING. - explicit QuicCryptoServerConfig( - base::StringPiece source_address_token_secret); + // |server_nonce_entropy|: an entropy source used to generate the orbit and + // key for server nonces, which are always local to a given instance of a + // server. + QuicCryptoServerConfig(base::StringPiece source_address_token_secret, + QuicRandom* server_nonce_entropy); ~QuicCryptoServerConfig(); // TESTING is a magic parameter for passing to the constructor in tests. static const char TESTING[]; // DefaultConfig generates a QuicServerConfigProtobuf protobuf suitable for - // using in tests. If |expiry_time| is non-zero then it's used as the expiry - // for the server config in UNIX epoch seconds. Otherwise the default expiry - // time is six months from now. + // using in tests. static QuicServerConfigProtobuf* DefaultConfig( QuicRandom* rand, const QuicClock* clock, - uint64 expiry_time); + const ConfigOptions& options); // AddConfig adds a QuicServerConfigProtobuf to the availible configurations. // It returns the SCFG message from the config if successful. The caller @@ -74,7 +81,7 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { CryptoHandshakeMessage* AddDefaultConfig( QuicRandom* rand, const QuicClock* clock, - uint64 expiry_time); + const ConfigOptions& options); // ProcessClientHello processes |client_hello| and decides whether to accept // or reject the connection. If the connection is to be accepted, |out| is @@ -159,6 +166,10 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // tag_value_map contains the raw key/value pairs for the config. QuicTagValueMap tag_value_map; + // channel_id_enabled is true if the config in |serialized| specifies that + // ChannelIDs are supported. + bool channel_id_enabled; + private: DISALLOW_COPY_AND_ASSIGN(Config); }; @@ -176,6 +187,17 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { const IPEndPoint& ip, QuicWallTime now) const; + // NewServerNonce generates and encrypts a random nonce. + std::string NewServerNonce(QuicRandom* rand, QuicWallTime now) const; + + // ValidateServerNonce decrypts |token| and verifies that it hasn't been + // previously used and is recent enough that it is plausible that it was part + // of a very recently provided rejection ("recent" will be on the order of + // 10-30 seconds). If so, it records that it has been used and returns true. + // Otherwise it returns false. + bool ValidateServerNonce(base::StringPiece echoed_server_nonce, + QuicWallTime now) const; + std::map<ServerConfigID, Config*> configs_; ServerConfigID active_config_; @@ -189,6 +211,21 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // that are given to clients. CryptoSecretBoxer source_address_token_boxer_; + // server_nonce_boxer_ is used to encrypt and validate suggested server + // nonces. + CryptoSecretBoxer server_nonce_boxer_; + + // server_nonce_orbit_ contains the random, per-server orbit values that this + // server will use to generate server nonces (the moral equivalent of a SYN + // cookies). + uint8 server_nonce_orbit_[8]; + + mutable base::Lock server_nonce_strike_register_lock_; + // server_nonce_strike_register_ contains a data structure that keeps track of + // previously observed server nonces from this server, in order to prevent + // replay attacks. + mutable scoped_ptr<StrikeRegister> server_nonce_strike_register_; + // proof_source_ contains an object that can provide certificate chains and // signatures. scoped_ptr<ProofSource> proof_source_; @@ -203,6 +240,8 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { uint32 strike_register_window_secs_; uint32 source_address_token_future_secs_; uint32 source_address_token_lifetime_secs_; + uint32 server_nonce_strike_register_max_entries_; + uint32 server_nonce_strike_register_window_secs_; }; } // namespace net diff --git a/net/quic/crypto/crypto_server_test.cc b/net/quic/crypto/crypto_server_test.cc index 7ab0efe..2d5c9f4 100644 --- a/net/quic/crypto/crypto_server_test.cc +++ b/net/quic/crypto/crypto_server_test.cc @@ -17,14 +17,15 @@ class CryptoServerTest : public ::testing::Test { public: CryptoServerTest() : rand_(QuicRandom::GetInstance()), - config_(QuicCryptoServerConfig::TESTING), + config_(QuicCryptoServerConfig::TESTING, rand_), addr_(ParseIPLiteralToNumber("192.0.2.33", &ip_) ? ip_ : IPAddressNumber(), 1) { } virtual void SetUp() { scoped_ptr<CryptoHandshakeMessage> msg( - config_.AddDefaultConfig(rand_, &clock_, 0)); + config_.AddDefaultConfig(rand_, &clock_, + QuicCryptoServerConfig::ConfigOptions())); } void ShouldSucceed(const CryptoHandshakeMessage& message) { diff --git a/net/quic/crypto/strike_register.cc b/net/quic/crypto/strike_register.cc index 95f358c..4d3ce41 100644 --- a/net/quic/crypto/strike_register.cc +++ b/net/quic/crypto/strike_register.cc @@ -55,22 +55,37 @@ class StrikeRegister::InternalNode { uint32 data_[2]; }; +// kCreationTimeFromInternalEpoch contains the number of seconds between the +// start of the internal epoch and |creation_time_external_|. This allows us +// to consider times that are before |creation_time_external_|. +static const uint32 kCreationTimeFromInternalEpoch = 63115200.0; // 2 years. + StrikeRegister::StrikeRegister(unsigned max_entries, uint32 current_time, uint32 window_secs, - const uint8 orbit[8]) + const uint8 orbit[8], + StartupType startup) : max_entries_(max_entries), window_secs_(window_secs), // The horizon is initially set |window_secs| into the future because, if // we just crashed, then we may have accepted nonces in the span // [current_time...current_time+window_secs) and so we conservatively - // reject the whole timespan. - horizon_(current_time + window_secs) { + // reject the whole timespan unless |startup| tells us otherwise. + creation_time_external_(current_time), + internal_epoch_(current_time > kCreationTimeFromInternalEpoch + ? current_time - kCreationTimeFromInternalEpoch + : 0), + horizon_(ExternalTimeToInternal(current_time) + window_secs), + horizon_valid_(startup == DENY_REQUESTS_AT_STARTUP) { memcpy(orbit_, orbit, sizeof(orbit_)); + // TODO(rtenneti): Remove the following check, Added the following to silence + // "is not used" error. + CHECK_GE(creation_time_external_, 0u); + // We only have 23 bits of index available. CHECK_LT(max_entries, 1u << 23); - CHECK_GT(max_entries, 1u); // There must be at least two entries. + CHECK_GT(max_entries, 1u); // There must be at least two entries. CHECK_EQ(sizeof(InternalNode), 8u); // in case of compiler changes. internal_nodes_ = new InternalNode[max_entries]; external_nodes_.reset(new uint8[kExternalNodeSize * max_entries]); @@ -98,44 +113,33 @@ void StrikeRegister::Reset() { } bool StrikeRegister::Insert(const uint8 nonce[32], - const uint32 current_time) { - // If current_time is very small or very large then we assume that we have - // just rolled over / are about to roll over and it's 2038 or 2106. Since - // we don't deal with this situation we flush everything and start over. - // This means that we reject everything for 2 * |window_secs_| every 68 - // years. - if (current_time < window_secs_ || - current_time + window_secs_ < current_time) { - if (internal_node_head_ != kNil) { - Reset(); - } - horizon_ = current_time; - return false; - } + const uint32 current_time_external) { + const uint32 current_time = ExternalTimeToInternal(current_time_external); // Check to see if the orbit is correct. if (memcmp(nonce + sizeof(current_time), orbit_, sizeof(orbit_))) { return false; } - const uint32 nonce_time = TimeFromBytes(nonce); + const uint32 nonce_time = ExternalTimeToInternal(TimeFromBytes(nonce)); // We have dropped one or more nonces with a time value of |horizon_|, so // we have to reject anything with a timestamp less than or equal to that. - if (nonce_time <= horizon_) { + if (horizon_valid_ && nonce_time <= horizon_) { return false; } // Check that the timestamp is in the current window. - if (nonce_time < (current_time - window_secs_) || + if ((current_time > window_secs_ && + nonce_time < (current_time - window_secs_)) || nonce_time > (current_time + window_secs_)) { return false; } // We strip the orbit out of the nonce. uint8 value[24]; - memcpy(value, nonce, sizeof(current_time)); - memcpy(value + sizeof(current_time), - nonce + sizeof(current_time) + sizeof(orbit_), - sizeof(value) - sizeof(current_time)); + memcpy(value, &nonce_time, sizeof(nonce_time)); + memcpy(value + sizeof(nonce_time), + nonce + sizeof(nonce_time) + sizeof(orbit_), + sizeof(value) - sizeof(nonce_time)); // Find the best match to |value| in the crit-bit tree. The best match is // simply the value which /could/ match |value|, if any does, so we still @@ -277,6 +281,10 @@ uint32 StrikeRegister::TimeFromBytes(const uint8 d[4]) { static_cast<uint32>(d[3]); } +uint32 StrikeRegister::ExternalTimeToInternal(uint32 external_time) { + return external_time - internal_epoch_; +} + uint32 StrikeRegister::BestMatch(const uint8 v[24]) const { if (internal_node_head_ == kNil) { return kNil; diff --git a/net/quic/crypto/strike_register.h b/net/quic/crypto/strike_register.h index 1285ede..401c850 100644 --- a/net/quic/crypto/strike_register.h +++ b/net/quic/crypto/strike_register.h @@ -30,18 +30,44 @@ namespace net { // This code is based on djb's public domain critbit tree from qhasm. // // A critbit tree has external and internal nodes. External nodes are just the -// nonce values (which are stored without the orbit values included). Internal -// nodes contain the bit number at which the tree is branching and exactly two -// children. The critical bit is stored as a byte number and a byte -// (|otherbits|) which has all the bits set /except/ the one in question. +// nonce values (which are stored with internal times, see below, and without +// the orbit values included). Internal nodes contain the bit number at which +// the tree is branching and exactly two children. The critical bit is stored +// as a byte number and a byte (|otherbits|) which has all the bits set +// /except/ the one in question. // // Internal nodes have exactly two children: an internal node with only a // single child would be useless. // // The branching bit number (considering the MSB to be the 1st bit) is // monotonically increasing as you go down the tree. +// +// There are two distinct time representations used. External times are those +// which are exposed to the users of this class. They are expected to be a +// count of the number of seconds since the UNIX epoch. Internal times are a +// count of the number of seconds since a point in time a couple of years +// before the creation time given to the constructor. (See +// |ExternalTimeToInternal|) This avoids having to worry about overflow since +// we assume that no process will run for 130 years. class NET_EXPORT_PRIVATE StrikeRegister { public: + enum StartupType { + // DENY_REQUESTS_AT_STARTUP is the typical mode for a strike register. + // Because servers can crash and the strike-register memory-based, the + // state of the strike-register may be lost at any time. Thus the previous + // instance of the server may have accepted an nonce with time + // now+window_secs, which was forgotten in the crash. Therefore + // DENY_REQUESTS_AT_STARTUP causes the strike-register to reject all + // requests timestampped before window_secs + the creation time (the + // quiescent period). + DENY_REQUESTS_AT_STARTUP, + // NO_STARTUP_PERIOD_NEEDED indicates that no quiescent period is required. + // This may be because the strike-register is using an orbit randomly + // generated at startup and therefore nonces accepted by the previous + // instance of the strike-register are invalid for that reason. + NO_STARTUP_PERIOD_NEEDED, + }; + // An external node takes 24 bytes as we don't record the orbit. static const uint32 kExternalNodeSize; @@ -56,18 +82,18 @@ class NET_EXPORT_PRIVATE StrikeRegister { static const uint32 kExternalFlag; // Construct a new set which can hold, at most, |max_entries| (which must be - // less than 2**23). Once constructed, all nonces created before - // |current_time| (in seconds) will be rejected. In the future, all nonces - // that are outside +/- |window_secs| from the current time will be rejected. - // Additionally, all nonces that have an orbit value other than |orbit| will - // be rejected. + // less than 2**23). See the comments around StartupType about initial + // behaviour. Otherwise, all nonces that are outside +/- |window_secs| from + // the current time will be rejected. Additionally, all nonces that have an + // orbit value other than |orbit| will be rejected. // // (Note that this code is independent of the actual units of time used, but // you should use seconds.) StrikeRegister(unsigned max_entries, - uint32 current_time, + uint32 current_time_external, uint32 window_secs, - const uint8 orbit[8]); + const uint8 orbit[8], + StartupType startup); ~StrikeRegister(); @@ -98,6 +124,10 @@ class NET_EXPORT_PRIVATE StrikeRegister { // TimeFromBytes returns a big-endian uint32 from |d|. static uint32 TimeFromBytes(const uint8 d[4]); + // ExternalTimeToInternal converts an external time value into an internal + // time value using |creation_time_external_|. + uint32 ExternalTimeToInternal(uint32 external_time); + // BestMatch returns either kNil, or an external node index which could // possibly match |v|. uint32 BestMatch(const uint8 v[24]) const; @@ -130,8 +160,16 @@ class NET_EXPORT_PRIVATE StrikeRegister { const uint32 max_entries_; const uint32 window_secs_; + // creation_time_external_ contains the uint32, external time when this + // object was created (i.e. the value passed to the constructor). This is + // used to translate external times to internal times. + const uint32 creation_time_external_; + // internal_epoch_ contains the external time value of the start of internal + // time. + const uint32 internal_epoch_; uint8 orbit_[8]; uint32 horizon_; + bool horizon_valid_; uint32 internal_node_free_head_; uint32 external_node_free_head_; diff --git a/net/quic/crypto/strike_register_test.cc b/net/quic/crypto/strike_register_test.cc index d805269..3d3c3c4 100644 --- a/net/quic/crypto/strike_register_test.cc +++ b/net/quic/crypto/strike_register_test.cc @@ -25,12 +25,14 @@ NonceSetTimeAndOrbit(uint8 nonce[32], unsigned time, const uint8 orbit[8]) { nonce[2] = time >> 8; nonce[3] = time; memcpy(nonce + 4, orbit, 8); + memset(nonce + 12, 0, 20); } TEST(StrikeRegisterTest, SimpleHorizon) { // The set must reject values created on or before its own creation time. StrikeRegister set(10 /* max size */, 1000 /* current time */, - 100 /* window secs */, kOrbit); + 100 /* window secs */, kOrbit, + StrikeRegister::DENY_REQUESTS_AT_STARTUP); uint8 nonce[32]; NonceSetTimeAndOrbit(nonce, 999, kOrbit); ASSERT_FALSE(set.Insert(nonce, 1000)); @@ -38,10 +40,23 @@ TEST(StrikeRegisterTest, SimpleHorizon) { ASSERT_FALSE(set.Insert(nonce, 1000)); } +TEST(StrikeRegisterTest, NoStartupMode) { + // Check that a strike register works immediately if NO_STARTUP_PERIOD_NEEDED + // is specified. + StrikeRegister set(10 /* max size */, 0 /* current time */, + 100 /* window secs */, kOrbit, + StrikeRegister::NO_STARTUP_PERIOD_NEEDED); + uint8 nonce[32]; + NonceSetTimeAndOrbit(nonce, 0, kOrbit); + ASSERT_TRUE(set.Insert(nonce, 0)); + ASSERT_FALSE(set.Insert(nonce, 0)); +} + TEST(StrikeRegisterTest, WindowFuture) { // The set must reject values outside the window. StrikeRegister set(10 /* max size */, 1000 /* current time */, - 100 /* window secs */, kOrbit); + 100 /* window secs */, kOrbit, + StrikeRegister::DENY_REQUESTS_AT_STARTUP); uint8 nonce[32]; NonceSetTimeAndOrbit(nonce, 1101, kOrbit); ASSERT_FALSE(set.Insert(nonce, 1000)); @@ -52,7 +67,8 @@ TEST(StrikeRegisterTest, WindowFuture) { TEST(StrikeRegisterTest, BadOrbit) { // The set must reject values with the wrong orbit StrikeRegister set(10 /* max size */, 1000 /* current time */, - 100 /* window secs */, kOrbit); + 100 /* window secs */, kOrbit, + StrikeRegister::DENY_REQUESTS_AT_STARTUP); uint8 nonce[32]; static const uint8 kBadOrbit[8] = { 0, 0, 0, 0, 1, 1, 1, 1 }; NonceSetTimeAndOrbit(nonce, 1101, kBadOrbit); @@ -61,7 +77,8 @@ TEST(StrikeRegisterTest, BadOrbit) { TEST(StrikeRegisterTest, OneValue) { StrikeRegister set(10 /* max size */, 1000 /* current time */, - 100 /* window secs */, kOrbit); + 100 /* window secs */, kOrbit, + StrikeRegister::DENY_REQUESTS_AT_STARTUP); uint8 nonce[32]; NonceSetTimeAndOrbit(nonce, 1101, kOrbit); ASSERT_TRUE(set.Insert(nonce, 1100)); @@ -70,7 +87,8 @@ TEST(StrikeRegisterTest, OneValue) { TEST(StrikeRegisterTest, RejectDuplicate) { // The set must reject values with the wrong orbit StrikeRegister set(10 /* max size */, 1000 /* current time */, - 100 /* window secs */, kOrbit); + 100 /* window secs */, kOrbit, + StrikeRegister::DENY_REQUESTS_AT_STARTUP); uint8 nonce[32]; memset(nonce, 0, sizeof(nonce)); NonceSetTimeAndOrbit(nonce, 1101, kOrbit); @@ -80,7 +98,8 @@ TEST(StrikeRegisterTest, RejectDuplicate) { TEST(StrikeRegisterTest, HorizonUpdating) { StrikeRegister set(5 /* max size */, 1000 /* current time */, - 100 /* window secs */, kOrbit); + 100 /* window secs */, kOrbit, + StrikeRegister::DENY_REQUESTS_AT_STARTUP); uint8 nonce[6][32]; for (unsigned i = 0; i < 5; i++) { NonceSetTimeAndOrbit(nonce[i], 1101, kOrbit); @@ -103,7 +122,8 @@ TEST(StrikeRegisterTest, HorizonUpdating) { TEST(StrikeRegisterTest, InsertMany) { StrikeRegister set(5000 /* max size */, 1000 /* current time */, - 500 /* window secs */, kOrbit); + 500 /* window secs */, kOrbit, + StrikeRegister::DENY_REQUESTS_AT_STARTUP); uint8 nonce[32]; NonceSetTimeAndOrbit(nonce, 1101, kOrbit); @@ -129,24 +149,20 @@ class SlowStrikeRegister { uint32 window_secs, const uint8 orbit[8]) : max_entries_(max_entries), window_secs_(window_secs), - horizon_(current_time + window_secs) { + creation_time_(current_time), + horizon_(ExternalTimeToInternal(current_time + window_secs)) { memcpy(orbit_, orbit, sizeof(orbit_)); } - bool Insert(const uint8 nonce_bytes[32], const uint32 current_time) { - // Same overflow and underflow check as the real StrikeRegister. - if (current_time < window_secs_ || - current_time + window_secs_ < current_time) { - nonces_.clear(); - horizon_ = current_time; - return false; - } + bool Insert(const uint8 nonce_bytes[32], const uint32 current_time_external) { + const uint32 current_time = ExternalTimeToInternal(current_time_external); // Check to see if the orbit is correct. if (memcmp(nonce_bytes + 4, orbit_, sizeof(orbit_))) { return false; } - const uint32 nonce_time = TimeFromBytes(nonce_bytes); + const uint32 nonce_time = + ExternalTimeToInternal(TimeFromBytes(nonce_bytes)); // We have dropped one or more nonces with a time value of |horizon_|, so // we have to reject anything with a timestamp less than or equal to that. if (nonce_time <= horizon_) { @@ -154,12 +170,19 @@ class SlowStrikeRegister { } // Check that the timestamp is in the current window. - if (nonce_time < (current_time - window_secs_) || + if ((current_time > window_secs_ && + nonce_time < (current_time - window_secs_)) || nonce_time > (current_time + window_secs_)) { return false; } - const string nonce(reinterpret_cast<const char*>(nonce_bytes), 32); + string nonce; + nonce.reserve(32); + nonce += + string(reinterpret_cast<const char*>(&nonce_time), sizeof(nonce_time)); + nonce += + string(reinterpret_cast<const char*>(nonce_bytes) + sizeof(nonce_time), + 32 - sizeof(nonce_time)); set<string>::const_iterator it = nonces_.find(nonce); if (it != nonces_.end()) { @@ -183,6 +206,16 @@ class SlowStrikeRegister { static_cast<uint32>(d[3]); } + uint32 ExternalTimeToInternal(uint32 external_time) { + static const uint32 kCreationTimeFromInternalEpoch = 63115200.0; + uint32 internal_epoch = 0; + if (creation_time_ > kCreationTimeFromInternalEpoch) { + internal_epoch = creation_time_ - kCreationTimeFromInternalEpoch; + } + + return external_time - internal_epoch; + } + void DropOldestEntry() { set<string>::iterator oldest = nonces_.begin(), it; uint32 oldest_time = @@ -203,6 +236,7 @@ class SlowStrikeRegister { const unsigned max_entries_; const unsigned window_secs_; + const uint32 creation_time_; uint8 orbit_[8]; uint32 horizon_; @@ -215,7 +249,8 @@ TEST(StrikeRegisterStressTest, Stress) { unsigned max_entries = 64; uint32 current_time = 10000, window = 200; scoped_ptr<StrikeRegister> s1( - new StrikeRegister(max_entries, current_time, window, kOrbit)); + new StrikeRegister(max_entries, current_time, window, kOrbit, + StrikeRegister::DENY_REQUESTS_AT_STARTUP)); scoped_ptr<SlowStrikeRegister> s2( new SlowStrikeRegister(max_entries, current_time, window, kOrbit)); uint64 i; @@ -230,7 +265,8 @@ TEST(StrikeRegisterStressTest, Stress) { max_entries = rand() % 300 + 2; current_time = rand() % 10000; window = rand() % 500; - s1.reset(new StrikeRegister(max_entries, current_time, window, kOrbit)); + s1.reset(new StrikeRegister(max_entries, current_time, window, kOrbit, + StrikeRegister::DENY_REQUESTS_AT_STARTUP)); s2.reset( new SlowStrikeRegister(max_entries, current_time, window, kOrbit)); } diff --git a/net/quic/quic_client_session_test.cc b/net/quic/quic_client_session_test.cc index 4f90b3e..afd8eab 100644 --- a/net/quic/quic_client_session_test.cc +++ b/net/quic/quic_client_session_test.cc @@ -30,7 +30,7 @@ class QuicClientSessionTest : public ::testing::Test { : guid_(1), connection_(new PacketSavingConnection(guid_, IPEndPoint(), false)), session_(connection_, NULL, NULL, NULL, kServerHostname, - QuicConfig(), &crypto_config_, &net_log_) { + DefaultQuicConfig(), &crypto_config_, &net_log_) { session_.config()->SetDefaults(); crypto_config_.SetDefaults(); QuicClientSessionPeer::SetMaxOpenStreams(&session_, 1, 1); diff --git a/net/quic/quic_config.cc b/net/quic/quic_config.cc index eacde84..545d4c1 100644 --- a/net/quic/quic_config.cc +++ b/net/quic/quic_config.cc @@ -223,7 +223,8 @@ QuicConfig::QuicConfig() : idle_connection_state_lifetime_seconds_( kICSL, QuicNegotiableValue::PRESENCE_REQUIRED), keepalive_timeout_seconds_(kKATO, QuicNegotiableValue::PRESENCE_OPTIONAL), - max_streams_per_connection_(kMSPC, QuicNegotiableValue::PRESENCE_REQUIRED) { + 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); } @@ -267,6 +268,15 @@ uint32 QuicConfig::max_streams_per_connection() const { return max_streams_per_connection_.GetUint32(); } +void QuicConfig::set_max_time_before_crypto_handshake( + QuicTime::Delta max_time_before_crypto_handshake) { + max_time_before_crypto_handshake_ = max_time_before_crypto_handshake; +} + +QuicTime::Delta QuicConfig::max_time_before_crypto_handshake() const { + return max_time_before_crypto_handshake_; +} + bool QuicConfig::negotiated() { return congestion_control_.negotiated() && idle_connection_state_lifetime_seconds_.negotiated() && @@ -277,11 +287,13 @@ bool QuicConfig::negotiated() { void QuicConfig::SetDefaults() { congestion_control_.set(QuicTagVector(1, kQBIC), kQBIC); idle_connection_state_lifetime_seconds_.set(kDefaultTimeoutSecs, - kDefaultTimeoutSecs); + kDefaultInitialTimeoutSecs); // kKATO is optional. Return 0 if not negotiated. keepalive_timeout_seconds_.set(0, 0); max_streams_per_connection_.set(kDefaultMaxStreamsPerConnection, kDefaultMaxStreamsPerConnection); + max_time_before_crypto_handshake_ = QuicTime::Delta::FromSeconds( + kDefaultMaxTimeForCryptoHandshakeSecs); } void QuicConfig::ToHandshakeMessage(CryptoHandshakeMessage* out) const { diff --git a/net/quic/quic_config.h b/net/quic/quic_config.h index 0f11493..c07f332 100644 --- a/net/quic/quic_config.h +++ b/net/quic/quic_config.h @@ -154,6 +154,11 @@ class NET_EXPORT_PRIVATE QuicConfig { uint32 max_streams_per_connection() const; + void set_max_time_before_crypto_handshake( + QuicTime::Delta max_time_before_crypto_handshake); + + QuicTime::Delta max_time_before_crypto_handshake() const; + bool negotiated(); // SetDefaults sets the members to sensible, default values. @@ -182,6 +187,9 @@ class NET_EXPORT_PRIVATE QuicConfig { QuicNegotiableUint32 keepalive_timeout_seconds_; // Maximum number of streams that the connection can support. QuicNegotiableUint32 max_streams_per_connection_; + // Maximum time till the session can be alive before crypto handshake is + // finished. (Not negotiated). + QuicTime::Delta max_time_before_crypto_handshake_; }; } // namespace net diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc index 63369eb..0698dd3 100644 --- a/net/quic/quic_connection.cc +++ b/net/quic/quic_connection.cc @@ -84,7 +84,10 @@ QuicConnection::QuicConnection(QuicGuid guid, debug_visitor_(NULL), packet_creator_(guid_, &framer_, random_generator_, is_server), packet_generator_(this, &packet_creator_), - timeout_(QuicTime::Delta::FromSeconds(kDefaultInitialTimeoutSecs)), + idle_network_timeout_( + QuicTime::Delta::FromSeconds(kDefaultInitialTimeoutSecs)), + overall_connection_timeout_(QuicTime::Delta::Infinite()), + creation_time_(clock_->ApproximateNow()), time_of_last_received_packet_(clock_->ApproximateNow()), time_of_last_sent_packet_(clock_->ApproximateNow()), time_largest_observed_(QuicTime::Zero()), @@ -98,7 +101,7 @@ QuicConnection::QuicConnection(QuicGuid guid, send_ack_in_response_to_packet_(false), address_migrating_(false) { helper_->SetConnection(this); - helper_->SetTimeoutAlarm(timeout_); + helper_->SetTimeoutAlarm(idle_network_timeout_); framer_.set_visitor(this); framer_.set_entropy_calculator(&entropy_manager_); outgoing_ack_.sent_info.least_unacked = 0; @@ -122,6 +125,7 @@ QuicConnection::~QuicConnection() { it != queued_packets_.end(); ++it) { delete it->packet; } + LOG(ERROR) << "Quic connection " << write_blocked_; } bool QuicConnection::SelectMutualVersion( @@ -313,6 +317,7 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { } void QuicConnection::OnFecProtectedPayload(StringPiece payload) { + DCHECK_EQ(IN_FEC_GROUP, last_header_.is_in_fec_group); DCHECK_NE(0u, last_header_.fec_group); QuicFecGroup* group = GetFecGroup(); if (group != NULL) { @@ -349,7 +354,7 @@ bool QuicConnection::OnAckFrame(const QuicAckFrame& incoming_ack) { received_truncated_ack_ = incoming_ack.received_info.missing_packets.size() >= - QuicFramer::GetMaxUnackedPackets(last_header_.public_header.version_flag); + QuicFramer::GetMaxUnackedPackets(last_header_); UpdatePacketInformationReceivedByPeer(incoming_ack); UpdatePacketInformationSentByPeer(incoming_ack); @@ -406,8 +411,7 @@ 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_.public_header.version_flag)); + QuicFramer::GetMaxUnackedPackets(last_header_)); if (incoming_ack.sent_info.least_unacked < peer_least_packet_awaiting_ack_) { DLOG(ERROR) << ENDPOINT << "Peer's sent low least_unacked: " @@ -565,11 +569,12 @@ void QuicConnection::UpdatePacketInformationSentByPeer( } void QuicConnection::OnFecData(const QuicFecData& fec) { + DCHECK_EQ(IN_FEC_GROUP, last_header_.is_in_fec_group); DCHECK_NE(0u, last_header_.fec_group); QuicFecGroup* group = GetFecGroup(); if (group != NULL) { group->UpdateFec(last_header_.packet_sequence_number, - last_header_.fec_entropy_flag, fec); + last_header_.entropy_flag, fec); } } @@ -802,8 +807,6 @@ bool QuicConnection::WriteQueuedPackets() { } void QuicConnection::RecordPacketReceived(const QuicPacketHeader& header) { - DLOG(INFO) << ENDPOINT << "Recording received packet: " - << header.packet_sequence_number; QuicPacketSequenceNumber sequence_number = header.packet_sequence_number; DCHECK(IsAwaitingPacket(outgoing_ack_.received_info, sequence_number)); @@ -1259,7 +1262,8 @@ void QuicConnection::MaybeProcessRevivedPacket() { revived_header.public_header.version_flag = false; revived_header.public_header.reset_flag = false; revived_header.fec_flag = false; - revived_header.fec_group = kNoFecOffset; + revived_header.is_in_fec_group = NOT_IN_FEC_GROUP; + revived_header.fec_group = 0; group_map_.erase(last_header_.fec_group); delete group; @@ -1369,12 +1373,25 @@ void QuicConnection::CloseFecGroupsBefore( } bool QuicConnection::HasQueuedData() const { - return !queued_packets_.empty() || packet_generator_.HasQueuedData(); + return !queued_packets_.empty() || packet_generator_.HasQueuedFrames(); } -void QuicConnection::SetConnectionTimeout(QuicTime::Delta timeout) { - timeout_ = timeout; - CheckForTimeout(); +void QuicConnection::SetIdleNetworkTimeout(QuicTime::Delta timeout) { + // if (timeout < idle_network_timeout_) { + idle_network_timeout_ = timeout; + CheckForTimeout(); + // } else { + // idle_network_timeout_ = timeout; + // } +} + +void QuicConnection::SetOverallConnectionTimeout(QuicTime::Delta timeout) { + // if (timeout < overall_connection_timeout_) { + overall_connection_timeout_ = timeout; + CheckForTimeout(); + // } else { + // overall_connection_timeout_ = timeout; + // } } bool QuicConnection::CheckForTimeout() { @@ -1386,12 +1403,38 @@ bool QuicConnection::CheckForTimeout() { DVLOG(1) << ENDPOINT << "last packet " << time_of_last_packet.ToDebuggingValue() << " now:" << now.ToDebuggingValue() - << " delta:" << delta.ToMicroseconds(); - if (delta >= timeout_) { + << " delta:" << delta.ToMicroseconds() + << " network_timeout: " << idle_network_timeout_.ToMicroseconds(); + if (delta >= idle_network_timeout_) { + DVLOG(1) << ENDPOINT << "Connection timedout due to no network activity."; SendConnectionClose(QUIC_CONNECTION_TIMED_OUT); return true; } - helper_->SetTimeoutAlarm(timeout_.Subtract(delta)); + + // Next timeout delta. + QuicTime::Delta timeout = idle_network_timeout_.Subtract(delta); + + if (!overall_connection_timeout_.IsInfinite()) { + QuicTime::Delta connected_time = now.Subtract(creation_time_); + DVLOG(1) << ENDPOINT << "connection time: " + << connected_time.ToMilliseconds() << " overall timeout: " + << overall_connection_timeout_.ToMilliseconds(); + if (connected_time >= overall_connection_timeout_) { + DVLOG(1) << ENDPOINT << + "Connection timedout due to overall connection timeout."; + SendConnectionClose(QUIC_CONNECTION_TIMED_OUT); + return true; + } + + // Take the min timeout. + QuicTime::Delta connection_timeout = + overall_connection_timeout_.Subtract(connected_time); + if (connection_timeout < timeout) { + timeout = connection_timeout; + } + } + + helper_->SetTimeoutAlarm(timeout); return false; } diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h index 773de2a..c37d9ad 100644 --- a/net/quic/quic_connection.h +++ b/net/quic/quic_connection.h @@ -338,7 +338,12 @@ class NET_EXPORT_PRIVATE QuicConnection // Sets (or resets) the idle state connection timeout. Also, checks and times // out the connection if network timer has expired for |timeout|. - void SetConnectionTimeout(QuicTime::Delta timeout); + void SetIdleNetworkTimeout(QuicTime::Delta timeout); + // Sets (or resets) the total time delta the connection can be alive for. + // Also, checks and times out the connection if timer has expired for + // |timeout|. Used to limit the time a connection can be alive before crypto + // handshake finishes. + void SetOverallConnectionTimeout(QuicTime::Delta timeout); // If the connection has timed out, this will close the connection and return // true. Otherwise, it will return false and will reset the timeout alarm. @@ -600,7 +605,11 @@ class NET_EXPORT_PRIVATE QuicConnection QuicPacketGenerator packet_generator_; // Network idle time before we kill of this connection. - QuicTime::Delta timeout_; + QuicTime::Delta idle_network_timeout_; + // Overall connection timeout. + QuicTime::Delta overall_connection_timeout_; + // Connection creation time. + QuicTime creation_time_; // Statistics for this session. QuicConnectionStats stats_; @@ -637,7 +646,6 @@ class NET_EXPORT_PRIVATE QuicConnection // True if the last ack received from the peer may have been truncated. False // otherwise. bool received_truncated_ack_; - bool send_ack_in_response_to_packet_; // Set to true if the udp packet headers have a new self or peer address. diff --git a/net/quic/quic_connection_helper.cc b/net/quic/quic_connection_helper.cc index 1a48ff1..ec2866e 100644 --- a/net/quic/quic_connection_helper.cc +++ b/net/quic/quic_connection_helper.cc @@ -117,7 +117,10 @@ void QuicConnectionHelper::SetSendAlarm(QuicTime alarm_time) { } void QuicConnectionHelper::SetTimeoutAlarm(QuicTime::Delta delay) { - DCHECK(!timeout_alarm_registered_); + // CheckForTimeout will call SetTimeoutAlarm for the remaining time if alarm + // goes off before the delay. + if (timeout_alarm_registered_) + return; timeout_alarm_registered_ = true; task_runner_->PostDelayedTask( FROM_HERE, diff --git a/net/quic/quic_connection_helper_test.cc b/net/quic/quic_connection_helper_test.cc index 9b6c86f..677044c 100644 --- a/net/quic/quic_connection_helper_test.cc +++ b/net/quic/quic_connection_helper_test.cc @@ -186,8 +186,8 @@ class QuicConnectionHelperTest : public ::testing::Test { header_.public_header.version_flag = true; header_.packet_sequence_number = sequence_number; header_.entropy_flag = false; - header_.fec_entropy_flag = false; header_.fec_flag = false; + header_.is_in_fec_group = NOT_IN_FEC_GROUP; header_.fec_group = 0; } diff --git a/net/quic/quic_connection_logger.cc b/net/quic/quic_connection_logger.cc index 288d3dd..8df0594 100644 --- a/net/quic/quic_connection_logger.cc +++ b/net/quic/quic_connection_logger.cc @@ -36,7 +36,6 @@ Value* NetLogQuicPacketHeaderCallback(const QuicPacketHeader* header, base::Uint64ToString(header->packet_sequence_number)); dict->SetInteger("entropy_flag", header->entropy_flag); dict->SetInteger("fec_flag", header->fec_flag); - dict->SetInteger("fec_entropy_flag", header->fec_entropy_flag); dict->SetInteger("fec_group", header->fec_group); return dict; } diff --git a/net/quic/quic_connection_test.cc b/net/quic/quic_connection_test.cc index bee2781..597664d 100644 --- a/net/quic/quic_connection_test.cc +++ b/net/quic/quic_connection_test.cc @@ -46,7 +46,7 @@ const char data2[] = "bar"; const bool kFin = true; const bool kEntropyFlag = true; -const bool kFecEntropyFlag = true; + const QuicPacketEntropyHash kTestEntropyHash = 76; class TestReceiveAlgorithm : public ReceiveAlgorithmInterface { @@ -520,9 +520,9 @@ class QuicConnectionTest : public ::testing::Test { // Sends an FEC packet that covers the packets that would have been sent. size_t ProcessFecPacket(QuicPacketSequenceNumber number, - QuicPacketSequenceNumber min_protected_packet, - bool expect_revival, - bool fec_entropy_flag) { + QuicPacketSequenceNumber min_protected_packet, + bool expect_revival, + bool entropy_flag) { if (expect_revival) { EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillOnce(DoAll( SaveArg<2>(&revived_header_), Return(accept_packet_))); @@ -536,10 +536,10 @@ class QuicConnectionTest : public ::testing::Test { header_.public_header.guid = guid_; header_.public_header.reset_flag = false; header_.public_header.version_flag = false; - header_.entropy_flag = kEntropyFlag; + header_.entropy_flag = entropy_flag; header_.fec_flag = true; - header_.fec_entropy_flag = fec_entropy_flag; header_.packet_sequence_number = number; + header_.is_in_fec_group = IN_FEC_GROUP; header_.fec_group = min_protected_packet; QuicFecData fec_data; fec_data.fec_group = header_.fec_group; @@ -548,6 +548,7 @@ class QuicConnectionTest : public ::testing::Test { // with itself, depending on the number of packets. if (((number - min_protected_packet) % 2) == 0) { for (size_t i = GetStartOfFecProtectedData( + header_.public_header.guid_length, header_.public_header.version_flag); i < data_packet->length(); ++i) { data_packet->mutable_data()[i] ^= data_packet->data()[i]; @@ -604,8 +605,8 @@ class QuicConnectionTest : public ::testing::Test { header_.public_header.version_flag = false; header_.entropy_flag = entropy_flag; header_.fec_flag = false; - header_.fec_entropy_flag = false; header_.packet_sequence_number = number; + header_.is_in_fec_group = fec_group == 0u ? NOT_IN_FEC_GROUP : IN_FEC_GROUP; header_.fec_group = fec_group; QuicFrames frames; @@ -625,7 +626,7 @@ class QuicConnectionTest : public ::testing::Test { header_.public_header.version_flag = false; header_.entropy_flag = false; header_.fec_flag = false; - header_.fec_entropy_flag = false; + header_.is_in_fec_group = fec_group == 0u ? NOT_IN_FEC_GROUP : IN_FEC_GROUP; header_.fec_group = fec_group; QuicConnectionCloseFrame qccf; @@ -937,7 +938,7 @@ TEST_F(QuicConnectionTest, FECSending) { // Limit to one byte per packet. // All packets carry version info till version is negotiated. connection_.options()->max_packet_length = - GetPacketLengthForOneStream(kIncludeVersion, 4); + GetPacketLengthForOneStream(kIncludeVersion, IN_FEC_GROUP, 4); // And send FEC every two packets. connection_.options()->max_packets_per_fec_group = 2; @@ -952,7 +953,7 @@ TEST_F(QuicConnectionTest, FECQueueing) { // Limit to one byte per packet. // All packets carry version info till version is negotiated. connection_.options()->max_packet_length = - GetPacketLengthForOneStream(kIncludeVersion, 4); + GetPacketLengthForOneStream(kIncludeVersion, IN_FEC_GROUP, 4); // And send FEC every two packets. connection_.options()->max_packets_per_fec_group = 2; @@ -1278,14 +1279,14 @@ TEST_F(QuicConnectionTest, DontLatchUnackedPacket) { TEST_F(QuicConnectionTest, ReviveMissingPacketAfterFecPacket) { // Don't send missing packet 1. - ProcessFecPacket(2, 1, true, !kFecEntropyFlag); + ProcessFecPacket(2, 1, true, !kEntropyFlag); EXPECT_FALSE(revived_header_.entropy_flag); } TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacketThenFecPacket) { ProcessFecProtectedPacket(1, false, kEntropyFlag); // Don't send missing packet 2. - ProcessFecPacket(3, 1, true, !kFecEntropyFlag); + ProcessFecPacket(3, 1, true, !kEntropyFlag); EXPECT_TRUE(revived_header_.entropy_flag); } @@ -1293,13 +1294,13 @@ TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacketsThenFecPacket) { ProcessFecProtectedPacket(1, false, !kEntropyFlag); // Don't send missing packet 2. ProcessFecProtectedPacket(3, false, !kEntropyFlag); - ProcessFecPacket(4, 1, true, kFecEntropyFlag); + ProcessFecPacket(4, 1, true, kEntropyFlag); EXPECT_TRUE(revived_header_.entropy_flag); } TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacket) { // Don't send missing packet 1. - ProcessFecPacket(3, 1, false, !kFecEntropyFlag); + ProcessFecPacket(3, 1, false, !kEntropyFlag); // out of order ProcessFecProtectedPacket(2, true, !kEntropyFlag); EXPECT_FALSE(revived_header_.entropy_flag); @@ -1308,7 +1309,7 @@ TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacket) { TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPackets) { ProcessFecProtectedPacket(1, false, !kEntropyFlag); // Don't send missing packet 2. - ProcessFecPacket(6, 1, false, kFecEntropyFlag); + ProcessFecPacket(6, 1, false, kEntropyFlag); ProcessFecProtectedPacket(3, false, kEntropyFlag); ProcessFecProtectedPacket(4, false, kEntropyFlag); ProcessFecProtectedPacket(5, true, !kEntropyFlag); @@ -1796,7 +1797,7 @@ TEST_F(QuicConnectionTest, TestQueueLimitsOnSendStreamData) { // Limit to one byte per packet. // All packets carry version info till version is negotiated. connection_.options()->max_packet_length = - GetPacketLengthForOneStream(kIncludeVersion, 4); + GetPacketLengthForOneStream(kIncludeVersion, NOT_IN_FEC_GROUP, 4); // Queue the first packet. EXPECT_CALL(*send_algorithm_, @@ -1811,7 +1812,7 @@ TEST_F(QuicConnectionTest, LoopThroughSendingPackets) { // Limit to 4 bytes per packet. // All packets carry version info till version is negotiated. connection_.options()->max_packet_length = - GetPacketLengthForOneStream(kIncludeVersion, 4); + GetPacketLengthForOneStream(kIncludeVersion, NOT_IN_FEC_GROUP, 4); // Queue the first packet. EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(7); @@ -1980,7 +1981,6 @@ TEST_F(QuicConnectionTest, SendVersionNegotiationPacket) { header.public_header.version_flag = true; header.entropy_flag = false; header.fec_flag = false; - header.fec_entropy_flag = false; header.packet_sequence_number = 12; header.fec_group = 0; @@ -2105,7 +2105,6 @@ TEST_F(QuicConnectionTest, DontProcessFramesIfPacketClosedConnection) { header_.public_header.version_flag = false; header_.entropy_flag = false; header_.fec_flag = false; - header_.fec_entropy_flag = false; header_.fec_group = 0; QuicConnectionCloseFrame qccf; diff --git a/net/quic/quic_crypto_client_stream.cc b/net/quic/quic_crypto_client_stream.cc index 80d298e..697efe4 100644 --- a/net/quic/quic_crypto_client_stream.cc +++ b/net/quic/quic_crypto_client_stream.cc @@ -58,7 +58,7 @@ void QuicCryptoClientStream::DoHandshakeLoop( crypto_config_->LookupOrCreate(server_hostname_); if (in != NULL) { - DLOG(INFO) << "Client received: " << in->DebugString(); + DVLOG(1) << "Client received: " << in->DebugString(); } for (;;) { @@ -72,11 +72,11 @@ void QuicCryptoClientStream::DoHandshakeLoop( } num_client_hellos_++; - if (!cached->is_complete()) { + if (!cached->IsComplete(session()->connection()->clock()->WallNow())) { crypto_config_->FillInchoateClientHello( server_hostname_, cached, &crypto_negotiated_params_, &out); next_state_ = STATE_RECV_REJ; - DLOG(INFO) << "Client Sending: " << out.DebugString(); + DVLOG(1) << "Client Sending: " << out.DebugString(); SendHandshakeMessage(out); return; } @@ -91,11 +91,14 @@ void QuicCryptoClientStream::DoHandshakeLoop( &out, &error_details); if (error != QUIC_NO_ERROR) { + // Flush the cached config so that, if it's bad, the server has a + // chance to send us another in the future. + cached->InvalidateServerConfig(); CloseConnectionWithDetails(error, error_details); return; } next_state_ = STATE_RECV_SHLO; - DLOG(INFO) << "Client Sending: " << out.DebugString(); + DVLOG(1) << "Client Sending: " << out.DebugString(); SendHandshakeMessage(out); // Be prepared to decrypt with the new server write key. session()->connection()->SetAlternativeDecrypter( @@ -134,9 +137,9 @@ void QuicCryptoClientStream::DoHandshakeLoop( "Expected REJ"); return; } - error = crypto_config_->ProcessRejection(cached, *in, - &crypto_negotiated_params_, - &error_details); + error = crypto_config_->ProcessRejection( + cached, *in, session()->connection()->clock()->WallNow(), + &crypto_negotiated_params_, &error_details); if (error != QUIC_NO_ERROR) { CloseConnectionWithDetails(error, error_details); return; diff --git a/net/quic/quic_crypto_client_stream_test.cc b/net/quic/quic_crypto_client_stream_test.cc index 160aaf9..9f9e7f7 100644 --- a/net/quic/quic_crypto_client_stream_test.cc +++ b/net/quic/quic_crypto_client_stream_test.cc @@ -51,11 +51,10 @@ class QuicCryptoClientStreamTest : public ::testing::Test { QuicCryptoClientStreamTest() : addr_(), connection_(new PacketSavingConnection(1, addr_, true)), - session_(connection_, QuicConfig(), true), - stream_(new QuicCryptoClientStream(kServerHostname, &session_, + session_(new TestSession(connection_, DefaultQuicConfig(), true)), + stream_(new QuicCryptoClientStream(kServerHostname, session_.get(), &crypto_config_)) { - session_.SetCryptoStream(stream_.get()); - session_.config()->SetDefaults(); + session_->SetCryptoStream(stream_.get()); crypto_config_.SetDefaults(); } @@ -71,7 +70,7 @@ class QuicCryptoClientStreamTest : public ::testing::Test { IPEndPoint addr_; PacketSavingConnection* connection_; - TestSession session_; + scoped_ptr<TestSession> session_; scoped_ptr<QuicCryptoClientStream> stream_; CryptoHandshakeMessage message_; scoped_ptr<QuicData> message_data_; @@ -138,7 +137,7 @@ TEST_F(QuicCryptoClientStreamTest, NegotiatedParameters) { CompleteCryptoHandshake(); - const QuicConfig* config = session_.config(); + const QuicConfig* config = session_->config(); EXPECT_EQ(kQBIC, config->congestion_control()); EXPECT_EQ(kDefaultTimeoutSecs, config->idle_connection_state_lifetime().ToSeconds()); @@ -158,15 +157,38 @@ TEST_F(QuicCryptoClientStreamTest, InvalidHostname) { return; } - stream_.reset(new QuicCryptoClientStream("invalid", &session_, + stream_.reset(new QuicCryptoClientStream("invalid", session_.get(), &crypto_config_)); - session_.SetCryptoStream(stream_.get()); + session_->SetCryptoStream(stream_.get()); CompleteCryptoHandshake(); EXPECT_TRUE(stream_->encryption_established()); EXPECT_TRUE(stream_->handshake_confirmed()); } +TEST_F(QuicCryptoClientStreamTest, ExpiredServerConfig) { + // Seed the config with a cached server config. + CompleteCryptoHandshake(); + + connection_ = new PacketSavingConnection(1, addr_, true); + session_.reset(new TestSession(connection_, QuicConfig(), true)); + stream_.reset(new QuicCryptoClientStream(kServerHostname, session_.get(), + &crypto_config_)); + + session_->SetCryptoStream(stream_.get()); + session_->config()->SetDefaults(); + + // Advance time 5 years to ensure that we pass the expiry time of the cached + // server config. + reinterpret_cast<MockClock*>(const_cast<QuicClock*>(connection_->clock())) + ->AdvanceTime(QuicTime::Delta::FromSeconds(60 * 60 * 24 * 365 * 5)); + + // Check that a client hello was sent and that CryptoConnect doesn't fail + // with an error. + EXPECT_TRUE(stream_->CryptoConnect()); + ASSERT_EQ(1u, connection_->packets_.size()); +} + } // namespace } // namespace test } // namespace net diff --git a/net/quic/quic_crypto_server_stream_test.cc b/net/quic/quic_crypto_server_stream_test.cc index 2db0881..8f9209f 100644 --- a/net/quic/quic_crypto_server_stream_test.cc +++ b/net/quic/quic_crypto_server_stream_test.cc @@ -16,6 +16,7 @@ #include "net/quic/crypto/crypto_utils.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_crypto_client_stream.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_session.h" @@ -62,7 +63,8 @@ class QuicCryptoServerStreamTest : public ::testing::Test { ip_ : IPAddressNumber(), 1), connection_(new PacketSavingConnection(guid_, addr_, true)), session_(connection_, QuicConfig(), true), - crypto_config_(QuicCryptoServerConfig::TESTING), + crypto_config_(QuicCryptoServerConfig::TESTING, + QuicRandom::GetInstance()), stream_(crypto_config_, &session_) { config_.SetDefaults(); session_.config()->SetDefaults(); @@ -242,11 +244,28 @@ TEST_F(QuicCryptoServerStreamTest, WithoutCertificates) { // Only 2 client hellos need to be sent in the no-certs case: one to get the // source-address token and the second to finish. + // TODO(rtenneti): Enable testing of ProofVerifier. EXPECT_EQ(2, CompleteCryptoHandshake()); EXPECT_TRUE(stream_.encryption_established()); EXPECT_TRUE(stream_.handshake_confirmed()); } +TEST_F(QuicCryptoServerStreamTest, ChannelID) { + if (!Aes128Gcm12Encrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + client_options_.channel_id_enabled = true; + // TODO(rtenneti): Enable testing of ProofVerifier. + EXPECT_EQ(2, CompleteCryptoHandshake()); + EXPECT_TRUE(stream_.encryption_established()); + EXPECT_TRUE(stream_.handshake_confirmed()); + // TODO(rtenneti): Enable testing of ChannelID. + // EXPECT_EQ(CryptoTestUtils::ChannelIDKeyForHostname("test.example.com"), + // stream_.crypto_negotiated_params().channel_id); +} + } // namespace } // namespace test } // namespace net diff --git a/net/quic/quic_fec_group.cc b/net/quic/quic_fec_group.cc index 8431ff3..f5f4a77 100644 --- a/net/quic/quic_fec_group.cc +++ b/net/quic/quic_fec_group.cc @@ -49,7 +49,7 @@ bool QuicFecGroup::Update(const QuicPacketHeader& header, bool QuicFecGroup::UpdateFec( QuicPacketSequenceNumber fec_packet_sequence_number, - bool fec_entropy_flag, + bool fec_packet_entropy, const QuicFecData& fec) { if (min_protected_packet_ != kNoSequenceNumber) { return false; @@ -63,7 +63,7 @@ bool QuicFecGroup::UpdateFec( } ++it; } - if (!UpdateParity(fec.redundancy, fec_entropy_flag)) { + if (!UpdateParity(fec.redundancy, fec_packet_entropy)) { return false; } min_protected_packet_ = fec.fec_group; diff --git a/net/quic/quic_fec_group.h b/net/quic/quic_fec_group.h index 0dd66ec..d905d03 100644 --- a/net/quic/quic_fec_group.h +++ b/net/quic/quic_fec_group.h @@ -29,8 +29,9 @@ class NET_EXPORT_PRIVATE QuicFecGroup { // Updates the FEC group based on the delivery of an FEC packet. // Returns false if this packet has already been seen or if it does // not claim to protect all the packets previously seen in this group. + // |fec_packet_entropy|: XOR of entropy of all packets in the fec group. bool UpdateFec(QuicPacketSequenceNumber fec_packet_sequence_number, - bool fec_entropy_flag, + bool fec_packet_entropy, const QuicFecData& fec); // Returns true if a packet can be revived from this FEC group. diff --git a/net/quic/quic_fec_group_test.cc b/net/quic/quic_fec_group_test.cc index 7372a11..d9c303a 100644 --- a/net/quic/quic_fec_group_test.cc +++ b/net/quic/quic_fec_group_test.cc @@ -35,7 +35,7 @@ const bool kEntropyFlag[] = { true, }; -const bool kTestFecEntropy = false; +const bool kTestFecPacketEntropy = false; } // namespace @@ -157,7 +157,7 @@ TEST_F(QuicFecGroupTest, UpdateFecIfReceivedPacketIsNotCovered) { fec.redundancy = redundancy; header.packet_sequence_number = 2; - ASSERT_FALSE(group.UpdateFec(2, kTestFecEntropy, fec)); + ASSERT_FALSE(group.UpdateFec(2, kTestFecPacketEntropy, fec)); } TEST_F(QuicFecGroupTest, ProtectsPacketsBefore) { @@ -206,7 +206,7 @@ TEST_F(QuicFecGroupTest, ProtectsPacketsBeforeWithFecData) { fec.redundancy = kData[0]; QuicFecGroup group; - ASSERT_TRUE(group.UpdateFec(3, kTestFecEntropy, fec)); + ASSERT_TRUE(group.UpdateFec(3, kTestFecPacketEntropy, fec)); EXPECT_FALSE(group.ProtectsPacketsBefore(1)); EXPECT_FALSE(group.ProtectsPacketsBefore(2)); diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc index deb55e6..4fd1539 100644 --- a/net/quic/quic_framer.cc +++ b/net/quic/quic_framer.cc @@ -25,6 +25,9 @@ namespace { const QuicPacketSequenceNumber kSequenceNumberMask = GG_UINT64_C(0x0000FFFFFFFFFFFF); +const QuicGuid k1ByteGuidMask = GG_UINT64_C(0x00000000000000FF); +const QuicGuid k4ByteGuidMask = GG_UINT64_C(0x00000000FFFFFFFF); + const uint32 kInvalidDeltaTime = 0xffffffff; // Returns the absolute value of the difference between |a| and |b|. @@ -52,6 +55,7 @@ QuicFramer::QuicFramer(QuicTag version, fec_builder_(NULL), error_(QUIC_NO_ERROR), last_sequence_number_(0), + last_serialized_guid_(0), quic_version_(version), decrypter_(QuicDecrypter::Create(kNULL)), alternative_decrypter_latch_(false), @@ -107,8 +111,8 @@ 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. -size_t QuicFramer::GetMaxUnackedPackets(bool include_version) { - return (kMaxPacketSize - GetPacketHeaderSize(include_version) - +size_t QuicFramer::GetMaxUnackedPackets(QuicPacketHeader header) { + return (kMaxPacketSize - GetPacketHeaderSize(header) - GetMinAckFrameSize() - 16) / kSequenceNumberSize; } @@ -117,7 +121,7 @@ bool QuicFramer::IsSupportedVersion(QuicTag version) { } size_t QuicFramer::GetVersionNegotiationPacketSize(size_t number_versions) { - return kPublicFlagsSize + kQuicGuidSize + + return kPublicFlagsSize + PACKET_8BYTE_GUID + number_versions * kQuicVersionSize; } @@ -161,7 +165,7 @@ SerializedPacket QuicFramer::ConstructFrameDataPacket( const QuicPacketHeader& header, const QuicFrames& frames) { const size_t max_plaintext_size = GetMaxPlaintextSize(kMaxPacketSize); - size_t packet_size = GetPacketHeaderSize(header.public_header.version_flag); + size_t packet_size = GetPacketHeaderSize(header); for (size_t i = 0; i < frames.size(); ++i) { DCHECK_LE(packet_size, max_plaintext_size); const size_t frame_size = GetSerializedFrameLength( @@ -236,7 +240,8 @@ SerializedPacket QuicFramer::ConstructFrameDataPacket( // length, even though they're typically slightly shorter. DCHECK_LE(len, packet_size); QuicPacket* packet = QuicPacket::NewDataPacket( - writer.take(), len, true, header.public_header.version_flag); + writer.take(), len, true, header.public_header.guid_length, + header.public_header.version_flag); if (fec_builder_) { fec_builder_->OnBuiltFecProtectedPayload(header, @@ -247,9 +252,12 @@ SerializedPacket QuicFramer::ConstructFrameDataPacket( GetPacketEntropyHash(header), NULL); } -SerializedPacket QuicFramer::ConstructFecPacket(const QuicPacketHeader& header, - const QuicFecData& fec) { - size_t len = GetPacketHeaderSize(header.public_header.version_flag); +SerializedPacket QuicFramer::ConstructFecPacket( + const QuicPacketHeader& header, + const QuicFecData& fec) { + DCHECK_EQ(IN_FEC_GROUP, header.is_in_fec_group); + DCHECK_NE(0u, header.fec_group); + size_t len = GetPacketHeaderSize(header); len += fec.redundancy.length(); QuicDataWriter writer(len); @@ -262,11 +270,12 @@ SerializedPacket QuicFramer::ConstructFecPacket(const QuicPacketHeader& header, return kNoPacket; } - return SerializedPacket(header.packet_sequence_number, - QuicPacket::NewFecPacket( - writer.take(), len, true, - header.public_header.version_flag), - GetPacketEntropyHash(header), NULL); + return SerializedPacket( + header.packet_sequence_number, + QuicPacket::NewFecPacket(writer.take(), len, true, + header.public_header.guid_length, + header.public_header.version_flag), + GetPacketEntropyHash(header), NULL); } // static @@ -399,7 +408,7 @@ bool QuicFramer::ProcessDataPacket( // Handle the payload. if (!header.fec_flag) { - if (header.fec_group != 0) { + if (header.is_in_fec_group == IN_FEC_GROUP) { StringPiece payload = reader_->PeekRemainingPayload(); visitor_->OnFecProtectedPayload(payload); } @@ -467,21 +476,46 @@ bool QuicFramer::ProcessRevivedPacket(QuicPacketHeader* header, bool QuicFramer::WritePacketHeader(const QuicPacketHeader& header, QuicDataWriter* writer) { - uint8 flags = 0; + DCHECK(header.fec_group > 0 || header.is_in_fec_group == NOT_IN_FEC_GROUP); + uint8 public_flags = 0; if (header.public_header.reset_flag) { - flags |= PACKET_PUBLIC_FLAGS_RST; + public_flags |= PACKET_PUBLIC_FLAGS_RST; } if (header.public_header.version_flag) { - flags |= PACKET_PUBLIC_FLAGS_VERSION; - } - flags |= PACKET_PUBLIC_FLAGS_8BYTE_GUID; - if (!writer->WriteUInt8(flags)) { - return false; + public_flags |= PACKET_PUBLIC_FLAGS_VERSION; } - - if (!writer->WriteUInt64(header.public_header.guid)) { - return false; + switch (header.public_header.guid_length) { + case PACKET_0BYTE_GUID: + if (!writer->WriteUInt8(public_flags | PACKET_PUBLIC_FLAGS_0BYTE_GUID)) { + return false; + } + break; + case PACKET_1BYTE_GUID: + if (!writer->WriteUInt8(public_flags | PACKET_PUBLIC_FLAGS_1BYTE_GUID)) { + return false; + } + if (!writer->WriteUInt8(header.public_header.guid & k1ByteGuidMask)) { + return false; + } + break; + case PACKET_4BYTE_GUID: + if (!writer->WriteUInt8(public_flags | PACKET_PUBLIC_FLAGS_4BYTE_GUID)) { + return false; + } + if (!writer->WriteUInt32(header.public_header.guid & k4ByteGuidMask)) { + return false; + } + break; + case PACKET_8BYTE_GUID: + if (!writer->WriteUInt8(public_flags | PACKET_PUBLIC_FLAGS_8BYTE_GUID)) { + return false; + } + if (!writer->WriteUInt64(header.public_header.guid)) { + return false; + } + break; } + last_serialized_guid_ = header.public_header.guid; if (header.public_header.version_flag) { DCHECK(!is_server_); @@ -492,34 +526,32 @@ bool QuicFramer::WritePacketHeader(const QuicPacketHeader& header, return false; } - flags = 0; - if (header.fec_flag) { - flags |= PACKET_PRIVATE_FLAGS_FEC; - } + uint8 private_flags = 0; if (header.entropy_flag) { - flags |= PACKET_PRIVATE_FLAGS_ENTROPY; + private_flags |= PACKET_PRIVATE_FLAGS_ENTROPY; } - if (header.fec_entropy_flag) { - flags |= PACKET_PRIVATE_FLAGS_FEC_ENTROPY; + if (header.is_in_fec_group == IN_FEC_GROUP) { + private_flags |= PACKET_PRIVATE_FLAGS_FEC_GROUP; + } + if (header.fec_flag) { + private_flags |= PACKET_PRIVATE_FLAGS_FEC; } - if (!writer->WriteUInt8(flags)) { + if (!writer->WriteUInt8(private_flags)) { return false; } - // Offset from the current packet sequence number to the first fec - // protected packet, or kNoFecOffset to signal no FEC protection. - uint8 first_fec_protected_packet_offset = kNoFecOffset; - // The FEC group number is the sequence number of the first fec // protected packet, or 0 if this packet is not protected. - if (header.fec_group != 0) { + if (header.is_in_fec_group == IN_FEC_GROUP) { DCHECK_GE(header.packet_sequence_number, header.fec_group); DCHECK_GT(255u, header.packet_sequence_number - header.fec_group); - first_fec_protected_packet_offset = + // Offset from the current packet sequence number to the first fec + // protected packet. + uint8 first_fec_protected_packet_offset = header.packet_sequence_number - header.fec_group; - } - if (!writer->WriteBytes(&first_fec_protected_packet_offset, 1)) { - return false; + if (!writer->WriteBytes(&first_fec_protected_packet_offset, 1)) { + return false; + } } return true; @@ -554,12 +586,6 @@ bool QuicFramer::ProcessPublicHeader(QuicPacketPublicHeader* public_header) { return false; } - if ((public_flags & PACKET_PUBLIC_FLAGS_8BYTE_GUID) != - PACKET_PUBLIC_FLAGS_8BYTE_GUID) { - set_detailed_error("Only full length guids (8 bytes) currently supported."); - return false; - } - public_header->reset_flag = (public_flags & PACKET_PUBLIC_FLAGS_RST) != 0; public_header->version_flag = (public_flags & PACKET_PUBLIC_FLAGS_VERSION) != 0; @@ -569,9 +595,47 @@ bool QuicFramer::ProcessPublicHeader(QuicPacketPublicHeader* public_header) { return false; } - if (!reader_->ReadUInt64(&public_header->guid)) { - set_detailed_error("Unable to read GUID."); - return false; + switch (public_flags & PACKET_PUBLIC_FLAGS_8BYTE_GUID) { + case PACKET_PUBLIC_FLAGS_8BYTE_GUID: + if (!reader_->ReadUInt64(&public_header->guid)) { + set_detailed_error("Unable to read GUID."); + return false; + } + public_header->guid_length = PACKET_8BYTE_GUID; + break; + case PACKET_PUBLIC_FLAGS_4BYTE_GUID: + // If the guid is truncated, expect to read the last serialized guid. + if (!reader_->ReadBytes(&public_header->guid, PACKET_4BYTE_GUID)) { + set_detailed_error("Unable to read GUID."); + return false; + } + if ((public_header->guid & k4ByteGuidMask) != + (last_serialized_guid_ & k4ByteGuidMask)) { + set_detailed_error( + "Truncated 4 byte GUID does not match previous guid."); + return false; + } + public_header->guid_length = PACKET_4BYTE_GUID; + public_header->guid = last_serialized_guid_; + break; + case PACKET_PUBLIC_FLAGS_1BYTE_GUID: + if (!reader_->ReadBytes(&public_header->guid, PACKET_1BYTE_GUID)) { + set_detailed_error("Unable to read GUID."); + return false; + } + if ((public_header->guid & k1ByteGuidMask) != + (last_serialized_guid_ & k1ByteGuidMask)) { + set_detailed_error( + "Truncated 1 byte GUID does not match previous guid."); + return false; + } + public_header->guid_length = PACKET_1BYTE_GUID; + public_header->guid = last_serialized_guid_; + break; + case PACKET_PUBLIC_FLAGS_0BYTE_GUID: + public_header->guid_length = PACKET_0BYTE_GUID; + public_header->guid = last_serialized_guid_; + break; } if (public_header->version_flag && is_server_) { @@ -618,6 +682,7 @@ bool QuicFramer::ProcessPacketHeader( } if (!DecryptPayload(header->packet_sequence_number, + header->public_header.guid_length, header->public_header.version_flag, packet)) { set_detailed_error("Unable to decrypt payload."); @@ -635,18 +700,19 @@ bool QuicFramer::ProcessPacketHeader( return RaiseError(QUIC_INVALID_PACKET_HEADER); } - header->fec_flag = (private_flags & PACKET_PRIVATE_FLAGS_FEC) != 0; header->entropy_flag = (private_flags & PACKET_PRIVATE_FLAGS_ENTROPY) != 0; - header->fec_entropy_flag = - (private_flags & PACKET_PRIVATE_FLAGS_FEC_ENTROPY) != 0; + header->fec_flag = (private_flags & PACKET_PRIVATE_FLAGS_FEC) != 0; - uint8 first_fec_protected_packet_offset; - if (!reader_->ReadBytes(&first_fec_protected_packet_offset, 1)) { - set_detailed_error("Unable to read first fec protected packet offset."); - return RaiseError(QUIC_INVALID_PACKET_HEADER); + if ((private_flags & PACKET_PRIVATE_FLAGS_FEC_GROUP) != 0) { + header->is_in_fec_group = IN_FEC_GROUP; + uint8 first_fec_protected_packet_offset; + if (!reader_->ReadBytes(&first_fec_protected_packet_offset, 1)) { + set_detailed_error("Unable to read first fec protected packet offset."); + return RaiseError(QUIC_INVALID_PACKET_HEADER); + } + header->fec_group = + header->packet_sequence_number - first_fec_protected_packet_offset; } - header->fec_group = first_fec_protected_packet_offset == kNoFecOffset ? 0 : - header->packet_sequence_number - first_fec_protected_packet_offset; header->entropy_hash = GetPacketEntropyHash(*header); // Set the last sequence number after we have decrypted the packet @@ -1062,10 +1128,12 @@ bool QuicFramer::ProcessGoAwayFrame(QuicGoAwayFrame* frame) { // static StringPiece QuicFramer::GetAssociatedDataFromEncryptedPacket( - const QuicEncryptedPacket& encrypted, bool includes_version) { + const QuicEncryptedPacket& encrypted, + QuicGuidLength guid_length, + bool includes_version) { return StringPiece(encrypted.data() + kStartOfHashData, - GetStartOfEncryptedData(includes_version) - - kStartOfHashData); + GetStartOfEncryptedData( + guid_length, includes_version) - kStartOfHashData); } void QuicFramer::SetDecrypter(QuicDecrypter* decrypter) { @@ -1154,6 +1222,7 @@ size_t QuicFramer::GetMaxPlaintextSize(size_t ciphertext_size) { } bool QuicFramer::DecryptPayload(QuicPacketSequenceNumber sequence_number, + QuicGuidLength guid_length, bool version_flag, const QuicEncryptedPacket& packet) { StringPiece encrypted; @@ -1163,12 +1232,12 @@ bool QuicFramer::DecryptPayload(QuicPacketSequenceNumber sequence_number, DCHECK(decrypter_.get() != NULL); decrypted_.reset(decrypter_->DecryptPacket( sequence_number, - GetAssociatedDataFromEncryptedPacket(packet, version_flag), + GetAssociatedDataFromEncryptedPacket(packet, guid_length, version_flag), encrypted)); if (decrypted_.get() == NULL && alternative_decrypter_.get() != NULL) { decrypted_.reset(alternative_decrypter_->DecryptPacket( sequence_number, - GetAssociatedDataFromEncryptedPacket(packet, version_flag), + GetAssociatedDataFromEncryptedPacket(packet, guid_length, version_flag), encrypted)); if (decrypted_.get() != NULL) { if (alternative_decrypter_latch_) { diff --git a/net/quic/quic_framer.h b/net/quic/quic_framer.h index fb6ced9..8eb1f28 100644 --- a/net/quic/quic_framer.h +++ b/net/quic/quic_framer.h @@ -233,7 +233,7 @@ class NET_EXPORT_PRIVATE QuicFramer { static size_t GetMinGoAwayFrameSize(); // The maximum number of nacks which can be transmitted in a single ack packet // without exceeding kMaxPacketSize. - static size_t GetMaxUnackedPackets(bool include_version); + static size_t GetMaxUnackedPackets(QuicPacketHeader header); // Size in bytes required for a serialized version negotiation packet size_t GetVersionNegotiationPacketSize(size_t number_versions); @@ -246,7 +246,9 @@ class NET_EXPORT_PRIVATE QuicFramer { // Returns the associated data from the encrypted packet |encrypted| as a // stringpiece. static base::StringPiece GetAssociatedDataFromEncryptedPacket( - const QuicEncryptedPacket& encrypted, bool includes_version); + const QuicEncryptedPacket& encrypted, + QuicGuidLength guid_length, + bool includes_version); // Returns a SerializedPacket whose |packet| member is owned by the caller, // and is populated with the fields in |header| and |frames|, or is NULL if @@ -353,6 +355,7 @@ class NET_EXPORT_PRIVATE QuicFramer { bool ProcessGoAwayFrame(QuicGoAwayFrame* frame); bool DecryptPayload(QuicPacketSequenceNumber packet_sequence_number, + QuicGuidLength guid_length, bool version_flag, const QuicEncryptedPacket& packet); @@ -400,6 +403,8 @@ class NET_EXPORT_PRIVATE QuicFramer { QuicErrorCode error_; // Updated by ProcessPacketHeader when it succeeds. QuicPacketSequenceNumber last_sequence_number_; + // Updated by WritePacketHeader. + QuicGuid last_serialized_guid_; // Buffer containing decrypted payload data during parsing. scoped_ptr<QuicData> decrypted_; // Version of the protocol being used. diff --git a/net/quic/quic_framer_test.cc b/net/quic/quic_framer_test.cc index 2677665..9e6df0d 100644 --- a/net/quic/quic_framer_test.cc +++ b/net/quic/quic_framer_test.cc @@ -41,26 +41,45 @@ const size_t kPublicFlagsOffset = 0; // Index into the guid offset in the header. const size_t kGuidOffset = kPublicFlagsSize; // Index into the version string in the header. (if present). -const size_t kVersionOffset = kGuidOffset + kQuicGuidSize; +const size_t kVersionOffset = kGuidOffset + PACKET_8BYTE_GUID; // Index into the sequence number offset in the header. -size_t GetSequenceNumberOffset(bool include_version) { - return kGuidOffset + kQuicGuidSize + +size_t GetSequenceNumberOffset(QuicGuidLength guid_length, + bool include_version) { + return kGuidOffset + guid_length + (include_version ? kQuicVersionSize : 0); } +size_t GetSequenceNumberOffset(bool include_version) { + return GetSequenceNumberOffset(PACKET_8BYTE_GUID, include_version); +} + // Index into the private flags offset in the data packet header. +size_t GetPrivateFlagsOffset(QuicGuidLength guid_length, bool include_version) { + return GetSequenceNumberOffset(guid_length, include_version) + + kSequenceNumberSize; +} + size_t GetPrivateFlagsOffset(bool include_version) { - return GetSequenceNumberOffset(include_version) + kSequenceNumberSize; + return GetPrivateFlagsOffset(PACKET_8BYTE_GUID, include_version); } // Index into the fec group offset in the header. +size_t GetFecGroupOffset(QuicGuidLength guid_length, bool include_version) { + return GetPrivateFlagsOffset(guid_length, include_version) + + kPrivateFlagsSize; +} + size_t GetFecGroupOffset(bool include_version) { - return GetPrivateFlagsOffset(include_version) + kPrivateFlagsSize; + return GetPrivateFlagsOffset(PACKET_8BYTE_GUID, include_version) + + kPrivateFlagsSize; } // Index into the nonce proof of the public reset packet. -const size_t kPublicResetPacketNonceProofOffset = kGuidOffset + kQuicGuidSize; +// Public resets always have full guids. +const size_t kPublicResetPacketNonceProofOffset = + kGuidOffset + PACKET_8BYTE_GUID; + // Index into the rejected sequence number of the public reset packet. const size_t kPublicResetPacketRejectedSequenceNumberOffset = kPublicResetPacketNonceProofOffset + kPublicResetNonceSize; @@ -317,15 +336,16 @@ class QuicFramerTest : public ::testing::Test { return false; } if (QuicFramer::GetAssociatedDataFromEncryptedPacket( - encrypted, includes_version) != decrypter_->associated_data_) { + encrypted, PACKET_8BYTE_GUID, includes_version) != + decrypter_->associated_data_) { LOG(ERROR) << "Decrypted incorrect associated data. expected " << QuicFramer::GetAssociatedDataFromEncryptedPacket( - encrypted, includes_version) + encrypted, PACKET_8BYTE_GUID, includes_version) << " actual: " << decrypter_->associated_data_; return false; } StringPiece ciphertext(encrypted.AsStringPiece().substr( - GetStartOfEncryptedData(includes_version))); + GetStartOfEncryptedData(PACKET_8BYTE_GUID, includes_version))); if (ciphertext != decrypter_->ciphertext_) { LOG(ERROR) << "Decrypted incorrect chipertext data. expected " << ciphertext << " actual: " @@ -495,12 +515,12 @@ TEST_F(QuicFramerTest, LargePacket) { 0x34, 0x12, // private flags 0x00, - // first fec protected packet offset - 0xFF }; - memset(packet + GetPacketHeaderSize(!kIncludeVersion), 0, - kMaxPacketSize - GetPacketHeaderSize(!kIncludeVersion) + 1); + memset(packet + GetPacketHeaderSize( + PACKET_8BYTE_GUID, !kIncludeVersion, NOT_IN_FEC_GROUP), 0, + kMaxPacketSize - GetPacketHeaderSize( + PACKET_8BYTE_GUID, !kIncludeVersion, NOT_IN_FEC_GROUP) + 1); QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); EXPECT_FALSE(framer_.ProcessPacket(encrypted)); @@ -525,8 +545,6 @@ TEST_F(QuicFramerTest, PacketHeader) { 0x34, 0x12, // private flags 0x00, - // first fec protected packet offset - 0xFF, }; QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); @@ -539,14 +557,16 @@ TEST_F(QuicFramerTest, PacketHeader) { EXPECT_FALSE(visitor_.header_->public_header.version_flag); EXPECT_FALSE(visitor_.header_->fec_flag); EXPECT_FALSE(visitor_.header_->entropy_flag); - EXPECT_FALSE(visitor_.header_->fec_entropy_flag); EXPECT_EQ(0, visitor_.header_->entropy_hash); EXPECT_EQ(GG_UINT64_C(0x123456789ABC), visitor_.header_->packet_sequence_number); + EXPECT_EQ(NOT_IN_FEC_GROUP, visitor_.header_->is_in_fec_group); EXPECT_EQ(0x00u, visitor_.header_->fec_group); // Now test framing boundaries - for (size_t i = 0; i < GetPacketHeaderSize(!kIncludeVersion); ++i) { + for (size_t i = 0; + i < GetPacketHeaderSize( + PACKET_8BYTE_GUID, !kIncludeVersion, NOT_IN_FEC_GROUP); ++i) { string expected_error; if (i < kGuidOffset) { expected_error = "Unable to read public flags."; @@ -563,6 +583,165 @@ TEST_F(QuicFramerTest, PacketHeader) { } } +TEST_F(QuicFramerTest, PacketHeaderWith4ByteGuid) { + QuicFramerPeer::SetLastSerializedGuid(&framer_, + GG_UINT64_C(0xFEDCBA9876543210)); + + unsigned char packet[] = { + // public flags (4 byte guid) + 0x08, + // guid + 0x10, 0x32, 0x54, 0x76, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags + 0x00, + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_FALSE(framer_.ProcessPacket(encrypted)); + EXPECT_EQ(QUIC_INVALID_FRAME_DATA, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), + visitor_.header_->public_header.guid); + EXPECT_FALSE(visitor_.header_->public_header.reset_flag); + EXPECT_FALSE(visitor_.header_->public_header.version_flag); + EXPECT_FALSE(visitor_.header_->fec_flag); + EXPECT_FALSE(visitor_.header_->entropy_flag); + EXPECT_EQ(0, visitor_.header_->entropy_hash); + EXPECT_EQ(GG_UINT64_C(0x123456789ABC), + visitor_.header_->packet_sequence_number); + EXPECT_EQ(NOT_IN_FEC_GROUP, visitor_.header_->is_in_fec_group); + EXPECT_EQ(0x00u, visitor_.header_->fec_group); + + // Now test framing boundaries + for (size_t i = 0; + i < GetPacketHeaderSize( + PACKET_4BYTE_GUID, !kIncludeVersion, NOT_IN_FEC_GROUP); ++i) { + string expected_error; + if (i < kGuidOffset) { + expected_error = "Unable to read public flags."; + } else if (i < GetSequenceNumberOffset(PACKET_4BYTE_GUID, + !kIncludeVersion)) { + expected_error = "Unable to read GUID."; + } else if (i < GetPrivateFlagsOffset(PACKET_4BYTE_GUID, + !kIncludeVersion)) { + expected_error = "Unable to read sequence number."; + } else if (i < GetFecGroupOffset(PACKET_4BYTE_GUID, !kIncludeVersion)) { + expected_error = "Unable to read private flags."; + } else { + expected_error = "Unable to read first fec protected packet offset."; + } + CheckProcessingFails(packet, i, expected_error, QUIC_INVALID_PACKET_HEADER); + } +} + +TEST_F(QuicFramerTest, PacketHeader1ByteGuid) { + QuicFramerPeer::SetLastSerializedGuid(&framer_, + GG_UINT64_C(0xFEDCBA9876543210)); + + unsigned char packet[] = { + // public flags (1 byte guid) + 0x04, + // guid + 0x10, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags + 0x00, + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_FALSE(framer_.ProcessPacket(encrypted)); + EXPECT_EQ(QUIC_INVALID_FRAME_DATA, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), + visitor_.header_->public_header.guid); + EXPECT_FALSE(visitor_.header_->public_header.reset_flag); + EXPECT_FALSE(visitor_.header_->public_header.version_flag); + EXPECT_FALSE(visitor_.header_->fec_flag); + EXPECT_FALSE(visitor_.header_->entropy_flag); + EXPECT_EQ(0, visitor_.header_->entropy_hash); + EXPECT_EQ(GG_UINT64_C(0x123456789ABC), + visitor_.header_->packet_sequence_number); + EXPECT_EQ(NOT_IN_FEC_GROUP, visitor_.header_->is_in_fec_group); + EXPECT_EQ(0x00u, visitor_.header_->fec_group); + + // Now test framing boundaries + for (size_t i = 0; + i < GetPacketHeaderSize( + PACKET_1BYTE_GUID, !kIncludeVersion, NOT_IN_FEC_GROUP); ++i) { + string expected_error; + if (i < kGuidOffset) { + expected_error = "Unable to read public flags."; + } else if (i < GetSequenceNumberOffset(PACKET_1BYTE_GUID, + !kIncludeVersion)) { + expected_error = "Unable to read GUID."; + } else if (i < GetPrivateFlagsOffset(PACKET_1BYTE_GUID, !kIncludeVersion)) { + expected_error = "Unable to read sequence number."; + } else if (i < GetFecGroupOffset(PACKET_1BYTE_GUID, !kIncludeVersion)) { + expected_error = "Unable to read private flags."; + } else { + expected_error = "Unable to read first fec protected packet offset."; + } + CheckProcessingFails(packet, i, expected_error, QUIC_INVALID_PACKET_HEADER); + } +} + +TEST_F(QuicFramerTest, PacketHeaderWith0ByteGuid) { + QuicFramerPeer::SetLastSerializedGuid(&framer_, + GG_UINT64_C(0xFEDCBA9876543210)); + + unsigned char packet[] = { + // public flags (0 byte guid) + 0x00, + // guid + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags + 0x00, + }; + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_FALSE(framer_.ProcessPacket(encrypted)); + EXPECT_EQ(QUIC_INVALID_FRAME_DATA, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), + visitor_.header_->public_header.guid); + EXPECT_FALSE(visitor_.header_->public_header.reset_flag); + EXPECT_FALSE(visitor_.header_->public_header.version_flag); + EXPECT_FALSE(visitor_.header_->fec_flag); + EXPECT_FALSE(visitor_.header_->entropy_flag); + EXPECT_EQ(0, visitor_.header_->entropy_hash); + EXPECT_EQ(GG_UINT64_C(0x123456789ABC), + visitor_.header_->packet_sequence_number); + EXPECT_EQ(NOT_IN_FEC_GROUP, visitor_.header_->is_in_fec_group); + EXPECT_EQ(0x00u, visitor_.header_->fec_group); + + // Now test framing boundaries + for (size_t i = 0; + i < GetPacketHeaderSize( + PACKET_0BYTE_GUID, !kIncludeVersion, NOT_IN_FEC_GROUP); ++i) { + string expected_error; + if (i < kGuidOffset) { + expected_error = "Unable to read public flags."; + } else if (i < GetSequenceNumberOffset(PACKET_0BYTE_GUID, + !kIncludeVersion)) { + expected_error = "Unable to read GUID."; + } else if (i < GetPrivateFlagsOffset(PACKET_0BYTE_GUID, !kIncludeVersion)) { + expected_error = "Unable to read sequence number."; + } else if (i < GetFecGroupOffset(PACKET_0BYTE_GUID, !kIncludeVersion)) { + expected_error = "Unable to read private flags."; + } else { + expected_error = "Unable to read first fec protected packet offset."; + } + CheckProcessingFails(packet, i, expected_error, QUIC_INVALID_PACKET_HEADER); + } +} + TEST_F(QuicFramerTest, PacketHeaderWithVersionFlag) { unsigned char packet[] = { // public flags (version) @@ -571,14 +750,12 @@ TEST_F(QuicFramerTest, PacketHeaderWithVersionFlag) { 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // version tag - 'Q', '0', '0', '4', + 'Q', '0', '0', '5', // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, // private flags 0x00, - // first fec protected packet offset - 0xFF, }; QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); @@ -592,14 +769,16 @@ TEST_F(QuicFramerTest, PacketHeaderWithVersionFlag) { EXPECT_EQ(kQuicVersion1, visitor_.header_->public_header.versions[0]); EXPECT_FALSE(visitor_.header_->fec_flag); EXPECT_FALSE(visitor_.header_->entropy_flag); - EXPECT_FALSE(visitor_.header_->fec_entropy_flag); EXPECT_EQ(0, visitor_.header_->entropy_hash); EXPECT_EQ(GG_UINT64_C(0x123456789ABC), visitor_.header_->packet_sequence_number); + EXPECT_EQ(NOT_IN_FEC_GROUP, visitor_.header_->is_in_fec_group); EXPECT_EQ(0x00u, visitor_.header_->fec_group); // Now test framing boundaries - for (size_t i = 0; i < GetPacketHeaderSize(kIncludeVersion); ++i) { + for (size_t i = 0; + i < GetPacketHeaderSize( + PACKET_8BYTE_GUID, kIncludeVersion, NOT_IN_FEC_GROUP); ++i) { string expected_error; if (i < kGuidOffset) { expected_error = "Unable to read public flags."; @@ -631,8 +810,6 @@ TEST_F(QuicFramerTest, InvalidPublicFlag) { 0x34, 0x12, // private flags 0x00, - // first fec protected packet offset - 0xFF, // frame count 0x01, @@ -669,9 +846,7 @@ TEST_F(QuicFramerTest, InvalidPrivateFlag) { 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, // private flags - 0x08, - // first fec protected packet offset - 0xFF, + 0x10, // frame count 0x01, @@ -709,8 +884,6 @@ TEST_F(QuicFramerTest, PaddingFrame) { 0x34, 0x12, // private flags 0x00, - // first fec protected packet offset - 0xFF, // frame type (padding frame) 0x00, @@ -735,8 +908,11 @@ TEST_F(QuicFramerTest, PaddingFrame) { ASSERT_EQ(0u, visitor_.stream_frames_.size()); EXPECT_EQ(0u, visitor_.ack_frames_.size()); // A packet with no frames is not acceptable. - CheckProcessingFails(packet, GetPacketHeaderSize(!kIncludeVersion), - "Unable to read frame type.", QUIC_INVALID_FRAME_DATA); + CheckProcessingFails( + packet, + GetPacketHeaderSize( + PACKET_8BYTE_GUID, !kIncludeVersion, NOT_IN_FEC_GROUP), + "Unable to read frame type.", QUIC_INVALID_FRAME_DATA); } TEST_F(QuicFramerTest, StreamFrame) { @@ -751,8 +927,6 @@ TEST_F(QuicFramerTest, StreamFrame) { 0x34, 0x12, // private flags 0x00, - // first fec protected packet offset - 0xFF, // frame type (stream frame) 0x01, @@ -803,8 +977,11 @@ TEST_F(QuicFramerTest, StreamFrame) { } else { expected_error = "Unable to read frame data."; } - CheckProcessingFails(packet, i + GetPacketHeaderSize(!kIncludeVersion), - expected_error, QUIC_INVALID_FRAME_DATA); + CheckProcessingFails( + packet, + i + GetPacketHeaderSize( + PACKET_8BYTE_GUID, !kIncludeVersion, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_FRAME_DATA); } } @@ -816,14 +993,12 @@ TEST_F(QuicFramerTest, StreamFrameWithVersion) { 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // version tag - 'Q', '0', '0', '4', + 'Q', '0', '0', '5', // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, // private flags 0x00, - // first fec protected packet offset - 0xFF, // frame type (stream frame) 0x01, @@ -876,8 +1051,10 @@ TEST_F(QuicFramerTest, StreamFrameWithVersion) { } else { expected_error = "Unable to read frame data."; } - CheckProcessingFails(packet, i + GetPacketHeaderSize(kIncludeVersion), - expected_error, QUIC_INVALID_FRAME_DATA); + CheckProcessingFails( + packet, i + GetPacketHeaderSize(PACKET_8BYTE_GUID, kIncludeVersion, + NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_FRAME_DATA); } } @@ -895,8 +1072,6 @@ TEST_F(QuicFramerTest, RejectPacket) { 0x34, 0x12, // private flags 0x00, - // first fec protected packet offset - 0xFF, // frame type (stream frame) 0x01, @@ -951,7 +1126,6 @@ TEST_F(QuicFramerTest, RevivedStreamFrame) { header.public_header.version_flag = false; header.fec_flag = true; header.entropy_flag = true; - header.fec_entropy_flag = false; header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); header.fec_group = 0; @@ -969,11 +1143,11 @@ TEST_F(QuicFramerTest, RevivedStreamFrame) { EXPECT_FALSE(visitor_.header_->public_header.version_flag); EXPECT_TRUE(visitor_.header_->fec_flag); EXPECT_TRUE(visitor_.header_->entropy_flag); - EXPECT_FALSE(visitor_.header_->fec_entropy_flag); EXPECT_EQ(1 << (header.packet_sequence_number % 8), visitor_.header_->entropy_hash); EXPECT_EQ(GG_UINT64_C(0x123456789ABC), visitor_.header_->packet_sequence_number); + EXPECT_EQ(NOT_IN_FEC_GROUP, visitor_.header_->is_in_fec_group); EXPECT_EQ(0x00u, visitor_.header_->fec_group); ASSERT_EQ(1u, visitor_.stream_frames_.size()); @@ -995,8 +1169,8 @@ TEST_F(QuicFramerTest, StreamFrameInFecGroup) { // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x12, 0x34, - // private flags - 0x00, + // private flags (fec group) + 0x02, // first fec protected packet offset 0x02, @@ -1023,11 +1197,14 @@ TEST_F(QuicFramerTest, StreamFrameInFecGroup) { EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); ASSERT_TRUE(visitor_.header_.get()); EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); + EXPECT_EQ(IN_FEC_GROUP, visitor_.header_->is_in_fec_group); EXPECT_EQ(GG_UINT64_C(0x341256789ABA), visitor_.header_->fec_group); EXPECT_EQ( - string(AsChars(packet) + GetStartOfFecProtectedData(!kIncludeVersion), - arraysize(packet) - GetStartOfFecProtectedData(!kIncludeVersion)), + string(AsChars(packet) + GetStartOfFecProtectedData(PACKET_8BYTE_GUID, + !kIncludeVersion), + arraysize(packet) - GetStartOfFecProtectedData(PACKET_8BYTE_GUID, + !kIncludeVersion)), visitor_.fec_protected_payload_); ASSERT_EQ(1u, visitor_.stream_frames_.size()); @@ -1051,8 +1228,6 @@ TEST_F(QuicFramerTest, AckFrame) { 0x34, 0x12, // private flags 0x00, - // first fec protected packet offset - 0xFF, // frame type (ack frame) 0x02, @@ -1128,8 +1303,11 @@ TEST_F(QuicFramerTest, AckFrame) { } else { expected_error = "Unable to read sequence number in missing packets."; } - CheckProcessingFails(packet, i + GetPacketHeaderSize(!kIncludeVersion), - expected_error, QUIC_INVALID_FRAME_DATA); + CheckProcessingFails( + packet, + i + GetPacketHeaderSize( + PACKET_8BYTE_GUID, !kIncludeVersion, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_FRAME_DATA); } } @@ -1145,8 +1323,6 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameTCP) { 0x34, 0x12, // private flags 0x00, - // first fec protected packet offset - 0xFF, // frame type (congestion feedback frame) 0x03, @@ -1186,8 +1362,11 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameTCP) { } else if (i < 6) { expected_error = "Unable to read receive window."; } - CheckProcessingFails(packet, i + GetPacketHeaderSize(!kIncludeVersion), - expected_error, QUIC_INVALID_FRAME_DATA); + CheckProcessingFails( + packet, + i + GetPacketHeaderSize( + PACKET_8BYTE_GUID, !kIncludeVersion, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_FRAME_DATA); } } @@ -1203,8 +1382,6 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameInterArrival) { 0x34, 0x12, // private flags 0x00, - // first fec protected packet offset - 0xFF, // frame type (congestion feedback frame) 0x03, @@ -1283,8 +1460,11 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameInterArrival) { } else if (i < 31) { expected_error = "Unable to read time delta in received packets."; } - CheckProcessingFails(packet, i + GetPacketHeaderSize(!kIncludeVersion), - expected_error, QUIC_INVALID_FRAME_DATA); + CheckProcessingFails( + packet, + i + GetPacketHeaderSize( + PACKET_8BYTE_GUID, !kIncludeVersion, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_FRAME_DATA); } } @@ -1300,8 +1480,6 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameFixRate) { 0x34, 0x12, // private flags 0x00, - // first fec protected packet offset - 0xFF, // frame type (congestion feedback frame) 0x03, @@ -1336,8 +1514,11 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameFixRate) { } else if (i < 6) { expected_error = "Unable to read bitrate."; } - CheckProcessingFails(packet, i + GetPacketHeaderSize(!kIncludeVersion), - expected_error, QUIC_INVALID_FRAME_DATA); + CheckProcessingFails( + packet, + i + GetPacketHeaderSize( + PACKET_8BYTE_GUID, !kIncludeVersion, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_FRAME_DATA); } } @@ -1354,8 +1535,6 @@ TEST_F(QuicFramerTest, CongestionFeedbackFrameInvalidFeedback) { 0x34, 0x12, // private flags 0x00, - // first fec protected packet offset - 0xFF, // frame type (congestion feedback frame) 0x03, @@ -1381,8 +1560,6 @@ TEST_F(QuicFramerTest, RstStreamFrame) { 0x34, 0x12, // private flags 0x00, - // first fec protected packet offset - 0xFF, // frame type (rst stream frame) 0x04, @@ -1422,8 +1599,11 @@ TEST_F(QuicFramerTest, RstStreamFrame) { } else { expected_error = "Unable to read rst stream error details."; } - CheckProcessingFails(packet, i + GetPacketHeaderSize(!kIncludeVersion), - expected_error, QUIC_INVALID_RST_STREAM_DATA); + CheckProcessingFails( + packet, + i + GetPacketHeaderSize( + PACKET_8BYTE_GUID, !kIncludeVersion, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_RST_STREAM_DATA); } } @@ -1439,8 +1619,6 @@ TEST_F(QuicFramerTest, ConnectionCloseFrame) { 0x34, 0x12, // private flags 0x00, - // first fec protected packet offset - 0xFF, // frame type (connection close frame) 0x05, @@ -1508,8 +1686,11 @@ TEST_F(QuicFramerTest, ConnectionCloseFrame) { } else { expected_error = "Unable to read connection close error details."; } - CheckProcessingFails(packet, i + GetPacketHeaderSize(!kIncludeVersion), - expected_error, QUIC_INVALID_CONNECTION_CLOSE_DATA); + CheckProcessingFails( + packet, + i + GetPacketHeaderSize( + PACKET_8BYTE_GUID, !kIncludeVersion, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_CONNECTION_CLOSE_DATA); } } @@ -1525,8 +1706,6 @@ TEST_F(QuicFramerTest, GoAwayFrame) { 0x34, 0x12, // private flags 0x00, - // first fec protected packet offset - 0xFF, // frame type (go away frame) 0x06, @@ -1568,8 +1747,11 @@ TEST_F(QuicFramerTest, GoAwayFrame) { } else { expected_error = "Unable to read goaway reason."; } - CheckProcessingFails(packet, i + GetPacketHeaderSize(!kIncludeVersion), - expected_error, QUIC_INVALID_GOAWAY_DATA); + CheckProcessingFails( + packet, + i + GetPacketHeaderSize( + PACKET_8BYTE_GUID, !kIncludeVersion, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_GOAWAY_DATA); } } @@ -1633,7 +1815,7 @@ TEST_F(QuicFramerTest, VersionNegotiationPacket) { 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // version tag - 'Q', '0', '0', '4', + 'Q', '0', '0', '5', 'Q', '2', '.', '0', }; @@ -1647,7 +1829,7 @@ TEST_F(QuicFramerTest, VersionNegotiationPacket) { EXPECT_EQ(kQuicVersion1, visitor_.version_negotiation_packet_->versions[0]); - for (size_t i = 0; i <= kPublicFlagsSize + kQuicGuidSize; ++i) { + for (size_t i = 0; i <= kPublicFlagsSize + PACKET_8BYTE_GUID; ++i) { string expected_error; QuicErrorCode error_code = QUIC_INVALID_PACKET_HEADER; if (i < kGuidOffset) { @@ -1672,8 +1854,8 @@ TEST_F(QuicFramerTest, FecPacket) { // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, - // private flags (FEC) - 0x01, + // private flags (fec group & FEC) + 0x06, // first fec protected packet offset 0x01, @@ -1706,7 +1888,6 @@ TEST_F(QuicFramerTest, ConstructPaddingFramePacket) { header.public_header.version_flag = false; header.fec_flag = false; header.entropy_flag = false; - header.fec_entropy_flag = false; header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); header.fec_group = 0; @@ -1726,15 +1907,14 @@ TEST_F(QuicFramerTest, ConstructPaddingFramePacket) { 0x34, 0x12, // private flags 0x00, - // first fec protected packet offset - 0xFF, // frame type (padding frame) 0x00, }; - memset(packet + GetPacketHeaderSize(!kIncludeVersion) + 1, 0x00, - kMaxPacketSize - GetPacketHeaderSize(!kIncludeVersion) - 1); + uint64 header_size = GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + NOT_IN_FEC_GROUP); + memset(packet + header_size + 1, 0x00, kMaxPacketSize - header_size - 1); scoped_ptr<QuicPacket> data( framer_.ConstructFrameDataPacket(header, frames).packet); @@ -1752,7 +1932,6 @@ TEST_F(QuicFramerTest, ConstructStreamFramePacket) { header.public_header.version_flag = false; header.fec_flag = false; header.entropy_flag = true; - header.fec_entropy_flag = false; header.packet_sequence_number = GG_UINT64_C(0x77123456789ABC); header.fec_group = 0; @@ -1775,9 +1954,7 @@ TEST_F(QuicFramerTest, ConstructStreamFramePacket) { 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, // private flags (entropy) - 0x02, - // first fec protected packet offset - 0xFF, + 0x01, // frame type (stream frame) 0x01, @@ -1812,7 +1989,6 @@ TEST_F(QuicFramerTest, ConstructStreamFramePacketWithVersionFlag) { header.public_header.version_flag = true; header.fec_flag = false; header.entropy_flag = true; - header.fec_entropy_flag = false; header.packet_sequence_number = GG_UINT64_C(0x77123456789ABC); header.fec_group = 0; @@ -1832,14 +2008,12 @@ TEST_F(QuicFramerTest, ConstructStreamFramePacketWithVersionFlag) { 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // version tag - 'Q', '0', '0', '4', + 'Q', '0', '0', '5', // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, // private flags (entropy) - 0x02, - // first fec protected packet offset - 0xFF, + 0x01, // frame type (stream frame) 0x01, @@ -1881,7 +2055,7 @@ TEST_F(QuicFramerTest, ConstructVersionNegotiationPacket) { 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, // version tag - 'Q', '0', '0', '4', + 'Q', '0', '0', '5', 'Q', '2', '.', '0', }; @@ -1904,7 +2078,6 @@ TEST_F(QuicFramerTest, ConstructAckFramePacket) { header.public_header.version_flag = false; header.fec_flag = false; header.entropy_flag = true; - header.fec_entropy_flag = true; header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); header.fec_group = 0; @@ -1929,10 +2102,8 @@ TEST_F(QuicFramerTest, ConstructAckFramePacket) { // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, - // private flags (entropy & fec_entropy -- not relevant) - 0x06, - // first fec protected packet offset - 0xFF, + // private flags (entropy) + 0x01, // frame type (ack frame) 0x02, @@ -1971,7 +2142,6 @@ TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketTCP) { header.public_header.version_flag = false; header.fec_flag = false; header.entropy_flag = false; - header.fec_entropy_flag = true; header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); header.fec_group = 0; @@ -1993,9 +2163,7 @@ TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketTCP) { 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, // private flags - 0x04, // (fec_entropy_flag) - // first fec protected packet offset - 0xFF, + 0x00, // frame type (congestion feedback frame) 0x03, @@ -2023,7 +2191,6 @@ TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketInterArrival) { header.public_header.version_flag = false; header.fec_flag = false; header.entropy_flag = false; - header.fec_entropy_flag = false; header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); header.fec_group = 0; @@ -2056,8 +2223,6 @@ TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketInterArrival) { 0x34, 0x12, // private flags 0x00, - // first fec protected packet offset - 0xFF, // frame type (congestion feedback frame) 0x03, @@ -2099,7 +2264,6 @@ TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketFixRate) { header.public_header.version_flag = false; header.fec_flag = false; header.entropy_flag = false; - header.fec_entropy_flag = false; header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); header.fec_group = 0; @@ -2122,8 +2286,6 @@ TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketFixRate) { 0x34, 0x12, // private flags 0x00, - // first fec protected packet offset - 0xFF, // frame type (congestion feedback frame) 0x03, @@ -2149,7 +2311,6 @@ TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketInvalidFeedback) { header.public_header.version_flag = false; header.fec_flag = false; header.entropy_flag = false; - header.fec_entropy_flag = false; header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); header.fec_group = 0; @@ -2172,7 +2333,6 @@ TEST_F(QuicFramerTest, ConstructRstFramePacket) { header.public_header.version_flag = false; header.fec_flag = false; header.entropy_flag = false; - header.fec_entropy_flag = false; header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); header.fec_group = 0; @@ -2192,8 +2352,6 @@ TEST_F(QuicFramerTest, ConstructRstFramePacket) { 0x34, 0x12, // private flags 0x00, - // first fec protected packet offset - 0xFF, // frame type (rst stream frame) 0x04, @@ -2229,7 +2387,6 @@ TEST_F(QuicFramerTest, ConstructCloseFramePacket) { header.public_header.version_flag = false; header.fec_flag = false; header.entropy_flag = true; - header.fec_entropy_flag = false; header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); header.fec_group = 0; @@ -2256,10 +2413,8 @@ TEST_F(QuicFramerTest, ConstructCloseFramePacket) { // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, - // private flags - 0x02, - // first fec protected packet offset - 0xFF, + // private flags (entropy) + 0x01, // frame type (connection close frame) 0x05, @@ -2309,7 +2464,6 @@ TEST_F(QuicFramerTest, ConstructGoAwayPacket) { header.public_header.version_flag = false; header.fec_flag = false; header.entropy_flag = true; - header.fec_entropy_flag = false; header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); header.fec_group = 0; @@ -2330,10 +2484,8 @@ TEST_F(QuicFramerTest, ConstructGoAwayPacket) { // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, - // private flags - 0x02, - // first fec protected packet offset - 0xFF, + // private flags(entropy) + 0x01, // frame type (go away frame) 0x06, @@ -2397,8 +2549,8 @@ TEST_F(QuicFramerTest, ConstructFecPacket) { header.public_header.version_flag = false; header.fec_flag = true; header.entropy_flag = true; - header.fec_entropy_flag = false; header.packet_sequence_number = (GG_UINT64_C(0x123456789ABC)); + header.is_in_fec_group = IN_FEC_GROUP; header.fec_group = GG_UINT64_C(0x123456789ABB);; QuicFecData fec_data; @@ -2414,8 +2566,8 @@ TEST_F(QuicFramerTest, ConstructFecPacket) { // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, - // private flags - 0x03, + // private flags (entropy & fec group & fec packet) + 0x07, // first fec protected packet offset 0x01, @@ -2446,8 +2598,8 @@ TEST_F(QuicFramerTest, EncryptPacket) { // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, - // private flags - 0x01, + // private flags (fec group & fec packet) + 0x06, // first fec protected packet offset 0x01, @@ -2460,7 +2612,7 @@ TEST_F(QuicFramerTest, EncryptPacket) { scoped_ptr<QuicPacket> raw( QuicPacket::NewDataPacket(AsChars(packet), arraysize(packet), false, - !kIncludeVersion)); + PACKET_8BYTE_GUID, !kIncludeVersion)); scoped_ptr<QuicEncryptedPacket> encrypted( framer_.EncryptPacket(ENCRYPTION_NONE, sequence_number, *raw)); @@ -2481,8 +2633,8 @@ TEST_F(QuicFramerTest, EncryptPacketWithVersionFlag) { // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, - // private flags - 0x01, + // private flags (fec group & fec flags) + 0x06, // first fec protected packet offset 0x01, @@ -2495,7 +2647,7 @@ TEST_F(QuicFramerTest, EncryptPacketWithVersionFlag) { scoped_ptr<QuicPacket> raw( QuicPacket::NewDataPacket(AsChars(packet), arraysize(packet), false, - kIncludeVersion)); + PACKET_8BYTE_GUID, kIncludeVersion)); scoped_ptr<QuicEncryptedPacket> encrypted( framer_.EncryptPacket(ENCRYPTION_NONE, sequence_number, *raw)); @@ -2532,7 +2684,6 @@ TEST_F(QuicFramerTest, DISABLED_Truncation) { header.public_header.version_flag = false; header.fec_flag = false; header.entropy_flag = false; - header.fec_entropy_flag = false; header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); header.fec_group = 0; @@ -2589,7 +2740,6 @@ TEST_F(QuicFramerTest, CleanTruncation) { header.public_header.version_flag = false; header.fec_flag = false; header.entropy_flag = true; - header.fec_entropy_flag = false; header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); header.fec_group = 0; @@ -2673,10 +2823,8 @@ TEST_F(QuicFramerTest, EntropyFlagTest) { // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, - // Entropy - 0x02, - // first fec protected packet offset - 0xFF, + // private flags (Entropy) + 0x01, // frame type (stream frame) 0x01, @@ -2704,7 +2852,7 @@ TEST_F(QuicFramerTest, EntropyFlagTest) { EXPECT_FALSE(visitor_.header_->fec_flag); }; -TEST_F(QuicFramerTest, FecEntropyFlagTest) { +TEST_F(QuicFramerTest, FecEntropyTest) { unsigned char packet[] = { // public flags (8 byte guid) 0x0C, @@ -2714,7 +2862,7 @@ TEST_F(QuicFramerTest, FecEntropyFlagTest) { // packet sequence number 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, - // Flags: Entropy, FEC-Entropy, FEC + // private flags (Entropy & fec group & FEC) 0x07, // first fec protected packet offset 0xFF, @@ -2742,7 +2890,6 @@ TEST_F(QuicFramerTest, FecEntropyFlagTest) { ASSERT_TRUE(visitor_.header_.get()); EXPECT_TRUE(visitor_.header_->fec_flag); EXPECT_TRUE(visitor_.header_->entropy_flag); - EXPECT_TRUE(visitor_.header_->fec_entropy_flag); EXPECT_EQ(1 << 4, visitor_.header_->entropy_hash); }; @@ -2757,9 +2904,7 @@ TEST_F(QuicFramerTest, StopPacketProcessing) { 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, // Entropy - 0x02, - // first fec protected packet offset - 0xFF, + 0x01, // frame type (stream frame) 0x01, @@ -2821,8 +2966,6 @@ TEST_F(QuicFramerTest, ConnectionCloseWithInvalidAck) { 0x34, 0x12, // private flags 0x00, - // first fec protected packet offset - 0xFF, // frame type (connection close frame) 0x05, diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc index cf8b235..9c00fe9 100644 --- a/net/quic/quic_http_stream_test.cc +++ b/net/quic/quic_http_stream_test.cc @@ -179,9 +179,8 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> { crypto_config_.SetDefaults(); session_.reset(new QuicClientSession(connection_, socket, NULL, &crypto_client_stream_factory_, - "www.google.com", QuicConfig(), + "www.google.com", DefaultQuicConfig(), &crypto_config_, NULL)); - session_->config()->SetDefaults(); session_->GetCryptoStream()->CryptoConnect(); EXPECT_TRUE(session_->IsCryptoHandshakeConfirmed()); QuicReliableClientStream* stream = @@ -280,7 +279,6 @@ class QuicHttpStreamTest : public ::testing::TestWithParam<bool> { header_.public_header.version_flag = should_include_version; header_.packet_sequence_number = sequence_number; header_.fec_group = 0; - header_.fec_entropy_flag = false; header_.entropy_flag = false; header_.fec_flag = false; } diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc index 02ad4d5..d87565e 100644 --- a/net/quic/quic_network_transaction_unittest.cc +++ b/net/quic/quic_network_transaction_unittest.cc @@ -100,7 +100,6 @@ class QuicNetworkTransactionTest : public PlatformTest { header.packet_sequence_number = num; header.entropy_flag = false; header.fec_flag = false; - header.fec_entropy_flag = false; header.fec_group = 0; QuicRstStreamFrame rst(stream_id, QUIC_STREAM_NO_ERROR); @@ -117,7 +116,6 @@ class QuicNetworkTransactionTest : public PlatformTest { header.packet_sequence_number = num; header.entropy_flag = false; header.fec_flag = false; - header.fec_entropy_flag = false; header.fec_group = 0; QuicAckFrame ack_frame(0, QuicTime::Zero(), 0); @@ -139,7 +137,6 @@ class QuicNetworkTransactionTest : public PlatformTest { header.packet_sequence_number = 2; header.entropy_flag = false; header.fec_flag = false; - header.fec_entropy_flag = false; header.fec_group = 0; QuicAckFrame ack(largest_received, QuicTime::Zero(), least_unacked); @@ -218,7 +215,6 @@ class QuicNetworkTransactionTest : public PlatformTest { header_.fec_group = 0; header_.entropy_flag = false; header_.fec_flag = false; - header_.fec_entropy_flag = false; } void CreateSession() { diff --git a/net/quic/quic_packet_creator.cc b/net/quic/quic_packet_creator.cc index 2cdfd69..c8d132f 100644 --- a/net/quic/quic_packet_creator.cc +++ b/net/quic/quic_packet_creator.cc @@ -28,7 +28,9 @@ QuicPacketCreator::QuicPacketCreator(QuicGuid guid, fec_group_number_(0), is_server_(is_server), send_version_in_packet_(!is_server), - packet_size_(GetPacketHeaderSize(send_version_in_packet_)) { + packet_size_(GetPacketHeaderSize(options_.send_guid_length, + send_version_in_packet_, + NOT_IN_FEC_GROUP)) { framer_->set_fec_builder(this); } @@ -43,16 +45,21 @@ void QuicPacketCreator::OnBuiltFecProtectedPayload( } bool QuicPacketCreator::ShouldSendFec(bool force_close) const { - return fec_group_.get() != NULL && + return fec_group_.get() != NULL && fec_group_->NumReceivedPackets() > 0 && (force_close || fec_group_->NumReceivedPackets() >= options_.max_packets_per_fec_group); } void QuicPacketCreator::MaybeStartFEC() { if (options_.max_packets_per_fec_group > 0 && fec_group_.get() == NULL) { + DCHECK(queued_frames_.empty()); // Set the fec group number to the sequence number of the next packet. fec_group_number_ = sequence_number() + 1; fec_group_.reset(new QuicFecGroup()); + packet_size_ = GetPacketHeaderSize(options_.send_guid_length, + send_version_in_packet_, + IN_FEC_GROUP); + DCHECK_LE(packet_size_, options_.max_packet_length); } } @@ -73,9 +80,12 @@ bool QuicPacketCreator::HasRoomForStreamFrame() const { } // static -size_t QuicPacketCreator::StreamFramePacketOverhead(int num_frames, - bool include_version) { - return GetPacketHeaderSize(include_version) + +size_t QuicPacketCreator::StreamFramePacketOverhead( + int num_frames, + QuicGuidLength guid_length, + bool include_version, + InFecGroup is_in_fec_group) { + return GetPacketHeaderSize(guid_length, include_version, is_in_fec_group) + QuicFramer::GetMinStreamFrameSize() * num_frames; } @@ -85,7 +95,8 @@ size_t QuicPacketCreator::CreateStreamFrame(QuicStreamId id, bool fin, QuicFrame* frame) { DCHECK_GT(options_.max_packet_length, - StreamFramePacketOverhead(1, kIncludeVersion)); + StreamFramePacketOverhead( + 1, PACKET_8BYTE_GUID, kIncludeVersion, IN_FEC_GROUP)); DCHECK(HasRoomForStreamFrame()); const size_t free_bytes = BytesFree(); @@ -147,7 +158,10 @@ SerializedPacket QuicPacketCreator::SerializePacket() { SerializedPacket serialized = framer_->ConstructFrameDataPacket( header, queued_frames_, packet_size_); queued_frames_.clear(); - packet_size_ = GetPacketHeaderSize(send_version_in_packet_); + packet_size_ = GetPacketHeaderSize(options_.send_guid_length, + send_version_in_packet_, + fec_group_.get() != NULL ? + IN_FEC_GROUP : NOT_IN_FEC_GROUP); serialized.retransmittable_frames = queued_retransmittable_frames_.release(); return serialized; } @@ -164,6 +178,10 @@ SerializedPacket QuicPacketCreator::SerializeFec() { SerializedPacket serialized = framer_->ConstructFecPacket(header, fec_data); fec_group_.reset(NULL); fec_group_number_ = 0; + // Reset packet_size_, since the next packet may not have an FEC group. + packet_size_ = GetPacketHeaderSize(options_.send_guid_length, + send_version_in_packet_, + NOT_IN_FEC_GROUP); DCHECK(serialized.packet); DCHECK_GE(options_.max_packet_length, serialized.packet->length()); return serialized; @@ -199,15 +217,23 @@ void QuicPacketCreator::FillPacketHeader(QuicFecGroupNumber fec_group, header->public_header.reset_flag = false; header->public_header.version_flag = send_version_in_packet_; header->fec_flag = fec_flag; - header->fec_entropy_flag = fec_entropy_flag; header->packet_sequence_number = ++sequence_number_; + + bool entropy_flag; if (header->packet_sequence_number == 1) { + DCHECK(!fec_flag); // TODO(satyamshekhar): No entropy in the first message. // For crypto tests to pass. Fix this by using deterministic QuicRandom. - header->entropy_flag = 0; + entropy_flag = 0; + } else if (fec_flag) { + // FEC packets don't have an entropy of their own. Entropy flag for FEC + // packets is the XOR of entropy of previous packets. + entropy_flag = fec_entropy_flag; } else { - header->entropy_flag = random_generator_->RandBool(); + entropy_flag = random_generator_->RandBool(); } + header->entropy_flag = entropy_flag; + header->is_in_fec_group = fec_group == 0 ? NOT_IN_FEC_GROUP : IN_FEC_GROUP; header->fec_group = fec_group; } diff --git a/net/quic/quic_packet_creator.h b/net/quic/quic_packet_creator.h index 6f557c0a..cb58625 100644 --- a/net/quic/quic_packet_creator.h +++ b/net/quic/quic_packet_creator.h @@ -32,13 +32,16 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { Options() : max_packet_length(kMaxPacketSize), random_reorder(false), - max_packets_per_fec_group(0) { + max_packets_per_fec_group(0), + send_guid_length(PACKET_8BYTE_GUID) { } size_t max_packet_length; bool random_reorder; // Inefficient: rewrite if used at scale. // 0 indicates fec is disabled. size_t max_packets_per_fec_group; + // Length of guid to send over the wire. + QuicGuidLength send_guid_length; }; // QuicRandom* required for packet entropy. @@ -65,7 +68,10 @@ class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { void StopSendingVersion(); // The overhead the framing will add for a packet with num_frames frames. - static size_t StreamFramePacketOverhead(int num_frames, bool include_version); + static size_t StreamFramePacketOverhead(int num_frames, + QuicGuidLength guid_length, + bool include_version, + InFecGroup is_in_fec_group); bool HasRoomForStreamFrame() const; diff --git a/net/quic/quic_packet_creator_test.cc b/net/quic/quic_packet_creator_test.cc index 4208b46..8e055b6 100644 --- a/net/quic/quic_packet_creator_test.cc +++ b/net/quic/quic_packet_creator_test.cc @@ -225,7 +225,8 @@ TEST_P(QuicPacketCreatorTest, CreateStreamFrameTooLarge) { } // A string larger than fits into a frame. creator_.options()->max_packet_length = GetPacketLengthForOneStream( - QuicPacketCreatorPeer::SendVersionInPacket(&creator_), 4); + QuicPacketCreatorPeer::SendVersionInPacket(&creator_), + NOT_IN_FEC_GROUP, 4); QuicFrame frame; size_t consumed = creator_.CreateStreamFrame(1u, "testTooLong", 0u, true, &frame); @@ -243,7 +244,9 @@ TEST_P(QuicPacketCreatorTest, AddFrameAndSerialize) { EXPECT_FALSE(creator_.HasPendingFrames()); EXPECT_EQ(max_plaintext_size - GetPacketHeaderSize( - QuicPacketCreatorPeer::SendVersionInPacket(&creator_)), + creator_.options()->send_guid_length, + QuicPacketCreatorPeer::SendVersionInPacket(&creator_), + NOT_IN_FEC_GROUP), creator_.BytesFree()); // Add a variety of frame types and then a padding frame. @@ -284,7 +287,9 @@ TEST_P(QuicPacketCreatorTest, AddFrameAndSerialize) { EXPECT_FALSE(creator_.HasPendingFrames()); EXPECT_EQ(max_plaintext_size - GetPacketHeaderSize( - QuicPacketCreatorPeer::SendVersionInPacket(&creator_)), + creator_.options()->send_guid_length, + QuicPacketCreatorPeer::SendVersionInPacket(&creator_), + NOT_IN_FEC_GROUP), creator_.BytesFree()); } diff --git a/net/quic/quic_packet_generator.cc b/net/quic/quic_packet_generator.cc index d0e491a..3d2a630 100644 --- a/net/quic/quic_packet_generator.cc +++ b/net/quic/quic_packet_generator.cc @@ -55,27 +55,25 @@ QuicPacketGenerator::~QuicPacketGenerator() { void QuicPacketGenerator::SetShouldSendAck(bool also_send_feedback) { should_send_ack_ = true; should_send_feedback_ = also_send_feedback; - SendQueuedData(); + SendQueuedFrames(); } void QuicPacketGenerator::AddControlFrame(const QuicFrame& frame) { queued_control_frames_.push_back(frame); - SendQueuedData(); + SendQueuedFrames(); } QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id, StringPiece data, QuicStreamOffset offset, bool fin) { - SendQueuedData(); + SendQueuedFrames(); size_t total_bytes_consumed = 0; bool fin_consumed = false; while (delegate_->CanWrite(NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA)) { - // TODO(rch) figure out FEC. - // packet_creator_.MaybeStartFEC(); QuicFrame frame; size_t bytes_consumed = packet_creator_->CreateStreamFrame( id, data, offset + total_bytes_consumed, fin, &frame); @@ -112,8 +110,9 @@ QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id, return QuicConsumedData(total_bytes_consumed, fin_consumed); } -void QuicPacketGenerator::SendQueuedData() { - while (HasPendingData() && delegate_->CanWrite(NOT_RETRANSMISSION, +void QuicPacketGenerator::SendQueuedFrames() { + packet_creator_->MaybeStartFEC(); + while (HasPendingFrames() && delegate_->CanWrite(NOT_RETRANSMISSION, packet_creator_->HasPendingFrames() ? HAS_RETRANSMITTABLE_DATA : NO_RETRANSMITTABLE_DATA)) { if (!AddNextPendingFrame()) { @@ -133,6 +132,7 @@ void QuicPacketGenerator::SendQueuedData() { SerializedPacket serialized_fec = packet_creator_->SerializeFec(); DCHECK(serialized_fec.packet); delegate_->OnSerializedPacket(serialized_fec); + packet_creator_->MaybeStartFEC(); } } } @@ -143,14 +143,14 @@ void QuicPacketGenerator::StartBatchOperations() { void QuicPacketGenerator::FinishBatchOperations() { should_flush_ = true; - SendQueuedData(); + SendQueuedFrames(); } -bool QuicPacketGenerator::HasQueuedData() const { - return packet_creator_->HasPendingFrames() || HasPendingData(); +bool QuicPacketGenerator::HasQueuedFrames() const { + return packet_creator_->HasPendingFrames() || HasPendingFrames(); } -bool QuicPacketGenerator::HasPendingData() const { +bool QuicPacketGenerator::HasPendingFrames() const { return should_send_ack_ || should_send_feedback_ || !queued_control_frames_.empty(); } @@ -187,7 +187,6 @@ bool QuicPacketGenerator::AddNextPendingFrame() { } void QuicPacketGenerator::SerializeAndSendPacket() { - packet_creator_->MaybeStartFEC(); SerializedPacket serialized_packet = packet_creator_->SerializePacket(); DCHECK(serialized_packet.packet); delegate_->OnSerializedPacket(serialized_packet); @@ -196,6 +195,7 @@ void QuicPacketGenerator::SerializeAndSendPacket() { SerializedPacket serialized_fec = packet_creator_->SerializeFec(); DCHECK(serialized_fec.packet); delegate_->OnSerializedPacket(serialized_fec); + packet_creator_->MaybeStartFEC(); } } diff --git a/net/quic/quic_packet_generator.h b/net/quic/quic_packet_generator.h index dad1924..9af5d8a 100644 --- a/net/quic/quic_packet_generator.h +++ b/net/quic/quic_packet_generator.h @@ -87,12 +87,12 @@ class NET_EXPORT_PRIVATE QuicPacketGenerator { // Enables flushing and flushes queued data. void FinishBatchOperations(); - bool HasQueuedData() const; + bool HasQueuedFrames() const; private: - void SendQueuedData(); + void SendQueuedFrames(); - bool HasPendingData() const; + bool HasPendingFrames() const; bool AddNextPendingFrame(); void SerializeAndSendPacket(); diff --git a/net/quic/quic_packet_generator_test.cc b/net/quic/quic_packet_generator_test.cc index a6af6c2..c4f514b 100644 --- a/net/quic/quic_packet_generator_test.cc +++ b/net/quic/quic_packet_generator_test.cc @@ -197,7 +197,7 @@ TEST_F(QuicPacketGeneratorTest, ShouldSendAck_NotWritable) { delegate_.SetCanWrite(false); generator_.SetShouldSendAck(false); - EXPECT_TRUE(generator_.HasQueuedData()); + EXPECT_TRUE(generator_.HasQueuedFrames()); } TEST_F(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldNotFlush) { @@ -207,7 +207,7 @@ TEST_F(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldNotFlush) { EXPECT_CALL(delegate_, CreateAckFrame()).WillOnce(Return(CreateAckFrame())); generator_.SetShouldSendAck(false); - EXPECT_TRUE(generator_.HasQueuedData()); + EXPECT_TRUE(generator_.HasQueuedFrames()); } TEST_F(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldFlush) { @@ -218,7 +218,7 @@ TEST_F(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldFlush) { DoAll(SaveArg<0>(&packet_), Return(true))); generator_.SetShouldSendAck(false); - EXPECT_FALSE(generator_.HasQueuedData()); + EXPECT_FALSE(generator_.HasQueuedFrames()); PacketContents contents; contents.num_ack_frames = 1; @@ -235,7 +235,7 @@ TEST_F(QuicPacketGeneratorTest, Return(CreateFeedbackFrame())); generator_.SetShouldSendAck(true); - EXPECT_TRUE(generator_.HasQueuedData()); + EXPECT_TRUE(generator_.HasQueuedFrames()); } TEST_F(QuicPacketGeneratorTest, @@ -250,7 +250,7 @@ TEST_F(QuicPacketGeneratorTest, DoAll(SaveArg<0>(&packet_), Return(true))); generator_.SetShouldSendAck(true); - EXPECT_FALSE(generator_.HasQueuedData()); + EXPECT_FALSE(generator_.HasQueuedFrames()); PacketContents contents; contents.num_ack_frames = 1; @@ -262,7 +262,7 @@ TEST_F(QuicPacketGeneratorTest, AddControlFrame_NotWritable) { delegate_.SetCanWrite(false); generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame())); - EXPECT_TRUE(generator_.HasQueuedData()); + EXPECT_TRUE(generator_.HasQueuedFrames()); } TEST_F(QuicPacketGeneratorTest, AddControlFrame_WritableAndShouldNotFlush) { @@ -270,7 +270,7 @@ TEST_F(QuicPacketGeneratorTest, AddControlFrame_WritableAndShouldNotFlush) { generator_.StartBatchOperations(); generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame())); - EXPECT_TRUE(generator_.HasQueuedData()); + EXPECT_TRUE(generator_.HasQueuedFrames()); } TEST_F(QuicPacketGeneratorTest, AddControlFrame_WritableAndShouldFlush) { @@ -280,7 +280,7 @@ TEST_F(QuicPacketGeneratorTest, AddControlFrame_WritableAndShouldFlush) { DoAll(SaveArg<0>(&packet_), Return(true))); generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame())); - EXPECT_FALSE(generator_.HasQueuedData()); + EXPECT_FALSE(generator_.HasQueuedFrames()); PacketContents contents; contents.num_rst_stream_frames = 1; @@ -293,7 +293,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_NotWritable) { QuicConsumedData consumed = generator_.ConsumeData(1, "foo", 2, true); EXPECT_EQ(0u, consumed.bytes_consumed); EXPECT_FALSE(consumed.fin_consumed); - EXPECT_FALSE(generator_.HasQueuedData()); + EXPECT_FALSE(generator_.HasQueuedFrames()); } TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldNotFlush) { @@ -303,7 +303,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldNotFlush) { QuicConsumedData consumed = generator_.ConsumeData(1, "foo", 2, true); EXPECT_EQ(3u, consumed.bytes_consumed); EXPECT_TRUE(consumed.fin_consumed); - EXPECT_TRUE(generator_.HasQueuedData()); + EXPECT_TRUE(generator_.HasQueuedFrames()); } TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldFlush) { @@ -314,7 +314,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldFlush) { QuicConsumedData consumed = generator_.ConsumeData(1, "foo", 2, true); EXPECT_EQ(3u, consumed.bytes_consumed); EXPECT_TRUE(consumed.fin_consumed); - EXPECT_FALSE(generator_.HasQueuedData()); + EXPECT_FALSE(generator_.HasQueuedFrames()); PacketContents contents; contents.num_stream_frames = 1; @@ -330,7 +330,7 @@ TEST_F(QuicPacketGeneratorTest, QuicConsumedData consumed = generator_.ConsumeData(3, "quux", 7, false); EXPECT_EQ(4u, consumed.bytes_consumed); EXPECT_FALSE(consumed.fin_consumed); - EXPECT_TRUE(generator_.HasQueuedData()); + EXPECT_TRUE(generator_.HasQueuedFrames()); } TEST_F(QuicPacketGeneratorTest, ConsumeData_BatchOperations) { @@ -341,13 +341,13 @@ TEST_F(QuicPacketGeneratorTest, ConsumeData_BatchOperations) { QuicConsumedData consumed = generator_.ConsumeData(3, "quux", 7, false); EXPECT_EQ(4u, consumed.bytes_consumed); EXPECT_FALSE(consumed.fin_consumed); - EXPECT_TRUE(generator_.HasQueuedData()); + EXPECT_TRUE(generator_.HasQueuedFrames()); // Now both frames will be flushed out. EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( DoAll(SaveArg<0>(&packet_), Return(true))); generator_.FinishBatchOperations(); - EXPECT_FALSE(generator_.HasQueuedData()); + EXPECT_FALSE(generator_.HasQueuedFrames()); PacketContents contents; contents.num_stream_frames = 2; @@ -380,7 +380,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataFEC) { generator_.ConsumeData(3, CreateData(data_len), 0, true); EXPECT_EQ(data_len, consumed.bytes_consumed); EXPECT_TRUE(consumed.fin_consumed); - EXPECT_FALSE(generator_.HasQueuedData()); + EXPECT_FALSE(generator_.HasQueuedFrames()); CheckPacketHasSingleStreamFrame(packet_); CheckPacketHasSingleStreamFrame(packet2_); @@ -412,7 +412,7 @@ TEST_F(QuicPacketGeneratorTest, ConsumeDataSendsFecAtEnd) { generator_.ConsumeData(3, CreateData(data_len), 0, true); EXPECT_EQ(data_len, consumed.bytes_consumed); EXPECT_TRUE(consumed.fin_consumed); - EXPECT_FALSE(generator_.HasQueuedData()); + EXPECT_FALSE(generator_.HasQueuedFrames()); CheckPacketHasSingleStreamFrame(packet_); CheckPacketHasSingleStreamFrame(packet2_); @@ -424,7 +424,7 @@ TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations) { generator_.SetShouldSendAck(true); generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame())); - EXPECT_TRUE(generator_.HasQueuedData()); + EXPECT_TRUE(generator_.HasQueuedFrames()); delegate_.SetCanWrite(true); @@ -444,7 +444,7 @@ TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations) { EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( DoAll(SaveArg<0>(&packet_), Return(true))); generator_.FinishBatchOperations(); - EXPECT_FALSE(generator_.HasQueuedData()); + EXPECT_FALSE(generator_.HasQueuedFrames()); PacketContents contents; contents.num_ack_frames = 1; @@ -460,7 +460,7 @@ TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations2) { generator_.SetShouldSendAck(true); generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame())); - EXPECT_TRUE(generator_.HasQueuedData()); + EXPECT_TRUE(generator_.HasQueuedFrames()); delegate_.SetCanWrite(true); @@ -490,7 +490,7 @@ TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations2) { generator_.AddControlFrame(QuicFrame(CreateGoAwayFrame())); generator_.FinishBatchOperations(); - EXPECT_FALSE(generator_.HasQueuedData()); + EXPECT_FALSE(generator_.HasQueuedFrames()); // The first packet should have the queued data and part of the stream data. PacketContents contents; diff --git a/net/quic/quic_protocol.cc b/net/quic/quic_protocol.cc index 4c0c88c..2293076 100644 --- a/net/quic/quic_protocol.cc +++ b/net/quic/quic_protocol.cc @@ -13,24 +13,35 @@ using std::string; namespace net { -size_t GetPacketHeaderSize(bool include_version) { - return kPublicFlagsSize + kQuicGuidSize + +size_t GetPacketHeaderSize(QuicPacketHeader header) { + return GetPacketHeaderSize(header.public_header.guid_length, + header.public_header.version_flag, + header.is_in_fec_group); +} + +size_t GetPacketHeaderSize(QuicGuidLength guid_length, + bool include_version, + InFecGroup is_in_fec_group) { + return kPublicFlagsSize + guid_length + (include_version ? kQuicVersionSize : 0) + kSequenceNumberSize + - kPrivateFlagsSize + kFecGroupSize; + kPrivateFlagsSize + (is_in_fec_group == IN_FEC_GROUP ? kFecGroupSize : 0); } size_t GetPublicResetPacketSize() { - return kPublicFlagsSize + kQuicGuidSize + kPublicResetNonceSize + + return kPublicFlagsSize + PACKET_8BYTE_GUID + kPublicResetNonceSize + kSequenceNumberSize; } -size_t GetStartOfFecProtectedData(bool include_version) { - return GetPacketHeaderSize(include_version); +size_t GetStartOfFecProtectedData(QuicGuidLength guid_length, + bool include_version) { + return GetPacketHeaderSize(guid_length, include_version, IN_FEC_GROUP); } -size_t GetStartOfEncryptedData(bool include_version) { - return GetPacketHeaderSize(include_version) - kPrivateFlagsSize - - kFecGroupSize; +size_t GetStartOfEncryptedData(QuicGuidLength guid_length, + bool include_version) { + // Don't include the fec size, since encryption starts before private flags. + return GetPacketHeaderSize(guid_length, include_version, NOT_IN_FEC_GROUP) - + kPrivateFlagsSize; } uint32 MakeQuicTag(char a, char b, char c, char d) { @@ -42,6 +53,7 @@ uint32 MakeQuicTag(char a, char b, char c, char d) { QuicPacketPublicHeader::QuicPacketPublicHeader() : guid(0), + guid_length(PACKET_8BYTE_GUID), reset_flag(false), version_flag(false) { } @@ -49,6 +61,7 @@ QuicPacketPublicHeader::QuicPacketPublicHeader() QuicPacketPublicHeader::QuicPacketPublicHeader( const QuicPacketPublicHeader& other) : guid(other.guid), + guid_length(other.guid_length), reset_flag(other.reset_flag), version_flag(other.version_flag), versions(other.versions) { @@ -67,20 +80,20 @@ QuicPacketPublicHeader& QuicPacketPublicHeader::operator=( QuicPacketHeader::QuicPacketHeader() : fec_flag(false), - fec_entropy_flag(false), entropy_flag(false), entropy_hash(0), packet_sequence_number(0), + is_in_fec_group(NOT_IN_FEC_GROUP), fec_group(0) { } QuicPacketHeader::QuicPacketHeader(const QuicPacketPublicHeader& header) : public_header(header), fec_flag(false), - fec_entropy_flag(false), entropy_flag(false), entropy_hash(0), packet_sequence_number(0), + is_in_fec_group(NOT_IN_FEC_GROUP), fec_group(0) { } @@ -98,6 +111,7 @@ QuicStreamFrame::QuicStreamFrame(QuicStreamId stream_id, ostream& operator<<(ostream& os, const QuicPacketHeader& header) { os << "{ guid: " << header.public_header.guid + << ", guid_length:" << header.public_header.guid_length << ", reset_flag: " << header.public_header.reset_flag << ", version_flag: " << header.public_header.version_flag; if (header.public_header.version_flag) { @@ -110,6 +124,7 @@ ostream& operator<<(ostream& os, const QuicPacketHeader& header) { << ", entropy_flag: " << header.entropy_flag << ", entropy hash: " << static_cast<int>(header.entropy_hash) << ", sequence_number: " << header.packet_sequence_number + << ", is_in_fec_group:" << header.is_in_fec_group << ", fec_group: " << header.fec_group<< "}\n"; return os; } @@ -253,23 +268,25 @@ QuicData::~QuicData() { } StringPiece QuicPacket::FecProtectedData() const { - const size_t start_of_fec = GetStartOfFecProtectedData(includes_version_); + const size_t start_of_fec = GetStartOfFecProtectedData(guid_length_, + includes_version_); return StringPiece(data() + start_of_fec, length() - start_of_fec); } StringPiece QuicPacket::AssociatedData() const { return StringPiece(data() + kStartOfHashData, - GetStartOfEncryptedData(includes_version_) - + GetStartOfEncryptedData(guid_length_, includes_version_) - kStartOfHashData); } StringPiece QuicPacket::BeforePlaintext() const { - return StringPiece(data(), GetStartOfEncryptedData(includes_version_)); + return StringPiece(data(), GetStartOfEncryptedData(guid_length_, + includes_version_)); } StringPiece QuicPacket::Plaintext() const { const size_t start_of_encrypted_data = - GetStartOfEncryptedData(includes_version_); + GetStartOfEncryptedData(guid_length_, includes_version_); return StringPiece(data() + start_of_encrypted_data, length() - start_of_encrypted_data); } diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h index 08d168c4f..4cde996 100644 --- a/net/quic/quic_protocol.h +++ b/net/quic/quic_protocol.h @@ -28,6 +28,7 @@ namespace net { using ::operator<<; class QuicPacket; +struct QuicPacketHeader; typedef uint64 QuicGuid; typedef uint32 QuicStreamId; @@ -50,8 +51,6 @@ const size_t kDefaultMaxStreamsPerConnection = 100; // Number of bytes reserved for public flags in the packet header. const size_t kPublicFlagsSize = 1; -// Number of bytes reserved for guid in the packet header. -const size_t kQuicGuidSize = 8; // Number of bytes reserved for version number in the packet header. const size_t kQuicVersionSize = 4; // Number of bytes reserved for sequence number in the packet header. @@ -66,15 +65,6 @@ const size_t kPublicResetNonceSize = 8; // Signifies that the QuicPacket will contain version of the protocol. const bool kIncludeVersion = true; -// Size in bytes of the data or fec packet header. -NET_EXPORT_PRIVATE size_t GetPacketHeaderSize(bool include_version); -// Size in bytes of the public reset packet. -NET_EXPORT_PRIVATE size_t GetPublicResetPacketSize(); - -// Index of the first byte in a QUIC packet of FEC protected data. -NET_EXPORT_PRIVATE size_t GetStartOfFecProtectedData(bool include_version); -// Index of the first byte in a QUIC packet of encrypted data. -NET_EXPORT_PRIVATE size_t GetStartOfEncryptedData(bool include_version); // Returns true if |version| is a supported protocol version. NET_EXPORT_PRIVATE bool IsSupportedVersion(QuicTag version); @@ -90,13 +80,11 @@ const QuicHeaderId kMaxHeaderIdDelta = 100; // TODO(rch): ensure that this is not usable by any other streams. const QuicStreamId kCryptoStreamId = 1; -// Value which indicates this packet is not FEC protected. -const uint8 kNoFecOffset = 0xFF; - // This is the default network timeout a for connection till the crypto // handshake succeeds and the negotiated timeout from the handshake is received. -const int64 kDefaultInitialTimeoutSecs = 30; // 30 secs. +const int64 kDefaultInitialTimeoutSecs = 120; // 2 mins. const int64 kDefaultTimeoutSecs = 60 * 10; // 10 minutes. +const int64 kDefaultMaxTimeForCryptoHandshakeSecs = 5; // 5 secs. enum Retransmission { NOT_RETRANSMISSION, @@ -119,6 +107,18 @@ enum QuicFrameType { NUM_FRAME_TYPES }; +enum QuicGuidLength { + PACKET_0BYTE_GUID = 0, + PACKET_1BYTE_GUID = 1, + PACKET_4BYTE_GUID = 4, + PACKET_8BYTE_GUID = 8 +}; + +enum InFecGroup { + NOT_IN_FEC_GROUP, + IN_FEC_GROUP, +}; + enum QuicPacketPublicFlags { PACKET_PUBLIC_FLAGS_NONE = 0, PACKET_PUBLIC_FLAGS_VERSION = 1 << 0, // Packet header contains version info. @@ -133,12 +133,29 @@ enum QuicPacketPublicFlags { enum QuicPacketPrivateFlags { PACKET_PRIVATE_FLAGS_NONE = 0, - PACKET_PRIVATE_FLAGS_FEC = 1 << 0, // Payload is FEC as opposed to frames. - PACKET_PRIVATE_FLAGS_ENTROPY = 1 << 1, - PACKET_PRIVATE_FLAGS_FEC_ENTROPY = 1 << 2, + PACKET_PRIVATE_FLAGS_ENTROPY = 1 << 0, + PACKET_PRIVATE_FLAGS_FEC_GROUP = 1 << 1, // Payload is part of an FEC group. + PACKET_PRIVATE_FLAGS_FEC = 1 << 2, // Payload is FEC as opposed to frames. PACKET_PRIVATE_FLAGS_MAX = (1 << 3) - 1 // All bits set. }; +// Size in bytes of the data or fec packet header. +NET_EXPORT_PRIVATE size_t GetPacketHeaderSize(QuicPacketHeader header); + +NET_EXPORT_PRIVATE size_t GetPacketHeaderSize(QuicGuidLength guid_length, + bool include_version, + InFecGroup is_in_fec_group); + +// Size in bytes of the public reset packet. +NET_EXPORT_PRIVATE size_t GetPublicResetPacketSize(); + +// Index of the first byte in a QUIC packet of FEC protected data. +NET_EXPORT_PRIVATE size_t GetStartOfFecProtectedData(QuicGuidLength guid_length, + bool include_version); +// Index of the first byte in a QUIC packet of encrypted data. +NET_EXPORT_PRIVATE size_t GetStartOfEncryptedData(QuicGuidLength guid_length, + bool include_version); + enum QuicRstStreamErrorCode { QUIC_STREAM_NO_ERROR = 0, @@ -278,7 +295,7 @@ const QuicTag kUnsupportedVersion = -1; // Each time the wire format changes, this need needs to be incremented. // At some point, we will actually freeze the wire format and make an official // version number, but this works for now. -const QuicTag kQuicVersion1 = TAG('Q', '0', '0', '4'); +const QuicTag kQuicVersion1 = TAG('Q', '0', '0', '5'); #undef TAG // MakeQuicTag returns a value given the four bytes. For example: @@ -294,6 +311,7 @@ struct NET_EXPORT_PRIVATE QuicPacketPublicHeader { // Universal header. All QuicPacket headers will have a guid and public flags. QuicGuid guid; + QuicGuidLength guid_length; bool reset_flag; bool version_flag; QuicTagVector versions; @@ -309,10 +327,10 @@ struct NET_EXPORT_PRIVATE QuicPacketHeader { QuicPacketPublicHeader public_header; bool fec_flag; - bool fec_entropy_flag; bool entropy_flag; QuicPacketEntropyHash entropy_hash; QuicPacketSequenceNumber packet_sequence_number; + InFecGroup is_in_fec_group; QuicFecGroupNumber fec_group; }; @@ -606,15 +624,19 @@ class NET_EXPORT_PRIVATE QuicPacket : public QuicData { static QuicPacket* NewDataPacket(char* buffer, size_t length, bool owns_buffer, + QuicGuidLength guid_length, bool includes_version) { - return new QuicPacket(buffer, length, owns_buffer, includes_version, false); + return new QuicPacket( + buffer, length, owns_buffer, guid_length, includes_version, false); } static QuicPacket* NewFecPacket(char* buffer, size_t length, bool owns_buffer, + QuicGuidLength guid_length, bool includes_version) { - return new QuicPacket(buffer, length, owns_buffer, includes_version, true); + return new QuicPacket( + buffer, length, owns_buffer, guid_length, includes_version, true); } base::StringPiece FecProtectedData() const; @@ -632,15 +654,18 @@ class NET_EXPORT_PRIVATE QuicPacket : public QuicData { QuicPacket(char* buffer, size_t length, bool owns_buffer, + QuicGuidLength guid_length, bool includes_version, bool is_fec_packet) : QuicData(buffer, length, owns_buffer), buffer_(buffer), is_fec_packet_(is_fec_packet), + guid_length_(guid_length), includes_version_(includes_version) {} char* buffer_; const bool is_fec_packet_; + const QuicGuidLength guid_length_; const bool includes_version_; DISALLOW_COPY_AND_ASSIGN(QuicPacket); diff --git a/net/quic/quic_session.cc b/net/quic/quic_session.cc index 12bec26..2ed5b07 100644 --- a/net/quic/quic_session.cc +++ b/net/quic/quic_session.cc @@ -76,7 +76,13 @@ QuicSession::QuicSession(QuicConnection* connection, largest_peer_created_stream_id_(0), goaway_received_(false), goaway_sent_(false) { - connection->set_visitor(visitor_shim_.get()); + set_max_open_streams(config_.max_streams_per_connection()); + + connection_->set_visitor(visitor_shim_.get()); + connection_->SetIdleNetworkTimeout(config_.idle_connection_state_lifetime()); + connection_->SetOverallConnectionTimeout( + config_.max_time_before_crypto_handshake()); + // TODO(satyamshekhar): Set congestion control and ICSL also. } QuicSession::~QuicSession() { @@ -114,16 +120,17 @@ bool QuicSession::OnPacket(const IPEndPoint& self_address, while (!decompression_blocked_streams_.empty()) { QuicHeaderId header_id = decompression_blocked_streams_.begin()->first; - if (header_id == decompressor_.current_header_id()) { - QuicStreamId stream_id = decompression_blocked_streams_.begin()->second; - decompression_blocked_streams_.erase(header_id); - ReliableQuicStream* stream = GetStream(stream_id); - if (!stream) { - connection()->SendConnectionClose( - QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED); - } - stream->OnDecompressorAvailable(); + if (header_id != decompressor_.current_header_id()) { + break; + } + QuicStreamId stream_id = decompression_blocked_streams_.begin()->second; + decompression_blocked_streams_.erase(header_id); + ReliableQuicStream* stream = GetStream(stream_id); + if (!stream) { + connection()->SendConnectionClose( + QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED); } + stream->OnDecompressorAvailable(); } return true; } @@ -232,8 +239,9 @@ void QuicSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { case HANDSHAKE_CONFIRMED: LOG_IF(DFATAL, !config_.negotiated()) << "Handshake confirmed without parameter negotiation."; - connection_->SetConnectionTimeout( + 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 10ede55..e5793d7 100644 --- a/net/quic/quic_session.h +++ b/net/quic/quic_session.h @@ -106,7 +106,7 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { // Returns mutable config for this session. Returned config is owned // by QuicSession. - virtual QuicConfig* config(); + QuicConfig* config(); // Returns true if the stream existed previously and has been closed. // Returns false if the stream is still active or if the stream has diff --git a/net/quic/quic_session_test.cc b/net/quic/quic_session_test.cc index 338caed..7ad64bf 100644 --- a/net/quic/quic_session_test.cc +++ b/net/quic/quic_session_test.cc @@ -60,9 +60,8 @@ class TestStream : public ReliableQuicStream { class TestSession : public QuicSession { public: TestSession(QuicConnection* connection, bool is_server) - : QuicSession(connection, QuicConfig(), is_server), + : QuicSession(connection, DefaultQuicConfig(), is_server), crypto_stream_(this) { - config()->SetDefaults(); } virtual QuicCryptoStream* GetCryptoStream() OVERRIDE { @@ -93,7 +92,6 @@ class TestSession : public QuicSession { } TestCryptoStream crypto_stream_; - QuicConfig config_; }; class QuicSessionTest : public ::testing::Test { @@ -235,11 +233,11 @@ TEST_F(QuicSessionTest, SendGoAway) { TEST_F(QuicSessionTest, IncreasedTimeoutAfterCryptoHandshake) { EXPECT_EQ(kDefaultInitialTimeoutSecs, - QuicConnectionPeer::GetTimeout(connection_).ToSeconds()); + QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds()); CryptoHandshakeMessage msg; session_.crypto_stream_.OnHandshakeMessage(msg); EXPECT_EQ(kDefaultTimeoutSecs, - QuicConnectionPeer::GetTimeout(connection_).ToSeconds()); + QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds()); } } // namespace diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc index f213696..6e767d7 100644 --- a/net/quic/quic_stream_factory.cc +++ b/net/quic/quic_stream_factory.cc @@ -226,6 +226,10 @@ QuicStreamFactory::QuicStreamFactory( random_generator_(random_generator), clock_(clock), weak_factory_(this) { + config_.SetDefaults(); + config_.set_idle_connection_state_lifetime( + QuicTime::Delta::FromSeconds(30), + QuicTime::Delta::FromSeconds(30)); } QuicStreamFactory::~QuicStreamFactory() { @@ -386,12 +390,8 @@ QuicClientSession* QuicStreamFactory::CreateSession( QuicClientSession* session = new QuicClientSession(connection, socket, this, quic_crypto_client_stream_factory_, - host_port_proxy_pair.first.host(), QuicConfig(), + host_port_proxy_pair.first.host(), config_, crypto_config, net_log.net_log()); - session->config()->SetDefaults(); - session->config()->set_idle_connection_state_lifetime( - QuicTime::Delta::FromSeconds(30), - QuicTime::Delta::FromSeconds(30)); all_sessions_.insert(session); // owning pointer return session; } diff --git a/net/quic/quic_stream_factory.h b/net/quic/quic_stream_factory.h index c9e287b..db3e7ac 100644 --- a/net/quic/quic_stream_factory.h +++ b/net/quic/quic_stream_factory.h @@ -154,6 +154,8 @@ class NET_EXPORT_PRIVATE QuicStreamFactory // clear the data in the map. CryptoConfigMap all_crypto_configs_; + QuicConfig config_; + JobMap active_jobs_; JobRequestsMap job_requests_map_; RequestMap active_requests_; diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc index 3772d13..608fdcf 100644 --- a/net/quic/quic_stream_factory_test.cc +++ b/net/quic/quic_stream_factory_test.cc @@ -43,7 +43,6 @@ class QuicStreamFactoryTest : public ::testing::Test { header.public_header.version_flag = true; header.packet_sequence_number = num; header.entropy_flag = false; - header.fec_entropy_flag = false; header.fec_flag = false; header.fec_group = 0; @@ -61,7 +60,6 @@ class QuicStreamFactoryTest : public ::testing::Test { header.public_header.version_flag = false; header.packet_sequence_number = 2; header.entropy_flag = false; - header.fec_entropy_flag = false; header.fec_flag = false; header.fec_group = 0; @@ -90,7 +88,6 @@ class QuicStreamFactoryTest : public ::testing::Test { header.public_header.version_flag = false; header.packet_sequence_number = sequence_number; header.entropy_flag = false; - header.fec_entropy_flag = false; header.fec_flag = false; header.fec_group = 0; diff --git a/net/quic/quic_stream_sequencer.cc b/net/quic/quic_stream_sequencer.cc index 6a9f8f1..3ab5211 100644 --- a/net/quic/quic_stream_sequencer.cc +++ b/net/quic/quic_stream_sequencer.cc @@ -139,10 +139,10 @@ bool QuicStreamSequencer::MaybeCloseStream() { return false; } -int QuicStreamSequencer::GetReadableRegions(iovec* iov, int iov_len) { +int QuicStreamSequencer::GetReadableRegions(iovec* iov, size_t iov_len) { FrameMap::iterator it = frames_.begin(); - int index = 0; - uint64 offset = num_bytes_consumed_; + size_t index = 0; + QuicStreamOffset offset = num_bytes_consumed_; while (it != frames_.end() && index < iov_len) { if (it->first != offset) return index; @@ -157,9 +157,9 @@ int QuicStreamSequencer::GetReadableRegions(iovec* iov, int iov_len) { return index; } -int QuicStreamSequencer::Readv(const struct iovec* iov, int iov_len) { +int QuicStreamSequencer::Readv(const struct iovec* iov, size_t iov_len) { FrameMap::iterator it = frames_.begin(); - int iov_index = 0; + size_t iov_index = 0; size_t iov_offset = 0; size_t frame_offset = 0; size_t initial_bytes_consumed = num_bytes_consumed_; diff --git a/net/quic/quic_stream_sequencer.h b/net/quic/quic_stream_sequencer.h index acd7ada..249838d 100644 --- a/net/quic/quic_stream_sequencer.h +++ b/net/quic/quic_stream_sequencer.h @@ -59,11 +59,11 @@ class NET_EXPORT_PRIVATE QuicStreamSequencer { // Fills in up to iov_len iovecs with the next readable regions. Returns the // number of iovs used. Non-destructive of the underlying data. - int GetReadableRegions(iovec* iov, int iov_len); + int GetReadableRegions(iovec* iov, size_t iov_len); // Copies the data into the iov_len buffers provided. Returns the number of // bytes read. Any buffered data no longer in use will be released. - int Readv(const struct iovec* iov, int iov_len); + int Readv(const struct iovec* iov, size_t iov_len); // Consumes |num_bytes| data. Used in conjunction with |GetReadableRegions| // to do zero-copy reads. diff --git a/net/quic/quic_time.cc b/net/quic/quic_time.cc index fa0d516..a567756 100644 --- a/net/quic/quic_time.cc +++ b/net/quic/quic_time.cc @@ -104,6 +104,11 @@ QuicWallTime QuicWallTime::FromUNIXSeconds(uint64 seconds) { return QuicWallTime(seconds); } +// static +QuicWallTime QuicWallTime::Zero() { + return QuicWallTime(0); +} + uint64 QuicWallTime::ToUNIXSeconds() const { return seconds_; } @@ -116,6 +121,10 @@ bool QuicWallTime::IsBefore(QuicWallTime other) const { return seconds_ < other.seconds_; } +bool QuicWallTime::IsZero() const { + return seconds_ == 0; +} + QuicTime::Delta QuicWallTime::AbsoluteDifference(QuicWallTime other) const { uint64 d; diff --git a/net/quic/quic_time.h b/net/quic/quic_time.h index 067027b..9c846c9 100644 --- a/net/quic/quic_time.h +++ b/net/quic/quic_time.h @@ -108,6 +108,10 @@ class NET_EXPORT_PRIVATE QuicWallTime { // since the UNIX epoch. static QuicWallTime FromUNIXSeconds(uint64 seconds); + // Zero returns a QuicWallTime set to zero. IsZero will return true for this + // value. + static QuicWallTime Zero(); + // ToUNIXSeconds converts a QuicWallTime into a count of seconds since the // UNIX epoch. uint64 ToUNIXSeconds() const; @@ -115,6 +119,9 @@ class NET_EXPORT_PRIVATE QuicWallTime { bool IsAfter(QuicWallTime other) const; bool IsBefore(QuicWallTime other) const; + // IsZero returns true if this object is the result of calling |Zero|. + bool IsZero() const; + // AbsoluteDifference returns the absolute value of the time difference // between |this| and |other|. QuicTime::Delta AbsoluteDifference(QuicWallTime other) const; diff --git a/net/quic/reliable_quic_stream.cc b/net/quic/reliable_quic_stream.cc index 6a82cce..0ed990e 100644 --- a/net/quic/reliable_quic_stream.cc +++ b/net/quic/reliable_quic_stream.cc @@ -98,12 +98,12 @@ void ReliableQuicStream::Close(QuicRstStreamErrorCode error) { session()->SendRstStream(id(), error); } -int ReliableQuicStream::Readv(const struct iovec* iov, int iov_len) { +int ReliableQuicStream::Readv(const struct iovec* iov, size_t iov_len) { if (headers_decompressed_ && decompressed_headers_.empty()) { return sequencer_.Readv(iov, iov_len); } size_t bytes_consumed = 0; - int iov_index = 0; + size_t iov_index = 0; while (iov_index < iov_len && decompressed_headers_.length() > bytes_consumed) { int bytes_to_read = min(iov[iov_index].iov_len, @@ -118,7 +118,7 @@ int ReliableQuicStream::Readv(const struct iovec* iov, int iov_len) { return bytes_consumed; } -int ReliableQuicStream::GetReadableRegions(iovec* iov, int iov_len) { +int ReliableQuicStream::GetReadableRegions(iovec* iov, size_t iov_len) { if (headers_decompressed_ && decompressed_headers_.empty()) { return sequencer_.GetReadableRegions(iov, iov_len); } diff --git a/net/quic/reliable_quic_stream.h b/net/quic/reliable_quic_stream.h index 51ca55e..279c0bf 100644 --- a/net/quic/reliable_quic_stream.h +++ b/net/quic/reliable_quic_stream.h @@ -85,8 +85,8 @@ class NET_EXPORT_PRIVATE ReliableQuicStream : public // This block of functions wraps the sequencer's functions of the same // name. These methods return uncompressed data until that has // been fully processed. Then they simply delegate to the sequencer. - virtual int Readv(const struct iovec* iov, int iov_len); - virtual int GetReadableRegions(iovec* iov, int iov_len); + virtual int Readv(const struct iovec* iov, size_t iov_len); + virtual int GetReadableRegions(iovec* iov, size_t iov_len); virtual bool IsHalfClosed() const; virtual bool IsClosed() const; virtual bool HasBytesToRead() const; diff --git a/net/quic/reliable_quic_stream_test.cc b/net/quic/reliable_quic_stream_test.cc index ede58bd..54f97e8 100644 --- a/net/quic/reliable_quic_stream_test.cc +++ b/net/quic/reliable_quic_stream_test.cc @@ -94,7 +94,8 @@ TEST_F(ReliableQuicStreamTest, WriteAllData) { Initialize(kShouldProcessData); connection_->options()->max_packet_length = - 1 + QuicPacketCreator::StreamFramePacketOverhead(1, !kIncludeVersion); + 1 + QuicPacketCreator::StreamFramePacketOverhead( + 1, PACKET_8BYTE_GUID, !kIncludeVersion, NOT_IN_FEC_GROUP); // TODO(rch): figure out how to get StrEq working here. //EXPECT_CALL(*session_, WriteData(kStreamId, StrEq(kData1), _, _)).WillOnce( EXPECT_CALL(*session_, WriteData(kStreamId, _, _, _)).WillOnce( @@ -106,7 +107,8 @@ TEST_F(ReliableQuicStreamTest, WriteData) { Initialize(kShouldProcessData); connection_->options()->max_packet_length = - 1 + QuicPacketCreator::StreamFramePacketOverhead(1, !kIncludeVersion); + 1 + QuicPacketCreator::StreamFramePacketOverhead( + 1, PACKET_8BYTE_GUID, !kIncludeVersion, NOT_IN_FEC_GROUP); // TODO(rch): figure out how to get StrEq working here. //EXPECT_CALL(*session_, WriteData(_, StrEq(kData1), _, _)).WillOnce( EXPECT_CALL(*session_, WriteData(_, _, _, _)).WillOnce( @@ -309,6 +311,7 @@ TEST_F(ReliableQuicStreamTest, ProcessHeadersEarly) { stream2_->OnDecompressorAvailable(); EXPECT_EQ(decompressed_headers2, stream2_->data()); } + TEST_F(ReliableQuicStreamTest, ProcessHeadersDelay) { Initialize(!kShouldProcessData); diff --git a/net/quic/test_tools/crypto_test_utils.cc b/net/quic/test_tools/crypto_test_utils.cc index 40a61a4..d469bfa 100644 --- a/net/quic/test_tools/crypto_test_utils.cc +++ b/net/quic/test_tools/crypto_test_utils.cc @@ -108,11 +108,11 @@ bool HexChar(char c, uint8* value) { return true; } if (c >= 'a' && c <= 'f') { - *value = c - 'a'; + *value = c - 'a' + 10; return true; } if (c >= 'A' && c <= 'F') { - *value = c - 'A'; + *value = c - 'A' + 10; return true; } return false; @@ -121,7 +121,8 @@ bool HexChar(char c, uint8* value) { } // anonymous namespace CryptoTestUtils::FakeClientOptions::FakeClientOptions() - : dont_verify_certs(false) { + : dont_verify_certs(false), + channel_id_enabled(false) { } // static @@ -136,7 +137,8 @@ int CryptoTestUtils::HandshakeWithFakeServer( new PacketSavingConnection(guid, addr, true); TestSession server_session(server_conn, QuicConfig(), true); - QuicCryptoServerConfig crypto_config(QuicCryptoServerConfig::TESTING); + QuicCryptoServerConfig crypto_config(QuicCryptoServerConfig::TESTING, + QuicRandom::GetInstance()); SetupCryptoServerConfigForTest( server_session.connection()->clock(), server_session.connection()->random_generator(), @@ -173,8 +175,11 @@ int CryptoTestUtils::HandshakeWithFakeClient( crypto_config.SetDefaults(); // TODO(rtenneti): Enable testing of ProofVerifier. // if (!options.dont_verify_certs) { - // crypto_config.SetProofVerifier(ProofVerifierForTesting()); + // crypto_config.SetProofVerifier(ProofVerifierForTesting()); // } + if (options.channel_id_enabled) { + crypto_config.SetChannelIDSigner(ChannelIDSignerForTesting()); + } QuicCryptoClientStream client("test.example.com", &client_session, &crypto_config); client_session.SetCryptoStream(&client); @@ -196,9 +201,10 @@ void CryptoTestUtils::SetupCryptoServerConfigForTest( QuicConfig* config, QuicCryptoServerConfig* crypto_config) { config->SetDefaults(); + QuicCryptoServerConfig::ConfigOptions options; + options.channel_id_enabled = true; scoped_ptr<CryptoHandshakeMessage> scfg( - crypto_config->AddDefaultConfig( - rand, clock, QuicCryptoServerConfig::kDefaultExpiry)); + crypto_config->AddDefaultConfig(rand, clock, options)); } // static diff --git a/net/quic/test_tools/crypto_test_utils.h b/net/quic/test_tools/crypto_test_utils.h index b811d9f..3946c44 100644 --- a/net/quic/test_tools/crypto_test_utils.h +++ b/net/quic/test_tools/crypto_test_utils.h @@ -17,6 +17,7 @@ namespace net { +class ChannelIDSigner; class CommonCertSets; class ProofSource; class ProofVerifier; @@ -42,6 +43,11 @@ class CryptoTestUtils { // If dont_verify_certs is true then no ProofVerifier is set on the client. // Thus no certificates will be requested or checked. bool dont_verify_certs; + + // If channel_id_enabled is true then the client will attempt to send a + // ChannelID. The key will be the same as is returned by + // |ChannelIDKeyForHostname|. + bool channel_id_enabled; }; // returns: the number of client hellos that the client sent. @@ -110,6 +116,14 @@ class CryptoTestUtils { static CryptoHandshakeMessage BuildMessage(const char* message_tag, va_list ap); + // ChannelIDSignerForTesting returns a ChannelIDSigner that generates keys + // deterministically based on the hostname given in the Sign call. + static ChannelIDSigner* ChannelIDSignerForTesting(); + + // ChannelIDKeyForHostname returns the ChannelID key that + // |ChannelIDSignerForTesting| will use for the given hostname. + static std::string ChannelIDKeyForHostname(const std::string& hostname); + private: static void CompareClientAndServerKeys(QuicCryptoClientStream* client, QuicCryptoServerStream* server); diff --git a/net/quic/test_tools/crypto_test_utils_nss.cc b/net/quic/test_tools/crypto_test_utils_nss.cc new file mode 100644 index 0000000..d6be917 --- /dev/null +++ b/net/quic/test_tools/crypto_test_utils_nss.cc @@ -0,0 +1,69 @@ +// 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/test_tools/crypto_test_utils.h" + +#include "net/quic/crypto/channel_id.h" + +using base::StringPiece; +using std::string; + +namespace net { + +namespace test { + +// TODO(rtenneti): Implement NSS support ChannelIDSigner. Convert Sign() to be +// asynchronous using completion callback. After porting TestChannelIDSigner, +// implement real ChannelIDSigner. +class TestChannelIDSigner : public ChannelIDSigner { + public: + virtual ~TestChannelIDSigner() { } + + // ChannelIDSigner implementation. + + virtual bool Sign(const string& hostname, + StringPiece signed_data, + string* out_key, + string* out_signature) OVERRIDE { + string key(HostnameToKey(hostname)); + + *out_key = SerializeKey(key); + if (out_key->empty()) { + return false; + } + + *out_signature = signed_data.as_string(); + + return true; + } + + static string KeyForHostname(const string& hostname) { + string key(HostnameToKey(hostname)); + return SerializeKey(key); + } + + private: + static string HostnameToKey(const string& hostname) { + string ret = hostname; + return ret; + } + + static string SerializeKey(string& key) { + return string(key); + } +}; + +// static +ChannelIDSigner* CryptoTestUtils::ChannelIDSignerForTesting() { + return new TestChannelIDSigner(); +} + +// static +string CryptoTestUtils::ChannelIDKeyForHostname(const string& hostname) { + return TestChannelIDSigner::KeyForHostname(hostname); +} + +} // namespace test + +} // namespace net diff --git a/net/quic/test_tools/crypto_test_utils_openssl.cc b/net/quic/test_tools/crypto_test_utils_openssl.cc new file mode 100644 index 0000000..24d0bc7 --- /dev/null +++ b/net/quic/test_tools/crypto_test_utils_openssl.cc @@ -0,0 +1,178 @@ +// 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/test_tools/crypto_test_utils.h" + +#include <openssl/bn.h> +#include <openssl/ec.h> +#include <openssl/ecdsa.h> +#include <openssl/evp.h> +#include <openssl/obj_mac.h> +#include <openssl/sha.h> + +#include "crypto/openssl_util.h" +#include "crypto/secure_hash.h" +#include "net/quic/crypto/channel_id.h" + +using base::StringPiece; +using std::string; + +namespace { + +void EvpMdCtxCleanUp(EVP_MD_CTX *ctx) { + (void)EVP_MD_CTX_cleanup(ctx); +} + +} // namespace anonymous + +namespace net { + +namespace test { + +class TestChannelIDSigner : public ChannelIDSigner { + public: + virtual ~TestChannelIDSigner() { } + + // ChannelIDSigner implementation. + + virtual bool Sign(const string& hostname, + StringPiece signed_data, + string* out_key, + string* out_signature) OVERRIDE { + crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> ecdsa_key( + HostnameToKey(hostname)); + + *out_key = SerializeKey(ecdsa_key.get()); + if (out_key->empty()) { + return false; + } + + EVP_MD_CTX md_ctx; + EVP_MD_CTX_init(&md_ctx); + crypto::ScopedOpenSSL<EVP_MD_CTX, EvpMdCtxCleanUp> + md_ctx_cleanup(&md_ctx); + + if (EVP_DigestSignInit(&md_ctx, NULL, EVP_sha256(), NULL, + ecdsa_key.get()) != 1) { + return false; + } + + EVP_DigestUpdate(&md_ctx, ChannelIDVerifier::kContextStr, + strlen(ChannelIDVerifier::kContextStr) + 1); + EVP_DigestUpdate(&md_ctx, ChannelIDVerifier::kClientToServerStr, + strlen(ChannelIDVerifier::kClientToServerStr) + 1); + EVP_DigestUpdate(&md_ctx, signed_data.data(), signed_data.size()); + + size_t sig_len; + if (!EVP_DigestSignFinal(&md_ctx, NULL, &sig_len)) { + return false; + } + + scoped_ptr<uint8[]> der_sig(new uint8[sig_len]); + if (!EVP_DigestSignFinal(&md_ctx, der_sig.get(), &sig_len)) { + return false; + } + + uint8* derp = der_sig.get(); + crypto::ScopedOpenSSL<ECDSA_SIG, ECDSA_SIG_free> sig( + d2i_ECDSA_SIG(NULL, const_cast<const uint8**>(&derp), sig_len)); + if (sig.get() == NULL) { + return false; + } + + // The signature consists of a pair of 32-byte numbers. + static const size_t kSignatureLength = 32 * 2; + scoped_ptr<uint8[]> signature(new uint8[kSignatureLength]); + memset(signature.get(), 0, kSignatureLength); + BN_bn2bin(sig.get()->r, signature.get() + 32 - BN_num_bytes(sig.get()->r)); + BN_bn2bin(sig.get()->s, signature.get() + 64 - BN_num_bytes(sig.get()->s)); + + *out_signature = string(reinterpret_cast<char*>(signature.get()), + kSignatureLength); + + return true; + } + + static string KeyForHostname(const string& hostname) { + crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> ecdsa_key( + HostnameToKey(hostname)); + return SerializeKey(ecdsa_key.get()); + } + + private: + static EVP_PKEY* HostnameToKey(const string& hostname) { + // In order to generate a deterministic key for a given hostname the + // hostname is hashed with SHA-256 and the resulting digest is treated as a + // big-endian number. The most-significant bit is cleared to ensure that + // the resulting value is less than the order of the group and then it's + // taken as a private key. Given the private key, the public key is + // calculated with a group multiplication. + SHA256_CTX sha256; + SHA256_Init(&sha256); + SHA256_Update(&sha256, hostname.data(), hostname.size()); + + unsigned char digest[SHA256_DIGEST_LENGTH]; + SHA256_Final(digest, &sha256); + + // Ensure that the digest is less than the order of the P-256 group by + // clearing the most-significant bit. + digest[0] &= 0x7f; + + crypto::ScopedOpenSSL<BIGNUM, BN_free> k(BN_new()); + CHECK(BN_bin2bn(digest, sizeof(digest), k.get()) != NULL); + + crypto::ScopedOpenSSL<EC_GROUP, EC_GROUP_free> p256( + EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); + CHECK(p256.get()); + + crypto::ScopedOpenSSL<EC_KEY, EC_KEY_free> ecdsa_key(EC_KEY_new()); + CHECK(ecdsa_key.get() != NULL && + EC_KEY_set_group(ecdsa_key.get(), p256.get())); + + crypto::ScopedOpenSSL<EC_POINT, EC_POINT_free> point( + EC_POINT_new(p256.get())); + CHECK(EC_POINT_mul(p256.get(), point.get(), k.get(), NULL, NULL, NULL)); + + EC_KEY_set_private_key(ecdsa_key.get(), k.get()); + EC_KEY_set_public_key(ecdsa_key.get(), point.get()); + + crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> pkey(EVP_PKEY_new()); + // EVP_PKEY_set1_EC_KEY takes a reference so no |release| here. + EVP_PKEY_set1_EC_KEY(pkey.get(), ecdsa_key.get()); + + return pkey.release(); + } + + static string SerializeKey(EVP_PKEY* key) { + // i2d_PublicKey will produce an ANSI X9.62 public key which, for a P-256 + // key, is 0x04 (meaning uncompressed) followed by the x and y field + // elements as 32-byte, big-endian numbers. + static const int kExpectedKeyLength = 65; + + int len = i2d_PublicKey(key, NULL); + if (len != kExpectedKeyLength) { + return ""; + } + + uint8 buf[kExpectedKeyLength]; + uint8* derp = buf; + i2d_PublicKey(key, &derp); + + return string(reinterpret_cast<char*>(buf + 1), kExpectedKeyLength - 1); + } +}; + +// static +ChannelIDSigner* CryptoTestUtils::ChannelIDSignerForTesting() { + return new TestChannelIDSigner(); +} + +// static +string CryptoTestUtils::ChannelIDKeyForHostname(const string& hostname) { + return TestChannelIDSigner::KeyForHostname(hostname); +} + +} // namespace test + +} // namespace net diff --git a/net/quic/test_tools/quic_connection_peer.cc b/net/quic/test_tools/quic_connection_peer.cc index 4f2b500..6e32e6c 100644 --- a/net/quic/test_tools/quic_connection_peer.cc +++ b/net/quic/test_tools/quic_connection_peer.cc @@ -63,8 +63,9 @@ size_t QuicConnectionPeer::GetNumRetransmissionTimeouts( } // static -QuicTime::Delta QuicConnectionPeer::GetTimeout(QuicConnection* connection) { - return connection->timeout_; +QuicTime::Delta QuicConnectionPeer::GetNetworkTimeout( + QuicConnection* connection) { + return connection->idle_network_timeout_; } // static diff --git a/net/quic/test_tools/quic_connection_peer.h b/net/quic/test_tools/quic_connection_peer.h index e2b4ff9..1ed8827 100644 --- a/net/quic/test_tools/quic_connection_peer.h +++ b/net/quic/test_tools/quic_connection_peer.h @@ -46,7 +46,7 @@ class QuicConnectionPeer { static size_t GetNumRetransmissionTimeouts(QuicConnection* connection); - static QuicTime::Delta GetTimeout(QuicConnection* connection); + static QuicTime::Delta GetNetworkTimeout(QuicConnection* connection); static bool IsSavedForRetransmission( QuicConnection* connection, diff --git a/net/quic/test_tools/quic_framer_peer.cc b/net/quic/test_tools/quic_framer_peer.cc index 9a3b968..49315f0 100644 --- a/net/quic/test_tools/quic_framer_peer.cc +++ b/net/quic/test_tools/quic_framer_peer.cc @@ -18,6 +18,10 @@ QuicPacketSequenceNumber QuicFramerPeer::CalculatePacketSequenceNumberFromWire( } // static +void QuicFramerPeer::SetLastSerializedGuid(QuicFramer* framer, QuicGuid guid) { + framer->last_serialized_guid_ = guid; +} + void QuicFramerPeer::SetLastSequenceNumber( QuicFramer* framer, QuicPacketSequenceNumber packet_sequence_number) { diff --git a/net/quic/test_tools/quic_framer_peer.h b/net/quic/test_tools/quic_framer_peer.h index 0c59d2d..6d4a1e1 100644 --- a/net/quic/test_tools/quic_framer_peer.h +++ b/net/quic/test_tools/quic_framer_peer.h @@ -18,6 +18,7 @@ class QuicFramerPeer { static QuicPacketSequenceNumber CalculatePacketSequenceNumberFromWire( QuicFramer* framer, QuicPacketSequenceNumber packet_sequence_number); + static void SetLastSerializedGuid(QuicFramer* framer, QuicGuid guid); static void SetLastSequenceNumber( QuicFramer* framer, QuicPacketSequenceNumber packet_sequence_number); diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc index 8821387..0d649d3 100644 --- a/net/quic/test_tools/quic_test_utils.cc +++ b/net/quic/test_tools/quic_test_utils.cc @@ -235,7 +235,7 @@ bool PacketSavingConnection::SendOrQueuePacket( } MockSession::MockSession(QuicConnection* connection, bool is_server) - : QuicSession(connection, QuicConfig(), is_server) { + : QuicSession(connection, DefaultQuicConfig(), is_server) { ON_CALL(*this, WriteData(_, _, _, _)) .WillByDefault(testing::Return(QuicConsumedData(0, false))); } @@ -250,8 +250,7 @@ TestSession::TestSession(QuicConnection* connection, crypto_stream_(NULL) { } -TestSession::~TestSession() { -} +TestSession::~TestSession() {} void TestSession::SetCryptoStream(QuicCryptoStream* stream) { crypto_stream_ = stream; @@ -365,7 +364,6 @@ static QuicPacket* ConstructPacketFromHandshakeMessage( header.entropy_flag = false; header.entropy_hash = 0; header.fec_flag = false; - header.fec_entropy_flag = false; header.fec_group = 0; QuicStreamFrame stream_frame(kCryptoStreamId, false, 0, @@ -383,13 +381,16 @@ QuicPacket* ConstructHandshakePacket(QuicGuid guid, QuicTag tag) { return ConstructPacketFromHandshakeMessage(guid, message, false); } -size_t GetPacketLengthForOneStream(bool include_version, size_t payload) { +size_t GetPacketLengthForOneStream( + bool include_version, InFecGroup is_in_fec_group, size_t payload) { // TODO(wtc): the hardcoded use of NullEncrypter here seems wrong. size_t packet_length = NullEncrypter().GetCiphertextSize(payload) + - QuicPacketCreator::StreamFramePacketOverhead(1, include_version); + QuicPacketCreator::StreamFramePacketOverhead( + 1, PACKET_8BYTE_GUID, include_version, is_in_fec_group); size_t ack_length = NullEncrypter().GetCiphertextSize( - QuicFramer::GetMinAckFrameSize()) + GetPacketHeaderSize(include_version); + QuicFramer::GetMinAckFrameSize()) + + GetPacketHeaderSize(PACKET_8BYTE_GUID, include_version, is_in_fec_group); // Make sure that if we change the size of the packet length for one stream // or the ack frame; that all our test are configured correctly. DCHECK_GE(packet_length, ack_length); @@ -401,6 +402,12 @@ QuicPacketEntropyHash TestEntropyCalculator::ReceivedEntropyHash( return 1u; } +QuicConfig DefaultQuicConfig() { + QuicConfig config; + config.SetDefaults(); + return config; +} + bool TestDecompressorVisitor::OnDecompressedData(StringPiece data) { data.AppendToString(&data_); return true; diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h index 8fbf054..e694982 100644 --- a/net/quic/test_tools/quic_test_utils.h +++ b/net/quic/test_tools/quic_test_utils.h @@ -37,10 +37,14 @@ void CompareQuicDataWithHexError(const std::string& description, // Returns the length of the QuicPacket that will be created if it contains // a stream frame that has |payload| bytes. -size_t GetPacketLengthForOneStream(bool include_version, size_t payload); +size_t GetPacketLengthForOneStream( + bool include_version, InFecGroup is_in_fec_group, size_t payload); string SerializeUncompressedHeaders(const SpdyHeaderBlock& headers); +// Returns QuicConfig set to default values. +QuicConfig DefaultQuicConfig(); + class MockFramerVisitor : public QuicFramerVisitorInterface { public: MockFramerVisitor(); @@ -318,10 +322,9 @@ class MockSendAlgorithm : public SendAlgorithmInterface { MockSendAlgorithm(); virtual ~MockSendAlgorithm(); - MOCK_METHOD4(OnIncomingQuicCongestionFeedbackFrame, + MOCK_METHOD3(OnIncomingQuicCongestionFeedbackFrame, void(const QuicCongestionFeedbackFrame&, QuicTime feedback_receive_time, - QuicBandwidth sent_bandwidth, const SentPacketsMap&)); MOCK_METHOD3(OnIncomingAck, void(QuicPacketSequenceNumber, QuicByteCount, QuicTime::Delta)); |