Add CachedNetworkParameters to address token for IETF QUIC, and
- min_rtt received from a validated token will be used to set the initial rtt, if connection option 'TRTT' is set.
- Enable bandwidth resumption for IETF QUIC when connection options BWRE or BWMX exists.

Protected by FLAGS_quic_reloadable_flag_quic_add_cached_network_parameters_to_address_token.

PiperOrigin-RevId: 407578759
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h
index b2776be..c4ea956 100644
--- a/quic/core/crypto/crypto_protocol.h
+++ b/quic/core/crypto/crypto_protocol.h
@@ -381,6 +381,9 @@
 const QuicTag kADE  = TAG('A', 'D', 'E', 0);     // Ack Delay Exponent (IETF
                                                  // QUIC ACK Frame Only).
 const QuicTag kIRTT = TAG('I', 'R', 'T', 'T');   // Estimated initial RTT in us.
+const QuicTag kTRTT = TAG('T', 'R', 'T', 'T');   // If server receives an rtt
+                                                 // from an address token, set
+                                                 // it as the initial rtt.
 const QuicTag kSNI  = TAG('S', 'N', 'I', '\0');  // Server name
                                                  // indication
 const QuicTag kPUBS = TAG('P', 'U', 'B', 'S');   // Public key values
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index 5f8675d..eae2996 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -1632,6 +1632,7 @@
 }
 
 TEST_P(EndToEndTest, AddressToken) {
+  client_extra_copts_.push_back(kTRTT);
   ASSERT_TRUE(Initialize());
   if (!version_.HasIetfQuicFrames()) {
     return;
@@ -1659,13 +1660,41 @@
   EXPECT_TRUE(client_->client()->EarlyDataAccepted());
 
   server_thread_->Pause();
+  QuicSpdySession* server_session = GetServerSession();
   QuicConnection* server_connection = GetServerConnection();
-  if (server_connection != nullptr) {
+  if (server_session != nullptr && server_connection != nullptr) {
     // Verify address is validated via validating token received in INITIAL
     // packet.
     EXPECT_FALSE(
         server_connection->GetStats().address_validated_via_decrypting_packet);
     EXPECT_TRUE(server_connection->GetStats().address_validated_via_token);
+
+    // Verify the server received a cached min_rtt from the token and used it as
+    // the initial rtt.
+    const CachedNetworkParameters* server_received_network_params =
+        static_cast<const QuicCryptoServerStreamBase*>(
+            server_session->GetCryptoStream())
+            ->PreviousCachedNetworkParams();
+    if (GetQuicReloadableFlag(
+            quic_add_cached_network_parameters_to_address_token)) {
+      ASSERT_NE(server_received_network_params, nullptr);
+      // QuicSentPacketManager::SetInitialRtt clamps the initial_rtt to between
+      // [min_initial_rtt, max_initial_rtt].
+      const QuicTime::Delta min_initial_rtt =
+          QuicTime::Delta::FromMicroseconds(kMinInitialRoundTripTimeUs);
+      const QuicTime::Delta max_initial_rtt =
+          QuicTime::Delta::FromMicroseconds(kMaxInitialRoundTripTimeUs);
+      const QuicTime::Delta expected_initial_rtt =
+          std::max(min_initial_rtt,
+                   std::min(max_initial_rtt,
+                            QuicTime::Delta::FromMilliseconds(
+                                server_received_network_params->min_rtt_ms())));
+      EXPECT_EQ(
+          server_connection->sent_packet_manager().GetRttStats()->initial_rtt(),
+          expected_initial_rtt);
+    } else {
+      EXPECT_EQ(server_received_network_params, nullptr);
+    }
   } else {
     ADD_FAILURE() << "Missing server connection";
   }
diff --git a/quic/core/http/quic_server_session_base.cc b/quic/core/http/quic_server_session_base.cc
index 86ccafb..8edcb69 100644
--- a/quic/core/http/quic_server_session_base.cc
+++ b/quic/core/http/quic_server_session_base.cc
@@ -10,6 +10,7 @@
 #include "quic/core/quic_connection.h"
 #include "quic/core/quic_stream.h"
 #include "quic/core/quic_tag.h"
+#include "quic/core/quic_types.h"
 #include "quic/core/quic_utils.h"
 #include "quic/platform/api/quic_bug_tracker.h"
 #include "quic/platform/api/quic_flag_utils.h"
@@ -50,6 +51,30 @@
     return;
   }
 
+  const CachedNetworkParameters* cached_network_params =
+      crypto_stream_->PreviousCachedNetworkParams();
+
+  // Set the initial rtt from cached_network_params.min_rtt_ms, which comes from
+  // a validated address token. This will override the initial rtt that may have
+  // been set by the transport parameters.
+  if (add_cached_network_parameters_to_address_token() && version().UsesTls() &&
+      cached_network_params != nullptr) {
+    if (cached_network_params->serving_region() == serving_region_) {
+      QUIC_CODE_COUNT(quic_server_received_network_params_at_same_region);
+      if (ContainsQuicTag(config()->ReceivedConnectionOptions(), kTRTT)) {
+        QUIC_DLOG(INFO)
+            << "Server: Setting initial rtt to "
+            << cached_network_params->min_rtt_ms()
+            << "ms which is received from a validated address token";
+        connection()->sent_packet_manager().SetInitialRtt(
+            QuicTime::Delta::FromMilliseconds(
+                cached_network_params->min_rtt_ms()));
+      }
+    } else {
+      QUIC_CODE_COUNT(quic_server_received_network_params_at_different_region);
+    }
+  }
+
   // Enable bandwidth resumption if peer sent correct connection options.
   const bool last_bandwidth_resumption =
       ContainsQuicTag(config()->ReceivedConnectionOptions(), kBWRE);
@@ -61,13 +86,14 @@
   // If the client has provided a bandwidth estimate from the same serving
   // region as this server, then decide whether to use the data for bandwidth
   // resumption.
-  const CachedNetworkParameters* cached_network_params =
-      crypto_stream_->PreviousCachedNetworkParams();
   if (cached_network_params != nullptr &&
       cached_network_params->serving_region() == serving_region_) {
-    // Log the received connection parameters, regardless of how they
-    // get used for bandwidth resumption.
-    connection()->OnReceiveConnectionState(*cached_network_params);
+    if (!add_cached_network_parameters_to_address_token() ||
+        !version().UsesTls()) {
+      // Log the received connection parameters, regardless of how they
+      // get used for bandwidth resumption.
+      connection()->OnReceiveConnectionState(*cached_network_params);
+    }
 
     if (bandwidth_resumption_enabled_) {
       // Only do bandwidth resumption if estimate is recent enough.
@@ -150,49 +176,76 @@
     return;
   }
 
-  bandwidth_estimate_sent_to_client_ = new_bandwidth_estimate;
-  QUIC_DVLOG(1) << "Server: sending new bandwidth estimate (KBytes/s): "
-                << bandwidth_estimate_sent_to_client_.ToKBytesPerSecond();
+  if (add_cached_network_parameters_to_address_token()) {
+    if (version().UsesTls()) {
+      if (version().HasIetfQuicFrames() && MaybeSendAddressToken()) {
+        bandwidth_estimate_sent_to_client_ = new_bandwidth_estimate;
+      }
+    } else {
+      absl::optional<CachedNetworkParameters> cached_network_params =
+          GenerateCachedNetworkParameters();
 
-  // Include max bandwidth in the update.
-  QuicBandwidth max_bandwidth_estimate =
-      bandwidth_recorder->MaxBandwidthEstimate();
-  int32_t max_bandwidth_timestamp = bandwidth_recorder->MaxBandwidthTimestamp();
+      if (cached_network_params.has_value()) {
+        bandwidth_estimate_sent_to_client_ = new_bandwidth_estimate;
+        QUIC_DVLOG(1) << "Server: sending new bandwidth estimate (KBytes/s): "
+                      << bandwidth_estimate_sent_to_client_.ToKBytesPerSecond();
 
-  // Fill the proto before passing it to the crypto stream to send.
-  const int32_t bw_estimate_bytes_per_second =
-      BandwidthToCachedParameterBytesPerSecond(
-          bandwidth_estimate_sent_to_client_);
-  const int32_t max_bw_estimate_bytes_per_second =
-      BandwidthToCachedParameterBytesPerSecond(max_bandwidth_estimate);
-  QUIC_BUG_IF(quic_bug_12513_1, max_bw_estimate_bytes_per_second < 0)
-      << max_bw_estimate_bytes_per_second;
-  QUIC_BUG_IF(quic_bug_10393_1, bw_estimate_bytes_per_second < 0)
-      << bw_estimate_bytes_per_second;
+        QUICHE_DCHECK_EQ(
+            BandwidthToCachedParameterBytesPerSecond(
+                bandwidth_estimate_sent_to_client_),
+            cached_network_params->bandwidth_estimate_bytes_per_second());
 
-  CachedNetworkParameters cached_network_params;
-  cached_network_params.set_bandwidth_estimate_bytes_per_second(
-      bw_estimate_bytes_per_second);
-  cached_network_params.set_max_bandwidth_estimate_bytes_per_second(
-      max_bw_estimate_bytes_per_second);
-  cached_network_params.set_max_bandwidth_timestamp_seconds(
-      max_bandwidth_timestamp);
-  cached_network_params.set_min_rtt_ms(
-      sent_packet_manager.GetRttStats()->min_rtt().ToMilliseconds());
-  cached_network_params.set_previous_connection_state(
-      bandwidth_recorder->EstimateRecordedDuringSlowStart()
-          ? CachedNetworkParameters::SLOW_START
-          : CachedNetworkParameters::CONGESTION_AVOIDANCE);
-  cached_network_params.set_timestamp(
-      connection()->clock()->WallNow().ToUNIXSeconds());
-  if (!serving_region_.empty()) {
-    cached_network_params.set_serving_region(serving_region_);
+        crypto_stream_->SendServerConfigUpdate(&cached_network_params.value());
+
+        connection()->OnSendConnectionState(*cached_network_params);
+      }
+    }
+  } else {
+    bandwidth_estimate_sent_to_client_ = new_bandwidth_estimate;
+    QUIC_DVLOG(1) << "Server: sending new bandwidth estimate (KBytes/s): "
+                  << bandwidth_estimate_sent_to_client_.ToKBytesPerSecond();
+
+    // Include max bandwidth in the update.
+    QuicBandwidth max_bandwidth_estimate =
+        bandwidth_recorder->MaxBandwidthEstimate();
+    int32_t max_bandwidth_timestamp =
+        bandwidth_recorder->MaxBandwidthTimestamp();
+
+    // Fill the proto before passing it to the crypto stream to send.
+    const int32_t bw_estimate_bytes_per_second =
+        BandwidthToCachedParameterBytesPerSecond(
+            bandwidth_estimate_sent_to_client_);
+    const int32_t max_bw_estimate_bytes_per_second =
+        BandwidthToCachedParameterBytesPerSecond(max_bandwidth_estimate);
+    QUIC_BUG_IF(quic_bug_12513_1, max_bw_estimate_bytes_per_second < 0)
+        << max_bw_estimate_bytes_per_second;
+    QUIC_BUG_IF(quic_bug_10393_1, bw_estimate_bytes_per_second < 0)
+        << bw_estimate_bytes_per_second;
+
+    CachedNetworkParameters cached_network_params;
+    cached_network_params.set_bandwidth_estimate_bytes_per_second(
+        bw_estimate_bytes_per_second);
+    cached_network_params.set_max_bandwidth_estimate_bytes_per_second(
+        max_bw_estimate_bytes_per_second);
+    cached_network_params.set_max_bandwidth_timestamp_seconds(
+        max_bandwidth_timestamp);
+    cached_network_params.set_min_rtt_ms(
+        sent_packet_manager.GetRttStats()->min_rtt().ToMilliseconds());
+    cached_network_params.set_previous_connection_state(
+        bandwidth_recorder->EstimateRecordedDuringSlowStart()
+            ? CachedNetworkParameters::SLOW_START
+            : CachedNetworkParameters::CONGESTION_AVOIDANCE);
+    cached_network_params.set_timestamp(
+        connection()->clock()->WallNow().ToUNIXSeconds());
+    if (!serving_region_.empty()) {
+      cached_network_params.set_serving_region(serving_region_);
+    }
+
+    crypto_stream_->SendServerConfigUpdate(&cached_network_params);
+
+    connection()->OnSendConnectionState(cached_network_params);
   }
 
-  crypto_stream_->SendServerConfigUpdate(&cached_network_params);
-
-  connection()->OnSendConnectionState(cached_network_params);
-
   last_scup_time_ = now;
   last_scup_packet_number_ =
       connection()->sent_packet_manager().GetLargestSentPacket();
@@ -262,7 +315,7 @@
 }
 
 int32_t QuicServerSessionBase::BandwidthToCachedParameterBytesPerSecond(
-    const QuicBandwidth& bandwidth) {
+    const QuicBandwidth& bandwidth) const {
   return static_cast<int32_t>(std::min<int64_t>(
       bandwidth.ToBytesPerSecond(), std::numeric_limits<uint32_t>::max()));
 }
@@ -303,4 +356,54 @@
   return ssl_config;
 }
 
+absl::optional<CachedNetworkParameters>
+QuicServerSessionBase::GenerateCachedNetworkParameters() const {
+  QUICHE_DCHECK(add_cached_network_parameters_to_address_token());
+  const QuicSentPacketManager& sent_packet_manager =
+      connection()->sent_packet_manager();
+  const QuicSustainedBandwidthRecorder* bandwidth_recorder =
+      sent_packet_manager.SustainedBandwidthRecorder();
+
+  CachedNetworkParameters cached_network_params;
+  cached_network_params.set_timestamp(
+      connection()->clock()->WallNow().ToUNIXSeconds());
+
+  if (!sent_packet_manager.GetRttStats()->min_rtt().IsZero()) {
+    cached_network_params.set_min_rtt_ms(
+        sent_packet_manager.GetRttStats()->min_rtt().ToMilliseconds());
+  }
+
+  // Populate bandwidth estimates if any.
+  if (bandwidth_recorder != nullptr && bandwidth_recorder->HasEstimate()) {
+    const int32_t bw_estimate_bytes_per_second =
+        BandwidthToCachedParameterBytesPerSecond(
+            bandwidth_recorder->BandwidthEstimate());
+    const int32_t max_bw_estimate_bytes_per_second =
+        BandwidthToCachedParameterBytesPerSecond(
+            bandwidth_recorder->MaxBandwidthEstimate());
+    QUIC_BUG_IF(quic_bug_12513_1, max_bw_estimate_bytes_per_second < 0)
+        << max_bw_estimate_bytes_per_second;
+    QUIC_BUG_IF(quic_bug_10393_1, bw_estimate_bytes_per_second < 0)
+        << bw_estimate_bytes_per_second;
+
+    cached_network_params.set_bandwidth_estimate_bytes_per_second(
+        bw_estimate_bytes_per_second);
+    cached_network_params.set_max_bandwidth_estimate_bytes_per_second(
+        max_bw_estimate_bytes_per_second);
+    cached_network_params.set_max_bandwidth_timestamp_seconds(
+        bandwidth_recorder->MaxBandwidthTimestamp());
+
+    cached_network_params.set_previous_connection_state(
+        bandwidth_recorder->EstimateRecordedDuringSlowStart()
+            ? CachedNetworkParameters::SLOW_START
+            : CachedNetworkParameters::CONGESTION_AVOIDANCE);
+  }
+
+  if (!serving_region_.empty()) {
+    cached_network_params.set_serving_region(serving_region_);
+  }
+
+  return cached_network_params;
+}
+
 }  // namespace quic
diff --git a/quic/core/http/quic_server_session_base.h b/quic/core/http/quic_server_session_base.h
index bd9f5e7..5903de6 100644
--- a/quic/core/http/quic_server_session_base.h
+++ b/quic/core/http/quic_server_session_base.h
@@ -76,6 +76,9 @@
 
   const QuicCryptoServerStreamBase* GetCryptoStream() const override;
 
+  absl::optional<CachedNetworkParameters> GenerateCachedNetworkParameters()
+      const override;
+
   // If an outgoing stream can be created, return true.
   // Return false when connection is closed or forward secure encryption hasn't
   // established yet or number of server initiated streams already reaches the
@@ -138,7 +141,7 @@
   // stored in CachedNetworkParameters.  TODO(jokulik): This function
   // should go away once we fix http://b//27897982
   int32_t BandwidthToCachedParameterBytesPerSecond(
-      const QuicBandwidth& bandwidth);
+      const QuicBandwidth& bandwidth) const;
 };
 
 }  // namespace quic
