diff options
Diffstat (limited to 'net/quic')
25 files changed, 720 insertions, 218 deletions
diff --git a/net/quic/congestion_control/cubic.cc b/net/quic/congestion_control/cubic.cc index 58b5385..7b99057 100644 --- a/net/quic/congestion_control/cubic.cc +++ b/net/quic/congestion_control/cubic.cc @@ -4,8 +4,8 @@ #include "net/quic/congestion_control/cubic.h" -#include <math.h> #include <algorithm> +#include <cmath> #include "base/basictypes.h" #include "base/logging.h" diff --git a/net/quic/congestion_control/hybrid_slow_start.cc b/net/quic/congestion_control/hybrid_slow_start.cc index e3789b4..b7e10b9f 100644 --- a/net/quic/congestion_control/hybrid_slow_start.cc +++ b/net/quic/congestion_control/hybrid_slow_start.cc @@ -16,7 +16,8 @@ namespace net { const int64 kHybridStartLowWindow = 16; // Number of delay samples for detecting the increase of delay. const uint32 kHybridStartMinSamples = 8; -const int kHybridStartDelayFactorExp = 4; // 2^4 = 16 +// Exit slow start if the min rtt has increased by more than 1/8th. +const int kHybridStartDelayFactorExp = 3; // 2^3 = 8 // The original paper specifies 2 and 8ms, but those have changed over time. const int64 kHybridStartDelayMinThresholdUs = 4000; const int64 kHybridStartDelayMaxThresholdUs = 16000; diff --git a/net/quic/congestion_control/hybrid_slow_start_test.cc b/net/quic/congestion_control/hybrid_slow_start_test.cc index bcdf8d9..5b98733 100644 --- a/net/quic/congestion_control/hybrid_slow_start_test.cc +++ b/net/quic/congestion_control/hybrid_slow_start_test.cc @@ -84,8 +84,8 @@ TEST_F(HybridSlowStartTest, AckTrain) { } TEST_F(HybridSlowStartTest, Delay) { - // We expect to detect the increase at +1/16 of the RTT; hence at a typical - // RTT of 60ms the detection will happen at 63.75 ms. + // We expect to detect the increase at +1/8 of the RTT; hence at a typical + // RTT of 60ms the detection will happen at 67.5 ms. const int kHybridStartMinSamples = 8; // Number of acks required to trigger. QuicPacketSequenceNumber end_sequence_number = 1; @@ -100,12 +100,12 @@ TEST_F(HybridSlowStartTest, Delay) { slow_start_->StartReceiveRound(end_sequence_number++); for (int n = 1; n < kHybridStartMinSamples; ++n) { EXPECT_FALSE(slow_start_->ShouldExitSlowStart( - rtt_.Add(QuicTime::Delta::FromMilliseconds(n + 5)), rtt_, 100)); + rtt_.Add(QuicTime::Delta::FromMilliseconds(n + 10)), rtt_, 100)); } // Expect to trigger since all packets in this burst was above the long term // RTT provided. EXPECT_TRUE(slow_start_->ShouldExitSlowStart( - rtt_.Add(QuicTime::Delta::FromMilliseconds(5)), rtt_, 100)); + rtt_.Add(QuicTime::Delta::FromMilliseconds(10)), rtt_, 100)); } } // namespace test diff --git a/net/quic/crypto/cached_network_parameters.cc b/net/quic/crypto/cached_network_parameters.cc index 20a438b7..c9582f1 100644 --- a/net/quic/crypto/cached_network_parameters.cc +++ b/net/quic/crypto/cached_network_parameters.cc @@ -11,6 +11,7 @@ CachedNetworkParameters::CachedNetworkParameters() max_bandwidth_estimate_bytes_per_second_(0), max_bandwidth_timestamp_seconds_(0), min_rtt_ms_(0), + has_min_rtt_ms_(false), previous_connection_state_(0), timestamp_(0) { } diff --git a/net/quic/crypto/cached_network_parameters.h b/net/quic/crypto/cached_network_parameters.h index 5f22070..34c8917 100644 --- a/net/quic/crypto/cached_network_parameters.h +++ b/net/quic/crypto/cached_network_parameters.h @@ -68,7 +68,9 @@ class NET_EXPORT_PRIVATE CachedNetworkParameters { } void set_min_rtt_ms(int32 min_rtt_ms) { min_rtt_ms_ = min_rtt_ms; + has_min_rtt_ms_ = true; } + bool has_min_rtt_ms() const { return has_min_rtt_ms_; } int32 previous_connection_state() const { return previous_connection_state_; @@ -98,6 +100,8 @@ class NET_EXPORT_PRIVATE CachedNetworkParameters { // The min RTT seen on a previous connection can be used by the server to // inform initial connection parameters for new connections. int32 min_rtt_ms_; + // Whenever min_rtt_ms_ is updated, it is set to true. + bool has_min_rtt_ms_; // Encodes the PreviousConnectionState enum. int32 previous_connection_state_; // UNIX timestamp when this bandwidth estimate was created. diff --git a/net/quic/crypto/quic_crypto_server_config.cc b/net/quic/crypto/quic_crypto_server_config.cc index f752a9e..4a5dc8a 100644 --- a/net/quic/crypto/quic_crypto_server_config.cc +++ b/net/quic/crypto/quic_crypto_server_config.cc @@ -50,6 +50,8 @@ namespace net { namespace { +const size_t kMaxTokenAddresses = 4; + string DeriveSourceAddressTokenKey(StringPiece source_address_token_secret) { crypto::HKDF hkdf(source_address_token_secret, StringPiece() /* no salt */, @@ -765,12 +767,10 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( (QuicVersionToQuicTag(supported_versions[i])); } out->SetVector(kVER, supported_version_tags); - out->SetStringPiece(kSourceAddressTokenTag, - NewSourceAddressToken(*requested_config.get(), - client_address, - rand, - info.now, - nullptr)); + out->SetStringPiece( + kSourceAddressTokenTag, + NewSourceAddressToken(*requested_config.get(), info.source_address_tokens, + client_address, rand, info.now, nullptr)); QuicSocketAddressCoder address_coder(client_address); out->SetStringPiece(kCADR, address_coder.Encode()); out->SetStringPiece(kPUBS, forward_secure_public_value); @@ -940,12 +940,20 @@ void QuicCryptoServerConfig::EvaluateClientHello( HandshakeFailureReason source_address_token_error; StringPiece srct; if (client_hello.GetStringPiece(kSourceAddressTokenTag, &srct)) { - source_address_token_error = - ValidateSourceAddressToken(*requested_config.get(), - srct, - info->client_ip, - info->now, - &client_hello_state->cached_network_params); + if (!FLAGS_quic_use_multiple_address_in_source_tokens) { + source_address_token_error = ValidateSourceAddressToken( + *requested_config.get(), srct, info->client_ip, info->now, + &client_hello_state->cached_network_params); + } else { + source_address_token_error = ParseSourceAddressToken( + *requested_config.get(), srct, &info->source_address_tokens); + + if (source_address_token_error == HANDSHAKE_OK) { + source_address_token_error = ValidateSourceAddressTokens( + info->source_address_tokens, info->client_ip, info->now, + &client_hello_state->cached_network_params); + } + } info->valid_source_address_token = (source_address_token_error == HANDSHAKE_OK); } else { @@ -1035,6 +1043,7 @@ void QuicCryptoServerConfig::EvaluateClientHello( } bool QuicCryptoServerConfig::BuildServerConfigUpdateMessage( + const SourceAddressTokens& previous_source_address_tokens, const IPEndPoint& server_ip, const IPEndPoint& client_ip, const QuicClock* clock, @@ -1045,12 +1054,11 @@ bool QuicCryptoServerConfig::BuildServerConfigUpdateMessage( base::AutoLock locked(configs_lock_); out->set_tag(kSCUP); out->SetStringPiece(kSCFG, primary_config_->serialized); - out->SetStringPiece(kSourceAddressTokenTag, - NewSourceAddressToken(*primary_config_.get(), - client_ip, - rand, - clock->WallNow(), - cached_network_params)); + out->SetStringPiece( + kSourceAddressTokenTag, + NewSourceAddressToken(*primary_config_.get(), + previous_source_address_tokens, client_ip, rand, + clock->WallNow(), cached_network_params)); if (proof_source_ == nullptr) { // Insecure QUIC, can send SCFG without proof. @@ -1086,13 +1094,10 @@ void QuicCryptoServerConfig::BuildRejection( CryptoHandshakeMessage* out) const { out->set_tag(kREJ); out->SetStringPiece(kSCFG, config.serialized); - out->SetStringPiece(kSourceAddressTokenTag, - NewSourceAddressToken( - config, - info.client_ip, - rand, - info.now, - &cached_network_params)); + out->SetStringPiece( + kSourceAddressTokenTag, + NewSourceAddressToken(config, info.source_address_tokens, info.client_ip, + rand, info.now, &cached_network_params)); if (replay_protection_) { out->SetStringPiece(kServerNonceTag, NewServerNonce(rand, info.now)); } @@ -1412,6 +1417,7 @@ void QuicCryptoServerConfig::AcquirePrimaryConfigChangedCb( string QuicCryptoServerConfig::NewSourceAddressToken( const Config& config, + const SourceAddressTokens& previous_tokens, const IPEndPoint& ip, QuicRandom* rand, QuicWallTime now, @@ -1420,21 +1426,80 @@ string QuicCryptoServerConfig::NewSourceAddressToken( if (ip.GetSockAddrFamily() == AF_INET) { ip_address = ConvertIPv4NumberToIPv6Number(ip_address); } - SourceAddressToken source_address_token; - source_address_token.set_ip(IPAddressToPackedString(ip_address)); - source_address_token.set_timestamp(now.ToUNIXSeconds()); + SourceAddressTokens source_address_tokens; + SourceAddressToken* source_address_token = source_address_tokens.add_tokens(); + source_address_token->set_ip(IPAddressToPackedString(ip_address)); + source_address_token->set_timestamp(now.ToUNIXSeconds()); if (cached_network_params != nullptr) { - source_address_token.set_cached_network_parameters(*cached_network_params); + *(source_address_token->mutable_cached_network_parameters()) = + *cached_network_params; + } + + if (!FLAGS_quic_use_multiple_address_in_source_tokens) { + return config.source_address_token_boxer->Box( + rand, source_address_token->SerializeAsString()); + } + + // Append previous tokens. + for (size_t i = 0; i < previous_tokens.tokens_size(); i++) { + const SourceAddressToken& token = previous_tokens.tokens(i); + if (source_address_tokens.tokens_size() > kMaxTokenAddresses) { + break; + } + + if (token.ip() == source_address_token->ip()) { + // It's for the same IP address. + continue; + } + + if (ValidateSourceAddressTokenTimestamp(token, now) != HANDSHAKE_OK) { + continue; + } + + *(source_address_tokens.add_tokens()) = token; } return config.source_address_token_boxer->Box( - rand, source_address_token.SerializeAsString()); + rand, source_address_tokens.SerializeAsString()); } bool QuicCryptoServerConfig::HasProofSource() const { return proof_source_ != nullptr; } +HandshakeFailureReason QuicCryptoServerConfig::ParseSourceAddressToken( + const Config& config, + StringPiece token, + SourceAddressTokens* tokens) const { + string storage; + StringPiece plaintext; + if (!config.source_address_token_boxer->Unbox(token, &storage, &plaintext)) { + return SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE; + } + + if (!FLAGS_quic_use_multiple_address_in_source_tokens) { + SourceAddressToken token; + if (!token.ParseFromArray(plaintext.data(), plaintext.size())) { + return SOURCE_ADDRESS_TOKEN_PARSE_FAILURE; + } + *(tokens->add_tokens()) = token; + return HANDSHAKE_OK; + } + + if (!tokens->ParseFromArray(plaintext.data(), plaintext.size())) { + // Some clients might still be using the old source token format so + // attempt to parse that format. + // TODO(rch): remove this code once the new format is ubiquitous. + SourceAddressToken token; + if (!token.ParseFromArray(plaintext.data(), plaintext.size())) { + return SOURCE_ADDRESS_TOKEN_PARSE_FAILURE; + } + *tokens->add_tokens() = token; + } + + return HANDSHAKE_OK; +} + HandshakeFailureReason QuicCryptoServerConfig::ValidateSourceAddressToken( const Config& config, StringPiece token, @@ -1483,6 +1548,63 @@ HandshakeFailureReason QuicCryptoServerConfig::ValidateSourceAddressToken( return HANDSHAKE_OK; } +HandshakeFailureReason QuicCryptoServerConfig::ValidateSourceAddressTokens( + const SourceAddressTokens& source_address_tokens, + const IPEndPoint& ip, + QuicWallTime now, + CachedNetworkParameters* cached_network_params) const { + HandshakeFailureReason reason = + SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE; + for (size_t i = 0; i < source_address_tokens.tokens_size(); i++) { + const SourceAddressToken& token = source_address_tokens.tokens(i); + reason = ValidateSingleSourceAddressToken(token, ip, now); + if (reason == HANDSHAKE_OK) { + if (token.has_cached_network_parameters()) { + *cached_network_params = token.cached_network_parameters(); + } + break; + } + } + return reason; +} + +HandshakeFailureReason QuicCryptoServerConfig::ValidateSingleSourceAddressToken( + const SourceAddressToken& source_address_token, + const IPEndPoint& ip, + QuicWallTime now) const { + IPAddressNumber ip_address = ip.address(); + if (ip.GetSockAddrFamily() == AF_INET) { + ip_address = ConvertIPv4NumberToIPv6Number(ip_address); + } + if (source_address_token.ip() != IPAddressToPackedString(ip_address)) { + // It's for a different IP address. + return SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE; + } + + return ValidateSourceAddressTokenTimestamp(source_address_token, now); +} + +HandshakeFailureReason +QuicCryptoServerConfig::ValidateSourceAddressTokenTimestamp( + const SourceAddressToken& source_address_token, + QuicWallTime now) const { + const QuicWallTime timestamp( + QuicWallTime::FromUNIXSeconds(source_address_token.timestamp())); + const QuicTime::Delta delta(now.AbsoluteDifference(timestamp)); + + if (now.IsBefore(timestamp) && + delta.ToSeconds() > source_address_token_future_secs_) { + return SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE; + } + + if (now.IsAfter(timestamp) && + delta.ToSeconds() > source_address_token_lifetime_secs_) { + return SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE; + } + + return HANDSHAKE_OK; +} + // kServerNoncePlaintextSize is the number of bytes in an unencrypted server // nonce. static const size_t kServerNoncePlaintextSize = diff --git a/net/quic/crypto/quic_crypto_server_config.h b/net/quic/crypto/quic_crypto_server_config.h index bce4a3f..da8713a 100644 --- a/net/quic/crypto/quic_crypto_server_config.h +++ b/net/quic/crypto/quic_crypto_server_config.h @@ -20,6 +20,7 @@ #include "net/quic/crypto/crypto_handshake_message.h" #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/crypto/crypto_secret_boxer.h" +#include "net/quic/crypto/source_address_token.h" #include "net/quic/quic_time.h" namespace net { @@ -54,6 +55,7 @@ struct ClientHelloInfo { base::StringPiece client_nonce; base::StringPiece server_nonce; base::StringPiece user_agent_id; + SourceAddressTokens source_address_tokens; // Errors from EvaluateClientHello. std::vector<uint32> reject_reasons; @@ -257,6 +259,7 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // // |cached_network_params| is optional, and can be nullptr. bool BuildServerConfigUpdateMessage( + const SourceAddressTokens& previous_source_address_tokens, const IPEndPoint& server_ip, const IPEndPoint& client_ip, const QuicClock* clock, @@ -442,16 +445,28 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { // IP address. |cached_network_params| is optional, and can be nullptr. std::string NewSourceAddressToken( const Config& config, + const SourceAddressTokens& previous_tokens, const IPEndPoint& ip, QuicRandom* rand, QuicWallTime now, const CachedNetworkParameters* cached_network_params) const; - // ValidateSourceAddressToken returns HANDSHAKE_OK if the source address token - // in |token| is a valid and timely token for the IP address |ip| given that - // the current time is |now|. Otherwise it returns the reason for failure. - // |cached_network_params| is populated if |token| contains a - // CachedNetworkParameters proto. + // ParseSourceAddressToken parses the source address tokens contained in + // the encrypted |token|, and populates |tokens| with the parsed tokens. + // Returns HANDSHAKE_OK if |token| could be parsed, or the reason for the + // failure. + HandshakeFailureReason ParseSourceAddressToken( + const Config& config, + base::StringPiece token, + SourceAddressTokens* tokens) const; + + // ValidateSourceAddressToken returns HANDSHAKE_OK if the source address + // tokens in |tokens| contain a valid and timely token for the IP address + // |ip| given that the current time is |now|. Otherwise it returns the + // reason for failure. |cached_network_params| is populated if the valid + // token contains a CachedNetworkParameters proto. + // TODO(rch): remove this method when we remove: + // FLAGS_quic_use_multiple_address_in_source_tokens. HandshakeFailureReason ValidateSourceAddressToken( const Config& config, base::StringPiece token, @@ -459,6 +474,33 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { QuicWallTime now, CachedNetworkParameters* cached_network_params) const; + // ValidateSourceAddressTokens returns HANDSHAKE_OK if the source address + // tokens in |tokens| contain a valid and timely token for the IP address + // |ip| given that the current time is |now|. Otherwise it returns the + // reason for failure. |cached_network_params| is populated if the valid + // token contains a CachedNetworkParameters proto. + HandshakeFailureReason ValidateSourceAddressTokens( + const SourceAddressTokens& tokens, + const IPEndPoint& ip, + QuicWallTime now, + CachedNetworkParameters* cached_network_params) const; + + // ValidateSingleSourceAddressToken returns HANDSHAKE_OK if the source + // address token in |token| is a timely token for the IP address |ip| + // given that the current time is |now|. Otherwise it returns the reason + // for failure. + HandshakeFailureReason ValidateSingleSourceAddressToken( + const SourceAddressToken& token, + const IPEndPoint& ip, + QuicWallTime now) const; + + // Returns HANDSHAKE_OK if the source address token in |token| is a timely + // token given that the current time is |now|. Otherwise it returns the + // reason for failure. + HandshakeFailureReason ValidateSourceAddressTokenTimestamp( + const SourceAddressToken& token, + QuicWallTime now) const; + // NewServerNonce generates and encrypts a random nonce. std::string NewServerNonce(QuicRandom* rand, QuicWallTime now) const; diff --git a/net/quic/crypto/quic_crypto_server_config_test.cc b/net/quic/crypto/quic_crypto_server_config_test.cc index 4453b76..6948165 100644 --- a/net/quic/crypto/quic_crypto_server_config_test.cc +++ b/net/quic/crypto/quic_crypto_server_config_test.cc @@ -13,6 +13,7 @@ #include "net/quic/crypto/crypto_server_config_protobuf.h" #include "net/quic/crypto/quic_random.h" #include "net/quic/crypto/strike_register_client.h" +#include "net/quic/quic_flags.h" #include "net/quic/quic_time.h" #include "net/quic/test_tools/mock_clock.h" #include "net/quic/test_tools/quic_test_utils.h" @@ -50,22 +51,31 @@ class QuicCryptoServerConfigPeer { &(server_config_->default_source_address_token_boxer_); } - string NewSourceAddressToken( - string config_id, - const IPEndPoint& ip, - QuicRandom* rand, - QuicWallTime now) { + string NewSourceAddressToken(string config_id, + const IPEndPoint& ip, + QuicRandom* rand, + QuicWallTime now) { return NewSourceAddressToken(config_id, ip, rand, now, NULL); } - string NewSourceAddressToken( - string config_id, - const IPEndPoint& ip, - QuicRandom* rand, - QuicWallTime now, - CachedNetworkParameters* cached_network_params) { + string NewSourceAddressToken(string config_id, + const IPEndPoint& ip, + QuicRandom* rand, + QuicWallTime now, + const SourceAddressTokens& previous_tokens) { return server_config_->NewSourceAddressToken( - *GetConfig(config_id), ip, rand, now, cached_network_params); + *GetConfig(config_id), previous_tokens, ip, rand, now, NULL); + } + + string NewSourceAddressToken(string config_id, + const IPEndPoint& ip, + QuicRandom* rand, + QuicWallTime now, + CachedNetworkParameters* cached_network_params) { + SourceAddressTokens previous_tokens; + return server_config_->NewSourceAddressToken(*GetConfig(config_id), + previous_tokens, ip, rand, now, + cached_network_params); } HandshakeFailureReason ValidateSourceAddressToken(string config_id, @@ -85,6 +95,30 @@ class QuicCryptoServerConfigPeer { *GetConfig(config_id), srct, ip, now, cached_network_params); } + HandshakeFailureReason ValidateSourceAddressTokens(string config_id, + StringPiece srct, + const IPEndPoint& ip, + QuicWallTime now) { + return ValidateSourceAddressTokens(config_id, srct, ip, now, NULL); + } + + HandshakeFailureReason ValidateSourceAddressTokens( + string config_id, + StringPiece srct, + const IPEndPoint& ip, + QuicWallTime now, + CachedNetworkParameters* cached_network_params) { + SourceAddressTokens tokens; + HandshakeFailureReason reason = server_config_->ParseSourceAddressToken( + *GetConfig(config_id), srct, &tokens); + if (reason != HANDSHAKE_OK) { + return reason; + } + + return server_config_->ValidateSourceAddressTokens(tokens, ip, now, + cached_network_params); + } + string NewServerNonce(QuicRandom* rand, QuicWallTime now) const { return server_config_->NewServerNonce(rand, now); } @@ -257,110 +291,294 @@ TEST(QuicCryptoServerConfigTest, GetOrbitIsCalledWithoutTheStrikeRegisterLock) { EXPECT_TRUE(strike_register->is_known_orbit_called()); } -TEST(QuicCryptoServerConfigTest, SourceAddressTokens) { +class SourceAddressTokenTest : public ::testing::Test { + public: + SourceAddressTokenTest() + : ip4_(IPEndPoint(Loopback4(), 1)), + ip4_dual_(ConvertIPv4NumberToIPv6Number(ip4_.address()), 1), + ip6_(IPEndPoint(Loopback6(), 2)), + original_time_(QuicWallTime::Zero()), + rand_(QuicRandom::GetInstance()), + server_(QuicCryptoServerConfig::TESTING, rand_), + peer_(&server_) { + // Advance the clock to some non-zero time. + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1000000)); + original_time_ = clock_.WallNow(); + + primary_config_.reset(server_.AddDefaultConfig( + rand_, &clock_, QuicCryptoServerConfig::ConfigOptions())); + + // Add a config that overrides the default boxer. + QuicCryptoServerConfig::ConfigOptions options; + options.id = kOverride; + override_config_protobuf_.reset( + QuicCryptoServerConfig::GenerateConfig(rand_, &clock_, options)); + override_config_protobuf_->set_source_address_token_secret_override( + "a secret key"); + // Lower priority than the default config. + override_config_protobuf_->set_priority(1); + override_config_.reset( + server_.AddConfig(override_config_protobuf_.get(), original_time_)); + } + const string kPrimary = "<primary>"; const string kOverride = "Config with custom source address token key"; - MockClock clock; - clock.AdvanceTime(QuicTime::Delta::FromSeconds(1000000)); + IPEndPoint ip4_; + IPEndPoint ip4_dual_; + IPEndPoint ip6_; - QuicWallTime now = clock.WallNow(); - const QuicWallTime original_time = now; + MockClock clock_; + QuicWallTime original_time_; + QuicRandom* rand_ = QuicRandom::GetInstance(); + QuicCryptoServerConfig server_; + QuicCryptoServerConfigPeer peer_; + // Stores the primary config. + scoped_ptr<CryptoHandshakeMessage> primary_config_; + scoped_ptr<QuicServerConfigProtobuf> override_config_protobuf_; + scoped_ptr<CryptoHandshakeMessage> override_config_; +}; - QuicRandom* rand = QuicRandom::GetInstance(); - QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand); - QuicCryptoServerConfigPeer peer(&server); +TEST_F(SourceAddressTokenTest, SourceAddressToken) { + ValueRestore<bool> old_flag(&FLAGS_quic_use_multiple_address_in_source_tokens, + false); - scoped_ptr<CryptoHandshakeMessage>( - server.AddDefaultConfig(rand, &clock, - QuicCryptoServerConfig::ConfigOptions())); + QuicWallTime now = clock_.WallNow(); - // Add a config that overrides the default boxer. - QuicCryptoServerConfig::ConfigOptions options; - options.id = kOverride; - scoped_ptr<QuicServerConfigProtobuf> protobuf( - QuicCryptoServerConfig::GenerateConfig(rand, &clock, options)); - protobuf->set_source_address_token_secret_override("a secret key"); - // Lower priority than the default config. - protobuf->set_priority(1); - scoped_ptr<CryptoHandshakeMessage>( - server.AddConfig(protobuf.get(), now)); + EXPECT_TRUE(peer_.ConfigHasDefaultSourceAddressTokenBoxer(kPrimary)); + EXPECT_FALSE(peer_.ConfigHasDefaultSourceAddressTokenBoxer(kOverride)); + + // Primary config generates configs that validate successfully. + const string token4 = peer_.NewSourceAddressToken(kPrimary, ip4_, rand_, now); + const string token4d = + peer_.NewSourceAddressToken(kPrimary, ip4_dual_, rand_, now); + const string token6 = peer_.NewSourceAddressToken(kPrimary, ip6_, rand_, now); + EXPECT_EQ(HANDSHAKE_OK, + peer_.ValidateSourceAddressToken(kPrimary, token4, ip4_, now)); + ASSERT_EQ(HANDSHAKE_OK, + peer_.ValidateSourceAddressToken(kPrimary, token4, ip4_dual_, now)); + ASSERT_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE, + peer_.ValidateSourceAddressToken(kPrimary, token4, ip6_, now)); + ASSERT_EQ(HANDSHAKE_OK, + peer_.ValidateSourceAddressToken(kPrimary, token4d, ip4_, now)); + ASSERT_EQ(HANDSHAKE_OK, peer_.ValidateSourceAddressToken(kPrimary, token4d, + ip4_dual_, now)); + ASSERT_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE, + peer_.ValidateSourceAddressToken(kPrimary, token4d, ip6_, now)); + ASSERT_EQ(HANDSHAKE_OK, + peer_.ValidateSourceAddressToken(kPrimary, token6, ip6_, now)); + + // Override config generates configs that validate successfully. + const string override_token4 = + peer_.NewSourceAddressToken(kOverride, ip4_, rand_, now); + const string override_token6 = + peer_.NewSourceAddressToken(kOverride, ip6_, rand_, now); + ASSERT_EQ(HANDSHAKE_OK, peer_.ValidateSourceAddressToken( + kOverride, override_token4, ip4_, now)); + ASSERT_EQ( + SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE, + peer_.ValidateSourceAddressToken(kOverride, override_token4, ip6_, now)); + ASSERT_EQ(HANDSHAKE_OK, peer_.ValidateSourceAddressToken( + kOverride, override_token6, ip6_, now)); + + // Tokens generated by the primary config do not validate + // successfully against the override config, and vice versa. + ASSERT_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, + peer_.ValidateSourceAddressToken(kOverride, token4, ip4_, now)); + ASSERT_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, + peer_.ValidateSourceAddressToken(kOverride, token6, ip6_, now)); + ASSERT_EQ( + SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, + peer_.ValidateSourceAddressToken(kPrimary, override_token4, ip4_, now)); + ASSERT_EQ( + SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, + peer_.ValidateSourceAddressToken(kPrimary, override_token6, ip6_, now)); +} + +TEST_F(SourceAddressTokenTest, SourceAddressTokenExpiration) { + ValueRestore<bool> old_flag(&FLAGS_quic_use_multiple_address_in_source_tokens, + false); + + QuicWallTime now = clock_.WallNow(); + + const string token = peer_.NewSourceAddressToken(kPrimary, ip4_, rand_, now); + + // Validation fails after tokens expire. + now = original_time_.Add(QuicTime::Delta::FromSeconds(86400 * 7)); + ASSERT_EQ(SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE, + peer_.ValidateSourceAddressToken(kPrimary, token, ip4_, now)); + + now = original_time_.Subtract(QuicTime::Delta::FromSeconds(3600 * 2)); + ASSERT_EQ(SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE, + peer_.ValidateSourceAddressToken(kPrimary, token, ip4_, now)); +} + +TEST_F(SourceAddressTokenTest, SourceAddressTokenWithNetworkParams) { + ValueRestore<bool> old_flag(&FLAGS_quic_use_multiple_address_in_source_tokens, + false); + + QuicWallTime now = clock_.WallNow(); + + // Make sure that if the source address token contains CachedNetworkParameters + // that this gets written to ValidateSourceAddressToken output argument. + CachedNetworkParameters cached_network_params_input; + cached_network_params_input.set_bandwidth_estimate_bytes_per_second(1234); + const string token4_with_cached_network_params = peer_.NewSourceAddressToken( + kPrimary, ip4_, rand_, now, &cached_network_params_input); - EXPECT_TRUE(peer.ConfigHasDefaultSourceAddressTokenBoxer(kPrimary)); - EXPECT_FALSE(peer.ConfigHasDefaultSourceAddressTokenBoxer(kOverride)); + CachedNetworkParameters cached_network_params_output; +#if 0 + // TODO(rtenneti): For server, enable the following check after serialization + // of optional CachedNetworkParameters is implemented. + EXPECT_NE(cached_network_params_output.DebugString(), + cached_network_params_input.DebugString()); +#endif + peer_.ValidateSourceAddressToken(kPrimary, token4_with_cached_network_params, + ip4_, now, &cached_network_params_output); +#if 0 + // TODO(rtenneti): For server, enable the following check after serialization + // of optional CachedNetworkParameters is implemented. + EXPECT_EQ(cached_network_params_output.DebugString(), + cached_network_params_input.DebugString()); +#endif +} + +// Test basic behavior of source address tokens including being specific +// to a single IP address and server config. +// +// TODO(rtenneti): For server, enable the following test after serialization of +// SourceAddressTokens is implemented. +TEST_F(SourceAddressTokenTest, DISABLED_NewSourceAddressToken) { + ValueRestore<bool> old_flag(&FLAGS_quic_use_multiple_address_in_source_tokens, + true); - IPEndPoint ip4 = IPEndPoint(Loopback4(), 1); - IPEndPoint ip4d = IPEndPoint(ConvertIPv4NumberToIPv6Number(ip4.address()), 1); - IPEndPoint ip6 = IPEndPoint(Loopback6(), 2); + QuicWallTime now = clock_.WallNow(); // Primary config generates configs that validate successfully. - const string token4 = peer.NewSourceAddressToken(kPrimary, ip4, rand, now); - const string token4d = peer.NewSourceAddressToken(kPrimary, ip4d, rand, now); - const string token6 = peer.NewSourceAddressToken(kPrimary, ip6, rand, now); - EXPECT_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken( - kPrimary, token4, ip4, now)); - DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken( - kPrimary, token4, ip4d, now)); - DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE, - peer.ValidateSourceAddressToken(kPrimary, token4, ip6, now)); - DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken( - kPrimary, token4d, ip4, now)); - DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken( - kPrimary, token4d, ip4d, now)); - DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE, - peer.ValidateSourceAddressToken(kPrimary, token4d, ip6, now)); - DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken( - kPrimary, token6, ip6, now)); + const string token4 = peer_.NewSourceAddressToken(kPrimary, ip4_, rand_, now); + const string token4d = + peer_.NewSourceAddressToken(kPrimary, ip4_dual_, rand_, now); + const string token6 = peer_.NewSourceAddressToken(kPrimary, ip6_, rand_, now); + EXPECT_EQ(HANDSHAKE_OK, + peer_.ValidateSourceAddressTokens(kPrimary, token4, ip4_, now)); + ASSERT_EQ(HANDSHAKE_OK, peer_.ValidateSourceAddressTokens(kPrimary, token4, + ip4_dual_, now)); + ASSERT_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE, + peer_.ValidateSourceAddressTokens(kPrimary, token4, ip6_, now)); + ASSERT_EQ(HANDSHAKE_OK, + peer_.ValidateSourceAddressTokens(kPrimary, token4d, ip4_, now)); + ASSERT_EQ(HANDSHAKE_OK, peer_.ValidateSourceAddressTokens(kPrimary, token4d, + ip4_dual_, now)); + ASSERT_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE, + peer_.ValidateSourceAddressTokens(kPrimary, token4d, ip6_, now)); + ASSERT_EQ(HANDSHAKE_OK, + peer_.ValidateSourceAddressTokens(kPrimary, token6, ip6_, now)); // Override config generates configs that validate successfully. - const string override_token4 = peer.NewSourceAddressToken( - kOverride, ip4, rand, now); - const string override_token6 = peer.NewSourceAddressToken( - kOverride, ip6, rand, now); - DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken( - kOverride, override_token4, ip4, now)); - DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE, - peer.ValidateSourceAddressToken(kOverride, override_token4, ip6, - now)); - DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken( - kOverride, override_token6, ip6, now)); + const string override_token4 = + peer_.NewSourceAddressToken(kOverride, ip4_, rand_, now); + const string override_token6 = + peer_.NewSourceAddressToken(kOverride, ip6_, rand_, now); + ASSERT_EQ(HANDSHAKE_OK, peer_.ValidateSourceAddressTokens( + kOverride, override_token4, ip4_, now)); + ASSERT_EQ( + SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE, + peer_.ValidateSourceAddressTokens(kOverride, override_token4, ip6_, now)); + ASSERT_EQ(HANDSHAKE_OK, peer_.ValidateSourceAddressTokens( + kOverride, override_token6, ip6_, now)); // Tokens generated by the primary config do not validate // successfully against the override config, and vice versa. - DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, - peer.ValidateSourceAddressToken(kOverride, token4, ip4, now)); - DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, - peer.ValidateSourceAddressToken(kOverride, token6, ip6, now)); - DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, - peer.ValidateSourceAddressToken(kPrimary, override_token4, ip4, - now)); - DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, - peer.ValidateSourceAddressToken(kPrimary, override_token6, ip6, - now)); + ASSERT_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, + peer_.ValidateSourceAddressTokens(kOverride, token4, ip4_, now)); + ASSERT_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, + peer_.ValidateSourceAddressTokens(kOverride, token6, ip6_, now)); + ASSERT_EQ( + SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, + peer_.ValidateSourceAddressTokens(kPrimary, override_token4, ip4_, now)); + ASSERT_EQ( + SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE, + peer_.ValidateSourceAddressTokens(kPrimary, override_token6, ip6_, now)); +} + +// TODO(rtenneti): For server, enable the following test after serialization of +// SourceAddressTokens is implemented. +TEST_F(SourceAddressTokenTest, DISABLED_NewSourceAddressTokenExpiration) { + ValueRestore<bool> old_flag(&FLAGS_quic_use_multiple_address_in_source_tokens, + true); + + QuicWallTime now = clock_.WallNow(); + + const string token = peer_.NewSourceAddressToken(kPrimary, ip4_, rand_, now); // Validation fails after tokens expire. - now = original_time.Add(QuicTime::Delta::FromSeconds(86400 * 7)); - DCHECK_EQ(SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE, - peer.ValidateSourceAddressToken(kPrimary, token4, ip4, now)); + now = original_time_.Add(QuicTime::Delta::FromSeconds(86400 * 7)); + ASSERT_EQ(SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE, + peer_.ValidateSourceAddressTokens(kPrimary, token, ip4_, now)); + + now = original_time_.Subtract(QuicTime::Delta::FromSeconds(3600 * 2)); + ASSERT_EQ(SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE, + peer_.ValidateSourceAddressTokens(kPrimary, token, ip4_, now)); +} - now = original_time.Subtract(QuicTime::Delta::FromSeconds(3600 * 2)); - DCHECK_EQ(SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE, - peer.ValidateSourceAddressToken(kPrimary, token4, ip4, now)); +TEST_F(SourceAddressTokenTest, NewSourceAddressTokenWithNetworkParams) { + ValueRestore<bool> old_flag(&FLAGS_quic_use_multiple_address_in_source_tokens, + true); + + QuicWallTime now = clock_.WallNow(); // Make sure that if the source address token contains CachedNetworkParameters // that this gets written to ValidateSourceAddressToken output argument. CachedNetworkParameters cached_network_params_input; cached_network_params_input.set_bandwidth_estimate_bytes_per_second(1234); - const string token4_with_cached_network_params = peer.NewSourceAddressToken( - kPrimary, ip4, rand, now, &cached_network_params_input); + const string token4_with_cached_network_params = peer_.NewSourceAddressToken( + kPrimary, ip4_, rand_, now, &cached_network_params_input); CachedNetworkParameters cached_network_params_output; - EXPECT_NE(cached_network_params_output, cached_network_params_input); - peer.ValidateSourceAddressToken(kPrimary, token4_with_cached_network_params, - ip4, now, &cached_network_params_output); +#if 0 // TODO(rtenneti): For server, enable the following check after serialization // of optional CachedNetworkParameters is implemented. - // EXPECT_EQ(cached_network_params_output, cached_network_params_input); + EXPECT_NE(cached_network_params_output.DebugString(), + cached_network_params_input.DebugString()); +#endif + peer_.ValidateSourceAddressTokens(kPrimary, token4_with_cached_network_params, + ip4_, now, &cached_network_params_output); +#if 0 + // TODO(rtenneti): For server, enable the following check after serialization + // of optional CachedNetworkParameters is implemented. + EXPECT_EQ(cached_network_params_output.DebugString(), + cached_network_params_input.DebugString()); +#endif +} + +// Test the ability for a source address token to be valid for multiple +// addresses. +// +// TODO(rtenneti): For server, enable the following test after serialization of +// SourceAddressTokens is implemented. +TEST_F(SourceAddressTokenTest, DISABLED_SourceAddressTokenMultipleAddresses) { + ValueRestore<bool> old_flag(&FLAGS_quic_use_multiple_address_in_source_tokens, + true); + + QuicWallTime now = clock_.WallNow(); + + // Now create a token which is usable for both addresses. + SourceAddressToken previous_token; + IPAddressNumber ip_address = ip6_.address(); + if (ip6_.GetSockAddrFamily() == AF_INET) { + ip_address = ConvertIPv4NumberToIPv6Number(ip_address); + } + previous_token.set_ip(IPAddressToPackedString(ip_address)); + previous_token.set_timestamp(now.ToUNIXSeconds()); + SourceAddressTokens previous_tokens; + (*previous_tokens.add_tokens()) = previous_token; + const string token4or6 = + peer_.NewSourceAddressToken(kPrimary, ip4_, rand_, now, previous_tokens); + + EXPECT_EQ(HANDSHAKE_OK, + peer_.ValidateSourceAddressTokens(kPrimary, token4or6, ip4_, now)); + ASSERT_EQ(HANDSHAKE_OK, + peer_.ValidateSourceAddressTokens(kPrimary, token4or6, ip6_, now)); } TEST(QuicCryptoServerConfigTest, ValidateServerNonce) { diff --git a/net/quic/crypto/source_address_token.cc b/net/quic/crypto/source_address_token.cc index 5c1877a..dcb7267 100644 --- a/net/quic/crypto/source_address_token.cc +++ b/net/quic/crypto/source_address_token.cc @@ -58,4 +58,28 @@ bool SourceAddressToken::ParseFromArray(const char* plaintext, return true; } +SourceAddressTokens::SourceAddressTokens() { +} + +SourceAddressTokens::~SourceAddressTokens() { + STLDeleteElements(&tokens_); +} + +string SourceAddressTokens::SerializeAsString() const { + string out; + + for (size_t i = 0; i < tokens_size(); i++) { + const SourceAddressToken& source_address_token = tokens(i); + out.append(source_address_token.SerializeAsString()); + } + return out; +} + +bool SourceAddressTokens::ParseFromArray(const char* plaintext, + size_t plaintext_length) { + // TODO(rtenneti): Implement parsing of SourceAddressTokens when they are + // used. + return true; +} + } // namespace net diff --git a/net/quic/crypto/source_address_token.h b/net/quic/crypto/source_address_token.h index 32f11c5..71ba54e 100644 --- a/net/quic/crypto/source_address_token.h +++ b/net/quic/crypto/source_address_token.h @@ -6,9 +6,11 @@ #define NET_QUIC_CRYPTO_SOURCE_ADDRESS_TOKEN_H_ #include <string> +#include <vector> #include "base/basictypes.h" #include "base/logging.h" +#include "base/stl_util.h" #include "base/strings/string_piece.h" #include "net/base/net_export.h" #include "net/quic/crypto/cached_network_parameters.h" @@ -46,6 +48,9 @@ class NET_EXPORT_PRIVATE SourceAddressToken { const CachedNetworkParameters& cached_network_parameters() const { return cached_network_parameters_; } + CachedNetworkParameters* mutable_cached_network_parameters() { + return &cached_network_parameters_; + } void set_cached_network_parameters( const CachedNetworkParameters& cached_network_parameters) { cached_network_parameters_ = cached_network_parameters; @@ -69,8 +74,32 @@ class NET_EXPORT_PRIVATE SourceAddressToken { // TODO(rtenneti): Delete |has_cached_network_parameters_| after we convert // SourceAddressToken to protobuf. bool has_cached_network_parameters_; +}; + +class NET_EXPORT_PRIVATE SourceAddressTokens { + public: + SourceAddressTokens(); + ~SourceAddressTokens(); + + std::string SerializeAsString() const; + + bool ParseFromArray(const char* plaintext, size_t plaintext_length); + + size_t tokens_size() const { return tokens_.size(); } + + const SourceAddressToken& tokens(size_t i) const { + DCHECK_GT(tokens_.size(), i); + return *tokens_[i]; + } + + SourceAddressToken* add_tokens() { + tokens_.push_back(new SourceAddressToken); + return tokens_.back(); + } + + void clear_tokens() { STLDeleteElements(&tokens_); } - DISALLOW_COPY_AND_ASSIGN(SourceAddressToken); + std::vector<SourceAddressToken*> tokens_; }; } // namespace net diff --git a/net/quic/quic_ack_notifier.cc b/net/quic/quic_ack_notifier.cc index 3bd65e5..2a9b585 100644 --- a/net/quic/quic_ack_notifier.cc +++ b/net/quic/quic_ack_notifier.cc @@ -68,8 +68,7 @@ void QuicAckNotifier::UpdateSequenceNumber( DCHECK(!ContainsKey(sequence_numbers_, new_sequence_number)); PacketInfo packet_info; - hash_map<QuicPacketSequenceNumber, PacketInfo>::iterator it = - sequence_numbers_.find(old_sequence_number); + auto it = sequence_numbers_.find(old_sequence_number); if (it != sequence_numbers_.end()) { packet_info = it->second; sequence_numbers_.erase(it); diff --git a/net/quic/quic_ack_notifier_manager.cc b/net/quic/quic_ack_notifier_manager.cc index 1d9ac9a..ad6193f 100644 --- a/net/quic/quic_ack_notifier_manager.cc +++ b/net/quic/quic_ack_notifier_manager.cc @@ -22,11 +22,10 @@ AckNotifierManager::~AckNotifierManager() { STLDeleteElements(&ack_notifiers_); } -void AckNotifierManager::OnPacketAcked( - QuicPacketSequenceNumber sequence_number, - QuicTime::Delta delta_largest_observed) { +void AckNotifierManager::OnPacketAcked(QuicPacketSequenceNumber sequence_number, + QuicTime::Delta delta_largest_observed) { // Inform all the registered AckNotifiers of the new ACK. - AckNotifierMap::iterator map_it = ack_notifier_map_.find(sequence_number); + auto map_it = ack_notifier_map_.find(sequence_number); if (map_it == ack_notifier_map_.end()) { // No AckNotifier is interested in this sequence number. return; @@ -34,9 +33,8 @@ void AckNotifierManager::OnPacketAcked( // One or more AckNotifiers are registered as interested in this sequence // number. Iterate through them and call OnAck on each. - for (AckNotifierSet::iterator set_it = map_it->second.begin(); - set_it != map_it->second.end(); ++set_it) { - QuicAckNotifier* ack_notifier = *set_it; + AckNotifierSet& ack_notifier_set = map_it->second; + for (QuicAckNotifier* ack_notifier : ack_notifier_set) { ack_notifier->OnAck(sequence_number, delta_largest_observed); // If this has resulted in an empty AckNotifer, erase it. @@ -54,53 +52,51 @@ void AckNotifierManager::OnPacketAcked( void AckNotifierManager::UpdateSequenceNumber( QuicPacketSequenceNumber old_sequence_number, QuicPacketSequenceNumber new_sequence_number) { - AckNotifierMap::iterator map_it = ack_notifier_map_.find(old_sequence_number); - if (map_it != ack_notifier_map_.end()) { - // We will add an entry to the map for the new sequence number, and move - // the - // list of AckNotifiers over. - AckNotifierSet new_set; - for (AckNotifierSet::iterator notifier_it = map_it->second.begin(); - notifier_it != map_it->second.end(); ++notifier_it) { - (*notifier_it) - ->UpdateSequenceNumber(old_sequence_number, new_sequence_number); - new_set.insert(*notifier_it); - } - ack_notifier_map_[new_sequence_number] = new_set; - ack_notifier_map_.erase(map_it); + auto map_it = ack_notifier_map_.find(old_sequence_number); + if (map_it == ack_notifier_map_.end()) { + // No AckNotifiers are interested in the old sequence number. + return; } + + // Update the existing QuicAckNotifiers to the new sequence number. + AckNotifierSet& ack_notifier_set = map_it->second; + for (QuicAckNotifier* ack_notifier : ack_notifier_set) { + ack_notifier->UpdateSequenceNumber(old_sequence_number, + new_sequence_number); + } + + // The old sequence number is no longer of interest, copy the updated + // AckNotifiers to the new sequence number before deleting the old. + ack_notifier_map_[new_sequence_number] = ack_notifier_set; + ack_notifier_map_.erase(map_it); } void AckNotifierManager::OnSerializedPacket( const SerializedPacket& serialized_packet) { - // Run through all the frames and if any of them are stream frames and have - // an AckNotifier registered, then inform the AckNotifier that it should be - // interested in this packet's sequence number. - - RetransmittableFrames* frames = serialized_packet.retransmittable_frames; - // AckNotifiers can only be attached to retransmittable frames. - if (!frames) { + RetransmittableFrames* frames = serialized_packet.retransmittable_frames; + if (frames == nullptr) { return; } - for (QuicFrames::const_iterator it = frames->frames().begin(); - it != frames->frames().end(); ++it) { - if (it->type == STREAM_FRAME && it->stream_frame->notifier != nullptr) { - QuicAckNotifier* notifier = it->stream_frame->notifier; + // For each frame in |serialized_packet|, inform any attached AckNotifiers of + // the packet's sequence number. + for (const QuicFrame& quic_frame : frames->frames()) { + if (quic_frame.type != STREAM_FRAME || + quic_frame.stream_frame->notifier == nullptr) { + continue; + } - // The AckNotifier needs to know it is tracking this packet's sequence - // number. - notifier->AddSequenceNumber(serialized_packet.sequence_number, - serialized_packet.packet->length()); + QuicAckNotifier* notifier = quic_frame.stream_frame->notifier; + notifier->AddSequenceNumber(serialized_packet.sequence_number, + serialized_packet.packet->length()); - // Update the mapping in the other direction, from sequence - // number to AckNotifier. - ack_notifier_map_[serialized_packet.sequence_number].insert(notifier); + // Update the mapping in the other direction, from sequence number to + // AckNotifier. + ack_notifier_map_[serialized_packet.sequence_number].insert(notifier); - // Take ownership of the AckNotifier. - ack_notifiers_.insert(notifier); - } + // Take ownership of the AckNotifier. + ack_notifiers_.insert(notifier); } } diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc index 74d1758..5fdc4b9 100644 --- a/net/quic/quic_connection.cc +++ b/net/quic/quic_connection.cc @@ -467,10 +467,8 @@ void QuicConnection::OnDecryptedPacket(EncryptionLevel level) { last_packet_decrypted_ = true; // If this packet was foward-secure encrypted and the forward-secure encrypter // is not being used, start using it. - if (FLAGS_enable_quic_delay_forward_security && - encryption_level_ != ENCRYPTION_FORWARD_SECURE && - has_forward_secure_encrypter_ && - level == ENCRYPTION_FORWARD_SECURE) { + if (encryption_level_ != ENCRYPTION_FORWARD_SECURE && + has_forward_secure_encrypter_ && level == ENCRYPTION_FORWARD_SECURE) { SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); } } @@ -942,9 +940,6 @@ void QuicConnection::ClearLastFrames() { } void QuicConnection::MaybeCloseIfTooManyOutstandingPackets() { - if (!FLAGS_quic_too_many_outstanding_packets) { - return; - } // This occurs if we don't discard old packets we've sent fast enough. // It's possible largest observed is less than least unacked. if (sent_packet_manager_.largest_observed() > @@ -1584,8 +1579,7 @@ void QuicConnection::OnSerializedPacket( // If a forward-secure encrypter is available but is not being used and this // packet's sequence number is after the first packet which requires // forward security, start using the forward-secure encrypter. - if (FLAGS_enable_quic_delay_forward_security && - encryption_level_ != ENCRYPTION_FORWARD_SECURE && + if (encryption_level_ != ENCRYPTION_FORWARD_SECURE && has_forward_secure_encrypter_ && serialized_packet.sequence_number >= first_required_forward_secure_packet_) { @@ -1696,8 +1690,7 @@ void QuicConnection::OnRetransmissionTimeout() { void QuicConnection::SetEncrypter(EncryptionLevel level, QuicEncrypter* encrypter) { framer_.SetEncrypter(level, encrypter); - if (FLAGS_enable_quic_delay_forward_security && - level == ENCRYPTION_FORWARD_SECURE) { + if (level == ENCRYPTION_FORWARD_SECURE) { has_forward_secure_encrypter_ = true; first_required_forward_secure_packet_ = sequence_number_of_last_sent_packet_ + diff --git a/net/quic/quic_connection_test.cc b/net/quic/quic_connection_test.cc index 6381c16..fe11766 100644 --- a/net/quic/quic_connection_test.cc +++ b/net/quic/quic_connection_test.cc @@ -1346,11 +1346,8 @@ TEST_P(QuicConnectionTest, TooManySentPackets) { // Ack packet 1, which leaves more than the limit outstanding. EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _)); - if (FLAGS_quic_too_many_outstanding_packets) { - EXPECT_CALL(visitor_, - OnConnectionClosed(QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS, - false)); - } + EXPECT_CALL(visitor_, OnConnectionClosed( + QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS, false)); // We're receive buffer limited, so the connection won't try to write more. EXPECT_CALL(visitor_, OnCanWrite()).Times(0); @@ -1364,12 +1361,8 @@ TEST_P(QuicConnectionTest, TooManySentPackets) { TEST_P(QuicConnectionTest, TooManyReceivedPackets) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - - if (FLAGS_quic_too_many_outstanding_packets) { - EXPECT_CALL(visitor_, - OnConnectionClosed(QUIC_TOO_MANY_OUTSTANDING_RECEIVED_PACKETS, - false)); - } + EXPECT_CALL(visitor_, OnConnectionClosed( + QUIC_TOO_MANY_OUTSTANDING_RECEIVED_PACKETS, false)); // Miss every other packet for 1000 packets. for (QuicPacketSequenceNumber i = 1; i < 1000; ++i) { @@ -2543,8 +2536,6 @@ TEST_P(QuicConnectionTest, RetransmitPacketsWithInitialEncryption) { } TEST_P(QuicConnectionTest, DelayForwardSecureEncryptionUntilClientIsReady) { - ValueRestore<bool> old_flag(&FLAGS_enable_quic_delay_forward_security, true); - // A TaggingEncrypter puts kTagSize copies of the given byte (0x02 here) at // the end of the packet. We can test this to check which encrypter was used. use_tagging_decrypter(); @@ -2568,8 +2559,6 @@ TEST_P(QuicConnectionTest, DelayForwardSecureEncryptionUntilClientIsReady) { } TEST_P(QuicConnectionTest, DelayForwardSecureEncryptionUntilManyPacketSent) { - ValueRestore<bool> old_flag(&FLAGS_enable_quic_delay_forward_security, true); - // Set a congestion window of 10 packets. QuicPacketCount congestion_window = 10; EXPECT_CALL(*send_algorithm_, GetCongestionWindow()).WillRepeatedly( diff --git a/net/quic/quic_crypto_server_stream.cc b/net/quic/quic_crypto_server_stream.cc index 50a6da4..63070cd 100644 --- a/net/quic/quic_crypto_server_stream.cc +++ b/net/quic/quic_crypto_server_stream.cc @@ -11,7 +11,6 @@ #include "net/quic/crypto/crypto_utils.h" #include "net/quic/crypto/quic_crypto_server_config.h" #include "net/quic/quic_config.h" -#include "net/quic/quic_flags.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_session.h" @@ -142,10 +141,6 @@ void QuicCryptoServerStream::FinishProcessingHandshakeMessage( session()->connection()->SetEncrypter( ENCRYPTION_FORWARD_SECURE, crypto_negotiated_params_.forward_secure_crypters.encrypter.release()); - if (!FLAGS_enable_quic_delay_forward_security) { - session()->connection()->SetDefaultEncryptionLevel( - ENCRYPTION_FORWARD_SECURE); - } session()->connection()->SetAlternativeDecrypter( crypto_negotiated_params_.forward_secure_crypters.decrypter.release(), ENCRYPTION_FORWARD_SECURE, false /* don't latch */); @@ -164,6 +159,7 @@ void QuicCryptoServerStream::SendServerConfigUpdate( CryptoHandshakeMessage server_config_update_message; if (!crypto_config_.BuildServerConfigUpdateMessage( + previous_source_address_tokens_, session()->connection()->self_address(), session()->connection()->peer_address(), session()->connection()->clock(), @@ -232,6 +228,7 @@ QuicErrorCode QuicCryptoServerStream::ProcessClientHello( previous_cached_network_params_.reset( new CachedNetworkParameters(result.cached_network_params)); } + previous_source_address_tokens_ = result.info.source_address_tokens; return crypto_config_.ProcessClientHello( result, session()->connection()->connection_id(), diff --git a/net/quic/quic_crypto_server_stream.h b/net/quic/quic_crypto_server_stream.h index f88affe..ece2da2 100644 --- a/net/quic/quic_crypto_server_stream.h +++ b/net/quic/quic_crypto_server_stream.h @@ -19,6 +19,7 @@ class CryptoHandshakeMessage; class QuicCryptoServerConfig; class QuicCryptoServerStream; class QuicSession; +class SourceAddressTokens; namespace test { class CryptoTestUtils; @@ -141,6 +142,9 @@ class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream { // estimate to send. scoped_ptr<CachedNetworkParameters> previous_cached_network_params_; + // Contains any source address tokens which were present in the CHLO. + SourceAddressTokens previous_source_address_tokens_; + DISALLOW_COPY_AND_ASSIGN(QuicCryptoServerStream); }; diff --git a/net/quic/quic_flags.cc b/net/quic/quic_flags.cc index d06234a..b395eac 100644 --- a/net/quic/quic_flags.cc +++ b/net/quic/quic_flags.cc @@ -39,14 +39,6 @@ bool FLAGS_quic_allow_bbr = false; // If true, truncate QUIC connection IDs if the client requests it. bool FLAGS_allow_truncated_connection_ids_for_quic = false; -// If true, close the connection when there are too many outstanding QUIC -// packets in the sent or received packet managers. -bool FLAGS_quic_too_many_outstanding_packets = false; - -// If true, QUIC connections will delay moving to forward security until the -// client starts sending foward secure encrypted packets. -bool FLAGS_enable_quic_delay_forward_security = true; - // Do not flip this flag. jokulik plans more testing and additional monitoring // before the flag can go the auto-flip process. // @@ -68,3 +60,11 @@ bool FLAGS_quic_allow_silent_close = true; // If true, use std::cbrt instead of custom cube root. bool FLAGS_quic_use_std_cbrt = true; + +// If true, the QUIC packet generator will not attempt to queue multiple ACK +// frames. +bool FLAGS_quic_disallow_multiple_pending_ack_frames = true; + +// If true, then the source address tokens generated for QUIC connects will +// store multiple addresses. +bool FLAGS_quic_use_multiple_address_in_source_tokens = false; diff --git a/net/quic/quic_flags.h b/net/quic/quic_flags.h index 6580083..6927ce0 100644 --- a/net/quic/quic_flags.h +++ b/net/quic/quic_flags.h @@ -17,12 +17,13 @@ NET_EXPORT_PRIVATE extern bool FLAGS_quic_use_bbr_congestion_control; NET_EXPORT_PRIVATE extern bool FLAGS_quic_allow_bbr; NET_EXPORT_PRIVATE extern bool FLAGS_allow_truncated_connection_ids_for_quic; NET_EXPORT_PRIVATE extern bool FLAGS_quic_too_many_outstanding_packets; -NET_EXPORT_PRIVATE extern bool FLAGS_enable_quic_delay_forward_security; NET_EXPORT_PRIVATE extern bool FLAGS_quic_record_send_time_before_write; NET_EXPORT_PRIVATE extern bool FLAGS_quic_enable_bandwidth_resumption_experiment; NET_EXPORT_PRIVATE extern bool FLAGS_quic_enable_pacing; NET_EXPORT_PRIVATE extern bool FLAGS_quic_allow_silent_close; NET_EXPORT_PRIVATE extern bool FLAGS_quic_use_std_cbrt; +NET_EXPORT_PRIVATE extern bool FLAGS_quic_disallow_multiple_pending_ack_frames; +NET_EXPORT_PRIVATE extern bool FLAGS_quic_use_multiple_address_in_source_tokens; #endif // NET_QUIC_QUIC_FLAGS_H_ diff --git a/net/quic/quic_headers_stream.cc b/net/quic/quic_headers_stream.cc index 9d26e4a..e8c8b58 100644 --- a/net/quic/quic_headers_stream.cc +++ b/net/quic/quic_headers_stream.cc @@ -4,6 +4,7 @@ #include "net/quic/quic_headers_stream.h" +#include "base/strings/stringprintf.h" #include "net/quic/quic_session.h" using base::StringPiece; @@ -81,7 +82,9 @@ class QuicHeadersStream::SpdyFramerVisitor } void OnError(SpdyFramer* framer) override { - CloseConnection("SPDY framing error."); + CloseConnection(base::StringPrintf( + "SPDY framing error: %s", + SpdyFramer::ErrorCodeToString(framer->error_code()))); } void OnDataFrameHeader(SpdyStreamId stream_id, diff --git a/net/quic/quic_headers_stream_test.cc b/net/quic/quic_headers_stream_test.cc index 8649a3e..446b73f 100644 --- a/net/quic/quic_headers_stream_test.cc +++ b/net/quic/quic_headers_stream_test.cc @@ -245,6 +245,15 @@ TEST_P(QuicHeadersStreamTest, ProcessRawData) { } } +TEST_P(QuicHeadersStreamTest, ProcessBadData) { + const char kBadData[] = "blah blah blah"; + EXPECT_CALL(*connection_, + SendConnectionCloseWithDetails( + QUIC_INVALID_HEADERS_STREAM_DATA, + "SPDY framing error: SPDY_INVALID_DATA_FRAME_FLAGS")); + headers_stream_->ProcessRawData(kBadData, strlen(kBadData)); +} + TEST_P(QuicHeadersStreamTest, ProcessSpdyDataFrame) { SpdyDataIR data(2, ""); scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data)); diff --git a/net/quic/quic_packet_generator.cc b/net/quic/quic_packet_generator.cc index 21ab10c..2262883 100644 --- a/net/quic/quic_packet_generator.cc +++ b/net/quic/quic_packet_generator.cc @@ -7,6 +7,7 @@ #include "base/basictypes.h" #include "base/logging.h" #include "net/quic/quic_fec_group.h" +#include "net/quic/quic_flags.h" #include "net/quic/quic_utils.h" using base::StringPiece; @@ -93,6 +94,23 @@ void QuicPacketGenerator::OnCongestionWindowChange( void QuicPacketGenerator::SetShouldSendAck(bool also_send_feedback, bool also_send_stop_waiting) { + if (FLAGS_quic_disallow_multiple_pending_ack_frames) { + if (pending_ack_frame_ != nullptr) { + // Ack already queued, nothing to do. + return; + } + + if (also_send_feedback && pending_feedback_frame_ != nullptr) { + LOG(DFATAL) << "Should only ever be one pending feedback frame."; + return; + } + + if (also_send_stop_waiting && pending_stop_waiting_frame_ != nullptr) { + LOG(DFATAL) << "Should only ever be one pending stop waiting frame."; + return; + } + } + should_send_ack_ = true; should_send_feedback_ = also_send_feedback; should_send_stop_waiting_ = also_send_stop_waiting; @@ -347,6 +365,13 @@ void QuicPacketGenerator::SerializeAndSendPacket() { DCHECK(serialized_packet.packet); delegate_->OnSerializedPacket(serialized_packet); MaybeSendFecPacketAndCloseGroup(false); + + // The packet has now been serialized, safe to delete pending frames. + if (FLAGS_quic_disallow_multiple_pending_ack_frames) { + pending_ack_frame_.reset(); + pending_feedback_frame_.reset(); + pending_stop_waiting_frame_.reset(); + } } void QuicPacketGenerator::StopSendingVersion() { diff --git a/net/quic/quic_packet_generator_test.cc b/net/quic/quic_packet_generator_test.cc index ac99144..7eea015 100644 --- a/net/quic/quic_packet_generator_test.cc +++ b/net/quic/quic_packet_generator_test.cc @@ -10,6 +10,7 @@ #include "net/quic/crypto/null_encrypter.h" #include "net/quic/crypto/quic_decrypter.h" #include "net/quic/crypto/quic_encrypter.h" +#include "net/quic/quic_flags.h" #include "net/quic/quic_utils.h" #include "net/quic/test_tools/quic_packet_creator_peer.h" #include "net/quic/test_tools/quic_packet_generator_peer.h" @@ -315,6 +316,30 @@ TEST_F(QuicPacketGeneratorTest, CheckPacketContains(contents, packet_); } +TEST_F(QuicPacketGeneratorTest, ShouldSendAck_MultipleCalls) { + ValueRestore<bool> old_flag(&FLAGS_quic_disallow_multiple_pending_ack_frames, + true); + + // Make sure that calling SetShouldSendAck multiple times does not result in a + // crash. Previously this would result in multiple QuicFrames queued in the + // packet generator, with all but the last with internal pointers to freed + // memory. + delegate_.SetCanWriteAnything(); + + // Only one AckFrame should be created. + EXPECT_CALL(delegate_, CreateAckFrame()) + .Times(1) + .WillOnce(Return(CreateAckFrame())); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .Times(1) + .WillOnce(SaveArg<0>(&packet_)); + + generator_.StartBatchOperations(); + generator_.SetShouldSendAck(false, false); + generator_.SetShouldSendAck(false, false); + generator_.FinishBatchOperations(); +} + TEST_F(QuicPacketGeneratorTest, AddControlFrame_NotWritable) { delegate_.SetCanNotWrite(); diff --git a/net/quic/quic_sent_packet_manager.cc b/net/quic/quic_sent_packet_manager.cc index f054673..5feb467 100644 --- a/net/quic/quic_sent_packet_manager.cc +++ b/net/quic/quic_sent_packet_manager.cc @@ -165,6 +165,13 @@ void QuicSentPacketManager::SetFromConfig(const QuicConfig& config) { bool QuicSentPacketManager::ResumeConnectionState( const CachedNetworkParameters& cached_network_params) { + if (cached_network_params.has_min_rtt_ms()) { + uint32 initial_rtt_us = + kNumMicrosPerMilli * cached_network_params.min_rtt_ms(); + rtt_stats_.set_initial_rtt_us( + max(kMinInitialRoundTripTimeUs, + min(kMaxInitialRoundTripTimeUs, initial_rtt_us))); + } return send_algorithm_->ResumeConnectionState(cached_network_params); } diff --git a/net/quic/quic_sent_packet_manager_test.cc b/net/quic/quic_sent_packet_manager_test.cc index 1a907ff..044bead 100644 --- a/net/quic/quic_sent_packet_manager_test.cc +++ b/net/quic/quic_sent_packet_manager_test.cc @@ -1465,6 +1465,19 @@ TEST_F(QuicSentPacketManagerTest, UseInitialRoundTripTimeToSend) { EXPECT_EQ(initial_rtt_us, manager_.GetRttStats()->initial_rtt_us()); } +TEST_F(QuicSentPacketManagerTest, ResumeConnectionState) { + // The sent packet manager should use the RTT from CachedNetworkParameters if + // it is provided. + const int kRttMs = 1234; + CachedNetworkParameters cached_network_params; + cached_network_params.set_min_rtt_ms(kRttMs); + + EXPECT_CALL(*send_algorithm_, ResumeConnectionState(_)); + manager_.ResumeConnectionState(cached_network_params); + EXPECT_EQ(kRttMs * kNumMicrosPerMilli, + static_cast<uint64>(manager_.GetRttStats()->initial_rtt_us())); +} + } // namespace } // namespace test } // namespace net diff --git a/net/quic/quic_session_test.cc b/net/quic/quic_session_test.cc index e404929..15dfe79 100644 --- a/net/quic/quic_session_test.cc +++ b/net/quic/quic_session_test.cc @@ -327,9 +327,9 @@ TEST_P(QuicSessionTest, DecompressionError) { 0x00, 0x00, 'a', 'b', 'c', 'd' // invalid compressed data }; - EXPECT_CALL(*connection_, - SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA, - "SPDY framing error.")); + EXPECT_CALL(*connection_, SendConnectionCloseWithDetails( + QUIC_INVALID_HEADERS_STREAM_DATA, + "SPDY framing error: DECOMPRESS_FAILURE")); stream->ProcessRawData(reinterpret_cast<const char*>(data), arraysize(data)); } |