Line data Source code
1 : #include "source/common/quic/envoy_quic_dispatcher.h"
2 :
3 : #include <openssl/crypto.h>
4 :
5 : #include <functional>
6 : #include <list>
7 :
8 : #include "envoy/common/optref.h"
9 :
10 : #include "source/common/common/safe_memcpy.h"
11 : #include "source/common/quic/envoy_quic_server_connection.h"
12 : #include "source/common/quic/envoy_quic_utils.h"
13 :
14 : namespace Envoy {
15 : namespace Quic {
16 :
17 : namespace {
18 :
19 0 : QuicDispatcherStats generateStats(Stats::Scope& store) {
20 0 : return {QUIC_DISPATCHER_STATS(POOL_COUNTER_PREFIX(store, "quic.dispatcher"))};
21 0 : }
22 :
23 : } // namespace
24 :
25 : EnvoyQuicTimeWaitListManager::EnvoyQuicTimeWaitListManager(quic::QuicPacketWriter* writer,
26 : Visitor* visitor,
27 : const quic::QuicClock* clock,
28 : quic::QuicAlarmFactory* alarm_factory,
29 : QuicDispatcherStats& stats)
30 0 : : quic::QuicTimeWaitListManager(writer, visitor, clock, alarm_factory), stats_(stats) {}
31 :
32 : void EnvoyQuicTimeWaitListManager::SendPublicReset(
33 : const quic::QuicSocketAddress& self_address, const quic::QuicSocketAddress& peer_address,
34 : quic::QuicConnectionId connection_id, bool ietf_quic, size_t received_packet_length,
35 0 : std::unique_ptr<quic::QuicPerPacketContext> packet_context) {
36 0 : ENVOY_LOG_EVERY_POW_2_MISC(info, "Sending Stateless Reset on connection {}",
37 0 : connection_id.ToString());
38 0 : stats_.stateless_reset_packets_sent_.inc();
39 0 : quic::QuicTimeWaitListManager::SendPublicReset(self_address, peer_address, connection_id,
40 0 : ietf_quic, received_packet_length,
41 0 : std::move(packet_context));
42 0 : }
43 :
44 : EnvoyQuicDispatcher::EnvoyQuicDispatcher(
45 : const quic::QuicCryptoServerConfig* crypto_config, const quic::QuicConfig& quic_config,
46 : quic::QuicVersionManager* version_manager,
47 : std::unique_ptr<quic::QuicConnectionHelperInterface> helper,
48 : std::unique_ptr<quic::QuicAlarmFactory> alarm_factory,
49 : uint8_t expected_server_connection_id_length, Network::ConnectionHandler& connection_handler,
50 : Network::ListenerConfig& listener_config, Server::ListenerStats& listener_stats,
51 : Server::PerHandlerListenerStats& per_worker_stats, Event::Dispatcher& dispatcher,
52 : Network::Socket& listen_socket, QuicStatNames& quic_stat_names,
53 : EnvoyQuicCryptoServerStreamFactoryInterface& crypto_server_stream_factory,
54 : quic::ConnectionIdGeneratorInterface& generator)
55 : : quic::QuicDispatcher(&quic_config, crypto_config, version_manager, std::move(helper),
56 : std::make_unique<EnvoyQuicCryptoServerStreamHelper>(),
57 : std::move(alarm_factory), expected_server_connection_id_length,
58 : generator),
59 : connection_handler_(connection_handler), listener_config_(&listener_config),
60 : listener_stats_(listener_stats), per_worker_stats_(per_worker_stats), dispatcher_(dispatcher),
61 : listen_socket_(listen_socket), quic_stat_names_(quic_stat_names),
62 : crypto_server_stream_factory_(crypto_server_stream_factory),
63 : quic_stats_(generateStats(listener_config.listenerScope())),
64 : connection_stats_({QUIC_CONNECTION_STATS(
65 0 : POOL_COUNTER_PREFIX(listener_config.listenerScope(), "quic.connection"))}) {}
66 :
67 : void EnvoyQuicDispatcher::OnConnectionClosed(quic::QuicConnectionId connection_id,
68 : quic::QuicErrorCode error,
69 : const std::string& error_details,
70 0 : quic::ConnectionCloseSource source) {
71 0 : quic::QuicDispatcher::OnConnectionClosed(connection_id, error, error_details, source);
72 0 : listener_stats_.downstream_cx_active_.dec();
73 0 : per_worker_stats_.downstream_cx_active_.dec();
74 0 : connection_handler_.decNumConnections();
75 0 : quic_stat_names_.chargeQuicConnectionCloseStats(listener_config_->listenerScope(), error, source,
76 0 : /*is_upstream*/ false);
77 0 : }
78 :
79 0 : quic::QuicTimeWaitListManager* EnvoyQuicDispatcher::CreateQuicTimeWaitListManager() {
80 0 : return new EnvoyQuicTimeWaitListManager(writer(), this, helper()->GetClock(), alarm_factory(),
81 0 : quic_stats_);
82 0 : }
83 :
84 : std::unique_ptr<quic::QuicSession> EnvoyQuicDispatcher::CreateQuicSession(
85 : quic::QuicConnectionId server_connection_id, const quic::QuicSocketAddress& self_address,
86 : const quic::QuicSocketAddress& peer_address, absl::string_view /*alpn*/,
87 : const quic::ParsedQuicVersion& version, const quic::ParsedClientHello& parsed_chlo,
88 0 : quic::ConnectionIdGeneratorInterface& connection_id_generator) {
89 0 : quic::QuicConfig quic_config = config();
90 : // TODO(danzh) use passed-in ALPN instead of hard-coded h3 after proof source interfaces takes in
91 : // ALPN.
92 0 : Network::ConnectionSocketPtr connection_socket = createServerConnectionSocket(
93 0 : listen_socket_.ioHandle(), self_address, peer_address, std::string(parsed_chlo.sni), "h3");
94 0 : auto stream_info = std::make_unique<StreamInfo::StreamInfoImpl>(
95 0 : dispatcher_.timeSource(), connection_socket->connectionInfoProviderSharedPtr());
96 :
97 0 : auto listener_filter_manager = std::make_unique<QuicListenerFilterManagerImpl>(
98 0 : dispatcher_, *connection_socket, *stream_info);
99 0 : const bool success = listener_config_->filterChainFactory().createQuicListenerFilterChain(
100 0 : *listener_filter_manager);
101 0 : const Network::FilterChain* filter_chain = nullptr;
102 0 : if (success) {
103 0 : listener_filter_manager->startFilterChain();
104 : // Quic listener filters are not supposed to pause the filter chain iteration unless it closes
105 : // the connection socket. If any listener filter have closed the socket, do not get a network
106 : // filter chain. Thus early fail the connection.
107 0 : if (connection_socket->ioHandle().isOpen()) {
108 0 : for (auto address_family : {quiche::IpAddressFamily::IP_V4, quiche::IpAddressFamily::IP_V6}) {
109 0 : absl::optional<quic::QuicSocketAddress> address =
110 0 : quic_config.GetPreferredAddressToSend(address_family);
111 0 : if (address.has_value() && address->IsInitialized() &&
112 0 : !listener_filter_manager->shouldAdvertiseServerPreferredAddress(address.value())) {
113 0 : quic_config.ClearAlternateServerAddressToSend(address_family);
114 0 : }
115 0 : }
116 0 : filter_chain =
117 0 : listener_config_->filterChainManager().findFilterChain(*connection_socket, *stream_info);
118 0 : }
119 0 : }
120 :
121 0 : auto quic_connection = std::make_unique<EnvoyQuicServerConnection>(
122 0 : server_connection_id, self_address, peer_address, *helper(), *alarm_factory(), writer(),
123 0 : /*owns_writer=*/false, quic::ParsedQuicVersionVector{version}, std::move(connection_socket),
124 0 : connection_id_generator, std::move(listener_filter_manager));
125 0 : auto quic_session = std::make_unique<EnvoyQuicServerSession>(
126 0 : quic_config, quic::ParsedQuicVersionVector{version}, std::move(quic_connection), this,
127 0 : session_helper(), crypto_config(), compressed_certs_cache(), dispatcher_,
128 0 : listener_config_->perConnectionBufferLimitBytes(), quic_stat_names_,
129 0 : listener_config_->listenerScope(), crypto_server_stream_factory_, std::move(stream_info),
130 0 : connection_stats_);
131 0 : if (filter_chain != nullptr) {
132 : // Setup filter chain before Initialize().
133 0 : const bool has_filter_initialized =
134 0 : listener_config_->filterChainFactory().createNetworkFilterChain(
135 0 : *quic_session, filter_chain->networkFilterFactories());
136 : // QUIC listener must have HCM filter configured. Otherwise, stream creation later will fail.
137 0 : ASSERT(has_filter_initialized);
138 0 : connections_by_filter_chain_[filter_chain].push_front(
139 0 : std::reference_wrapper<Network::Connection>(*quic_session));
140 0 : quic_session->storeConnectionMapPosition(connections_by_filter_chain_, *filter_chain,
141 0 : connections_by_filter_chain_[filter_chain].begin());
142 0 : } else {
143 0 : quic_session->close(Network::ConnectionCloseType::FlushWrite, "no filter chain found");
144 0 : }
145 0 : quic_session->Initialize();
146 0 : connection_handler_.incNumConnections();
147 0 : listener_stats_.downstream_cx_active_.inc();
148 0 : listener_stats_.downstream_cx_total_.inc();
149 0 : per_worker_stats_.downstream_cx_active_.inc();
150 0 : per_worker_stats_.downstream_cx_total_.inc();
151 0 : return quic_session;
152 0 : }
153 :
154 : bool EnvoyQuicDispatcher::processPacket(const quic::QuicSocketAddress& self_address,
155 : const quic::QuicSocketAddress& peer_address,
156 0 : const quic::QuicReceivedPacket& packet) {
157 : // Assume a packet was dispatched successfully, if OnFailedToDispatchPacket is not called.
158 0 : current_packet_dispatch_success_ = true;
159 0 : ProcessPacket(self_address, peer_address, packet);
160 0 : return current_packet_dispatch_success_;
161 0 : }
162 :
163 : bool EnvoyQuicDispatcher::OnFailedToDispatchPacket(
164 0 : const quic::ReceivedPacketInfo& received_packet_info) {
165 0 : if (!accept_new_connections()) {
166 0 : current_packet_dispatch_success_ = false;
167 0 : return true;
168 0 : }
169 0 : return quic::QuicDispatcher::OnFailedToDispatchPacket(received_packet_info);
170 0 : }
171 :
172 : void EnvoyQuicDispatcher::closeConnectionsWithFilterChain(
173 0 : const Network::FilterChain* filter_chain) {
174 0 : auto iter = connections_by_filter_chain_.find(filter_chain);
175 0 : if (iter != connections_by_filter_chain_.end()) {
176 0 : std::list<std::reference_wrapper<Network::Connection>>& connections = iter->second;
177 : // Retain the number of connections in the list early because closing the connection will change
178 : // the size.
179 0 : const size_t num_connections = connections.size();
180 0 : bool delete_sessions_immediately = false;
181 0 : for (size_t i = 0; i < num_connections; ++i) {
182 0 : Network::Connection& connection = connections.front().get();
183 : // This will remove the connection from the list. And the last removal will remove connections
184 : // from the map as well.
185 0 : connection.close(Network::ConnectionCloseType::NoFlush);
186 0 : if (!delete_sessions_immediately &&
187 0 : dynamic_cast<QuicFilterManagerConnectionImpl&>(connection).fix_quic_lifetime_issues()) {
188 : // If `envoy.reloadable_features.quic_fix_filter_manager_uaf` is true, the closed sessions
189 : // need to be deleted right away to consistently handle quic lifetimes. Because upon
190 : // returning the filter chain configs will be destroyed, and no longer safe to be accessed.
191 : // If any filters access those configs during destruction, it'll be use-after-free
192 0 : delete_sessions_immediately = true;
193 0 : }
194 0 : }
195 0 : ASSERT(connections_by_filter_chain_.find(filter_chain) == connections_by_filter_chain_.end());
196 0 : if (delete_sessions_immediately) {
197 : // Explicitly destroy closed sessions in the current call stack.
198 0 : DeleteSessions();
199 0 : }
200 0 : }
201 0 : }
202 :
203 0 : void EnvoyQuicDispatcher::updateListenerConfig(Network::ListenerConfig& new_listener_config) {
204 0 : listener_config_ = &new_listener_config;
205 0 : }
206 :
207 : } // namespace Quic
208 : } // namespace Envoy
|