diff --git a/quic/core/http/quic_server_session_base_test.cc b/quic/core/http/quic_server_session_base_test.cc
index 279ff60..bde1384 100644
--- a/quic/core/http/quic_server_session_base_test.cc
+++ b/quic/core/http/quic_server_session_base_test.cc
@@ -503,13 +503,19 @@
   MockTlsServerHandshaker& operator=(const MockTlsServerHandshaker&) = delete;
   ~MockTlsServerHandshaker() override {}
 
-  MOCK_METHOD(void,
-              SendServerConfigUpdate,
-              (const CachedNetworkParameters*),
+  MOCK_METHOD(void, SendServerConfigUpdate, (const CachedNetworkParameters*),
               (override));
+
+  MOCK_METHOD(std::string, GetAddressToken, (const CachedNetworkParameters*),
+              (const, override));
 };
 
 TEST_P(QuicServerSessionBaseTest, BandwidthEstimates) {
+  if (version().UsesTls() && !version().HasIetfQuicFrames()) {
+    // Skip the Txxx versions.
+    return;
+  }
+
   // Test that bandwidth estimate updates are sent to the client, only when
   // bandwidth resumption is enabled, the bandwidth estimate has changed
   // sufficiently, enough time has passed,
@@ -637,22 +643,30 @@
     EXPECT_CALL(*quic_crypto_stream,
                 SendServerConfigUpdate(EqualsProto(expected_network_params)))
         .Times(1);
-  } else {
+  } else if (!GetQuicReloadableFlag(
+                 quic_add_cached_network_parameters_to_address_token)) {
     EXPECT_CALL(*tls_server_stream,
                 SendServerConfigUpdate(EqualsProto(expected_network_params)))
         .Times(1);
+  } else {
+    EXPECT_CALL(*tls_server_stream,
+                GetAddressToken(EqualsProto(expected_network_params)))
+        .WillOnce(testing::Return("Test address token"));
   }
   EXPECT_CALL(*connection_, OnSendConnectionState(_)).Times(1);
   session_->OnCongestionWindowChange(now);
 }
 
 TEST_P(QuicServerSessionBaseTest, BandwidthResumptionExperiment) {
-  if (version().handshake_protocol == PROTOCOL_TLS1_3) {
-    // This test relies on resumption, which is not currently supported by the
-    // TLS handshake.
-    // TODO(nharper): Add support for resumption to the TLS handshake.
-    return;
+  if (version().UsesTls()) {
+    if (!version().HasIetfQuicFrames()) {
+      // Skip the Txxx versions.
+      return;
+    }
+    // Avoid a QUIC_BUG in QuicSession::OnConfigNegotiated.
+    connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
   }
+
   // Test that if a client provides a CachedNetworkParameters with the same
   // serving region as the current server, and which was made within an hour of
   // now, that this data is passed down to the send algorithm.
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc
index bf86955..964f6fb 100644
--- a/quic/core/http/quic_spdy_session_test.cc
+++ b/quic/core/http/quic_spdy_session_test.cc
@@ -173,7 +173,10 @@
   void OnHandshakePacketSent() override {}
   void OnHandshakeDoneReceived() override {}
   void OnNewTokenReceived(absl::string_view /*token*/) override {}
-  std::string GetAddressToken() const override { return ""; }
+  std::string GetAddressToken(
+      const CachedNetworkParameters* /*cached_network_params*/) const override {
+    return "";
+  }
   bool ValidateAddressToken(absl::string_view /*token*/) const override {
     return true;
   }
diff --git a/quic/core/http/quic_spdy_stream_test.cc b/quic/core/http/quic_spdy_stream_test.cc
index d75ea97..2732de2 100644
--- a/quic/core/http/quic_spdy_stream_test.cc
+++ b/quic/core/http/quic_spdy_stream_test.cc
@@ -157,7 +157,11 @@
                           ConnectionCloseSource /*source*/) override {}
   void OnHandshakeDoneReceived() override {}
   void OnNewTokenReceived(absl::string_view /*token*/) override {}
-  std::string GetAddressToken() const override { return ""; }
+  std::string GetAddressToken(
+      const CachedNetworkParameters* /*cached_network_parameters*/)
+      const override {
+    return "";
+  }
   bool ValidateAddressToken(absl::string_view /*token*/) const override {
     return true;
   }
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index 1d9bf95..1ec7c7e 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -235,11 +235,11 @@
   // Called by the server to validate |token| in received INITIAL packets.
   // Consider the client address gets validated (and therefore remove
   // amplification factor) once the |token| gets successfully validated.
-  virtual bool ValidateToken(absl::string_view token) const = 0;
+  virtual bool ValidateToken(absl::string_view token) = 0;
 
   // Called by the server to send another token.
   // Return false if the crypto stream fail to generate one.
-  virtual void MaybeSendAddressToken() = 0;
+  virtual bool MaybeSendAddressToken() = 0;
 
   // Whether the server address is known to the connection.
   virtual bool IsKnownServerAddress(const QuicSocketAddress& address) const = 0;
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index 1e3b70d..a6ed2a1 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -2180,6 +2180,7 @@
     EXPECT_CALL(visitor_, MaybeSendAddressToken()).WillOnce(Invoke([this]() {
       connection_.SendControlFrame(
           QuicFrame(new QuicNewTokenFrame(1, "new_token")));
+      return true;
     }));
     ProcessFramesPacketWithAddresses({QuicFrame(new QuicPathResponseFrame(
                                           0, reverse_path_challenge_payload)),
diff --git a/quic/core/quic_crypto_client_stream.h b/quic/core/quic_crypto_client_stream.h
index 688d603..e37a570 100644
--- a/quic/core/quic_crypto_client_stream.h
+++ b/quic/core/quic_crypto_client_stream.h
@@ -75,7 +75,8 @@
     return false;
   }
 
-  std::string GetAddressToken() const override {
+  std::string GetAddressToken(
+      const CachedNetworkParameters* /*cached_network_params*/) const override {
     QUICHE_DCHECK(false);
     return "";
   }
diff --git a/quic/core/quic_crypto_server_stream.cc b/quic/core/quic_crypto_server_stream.cc
index b598d3c..28bfe4a 100644
--- a/quic/core/quic_crypto_server_stream.cc
+++ b/quic/core/quic_crypto_server_stream.cc
@@ -353,7 +353,8 @@
   QUICHE_DCHECK(false);
 }
 
-std::string QuicCryptoServerStream::GetAddressToken() const {
+std::string QuicCryptoServerStream::GetAddressToken(
+    const CachedNetworkParameters* /*cached_network_parameters*/) const {
   QUICHE_DCHECK(false);
   return "";
 }
diff --git a/quic/core/quic_crypto_server_stream.h b/quic/core/quic_crypto_server_stream.h
index 399eca3..cf4f6e0 100644
--- a/quic/core/quic_crypto_server_stream.h
+++ b/quic/core/quic_crypto_server_stream.h
@@ -48,7 +48,8 @@
                           ConnectionCloseSource /*source*/) override {}
   void OnHandshakeDoneReceived() override;
   void OnNewTokenReceived(absl::string_view token) override;
-  std::string GetAddressToken() const override;
+  std::string GetAddressToken(
+      const CachedNetworkParameters* /*cached_network_params*/) const override;
   bool ValidateAddressToken(absl::string_view token) const override;
   bool ShouldSendExpectCTHeader() const override;
   bool DidCertMatchSni() const override;
diff --git a/quic/core/quic_crypto_stream.h b/quic/core/quic_crypto_stream.h
index 056186a..86b6913 100644
--- a/quic/core/quic_crypto_stream.h
+++ b/quic/core/quic_crypto_stream.h
@@ -13,6 +13,7 @@
 #include "third_party/boringssl/src/include/openssl/ssl.h"
 #include "quic/core/crypto/crypto_framer.h"
 #include "quic/core/crypto/crypto_utils.h"
+#include "quic/core/proto/cached_network_parameters_proto.h"
 #include "quic/core/quic_config.h"
 #include "quic/core/quic_packets.h"
 #include "quic/core/quic_stream.h"
@@ -110,7 +111,8 @@
   virtual void OnNewTokenReceived(absl::string_view token) = 0;
 
   // Called to get an address token.
-  virtual std::string GetAddressToken() const = 0;
+  virtual std::string GetAddressToken(
+      const CachedNetworkParameters* cached_network_params) const = 0;
 
   // Called to validate |token|.
   virtual bool ValidateAddressToken(absl::string_view token) const = 0;
diff --git a/quic/core/quic_crypto_stream_test.cc b/quic/core/quic_crypto_stream_test.cc
index fb1374f..1c18c7b 100644
--- a/quic/core/quic_crypto_stream_test.cc
+++ b/quic/core/quic_crypto_stream_test.cc
@@ -64,7 +64,11 @@
   void OnHandshakePacketSent() override {}
   void OnHandshakeDoneReceived() override {}
   void OnNewTokenReceived(absl::string_view /*token*/) override {}
-  std::string GetAddressToken() const override { return ""; }
+  std::string GetAddressToken(
+      const CachedNetworkParameters* /*cached_network_parameters*/)
+      const override {
+    return "";
+  }
   bool ValidateAddressToken(absl::string_view /*token*/) const override {
     return true;
   }
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 4fbb0ec..fb6032b 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -19,6 +19,8 @@
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_no_probe_up_exit_if_no_queue, true)
 // If true and connection option B201 is used, check if cwnd limited before aggregation epoch, instead of ack event, in PROBE_UP phase.
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_check_cwnd_limited_before_aggregation_epoch, true)
+// If true, 1) NEW_TOKENs sent from a IETF QUIC session will include the cached network parameters proto, 2) A min_rtt received from a validated token will be used to set the initial rtt, 3) Enable bandwidth resumption for IETF QUIC when connection options BWRE or BWMX exists.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_add_cached_network_parameters_to_address_token, true)
 // If true, QPACK decoder rejects CR, LF, and NULL in field (header) values, and causes the stream to be reset with H3_MESSAGE_ERROR.
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_reject_invalid_chars_in_field_value, true)
 // If true, QUIC will default enable MTU discovery at server, with a target of 1450 bytes.
