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

            
3
#include <openssl/crypto.h>
4

            
5
#include <functional>
6
#include <list>
7
#include <memory>
8

            
9
#include "envoy/common/optref.h"
10
#include "envoy/network/connection.h"
11

            
12
#include "source/common/common/safe_memcpy.h"
13
#include "source/common/http/session_idle_list.h"
14
#include "source/common/quic/envoy_quic_connection_debug_visitor_factory_interface.h"
15
#include "source/common/quic/envoy_quic_server_connection.h"
16
#include "source/common/quic/envoy_quic_server_session.h"
17
#include "source/common/quic/envoy_quic_utils.h"
18

            
19
#include "quiche/quic/core/crypto/quic_crypto_server_config.h"
20
#include "quiche/quic/core/quic_dispatcher.h"
21

            
22
namespace Envoy {
23
namespace Quic {
24

            
25
namespace {
26

            
27
2203
QuicDispatcherStats generateStats(Stats::Scope& store) {
28
2203
  return {QUIC_DISPATCHER_STATS(POOL_COUNTER_PREFIX(store, "quic.dispatcher"))};
29
2203
}
30

            
31
} // namespace
32

            
33
EnvoyQuicTimeWaitListManager::EnvoyQuicTimeWaitListManager(quic::QuicPacketWriter* writer,
34
                                                           Visitor* visitor,
35
                                                           const quic::QuicClock* clock,
36
                                                           quic::QuicAlarmFactory* alarm_factory,
37
                                                           QuicDispatcherStats& stats)
38
2203
    : quic::QuicTimeWaitListManager(writer, visitor, clock, alarm_factory), stats_(stats) {}
39

            
40
void EnvoyQuicTimeWaitListManager::SendPublicReset(const quic::QuicSocketAddress& self_address,
41
                                                   const quic::QuicSocketAddress& peer_address,
42
                                                   quic::QuicConnectionId connection_id,
43
23
                                                   bool ietf_quic, size_t received_packet_length) {
44
23
  ENVOY_LOG_EVERY_POW_2_MISC(info, "Sending Stateless Reset on connection {}",
45
23
                             connection_id.ToString());
46
23
  stats_.stateless_reset_packets_sent_.inc();
47
23
  quic::QuicTimeWaitListManager::SendPublicReset(self_address, peer_address, connection_id,
48
23
                                                 ietf_quic, received_packet_length);
49
23
}
50

            
51
EnvoyQuicDispatcher::EnvoyQuicDispatcher(
52
    const quic::QuicCryptoServerConfig* crypto_config, const quic::QuicConfig& quic_config,
53
    quic::QuicVersionManager* version_manager,
54
    std::unique_ptr<quic::QuicConnectionHelperInterface> helper,
55
    std::unique_ptr<quic::QuicAlarmFactory> alarm_factory,
56
    uint8_t expected_server_connection_id_length, Network::ConnectionHandler& connection_handler,
57
    Network::ListenerConfig& listener_config, Server::ListenerStats& listener_stats,
58
    Server::PerHandlerListenerStats& per_worker_stats, Event::Dispatcher& dispatcher,
59
    Network::Socket& listen_socket, QuicStatNames& quic_stat_names,
60
    EnvoyQuicCryptoServerStreamFactoryInterface& crypto_server_stream_factory,
61
    quic::ConnectionIdGeneratorInterface& generator,
62
    EnvoyQuicConnectionDebugVisitorFactoryInterfaceOptRef debug_visitor_factory,
63
    std::unique_ptr<Http::SessionIdleList> session_idle_list)
64
2203
    : quic::QuicDispatcher(&quic_config, crypto_config, version_manager, std::move(helper),
65
2203
                           std::make_unique<EnvoyQuicCryptoServerStreamHelper>(),
66
2203
                           std::move(alarm_factory), expected_server_connection_id_length,
67
2203
                           generator),
68
2203
      connection_handler_(connection_handler), listener_config_(&listener_config),
69
2203
      listener_stats_(listener_stats), per_worker_stats_(per_worker_stats), dispatcher_(dispatcher),
70
2203
      listen_socket_(listen_socket), quic_stat_names_(quic_stat_names),
71
2203
      crypto_server_stream_factory_(crypto_server_stream_factory),
72
2203
      quic_stats_(generateStats(listener_config.listenerScope())),
73
2203
      connection_stats_({QUIC_CONNECTION_STATS(
74
2203
          POOL_COUNTER_PREFIX(listener_config.listenerScope(), "quic.connection"))}),
75
2203
      debug_visitor_factory_(debug_visitor_factory),
76
2203
      session_idle_list_(std::move(session_idle_list)) {}
77

            
78
void EnvoyQuicDispatcher::OnConnectionClosed(quic::QuicConnectionId connection_id,
79
                                             quic::QuicErrorCode error,
80
                                             const std::string& error_details,
81
2973
                                             quic::ConnectionCloseSource source) {
82
2973
  quic::QuicDispatcher::OnConnectionClosed(connection_id, error, error_details, source);
83
2973
  listener_stats_.downstream_cx_active_.dec();
84
2973
  per_worker_stats_.downstream_cx_active_.dec();
85
2973
  connection_handler_.decNumConnections();
86
2973
  quic_stat_names_.chargeQuicConnectionCloseStats(listener_config_->listenerScope(), error, source,
87
2973
                                                  /*is_upstream*/ false);
88
2973
}
89

            
90
2203
quic::QuicTimeWaitListManager* EnvoyQuicDispatcher::CreateQuicTimeWaitListManager() {
91
2203
  return new EnvoyQuicTimeWaitListManager(writer(), this, helper()->GetClock(), alarm_factory(),
92
2203
                                          quic_stats_);
93
2203
}
94

            
95
std::unique_ptr<quic::QuicSession> EnvoyQuicDispatcher::CreateQuicSession(
96
    quic::QuicConnectionId server_connection_id, const quic::QuicSocketAddress& self_address,
97
    const quic::QuicSocketAddress& peer_address, absl::string_view /*alpn*/,
98
    const quic::ParsedQuicVersion& version, const quic::ParsedClientHello& parsed_chlo,
99
2973
    quic::ConnectionIdGeneratorInterface& connection_id_generator) {
100
2973
  quic::QuicConfig quic_config = config();
101
  // TODO(danzh) use passed-in ALPN instead of hard-coded h3 after proof source interfaces takes in
102
  // ALPN.
103
2973
  Network::ConnectionSocketPtr connection_socket = createServerConnectionSocket(
104
2973
      listen_socket_.ioHandle(), self_address, peer_address, std::string(parsed_chlo.sni), "h3");
105
2973
  connection_socket->connectionInfoProvider().setListenerInfo(listener_config_->listenerInfo());
106
2973
  auto stream_info = std::make_unique<StreamInfo::StreamInfoImpl>(
107
2973
      dispatcher_.timeSource(), connection_socket->connectionInfoProviderSharedPtr(),
108
2973
      StreamInfo::FilterState::LifeSpan::Connection);
109

            
110
2973
  auto listener_filter_manager = std::make_unique<QuicListenerFilterManagerImpl>(
111
2973
      dispatcher_, *connection_socket, *stream_info);
112
2973
  const bool success = listener_config_->filterChainFactory().createQuicListenerFilterChain(
113
2973
      *listener_filter_manager);
114
2973
  const Network::FilterChain* filter_chain = nullptr;
115
2973
  if (success) {
116
2970
    listener_filter_manager->startFilterChain();
117
    // Quic listener filters are not supposed to pause the filter chain iteration unless it closes
118
    // the connection socket. If any listener filter have closed the socket, do not get a network
119
    // filter chain. Thus early fail the connection.
120
2970
    if (connection_socket->ioHandle().isOpen()) {
121
5940
      for (auto address_family : {quiche::IpAddressFamily::IP_V4, quiche::IpAddressFamily::IP_V6}) {
122
5940
        absl::optional<quic::QuicSocketAddress> address =
123
5940
            quic_config.GetPreferredAddressToSend(address_family);
124
5940
        if (address.has_value() && address->IsInitialized() &&
125
5940
            !listener_filter_manager->shouldAdvertiseServerPreferredAddress(address.value())) {
126
2
          quic_config.ClearAlternateServerAddressToSend(address_family);
127
2
        }
128
5940
      }
129
2970
      filter_chain =
130
2970
          listener_config_->filterChainManager().findFilterChain(*connection_socket, *stream_info);
131
2970
    }
132
2970
  }
133

            
134
2973
  auto quic_connection = std::make_unique<EnvoyQuicServerConnection>(
135
2973
      server_connection_id, self_address, peer_address, *helper(), *alarm_factory(), writer(),
136
2973
      quic::ParsedQuicVersionVector{version}, std::move(connection_socket), connection_id_generator,
137
2973
      std::move(listener_filter_manager));
138

            
139
2973
  auto quic_session = std::make_unique<EnvoyQuicServerSession>(
140
2973
      quic_config, quic::ParsedQuicVersionVector{version}, std::move(quic_connection), this,
141
2973
      session_helper(), crypto_config(), compressed_certs_cache(), dispatcher_,
142
2973
      listener_config_->perConnectionBufferLimitBytes(), quic_stat_names_,
143
2973
      listener_config_->listenerScope(), crypto_server_stream_factory_, std::move(stream_info),
144
2973
      connection_stats_, debug_visitor_factory_, session_idle_list_.get());
145
2973
  if (filter_chain != nullptr) {
146
    // Setup filter chain before Initialize().
147
2970
    const bool has_filter_initialized =
148
2970
        listener_config_->filterChainFactory().createNetworkFilterChain(
149
2970
            *quic_session, filter_chain->networkFilterFactories());
150
    // QUIC listener must have HCM filter configured. Otherwise, stream creation later will fail.
151
2970
    ASSERT(has_filter_initialized);
152
2970
    connections_by_filter_chain_[filter_chain].push_front(
153
2970
        std::reference_wrapper<Network::Connection>(*quic_session));
154
2970
    quic_session->storeConnectionMapPosition(connections_by_filter_chain_, *filter_chain,
155
2970
                                             connections_by_filter_chain_[filter_chain].begin());
156
2970
  } else {
157
3
    quic_session->close(Network::ConnectionCloseType::FlushWrite, "no filter chain found");
158
3
  }
159
2973
  quic_session->Initialize();
160
2973
  connection_handler_.incNumConnections();
161
2973
  listener_stats_.downstream_cx_active_.inc();
162
2973
  listener_stats_.downstream_cx_total_.inc();
163
2973
  per_worker_stats_.downstream_cx_active_.inc();
164
2973
  per_worker_stats_.downstream_cx_total_.inc();
165
2973
  return quic_session;
166
2973
}
167

            
168
bool EnvoyQuicDispatcher::processPacket(const quic::QuicSocketAddress& self_address,
169
                                        const quic::QuicSocketAddress& peer_address,
170
240287
                                        const quic::QuicReceivedPacket& packet) {
171
  // Assume a packet was dispatched successfully, if OnFailedToDispatchPacket is not called.
172
240287
  current_packet_dispatch_success_ = true;
173
240287
  ProcessPacket(self_address, peer_address, packet);
174
240287
  return current_packet_dispatch_success_;
175
240287
}
176

            
177
bool EnvoyQuicDispatcher::OnFailedToDispatchPacket(
178
3857
    const quic::ReceivedPacketInfo& received_packet_info) {
179
3857
  if (!accept_new_connections()) {
180
17
    current_packet_dispatch_success_ = false;
181
17
    return true;
182
17
  }
183
3840
  return quic::QuicDispatcher::OnFailedToDispatchPacket(received_packet_info);
184
3857
}
185

            
186
void EnvoyQuicDispatcher::closeConnectionsWithFilterChain(
187
6
    const Network::FilterChain* filter_chain) {
188
6
  auto iter = connections_by_filter_chain_.find(filter_chain);
189
6
  if (iter != connections_by_filter_chain_.end()) {
190
4
    std::list<std::reference_wrapper<Network::Connection>>& connections = iter->second;
191
    // Retain the number of connections in the list early because closing the connection will change
192
    // the size.
193
4
    const size_t num_connections = connections.size();
194
8
    for (size_t i = 0; i < num_connections; ++i) {
195
4
      Network::Connection& connection = connections.front().get();
196
      // This will remove the connection from the list. And the last removal will remove connections
197
      // from the map as well.
198
4
      connection.close(Network::ConnectionCloseType::NoFlush);
199
4
    }
200
4
    ASSERT(connections_by_filter_chain_.find(filter_chain) == connections_by_filter_chain_.end());
201
4
    if (num_connections > 0) {
202
      // Explicitly destroy closed sessions in the current call stack. Because upon
203
      // returning the filter chain configs will be destroyed, and no longer safe to be accessed.
204
      // If any filters access those configs during destruction, it'll be use-after-free
205
4
      DeleteSessions();
206
4
    }
207
4
  }
208
6
}
209

            
210
6
void EnvoyQuicDispatcher::updateListenerConfig(Network::ListenerConfig& new_listener_config) {
211
6
  listener_config_ = &new_listener_config;
212
6
}
213

            
214
// Close all connections currently in the idle list.
215
4
void EnvoyQuicDispatcher::closeIdleQuicConnections(bool is_saturated) {
216
  // This method is called from the worker thread, triggered by the
217
  // Overload Manager.
218
4
  session_idle_list_->MaybeTerminateIdleSessions(is_saturated);
219
4
}
220

            
221
} // namespace Quic
222
} // namespace Envoy