1
#include "source/common/quic/client_connection_factory_impl.h"
2

            
3
#include <exception>
4

            
5
#include "envoy/extensions/quic/client_writer_factory/v3/default_client_writer.pb.h"
6
#include "envoy/registry/registry.h"
7

            
8
#include "source/common/config/utility.h"
9
#include "source/common/network/udp_packet_writer_handler_impl.h"
10
#include "source/common/quic/envoy_quic_client_packet_writer_factory.h"
11
#include "source/common/quic/envoy_quic_packet_writer.h"
12
#include "source/common/quic/envoy_quic_utils.h"
13
#include "source/common/quic/quic_transport_socket_factory.h"
14
#include "source/common/runtime/runtime_features.h"
15

            
16
namespace Envoy {
17
namespace Quic {
18

            
19
PersistentQuicInfoImpl::PersistentQuicInfoImpl(Event::Dispatcher& dispatcher, uint32_t buffer_limit,
20
                                               quic::QuicByteCount max_packet_length)
21
2014
    : conn_helper_(dispatcher), alarm_factory_(dispatcher, *conn_helper_.GetClock()),
22
2014
      buffer_limit_(buffer_limit), max_packet_length_(max_packet_length) {
23
2014
  quiche::FlagRegistry::getInstance();
24
  // Allow migration to server preferred address by default.
25
2014
  migration_config_.allow_server_preferred_address = true;
26
2014
  migration_config_.max_port_migrations_per_session = kMaxNumSocketSwitches;
27
2014
  migration_config_.migrate_session_on_network_change = false;
28
2014
}
29

            
30
std::unique_ptr<PersistentQuicInfoImpl>
31
createPersistentQuicInfoForCluster(Event::Dispatcher& dispatcher,
32
                                   const Upstream::ClusterInfo& cluster,
33
2013
                                   Server::Configuration::ServerFactoryContext& server_context) {
34
2013
  auto quic_info = std::make_unique<Quic::PersistentQuicInfoImpl>(
35
2013
      dispatcher, cluster.perConnectionBufferLimitBytes());
36
2013
  const envoy::config::core::v3::QuicProtocolOptions& quic_config =
37
2013
      cluster.httpProtocolOptions().http3Options().quic_protocol_options();
38
2013
  Quic::convertQuicConfig(quic_config, quic_info->quic_config_);
39
2013
  quic::QuicTime::Delta crypto_timeout =
40
2013
      quic::QuicTime::Delta::FromMilliseconds(cluster.connectTimeout().count());
41

            
42
2013
  quic_info->quic_config_.set_max_time_before_crypto_handshake(crypto_timeout);
43
2013
  if (quic_info->quic_config_.max_time_before_crypto_handshake() <
44
2013
      quic_info->quic_config_.max_idle_time_before_crypto_handshake()) {
45
2
    quic_info->quic_config_.set_max_idle_time_before_crypto_handshake(crypto_timeout);
46
2
  }
47
2013
  quic_info->max_packet_length_ =
48
2013
      PROTOBUF_GET_WRAPPED_OR_DEFAULT(quic_config, max_packet_length, 0);
49

            
50
2013
  uint32_t num_timeouts_to_trigger_port_migration =
51
2013
      PROTOBUF_GET_WRAPPED_OR_DEFAULT(quic_config, num_timeouts_to_trigger_port_migration, 0);
52
2013
  quic_info->migration_config_.allow_port_migration = (num_timeouts_to_trigger_port_migration > 0);
53
2013
  if (quic_config.has_connection_migration()) {
54
2
    quic_info->migration_config_.migrate_session_on_network_change = true;
55
2
    quic_info->migration_config_.migrate_session_early = true;
56
2
    if (quic_config.connection_migration().has_migrate_idle_connections()) {
57
1
      quic_info->migration_config_.migrate_idle_session = true;
58
1
      quic_info->migration_config_.idle_migration_period =
59
1
          quic::QuicTime::Delta::FromSeconds(PROTOBUF_GET_SECONDS_OR_DEFAULT(
60
1
              quic_config.connection_migration().migrate_idle_connections(),
61
1
              max_idle_time_before_migration, 30));
62
1
    } else {
63
1
      quic_info->migration_config_.migrate_idle_session = false;
64
1
    }
65
2
    quic_info->migration_config_.max_time_on_non_default_network =
66
2
        quic::QuicTime::Delta::FromSeconds(PROTOBUF_GET_SECONDS_OR_DEFAULT(
67
2
            quic_config.connection_migration(), max_time_on_non_default_network, 128));
68
2
  }
69
2013
  envoy::config::core::v3::TypedExtensionConfig client_writer_config;
70
2013
  if (quic_config.has_client_packet_writer()) {
71
    client_writer_config = quic_config.client_packet_writer();
72
2013
  } else {
73
2013
    client_writer_config.set_name("envoy.quic.packet_writer.default");
74
2013
    envoy::extensions::quic::client_writer_factory::v3::DefaultClientWriter empty_default_config;
75
2013
    client_writer_config.mutable_typed_config()->PackFrom(empty_default_config);
76
2013
  }
77
2013
  auto& factory = Envoy::Config::Utility::getAndCheckFactory<QuicClientPacketWriterConfigFactory>(
78
2013
      client_writer_config);
79
2013
  ProtobufTypes::MessagePtr message = Config::Utility::translateToFactoryConfig(
80
2013
      client_writer_config, server_context.messageValidationVisitor(), factory);
81
2013
  quic_info->writer_factory_ = factory.createQuicClientPacketWriterFactory(
82
2013
      *message, dispatcher, server_context.messageValidationVisitor());
83
2013
  return quic_info;
84
2013
}
85

            
86
std::unique_ptr<Network::ClientConnection> createQuicNetworkConnection(
87
    Http::PersistentQuicInfo& info, std::shared_ptr<quic::QuicCryptoClientConfig> crypto_config,
88
    const quic::QuicServerId& server_id, Event::Dispatcher& dispatcher,
89
    Network::Address::InstanceConstSharedPtr server_addr,
90
    Network::Address::InstanceConstSharedPtr local_addr, QuicStatNames& quic_stat_names,
91
    OptRef<Http::HttpServerPropertiesCache> rtt_cache, Stats::Scope& scope,
92
    const Network::ConnectionSocket::OptionsSharedPtr& options,
93
    const Network::TransportSocketOptionsConstSharedPtr& transport_socket_options,
94
    quic::ConnectionIdGeneratorInterface& generator,
95
    Network::UpstreamTransportSocketFactory& transport_socket_factory,
96
2807
    EnvoyQuicNetworkObserverRegistry* network_observer_registry) {
97
  // TODO: Quic should take into account the set_local_interface_name_on_upstream_connections config
98
  // and call maybeSetInterfaceName based on that upon acquiring a local socket.
99
  // Similar to what is done in ClientConnectionImpl::onConnected().
100
2807
  ASSERT(crypto_config != nullptr);
101
2807
  PersistentQuicInfoImpl* info_impl = reinterpret_cast<PersistentQuicInfoImpl*>(&info);
102
2807
  quic::ParsedQuicVersionVector quic_versions = quic::CurrentSupportedHttp3Versions();
103
2807
  ASSERT(!quic_versions.empty());
104
2807
  ASSERT(info_impl->writer_factory_ != nullptr);
105
2807
  quic::QuicNetworkHandle current_network = quic::kInvalidNetworkHandle;
106
2807
  if (network_observer_registry != nullptr) {
107
7
    current_network = network_observer_registry->getDefaultNetwork();
108
7
    if (current_network == quic::kInvalidNetworkHandle) {
109
      // In case the platform default network is invalid, pick another working network.
110
7
      current_network =
111
7
          network_observer_registry->getAlternativeNetwork(quic::kInvalidNetworkHandle);
112
7
    }
113
    // If current_network is still invalid at this point, the created socket
114
    // will likely not work. Let the connection figure it out and fail by
115
    // itself.
116
7
  }
117
2807
  QuicClientPacketWriterFactory::CreationResult creation_result =
118
2807
      info_impl->writer_factory_->createSocketAndQuicPacketWriter(server_addr, current_network,
119
2807
                                                                  local_addr, options);
120
2807
  const bool use_migration_in_quiche =
121
2807
      Runtime::runtimeFeatureEnabled("envoy.reloadable_features.use_migration_in_quiche");
122
2807
  quic::QuicForceBlockablePacketWriter* wrapper = nullptr;
123
2807
  if (use_migration_in_quiche) {
124
2807
    wrapper = new quic::QuicForceBlockablePacketWriter();
125
    // Owns the inner writer.
126
2807
    wrapper->set_writer(creation_result.writer_.release());
127
2807
  }
128
2807
  auto connection = std::make_unique<EnvoyQuicClientConnection>(
129
2807
      quic::QuicUtils::CreateRandomConnectionId(), info_impl->conn_helper_,
130
2807
      info_impl->alarm_factory_,
131
2807
      (use_migration_in_quiche
132
2807
           ? wrapper
133
2807
           : static_cast<quic::QuicPacketWriter*>(creation_result.writer_.release())),
134
2807
      /*owns_writer=*/true, quic_versions, dispatcher, std::move(creation_result.socket_),
135
2807
      generator);
136
  // Override the max packet length of the QUIC connection if the option value is not 0.
137
2807
  if (info_impl->max_packet_length_ > 0) {
138
    connection->SetMaxPacketLength(info_impl->max_packet_length_);
139
  }
140

            
141
2807
  EnvoyQuicClientConnection::EnvoyQuicMigrationHelper* migration_helper = nullptr;
142
2807
  quic::QuicConnectionMigrationConfig migration_config = info_impl->migration_config_;
143
2807
  if (use_migration_in_quiche) {
144
2807
    migration_helper = &connection->getOrCreateMigrationHelper(
145
2807
        *info_impl->writer_factory_, current_network,
146
2807
        makeOptRefFromPtr<EnvoyQuicNetworkObserverRegistry>(network_observer_registry));
147
2807
  } else {
148
    // The connection needs to be aware of the writer factory so it can create migration probing
149
    // sockets.
150
    connection->setWriterFactory(*info_impl->writer_factory_);
151
    // Disable all kinds of migration in QUICHE as the session won't be setup to handle it.
152
    migration_config = quicConnectionMigrationDisableAllConfig();
153
  }
154
  // TODO (danzh) move this temporary config and initial RTT configuration to h3 pool.
155
2807
  quic::QuicConfig config = info_impl->quic_config_;
156
  // Update config with latest srtt, if available.
157
2807
  if (rtt_cache.has_value()) {
158
29
    Http::HttpServerPropertiesCache::Origin origin("https", server_id.host(), server_id.port());
159
29
    std::chrono::microseconds rtt = rtt_cache.value().get().getSrtt(
160
29
        origin, Runtime::runtimeFeatureEnabled(
161
29
                    "envoy.reloadable_features.use_canonical_suffix_for_initial_rtt_estimate"));
162
29
    if (rtt.count() != 0) {
163
5
      config.SetInitialRoundTripTimeUsToSend(rtt.count());
164
5
    }
165
29
  }
166

            
167
  // QUICHE client session always use the 1st version to start handshake.
168
2807
  auto session = std::make_unique<EnvoyQuicClientSession>(
169
2807
      config, quic_versions, std::move(connection), wrapper, migration_helper, migration_config,
170
2807
      server_id, std::move(crypto_config), dispatcher, info_impl->buffer_limit_,
171
2807
      info_impl->crypto_stream_factory_, quic_stat_names, rtt_cache, scope,
172
2807
      transport_socket_options, transport_socket_factory);
173
2807
  if (network_observer_registry != nullptr) {
174
7
    session->registerNetworkObserver(*network_observer_registry);
175
7
  }
176
2807
  return session;
177
2807
}
178

            
179
} // namespace Quic
180
} // namespace Envoy