diff --git a/quic/core/quic_sent_packet_manager.h b/quic/core/quic_sent_packet_manager.h
index ccd1e56..e4a7036 100644
--- a/quic/core/quic_sent_packet_manager.h
+++ b/quic/core/quic_sent_packet_manager.h
@@ -471,6 +471,9 @@
     num_ptos_for_path_degrading_ = num_ptos_for_path_degrading;
   }
 
+  // Sets the initial RTT of the connection.
+  void SetInitialRtt(QuicTime::Delta rtt);
+
  private:
   friend class test::QuicConnectionPeer;
   friend class test::QuicSentPacketManagerPeer;
@@ -554,9 +557,6 @@
   // this function.
   void RecordOneSpuriousRetransmission(const QuicTransmissionInfo& info);
 
-  // Sets the initial RTT of the connection.
-  void SetInitialRtt(QuicTime::Delta rtt);
-
   // Called when handshake is confirmed to remove the retransmittable frames
   // from all packets of HANDSHAKE_DATA packet number space to ensure they don't
   // get retransmitted and will eventually be removed from unacked packets map.
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index dc0e8f5..4664a44 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -1677,12 +1677,18 @@
   }
 }
 
-void QuicSession::MaybeSendAddressToken() {
+bool QuicSession::MaybeSendAddressToken() {
   QUICHE_DCHECK(perspective_ == Perspective::IS_SERVER &&
                 connection()->version().HasIetfQuicFrames());
-  std::string address_token = GetCryptoStream()->GetAddressToken();
+  absl::optional<CachedNetworkParameters> cached_network_params;
+  if (add_cached_network_parameters_to_address_token()) {
+    cached_network_params = GenerateCachedNetworkParameters();
+  }
+  std::string address_token = GetCryptoStream()->GetAddressToken(
+      cached_network_params.has_value() ? &cached_network_params.value()
+                                        : nullptr);
   if (address_token.empty()) {
-    return;
+    return false;
   }
   const size_t buf_len = address_token.length() + 1;
   auto buffer = std::make_unique<char[]>(buf_len);
@@ -1692,6 +1698,13 @@
   writer.WriteBytes(address_token.data(), address_token.length());
   control_frame_manager_.WriteOrBufferNewToken(
       absl::string_view(buffer.get(), buf_len));
+  if (add_cached_network_parameters_to_address_token() &&
+      cached_network_params.has_value()) {
+    connection()->OnSendConnectionState(*cached_network_params);
+    QUIC_RELOADABLE_FLAG_COUNT_N(
+        quic_add_cached_network_parameters_to_address_token, 1, 2);
+  }
+  return true;
 }
 
 void QuicSession::DiscardOldDecryptionKey(EncryptionLevel level) {
@@ -2638,7 +2651,7 @@
                                   owns_writer);
 }
 
