xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/tools/quic_client_base.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "quiche/quic/tools/quic_client_base.h"
6 
7 #include <algorithm>
8 #include <cstdint>
9 #include <memory>
10 #include <utility>
11 
12 #include "quiche/quic/core/crypto/quic_random.h"
13 #include "quiche/quic/core/http/spdy_utils.h"
14 #include "quiche/quic/core/quic_alarm_factory.h"
15 #include "quiche/quic/core/quic_config.h"
16 #include "quiche/quic/core/quic_connection.h"
17 #include "quiche/quic/core/quic_connection_id.h"
18 #include "quiche/quic/core/quic_constants.h"
19 #include "quiche/quic/core/quic_crypto_client_stream.h"
20 #include "quiche/quic/core/quic_error_codes.h"
21 #include "quiche/quic/core/quic_packet_writer.h"
22 #include "quiche/quic/core/quic_path_validator.h"
23 #include "quiche/quic/core/quic_server_id.h"
24 #include "quiche/quic/core/quic_session.h"
25 #include "quiche/quic/core/quic_time.h"
26 #include "quiche/quic/core/quic_types.h"
27 #include "quiche/quic/core/quic_utils.h"
28 #include "quiche/quic/core/quic_versions.h"
29 #include "quiche/quic/platform/api/quic_bug_tracker.h"
30 #include "quiche/quic/platform/api/quic_flags.h"
31 #include "quiche/quic/platform/api/quic_ip_address.h"
32 #include "quiche/quic/platform/api/quic_logging.h"
33 #include "quiche/quic/platform/api/quic_socket_address.h"
34 #include "quiche/common/platform/api/quiche_logging.h"
35 
36 namespace quic {
37 
38 namespace {
39 
40 // Implements the basic feature of a result delegate for path validation for
41 // connection migration. If the validation succeeds, migrate to the alternative
42 // path. Otherwise, stay on the current path.
43 class QuicClientSocketMigrationValidationResultDelegate
44     : public QuicPathValidator::ResultDelegate {
45  public:
QuicClientSocketMigrationValidationResultDelegate(QuicClientBase * client)46   explicit QuicClientSocketMigrationValidationResultDelegate(
47       QuicClientBase* client)
48       : QuicPathValidator::ResultDelegate(), client_(client) {}
49 
50   virtual ~QuicClientSocketMigrationValidationResultDelegate() = default;
51 
52   // QuicPathValidator::ResultDelegate
53   // Overridden to start migration and takes the ownership of the writer in the
54   // context.
OnPathValidationSuccess(std::unique_ptr<QuicPathValidationContext> context,QuicTime)55   void OnPathValidationSuccess(
56       std::unique_ptr<QuicPathValidationContext> context,
57       QuicTime /*start_time*/) override {
58     QUIC_DLOG(INFO) << "Successfully validated path from " << *context
59                     << ". Migrate to it now.";
60     client_->OnSocketMigrationProbingSuccess(std::move(context));
61   }
62 
OnPathValidationFailure(std::unique_ptr<QuicPathValidationContext> context)63   void OnPathValidationFailure(
64       std::unique_ptr<QuicPathValidationContext> context) override {
65     QUIC_LOG(WARNING) << "Fail to validate path " << *context
66                       << ", stop migrating.";
67     client_->OnSocketMigrationProbingFailure();
68     client_->session()->connection()->OnPathValidationFailureAtClient(
69         /*is_multi_port=*/false, *context);
70   }
71 
72  protected:
client()73   QuicClientBase* client() { return client_; }
74 
75  private:
76   QuicClientBase* client_;
77 };
78 
79 class ServerPreferredAddressResultDelegateWithWriter
80     : public QuicClientSocketMigrationValidationResultDelegate {
81  public:
ServerPreferredAddressResultDelegateWithWriter(QuicClientBase * client)82   ServerPreferredAddressResultDelegateWithWriter(QuicClientBase* client)
83       : QuicClientSocketMigrationValidationResultDelegate(client) {}
84 
85   // Overridden to transfer the ownership of the new writer.
OnPathValidationSuccess(std::unique_ptr<QuicPathValidationContext> context,QuicTime)86   void OnPathValidationSuccess(
87       std::unique_ptr<QuicPathValidationContext> context,
88       QuicTime /*start_time*/) override {
89     client()->session()->connection()->OnServerPreferredAddressValidated(
90         *context, false);
91     auto migration_context = std::unique_ptr<PathMigrationContext>(
92         static_cast<PathMigrationContext*>(context.release()));
93     client()->set_writer(migration_context->ReleaseWriter());
94   }
95 };
96 
97 }  // namespace
98 
OnSocketMigrationProbingSuccess(std::unique_ptr<QuicPathValidationContext> context)99 void QuicClientBase::OnSocketMigrationProbingSuccess(
100     std::unique_ptr<QuicPathValidationContext> context) {
101   auto migration_context = std::unique_ptr<PathMigrationContext>(
102       static_cast<PathMigrationContext*>(context.release()));
103   session()->MigratePath(
104       migration_context->self_address(), migration_context->peer_address(),
105       migration_context->WriterToUse(), /*owns_writer=*/false);
106   QUICHE_DCHECK(migration_context->WriterToUse() != nullptr);
107   // Hand the ownership of the alternative writer to the client.
108   set_writer(migration_context->ReleaseWriter());
109 }
110 
111 QuicClientBase::NetworkHelper::~NetworkHelper() = default;
112 
QuicClientBase(const QuicServerId & server_id,const ParsedQuicVersionVector & supported_versions,const QuicConfig & config,QuicConnectionHelperInterface * helper,QuicAlarmFactory * alarm_factory,std::unique_ptr<NetworkHelper> network_helper,std::unique_ptr<ProofVerifier> proof_verifier,std::unique_ptr<SessionCache> session_cache)113 QuicClientBase::QuicClientBase(
114     const QuicServerId& server_id,
115     const ParsedQuicVersionVector& supported_versions, const QuicConfig& config,
116     QuicConnectionHelperInterface* helper, QuicAlarmFactory* alarm_factory,
117     std::unique_ptr<NetworkHelper> network_helper,
118     std::unique_ptr<ProofVerifier> proof_verifier,
119     std::unique_ptr<SessionCache> session_cache)
120     : server_id_(server_id),
121       initialized_(false),
122       local_port_(0),
123       config_(config),
124       crypto_config_(std::move(proof_verifier), std::move(session_cache)),
125       helper_(helper),
126       alarm_factory_(alarm_factory),
127       supported_versions_(supported_versions),
128       initial_max_packet_length_(0),
129       num_sent_client_hellos_(0),
130       connection_error_(QUIC_NO_ERROR),
131       connected_or_attempting_connect_(false),
132       network_helper_(std::move(network_helper)),
133       connection_debug_visitor_(nullptr),
134       server_connection_id_length_(kQuicDefaultConnectionIdLength),
135       client_connection_id_length_(0) {}
136 
137 QuicClientBase::~QuicClientBase() = default;
138 
Initialize()139 bool QuicClientBase::Initialize() {
140   num_sent_client_hellos_ = 0;
141   connection_error_ = QUIC_NO_ERROR;
142   connected_or_attempting_connect_ = false;
143 
144   // If an initial flow control window has not explicitly been set, then use the
145   // same values that Chrome uses.
146   const uint32_t kSessionMaxRecvWindowSize = 15 * 1024 * 1024;  // 15 MB
147   const uint32_t kStreamMaxRecvWindowSize = 6 * 1024 * 1024;    //  6 MB
148   if (config()->GetInitialStreamFlowControlWindowToSend() ==
149       kDefaultFlowControlSendWindow) {
150     config()->SetInitialStreamFlowControlWindowToSend(kStreamMaxRecvWindowSize);
151   }
152   if (config()->GetInitialSessionFlowControlWindowToSend() ==
153       kDefaultFlowControlSendWindow) {
154     config()->SetInitialSessionFlowControlWindowToSend(
155         kSessionMaxRecvWindowSize);
156   }
157 
158   if (!network_helper_->CreateUDPSocketAndBind(server_address_,
159                                                bind_to_address_, local_port_)) {
160     return false;
161   }
162 
163   initialized_ = true;
164   return true;
165 }
166 
Connect()167 bool QuicClientBase::Connect() {
168   // Attempt multiple connects until the maximum number of client hellos have
169   // been sent.
170   int num_attempts = 0;
171   while (!connected() &&
172          num_attempts <= QuicCryptoClientStream::kMaxClientHellos) {
173     StartConnect();
174     while (EncryptionBeingEstablished()) {
175       WaitForEvents();
176     }
177     ParsedQuicVersion version = UnsupportedQuicVersion();
178     if (session() != nullptr && !CanReconnectWithDifferentVersion(&version)) {
179       // We've successfully created a session but we're not connected, and we
180       // cannot reconnect with a different version.  Give up trying.
181       break;
182     }
183     num_attempts++;
184   }
185   if (session() == nullptr) {
186     QUIC_BUG(quic_bug_10906_1) << "Missing session after Connect";
187     return false;
188   }
189   return session()->connection()->connected();
190 }
191 
StartConnect()192 void QuicClientBase::StartConnect() {
193   QUICHE_DCHECK(initialized_);
194   QUICHE_DCHECK(!connected());
195   QuicPacketWriter* writer = network_helper_->CreateQuicPacketWriter();
196   ParsedQuicVersion mutual_version = UnsupportedQuicVersion();
197   const bool can_reconnect_with_different_version =
198       CanReconnectWithDifferentVersion(&mutual_version);
199   if (connected_or_attempting_connect()) {
200     // Clear queued up data if client can not try to connect with a different
201     // version.
202     if (!can_reconnect_with_different_version) {
203       ClearDataToResend();
204     }
205     // Before we destroy the last session and create a new one, gather its stats
206     // and update the stats for the overall connection.
207     UpdateStats();
208   }
209 
210   const quic::ParsedQuicVersionVector client_supported_versions =
211       can_reconnect_with_different_version
212           ? ParsedQuicVersionVector{mutual_version}
213           : supported_versions();
214 
215   session_ = CreateQuicClientSession(
216       client_supported_versions,
217       new QuicConnection(GetNextConnectionId(), QuicSocketAddress(),
218                          server_address(), helper(), alarm_factory(), writer,
219                          /* owns_writer= */ false, Perspective::IS_CLIENT,
220                          client_supported_versions, connection_id_generator_));
221   if (can_reconnect_with_different_version) {
222     session()->set_client_original_supported_versions(supported_versions());
223   }
224   if (connection_debug_visitor_ != nullptr) {
225     session()->connection()->set_debug_visitor(connection_debug_visitor_);
226   }
227   session()->connection()->set_client_connection_id(GetClientConnectionId());
228   if (initial_max_packet_length_ != 0) {
229     session()->connection()->SetMaxPacketLength(initial_max_packet_length_);
230   }
231   // Reset |writer()| after |session()| so that the old writer outlives the old
232   // session.
233   set_writer(writer);
234   InitializeSession();
235   if (can_reconnect_with_different_version) {
236     // This is a reconnect using server supported |mutual_version|.
237     session()->connection()->SetVersionNegotiated();
238   }
239   set_connected_or_attempting_connect(true);
240 }
241 
InitializeSession()242 void QuicClientBase::InitializeSession() { session()->Initialize(); }
243 
Disconnect()244 void QuicClientBase::Disconnect() {
245   QUICHE_DCHECK(initialized_);
246 
247   initialized_ = false;
248   if (connected()) {
249     session()->connection()->CloseConnection(
250         QUIC_PEER_GOING_AWAY, "Client disconnecting",
251         ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
252   }
253 
254   ClearDataToResend();
255 
256   network_helper_->CleanUpAllUDPSockets();
257 }
258 
proof_verifier() const259 ProofVerifier* QuicClientBase::proof_verifier() const {
260   return crypto_config_.proof_verifier();
261 }
262 
EncryptionBeingEstablished()263 bool QuicClientBase::EncryptionBeingEstablished() {
264   return !session_->IsEncryptionEstablished() &&
265          session_->connection()->connected();
266 }
267 
WaitForEvents()268 bool QuicClientBase::WaitForEvents() {
269   if (!connected()) {
270     QUIC_BUG(quic_bug_10906_2)
271         << "Cannot call WaitForEvents on non-connected client";
272     return false;
273   }
274 
275   network_helper_->RunEventLoop();
276 
277   return WaitForEventsPostprocessing();
278 }
279 
WaitForEventsPostprocessing()280 bool QuicClientBase::WaitForEventsPostprocessing() {
281   QUICHE_DCHECK(session() != nullptr);
282   ParsedQuicVersion version = UnsupportedQuicVersion();
283   if (!connected() && CanReconnectWithDifferentVersion(&version)) {
284     QUIC_DLOG(INFO) << "Can reconnect with version: " << version
285                     << ", attempting to reconnect.";
286 
287     Connect();
288   }
289 
290   return HasActiveRequests();
291 }
292 
MigrateSocket(const QuicIpAddress & new_host)293 bool QuicClientBase::MigrateSocket(const QuicIpAddress& new_host) {
294   return MigrateSocketWithSpecifiedPort(new_host, local_port_);
295 }
296 
MigrateSocketWithSpecifiedPort(const QuicIpAddress & new_host,int port)297 bool QuicClientBase::MigrateSocketWithSpecifiedPort(
298     const QuicIpAddress& new_host, int port) {
299   if (!connected()) {
300     QUICHE_DVLOG(1)
301         << "MigrateSocketWithSpecifiedPort failed as connection has closed";
302     return false;
303   }
304 
305   network_helper_->CleanUpAllUDPSockets();
306   std::unique_ptr<QuicPacketWriter> writer =
307       CreateWriterForNewNetwork(new_host, port);
308   if (writer == nullptr) {
309     QUICHE_DVLOG(1)
310         << "MigrateSocketWithSpecifiedPort failed from writer creation";
311     return false;
312   }
313   if (!session()->MigratePath(network_helper_->GetLatestClientAddress(),
314                               session()->connection()->peer_address(),
315                               writer.get(), false)) {
316     QUICHE_DVLOG(1)
317         << "MigrateSocketWithSpecifiedPort failed from session()->MigratePath";
318     return false;
319   }
320   set_writer(writer.release());
321   return true;
322 }
323 
ValidateAndMigrateSocket(const QuicIpAddress & new_host)324 bool QuicClientBase::ValidateAndMigrateSocket(const QuicIpAddress& new_host) {
325   QUICHE_DCHECK(VersionHasIetfQuicFrames(
326       session_->connection()->version().transport_version));
327   if (!connected()) {
328     return false;
329   }
330 
331   std::unique_ptr<QuicPacketWriter> writer =
332       CreateWriterForNewNetwork(new_host, local_port_);
333   if (writer == nullptr) {
334     return false;
335   }
336   // Asynchronously start migration.
337   session_->ValidatePath(
338       std::make_unique<PathMigrationContext>(
339           std::move(writer), network_helper_->GetLatestClientAddress(),
340           session_->peer_address()),
341       std::make_unique<QuicClientSocketMigrationValidationResultDelegate>(this),
342       PathValidationReason::kConnectionMigration);
343   return true;
344 }
345 
CreateWriterForNewNetwork(const QuicIpAddress & new_host,int port)346 std::unique_ptr<QuicPacketWriter> QuicClientBase::CreateWriterForNewNetwork(
347     const QuicIpAddress& new_host, int port) {
348   set_bind_to_address(new_host);
349   set_local_port(port);
350   if (!network_helper_->CreateUDPSocketAndBind(server_address_,
351                                                bind_to_address_, port)) {
352     return nullptr;
353   }
354 
355   QuicPacketWriter* writer = network_helper_->CreateQuicPacketWriter();
356   QUIC_LOG_IF(WARNING, writer == writer_.get())
357       << "The new writer is wrapped in the same wrapper as the old one, thus "
358          "appearing to have the same address as the old one.";
359   return std::unique_ptr<QuicPacketWriter>(writer);
360 }
361 
ChangeEphemeralPort()362 bool QuicClientBase::ChangeEphemeralPort() {
363   auto current_host = network_helper_->GetLatestClientAddress().host();
364   return MigrateSocketWithSpecifiedPort(current_host, 0 /*any ephemeral port*/);
365 }
366 
session()367 QuicSession* QuicClientBase::session() { return session_.get(); }
368 
session() const369 const QuicSession* QuicClientBase::session() const { return session_.get(); }
370 
network_helper()371 QuicClientBase::NetworkHelper* QuicClientBase::network_helper() {
372   return network_helper_.get();
373 }
374 
network_helper() const375 const QuicClientBase::NetworkHelper* QuicClientBase::network_helper() const {
376   return network_helper_.get();
377 }
378 
WaitForStreamToClose(QuicStreamId id)379 void QuicClientBase::WaitForStreamToClose(QuicStreamId id) {
380   if (!connected()) {
381     QUIC_BUG(quic_bug_10906_3)
382         << "Cannot WaitForStreamToClose on non-connected client";
383     return;
384   }
385 
386   while (connected() && !session_->IsClosedStream(id)) {
387     WaitForEvents();
388   }
389 }
390 
WaitForOneRttKeysAvailable()391 bool QuicClientBase::WaitForOneRttKeysAvailable() {
392   if (!connected()) {
393     QUIC_BUG(quic_bug_10906_4)
394         << "Cannot WaitForOneRttKeysAvailable on non-connected client";
395     return false;
396   }
397 
398   while (connected() && !session_->OneRttKeysAvailable()) {
399     WaitForEvents();
400   }
401 
402   // If the handshake fails due to a timeout, the connection will be closed.
403   QUIC_LOG_IF(ERROR, !connected()) << "Handshake with server failed.";
404   return connected();
405 }
406 
WaitForHandshakeConfirmed()407 bool QuicClientBase::WaitForHandshakeConfirmed() {
408   if (!session_->connection()->version().UsesTls()) {
409     return WaitForOneRttKeysAvailable();
410   }
411   // Otherwise, wait for receipt of HANDSHAKE_DONE frame.
412   while (connected() && session_->GetHandshakeState() < HANDSHAKE_CONFIRMED) {
413     WaitForEvents();
414   }
415 
416   // If the handshake fails due to a timeout, the connection will be closed.
417   QUIC_LOG_IF(ERROR, !connected()) << "Handshake with server failed.";
418   return connected();
419 }
420 
connected() const421 bool QuicClientBase::connected() const {
422   return session_.get() && session_->connection() &&
423          session_->connection()->connected();
424 }
425 
goaway_received() const426 bool QuicClientBase::goaway_received() const {
427   return session_ != nullptr && session_->transport_goaway_received();
428 }
429 
GetNumSentClientHellos()430 int QuicClientBase::GetNumSentClientHellos() {
431   // If we are not actively attempting to connect, the session object
432   // corresponds to the previous connection and should not be used.
433   const int current_session_hellos = !connected_or_attempting_connect_
434                                          ? 0
435                                          : GetNumSentClientHellosFromSession();
436   return num_sent_client_hellos_ + current_session_hellos;
437 }
438 
UpdateStats()439 void QuicClientBase::UpdateStats() {
440   num_sent_client_hellos_ += GetNumSentClientHellosFromSession();
441 }
442 
GetNumReceivedServerConfigUpdates()443 int QuicClientBase::GetNumReceivedServerConfigUpdates() {
444   // If we are not actively attempting to connect, the session object
445   // corresponds to the previous connection and should not be used.
446   return !connected_or_attempting_connect_
447              ? 0
448              : GetNumReceivedServerConfigUpdatesFromSession();
449 }
450 
connection_error() const451 QuicErrorCode QuicClientBase::connection_error() const {
452   // Return the high-level error if there was one.  Otherwise, return the
453   // connection error from the last session.
454   if (connection_error_ != QUIC_NO_ERROR) {
455     return connection_error_;
456   }
457   if (session_ == nullptr) {
458     return QUIC_NO_ERROR;
459   }
460   return session_->error();
461 }
462 
GetNextConnectionId()463 QuicConnectionId QuicClientBase::GetNextConnectionId() {
464   if (server_connection_id_override_.has_value()) {
465     return *server_connection_id_override_;
466   }
467   return GenerateNewConnectionId();
468 }
469 
GenerateNewConnectionId()470 QuicConnectionId QuicClientBase::GenerateNewConnectionId() {
471   return QuicUtils::CreateRandomConnectionId(server_connection_id_length_);
472 }
473 
GetClientConnectionId()474 QuicConnectionId QuicClientBase::GetClientConnectionId() {
475   return QuicUtils::CreateRandomConnectionId(client_connection_id_length_);
476 }
477 
CanReconnectWithDifferentVersion(ParsedQuicVersion * version) const478 bool QuicClientBase::CanReconnectWithDifferentVersion(
479     ParsedQuicVersion* version) const {
480   if (session_ == nullptr || session_->connection() == nullptr ||
481       session_->error() != QUIC_INVALID_VERSION) {
482     return false;
483   }
484 
485   const auto& server_supported_versions =
486       session_->connection()->server_supported_versions();
487   if (server_supported_versions.empty()) {
488     return false;
489   }
490 
491   for (const auto& client_version : supported_versions_) {
492     if (std::find(server_supported_versions.begin(),
493                   server_supported_versions.end(),
494                   client_version) != server_supported_versions.end()) {
495       *version = client_version;
496       return true;
497     }
498   }
499   return false;
500 }
501 
HasPendingPathValidation()502 bool QuicClientBase::HasPendingPathValidation() {
503   return session()->HasPendingPathValidation();
504 }
505 
506 class ValidationResultDelegate : public QuicPathValidator::ResultDelegate {
507  public:
ValidationResultDelegate(QuicClientBase * client)508   ValidationResultDelegate(QuicClientBase* client)
509       : QuicPathValidator::ResultDelegate(), client_(client) {}
510 
OnPathValidationSuccess(std::unique_ptr<QuicPathValidationContext> context,QuicTime start_time)511   void OnPathValidationSuccess(
512       std::unique_ptr<QuicPathValidationContext> context,
513       QuicTime start_time) override {
514     QUIC_DLOG(INFO) << "Successfully validated path from " << *context
515                     << ", validation started at " << start_time;
516     client_->AddValidatedPath(std::move(context));
517   }
OnPathValidationFailure(std::unique_ptr<QuicPathValidationContext> context)518   void OnPathValidationFailure(
519       std::unique_ptr<QuicPathValidationContext> context) override {
520     QUIC_LOG(WARNING) << "Fail to validate path " << *context
521                       << ", stop migrating.";
522     client_->session()->connection()->OnPathValidationFailureAtClient(
523         /*is_multi_port=*/false, *context);
524   }
525 
526  private:
527   QuicClientBase* client_;
528 };
529 
ValidateNewNetwork(const QuicIpAddress & host)530 void QuicClientBase::ValidateNewNetwork(const QuicIpAddress& host) {
531   std::unique_ptr<QuicPacketWriter> writer =
532       CreateWriterForNewNetwork(host, local_port_);
533   auto result_delegate = std::make_unique<ValidationResultDelegate>(this);
534   if (writer == nullptr) {
535     result_delegate->OnPathValidationFailure(
536         std::make_unique<PathMigrationContext>(
537             nullptr, network_helper_->GetLatestClientAddress(),
538             session_->peer_address()));
539     return;
540   }
541   session()->ValidatePath(
542       std::make_unique<PathMigrationContext>(
543           std::move(writer), network_helper_->GetLatestClientAddress(),
544           session_->peer_address()),
545       std::move(result_delegate), PathValidationReason::kConnectionMigration);
546 }
547 
OnServerPreferredAddressAvailable(const QuicSocketAddress & server_preferred_address)548 void QuicClientBase::OnServerPreferredAddressAvailable(
549     const QuicSocketAddress& server_preferred_address) {
550   const auto self_address = session_->self_address();
551   if (network_helper_ == nullptr ||
552       !network_helper_->CreateUDPSocketAndBind(server_preferred_address,
553                                                self_address.host(), 0)) {
554     return;
555   }
556   QuicPacketWriter* writer = network_helper_->CreateQuicPacketWriter();
557   if (writer == nullptr) {
558     return;
559   }
560   session()->ValidatePath(
561       std::make_unique<PathMigrationContext>(
562           std::unique_ptr<QuicPacketWriter>(writer),
563           network_helper_->GetLatestClientAddress(), server_preferred_address),
564       std::make_unique<ServerPreferredAddressResultDelegateWithWriter>(this),
565       PathValidationReason::kServerPreferredAddressMigration);
566 }
567 
OnPathDegrading()568 void QuicClientBase::OnPathDegrading() {
569   if (!allow_port_migration_ ||
570       session_->GetHandshakeState() != HANDSHAKE_CONFIRMED ||
571       session_->HasPendingPathValidation() ||
572       session_->connection()->multi_port_stats() != nullptr ||
573       config_.DisableConnectionMigration()) {
574     return;
575   }
576   const auto self_address = session_->self_address();
577   if (network_helper_ == nullptr ||
578       !network_helper_->CreateUDPSocketAndBind(session_->peer_address(),
579                                                self_address.host(), 0)) {
580     return;
581   }
582   QuicPacketWriter* writer = network_helper_->CreateQuicPacketWriter();
583   if (writer == nullptr) {
584     return;
585   }
586   session()->ValidatePath(
587       std::make_unique<PathMigrationContext>(
588           std::unique_ptr<QuicPacketWriter>(writer),
589           network_helper_->GetLatestClientAddress(), session_->peer_address()),
590       std::make_unique<QuicClientSocketMigrationValidationResultDelegate>(this),
591       PathValidationReason::kPortMigration);
592   if (!session()->HasPendingPathValidation()) {
593     QUIC_CODE_COUNT(fail_to_probe_new_path_after_current_one_degraded);
594   }
595 }
596 
597 }  // namespace quic
598