-bool QuicSession::ValidateToken(absl::string_view token) const {
+bool QuicSession::ValidateToken(absl::string_view token) {
   QUICHE_DCHECK_EQ(perspective_, Perspective::IS_SERVER);
   if (GetQuicFlag(FLAGS_quic_reject_retry_token_in_initial_packet)) {
     return false;
@@ -2647,8 +2660,19 @@
     // Validate the prefix for token received in NEW_TOKEN frame.
     return false;
   }
-  return GetCryptoStream()->ValidateAddressToken(
+  const bool valid = GetCryptoStream()->ValidateAddressToken(
       absl::string_view(token.data() + 1, token.length() - 1));
+  if (add_cached_network_parameters_to_address_token() && valid) {
+    const CachedNetworkParameters* cached_network_params =
+        GetCryptoStream()->PreviousCachedNetworkParams();
+    if (cached_network_params != nullptr &&
+        cached_network_params->timestamp() > 0) {
+      connection()->OnReceiveConnectionState(*cached_network_params);
+      QUIC_RELOADABLE_FLAG_COUNT_N(
+          quic_add_cached_network_parameters_to_address_token, 2, 2);
+    }
+  }
+  return valid;
 }
 
 #undef ENDPOINT  // undef for jumbo builds
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h
index 56ec3b2..cb06d87 100644
--- a/quic/core/quic_session.h
+++ b/quic/core/quic_session.h
@@ -24,6 +24,7 @@
 #include "quic/core/frames/quic_window_update_frame.h"
 #include "quic/core/handshaker_delegate_interface.h"
 #include "quic/core/legacy_quic_stream_id_manager.h"
+#include "quic/core/proto/cached_network_parameters_proto.h"
 #include "quic/core/quic_connection.h"
 #include "quic/core/quic_control_frame_manager.h"
 #include "quic/core/quic_crypto_stream.h"
@@ -171,8 +172,8 @@
       override;
   std::unique_ptr<QuicEncrypter> CreateCurrentOneRttEncrypter() override;
   void BeforeConnectionCloseSent() override {}
-  bool ValidateToken(absl::string_view token) const override;
-  void MaybeSendAddressToken() override;
+  bool ValidateToken(absl::string_view token) override;
+  bool MaybeSendAddressToken() override;
   bool IsKnownServerAddress(
       const QuicSocketAddress& /*address*/) const override {
     return false;
@@ -621,6 +622,11 @@
   // Latched value of flag --quic_tls_server_support_client_cert.
   bool support_client_cert() const { return support_client_cert_; }
 
+  // Get latched flag value.
+  bool add_cached_network_parameters_to_address_token() const {
+    return add_cached_network_parameters_to_address_token_;
+  }
+
   // Try converting all pending streams to normal streams.
   void ProcessAllPendingStreams();
 
@@ -793,6 +799,15 @@
     return ietf_streamid_manager_;
   }
 
+  // Only called at a server session. Generate a CachedNetworkParameters that
+  // can be sent to the client as part of the address token, based on the latest
+  // bandwidth/rtt information. If return absl::nullopt, address token will not
+  // contain the CachedNetworkParameters.
+  virtual absl::optional<CachedNetworkParameters>
+  GenerateCachedNetworkParameters() const {
+    return absl::nullopt;
+  }
+
  private:
   friend class test::QuicSessionPeer;
 
@@ -981,6 +996,10 @@
   // creation of new outgoing bidirectional streams.
   bool liveness_testing_in_progress_;
 
+  const bool add_cached_network_parameters_to_address_token_ =
+      GetQuicReloadableFlag(
+          quic_add_cached_network_parameters_to_address_token);
+
   // Whether BoringSSL randomizes the order of TLS extensions.
   bool permutes_tls_extensions_ = true;
 
diff --git a/quic/core/quic_session_test.cc b/quic/core/quic_session_test.cc
index 81decdc..8d5e477 100644
--- a/quic/core/quic_session_test.cc
+++ b/quic/core/quic_session_test.cc
@@ -141,7 +141,11 @@
   void OnHandshakePacketSent() override {}
   void OnHandshakeDoneReceived() override {}
   void OnNewTokenReceived(absl::string_view /*token*/) override {}
-  std::string GetAddressToken() const override { return ""; }
+  std::string GetAddressToken(
+      const CachedNetworkParameters* /*cached_network_parameters*/)
+      const override {
+    return "";
+  }
   bool ValidateAddressToken(absl::string_view /*token*/) const override {
     return true;
   }
diff --git a/quic/core/tls_server_handshaker.cc b/quic/core/tls_server_handshaker.cc
index 637435c..49bd5a0 100644
--- a/quic/core/tls_server_handshaker.cc
+++ b/quic/core/tls_server_handshaker.cc
@@ -296,11 +296,14 @@
 
 const CachedNetworkParameters*
 TlsServerHandshaker::PreviousCachedNetworkParams() const {
-  return nullptr;
+  return last_received_cached_network_params_.get();
 }
 
 void TlsServerHandshaker::SetPreviousCachedNetworkParams(
-    CachedNetworkParameters /*cached_network_params*/) {}
+    CachedNetworkParameters cached_network_params) {
+  last_received_cached_network_params_ =
+      std::make_unique<CachedNetworkParameters>(cached_network_params);
+}
 
 void TlsServerHandshaker::OnPacketDecrypted(EncryptionLevel level) {
   if (level == ENCRYPTION_HANDSHAKE && state_ < HANDSHAKE_PROCESSED) {
@@ -318,14 +321,17 @@
   QUICHE_DCHECK(false);
 }
 
-std::string TlsServerHandshaker::GetAddressToken() const {
+std::string TlsServerHandshaker::GetAddressToken(
+    const CachedNetworkParameters* cached_network_params) const {
   SourceAddressTokens empty_previous_tokens;
   const QuicConnection* connection = session()->connection();
   return crypto_config_->NewSourceAddressToken(
       crypto_config_->source_address_token_boxer(), empty_previous_tokens,
       connection->effective_peer_address().host(),
       connection->random_generator(), connection->clock()->WallNow(),
-      /*cached_network_params=*/nullptr);
+      session()->add_cached_network_parameters_to_address_token()
+          ? cached_network_params
+          : nullptr);
 }
 
 bool TlsServerHandshaker::ValidateAddressToken(absl::string_view token) const {
@@ -337,15 +343,21 @@
                        << CryptoUtils::HandshakeFailureReasonToString(reason);
     return false;
   }
+  auto cached_network_params = std::make_unique<CachedNetworkParameters>();
   reason = crypto_config_->ValidateSourceAddressTokens(
       tokens, session()->connection()->effective_peer_address().host(),
       session()->connection()->clock()->WallNow(),
-      /*cached_network_params=*/nullptr);
+      session()->add_cached_network_parameters_to_address_token()
+          ? cached_network_params.get()
+          : nullptr);
   if (reason != HANDSHAKE_OK) {
     QUIC_DLOG(WARNING) << "Failed to validate source address token: "
                        << CryptoUtils::HandshakeFailureReasonToString(reason);
     return false;
   }
+  if (session()->add_cached_network_parameters_to_address_token()) {
+    last_received_cached_network_params_ = std::move(cached_network_params);
+  }
   return true;
 }
 
diff --git a/quic/core/tls_server_handshaker.h b/quic/core/tls_server_handshaker.h
index 6a7f358..9a492c5 100644
--- a/quic/core/tls_server_handshaker.h
+++ b/quic/core/tls_server_handshaker.h
@@ -58,7 +58,8 @@
   void OnConnectionClosed(QuicErrorCode error,
                           ConnectionCloseSource source) override;
   void OnHandshakeDoneReceived() override;
-  std::string GetAddressToken() const override;
+  std::string GetAddressToken(
+      const CachedNetworkParameters* cached_network_params) const override;
   bool ValidateAddressToken(absl::string_view token) const override;
   void OnNewTokenReceived(absl::string_view token) override;
   bool ShouldSendExpectCTHeader() const override;
@@ -372,6 +373,9 @@
       crypto_negotiated_params_;
   TlsServerConnection tls_connection_;
   const QuicCryptoServerConfig* crypto_config_;  // Unowned.
+  // The last received CachedNetworkParameters from a validated address token.
+  mutable std::unique_ptr<CachedNetworkParameters>
+      last_received_cached_network_params_;
   const bool restore_connection_context_in_callbacks_ =
       GetQuicReloadableFlag(quic_tls_restore_connection_context_in_callbacks);
 
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h
index 77b7cbb..b064a4a 100644
--- a/quic/test_tools/quic_test_utils.h
+++ b/quic/test_tools/quic_test_utils.h
@@ -500,8 +500,8 @@
   MOCK_METHOD(std::unique_ptr<QuicEncrypter>, CreateCurrentOneRttEncrypter, (),
               (override));
   MOCK_METHOD(void, BeforeConnectionCloseSent, (), (override));
-  MOCK_METHOD(bool, ValidateToken, (absl::string_view), (const, override));
-  MOCK_METHOD(void, MaybeSendAddressToken, (), (override));
+  MOCK_METHOD(bool, ValidateToken, (absl::string_view), (override));
+  MOCK_METHOD(bool, MaybeSendAddressToken, (), (override));
 
   bool IsKnownServerAddress(
       const QuicSocketAddress& /*address*/) const override {
@@ -827,7 +827,11 @@
   void OnHandshakePacketSent() override {}
   void OnHandshakeDoneReceived() override {}
   void OnNewTokenReceived(absl::string_view /*token*/) override {}
-  std::string GetAddressToken() const override { return ""; }
+  std::string GetAddressToken(
+      const CachedNetworkParameters* /*cached_network_parameters*/)
+      const override {
+    return "";
+  }
   bool ValidateAddressToken(absl::string_view /*token*/) const override {
     return true;
   }
diff --git a/quic/test_tools/simulator/quic_endpoint.h b/quic/test_tools/simulator/quic_endpoint.h
index 01d35d3..3e445dc 100644
--- a/quic/test_tools/simulator/quic_endpoint.h
+++ b/quic/test_tools/simulator/quic_endpoint.h
@@ -110,10 +110,8 @@
     return nullptr;
   }
   void BeforeConnectionCloseSent() override {}
-  bool ValidateToken(absl::string_view /*token*/) const override {
-    return true;
-  }
-  void MaybeSendAddressToken() override {}
+  bool ValidateToken(absl::string_view /*token*/) override { return true; }
+  bool MaybeSendAddressToken() override { return false; }
   bool IsKnownServerAddress(
       const QuicSocketAddress& /*address*/) const override {
     return false;