Line data Source code
1 : #include "source/common/quic/envoy_quic_client_connection.h"
2 :
3 : #include <memory>
4 :
5 : #include "envoy/config/core/v3/base.pb.h"
6 :
7 : #include "source/common/network/socket_option_factory.h"
8 : #include "source/common/network/udp_packet_writer_handler_impl.h"
9 : #include "source/common/quic/envoy_quic_utils.h"
10 :
11 : namespace Envoy {
12 : namespace Quic {
13 :
14 : EnvoyQuicClientConnection::EnvoyQuicClientConnection(
15 : const quic::QuicConnectionId& server_connection_id,
16 : Network::Address::InstanceConstSharedPtr& initial_peer_address,
17 : quic::QuicConnectionHelperInterface& helper, quic::QuicAlarmFactory& alarm_factory,
18 : const quic::ParsedQuicVersionVector& supported_versions,
19 : Network::Address::InstanceConstSharedPtr local_addr, Event::Dispatcher& dispatcher,
20 : const Network::ConnectionSocket::OptionsSharedPtr& options,
21 : quic::ConnectionIdGeneratorInterface& generator)
22 : : EnvoyQuicClientConnection(
23 : server_connection_id, helper, alarm_factory, supported_versions, dispatcher,
24 0 : createConnectionSocket(initial_peer_address, local_addr, options), generator) {}
25 :
26 : EnvoyQuicClientConnection::EnvoyQuicClientConnection(
27 : const quic::QuicConnectionId& server_connection_id, quic::QuicConnectionHelperInterface& helper,
28 : quic::QuicAlarmFactory& alarm_factory, const quic::ParsedQuicVersionVector& supported_versions,
29 : Event::Dispatcher& dispatcher, Network::ConnectionSocketPtr&& connection_socket,
30 : quic::ConnectionIdGeneratorInterface& generator)
31 : : EnvoyQuicClientConnection(
32 : server_connection_id, helper, alarm_factory,
33 : new EnvoyQuicPacketWriter(
34 : std::make_unique<Network::UdpDefaultWriter>(connection_socket->ioHandle())),
35 : /*owns_writer=*/true, supported_versions, dispatcher, std::move(connection_socket),
36 0 : generator) {}
37 :
38 : EnvoyQuicClientConnection::EnvoyQuicClientConnection(
39 : const quic::QuicConnectionId& server_connection_id, quic::QuicConnectionHelperInterface& helper,
40 : quic::QuicAlarmFactory& alarm_factory, quic::QuicPacketWriter* writer, bool owns_writer,
41 : const quic::ParsedQuicVersionVector& supported_versions, Event::Dispatcher& dispatcher,
42 : Network::ConnectionSocketPtr&& connection_socket,
43 : quic::ConnectionIdGeneratorInterface& generator)
44 : : quic::QuicConnection(server_connection_id, quic::QuicSocketAddress(),
45 : envoyIpAddressToQuicSocketAddress(
46 : connection_socket->connectionInfoProvider().remoteAddress()->ip()),
47 : &helper, &alarm_factory, writer, owns_writer,
48 : quic::Perspective::IS_CLIENT, supported_versions, generator),
49 0 : QuicNetworkConnection(std::move(connection_socket)), dispatcher_(dispatcher) {}
50 :
51 : void EnvoyQuicClientConnection::processPacket(
52 : Network::Address::InstanceConstSharedPtr local_address,
53 : Network::Address::InstanceConstSharedPtr peer_address, Buffer::InstancePtr buffer,
54 0 : MonotonicTime receive_time) {
55 0 : quic::QuicTime timestamp =
56 0 : quic::QuicTime::Zero() +
57 0 : quic::QuicTime::Delta::FromMicroseconds(
58 0 : std::chrono::duration_cast<std::chrono::microseconds>(receive_time.time_since_epoch())
59 0 : .count());
60 0 : ASSERT(buffer->getRawSlices().size() == 1);
61 0 : Buffer::RawSlice slice = buffer->frontSlice();
62 0 : quic::QuicReceivedPacket packet(reinterpret_cast<char*>(slice.mem_), slice.len_, timestamp,
63 0 : /*owns_buffer=*/false, /*ttl=*/0, /*ttl_valid=*/false,
64 0 : /*packet_headers=*/nullptr, /*headers_length=*/0,
65 0 : /*owns_header_buffer*/ false);
66 0 : ProcessUdpPacket(envoyIpAddressToQuicSocketAddress(local_address->ip()),
67 0 : envoyIpAddressToQuicSocketAddress(peer_address->ip()), packet);
68 0 : }
69 :
70 0 : uint64_t EnvoyQuicClientConnection::maxDatagramSize() const {
71 : // TODO(danzh) make this variable configurable to support jumbo frames.
72 0 : return Network::DEFAULT_UDP_MAX_DATAGRAM_SIZE;
73 0 : }
74 :
75 : void EnvoyQuicClientConnection::setUpConnectionSocket(Network::ConnectionSocket& connection_socket,
76 0 : OptRef<PacketsToReadDelegate> delegate) {
77 0 : delegate_ = delegate;
78 0 : if (connection_socket.ioHandle().isOpen()) {
79 0 : connection_socket.ioHandle().initializeFileEvent(
80 0 : dispatcher_,
81 0 : [this, &connection_socket](uint32_t events) -> void {
82 0 : onFileEvent(events, connection_socket);
83 0 : },
84 0 : Event::PlatformDefaultTriggerType,
85 0 : Event::FileReadyType::Read | Event::FileReadyType::Write);
86 :
87 0 : if (!Network::Socket::applyOptions(connection_socket.options(), connection_socket,
88 0 : envoy::config::core::v3::SocketOption::STATE_LISTENING)) {
89 0 : ENVOY_CONN_LOG(error, "Fail to apply listening options", *this);
90 0 : connection_socket.close();
91 0 : }
92 0 : }
93 0 : if (!connection_socket.ioHandle().isOpen()) {
94 0 : CloseConnection(quic::QUIC_CONNECTION_CANCELLED, "Fail to set up connection socket.",
95 0 : quic::ConnectionCloseBehavior::SILENT_CLOSE);
96 0 : }
97 0 : }
98 :
99 : void EnvoyQuicClientConnection::switchConnectionSocket(
100 0 : Network::ConnectionSocketPtr&& connection_socket) {
101 0 : auto writer = std::make_unique<EnvoyQuicPacketWriter>(
102 0 : std::make_unique<Network::UdpDefaultWriter>(connection_socket->ioHandle()));
103 0 : quic::QuicSocketAddress self_address = envoyIpAddressToQuicSocketAddress(
104 0 : connection_socket->connectionInfoProvider().localAddress()->ip());
105 0 : quic::QuicSocketAddress peer_address = envoyIpAddressToQuicSocketAddress(
106 0 : connection_socket->connectionInfoProvider().remoteAddress()->ip());
107 :
108 : // The old socket is not closed in this call, because it could still receive useful packets.
109 0 : num_socket_switches_++;
110 0 : setConnectionSocket(std::move(connection_socket));
111 0 : setUpConnectionSocket(*connectionSocket(), delegate_);
112 0 : MigratePath(self_address, peer_address, writer.release(), true);
113 0 : }
114 :
115 0 : void EnvoyQuicClientConnection::OnPathDegradingDetected() {
116 0 : QuicConnection::OnPathDegradingDetected();
117 0 : maybeMigratePort();
118 0 : }
119 :
120 0 : void EnvoyQuicClientConnection::maybeMigratePort() {
121 0 : if (!IsHandshakeConfirmed() || HasPendingPathValidation() || !migrate_port_on_path_degrading_ ||
122 0 : num_socket_switches_ >= kMaxNumSocketSwitches) {
123 0 : return;
124 0 : }
125 :
126 0 : probeWithNewPort(peer_address(), quic::PathValidationReason::kPortMigration);
127 0 : }
128 :
129 : void EnvoyQuicClientConnection::probeWithNewPort(const quic::QuicSocketAddress& peer_address,
130 0 : quic::PathValidationReason reason) {
131 0 : const Network::Address::InstanceConstSharedPtr& current_local_address =
132 0 : connectionSocket()->connectionInfoProvider().localAddress();
133 : // Creates an IP address with unset port. The port will be set when the new socket is created.
134 0 : Network::Address::InstanceConstSharedPtr new_local_address;
135 0 : if (current_local_address->ip()->version() == Network::Address::IpVersion::v4) {
136 0 : new_local_address = std::make_shared<Network::Address::Ipv4Instance>(
137 0 : current_local_address->ip()->addressAsString());
138 0 : } else {
139 0 : new_local_address = std::make_shared<Network::Address::Ipv6Instance>(
140 0 : current_local_address->ip()->addressAsString());
141 0 : }
142 :
143 : // The probing socket will have the same host but a different port.
144 0 : auto probing_socket =
145 0 : createConnectionSocket(connectionSocket()->connectionInfoProvider().remoteAddress(),
146 0 : new_local_address, connectionSocket()->options());
147 0 : setUpConnectionSocket(*probing_socket, delegate_);
148 0 : auto writer = std::make_unique<EnvoyQuicPacketWriter>(
149 0 : std::make_unique<Network::UdpDefaultWriter>(probing_socket->ioHandle()));
150 0 : quic::QuicSocketAddress self_address = envoyIpAddressToQuicSocketAddress(
151 0 : probing_socket->connectionInfoProvider().localAddress()->ip());
152 :
153 0 : auto context = std::make_unique<EnvoyQuicPathValidationContext>(
154 0 : self_address, peer_address, std::move(writer), std::move(probing_socket));
155 0 : ValidatePath(std::move(context), std::make_unique<EnvoyPathValidationResultDelegate>(*this),
156 0 : reason);
157 0 : }
158 :
159 : void EnvoyQuicClientConnection::onPathValidationSuccess(
160 0 : std::unique_ptr<quic::QuicPathValidationContext> context) {
161 0 : auto envoy_context =
162 0 : static_cast<EnvoyQuicClientConnection::EnvoyQuicPathValidationContext*>(context.get());
163 :
164 0 : auto probing_socket = envoy_context->releaseSocket();
165 0 : if (envoy_context->peer_address() != peer_address()) {
166 0 : OnServerPreferredAddressValidated(*envoy_context, true);
167 0 : envoy_context->releaseWriter();
168 0 : } else {
169 0 : MigratePath(envoy_context->self_address(), envoy_context->peer_address(),
170 0 : envoy_context->releaseWriter(), true);
171 0 : }
172 :
173 0 : if (self_address() == envoy_context->self_address() &&
174 0 : peer_address() == envoy_context->peer_address()) {
175 : // probing_socket will be set as the new default socket. But old sockets are still able to
176 : // receive packets.
177 0 : num_socket_switches_++;
178 0 : setConnectionSocket(std::move(probing_socket));
179 0 : return;
180 0 : }
181 : // MigratePath should always succeed since the migration happens after path
182 : // validation.
183 0 : ENVOY_CONN_LOG(error, "connection fails to migrate path after validation", *this);
184 0 : }
185 :
186 : void EnvoyQuicClientConnection::onPathValidationFailure(
187 0 : std::unique_ptr<quic::QuicPathValidationContext> context) {
188 : // Note that the probing socket and probing writer will be deleted once context goes out of
189 : // scope.
190 0 : OnPathValidationFailureAtClient(/*is_multi_port=*/false, *context);
191 0 : }
192 :
193 : void EnvoyQuicClientConnection::onFileEvent(uint32_t events,
194 0 : Network::ConnectionSocket& connection_socket) {
195 0 : ENVOY_CONN_LOG(trace, "socket event: {}", *this, events);
196 0 : ASSERT(events & (Event::FileReadyType::Read | Event::FileReadyType::Write));
197 :
198 0 : if (events & Event::FileReadyType::Write) {
199 0 : OnCanWrite();
200 0 : }
201 :
202 0 : bool is_probing_socket =
203 0 : HasPendingPathValidation() &&
204 0 : (&connection_socket ==
205 0 : &static_cast<EnvoyQuicClientConnection::EnvoyQuicPathValidationContext*>(
206 0 : GetPathValidationContext())
207 0 : ->probingSocket());
208 :
209 : // It's possible for a write event callback to close the connection, in such case ignore read
210 : // event processing.
211 : // TODO(mattklein123): Right now QUIC client is hard coded to use GRO because it is probably the
212 : // right default for QUIC. Determine whether this should be configurable or not.
213 0 : if (connected() && (events & Event::FileReadyType::Read)) {
214 0 : Api::IoErrorPtr err = Network::Utility::readPacketsFromSocket(
215 0 : connection_socket.ioHandle(), *connection_socket.connectionInfoProvider().localAddress(),
216 0 : *this, dispatcher_.timeSource(), /*prefer_gro=*/false, packets_dropped_);
217 0 : if (err == nullptr) {
218 : // In the case where the path validation fails, the probing socket will be closed and its IO
219 : // events are no longer interesting.
220 0 : if (!is_probing_socket || HasPendingPathValidation() ||
221 0 : connectionSocket().get() == &connection_socket) {
222 0 : connection_socket.ioHandle().activateFileEvents(Event::FileReadyType::Read);
223 0 : return;
224 0 : }
225 :
226 0 : } else if (err->getErrorCode() != Api::IoError::IoErrorCode::Again) {
227 0 : ENVOY_CONN_LOG(error, "recvmsg result {}: {}", *this, static_cast<int>(err->getErrorCode()),
228 0 : err->getErrorDetails());
229 0 : }
230 0 : }
231 0 : }
232 :
233 0 : void EnvoyQuicClientConnection::setNumPtosForPortMigration(uint32_t num_ptos_for_path_degrading) {
234 0 : if (num_ptos_for_path_degrading < 1) {
235 0 : return;
236 0 : }
237 0 : migrate_port_on_path_degrading_ = true;
238 0 : sent_packet_manager().set_num_ptos_for_path_degrading(num_ptos_for_path_degrading);
239 0 : }
240 :
241 : EnvoyQuicClientConnection::EnvoyQuicPathValidationContext::EnvoyQuicPathValidationContext(
242 : const quic::QuicSocketAddress& self_address, const quic::QuicSocketAddress& peer_address,
243 : std::unique_ptr<EnvoyQuicPacketWriter> writer,
244 : std::unique_ptr<Network::ConnectionSocket> probing_socket)
245 : : QuicPathValidationContext(self_address, peer_address), writer_(std::move(writer)),
246 0 : socket_(std::move(probing_socket)) {}
247 :
248 0 : EnvoyQuicClientConnection::EnvoyQuicPathValidationContext::~EnvoyQuicPathValidationContext() =
249 : default;
250 :
251 0 : quic::QuicPacketWriter* EnvoyQuicClientConnection::EnvoyQuicPathValidationContext::WriterToUse() {
252 0 : return writer_.get();
253 0 : }
254 :
255 0 : EnvoyQuicPacketWriter* EnvoyQuicClientConnection::EnvoyQuicPathValidationContext::releaseWriter() {
256 0 : return writer_.release();
257 0 : }
258 :
259 : std::unique_ptr<Network::ConnectionSocket>
260 0 : EnvoyQuicClientConnection::EnvoyQuicPathValidationContext::releaseSocket() {
261 0 : return std::move(socket_);
262 0 : }
263 :
264 : Network::ConnectionSocket&
265 0 : EnvoyQuicClientConnection::EnvoyQuicPathValidationContext::probingSocket() {
266 0 : return *socket_;
267 0 : }
268 :
269 : EnvoyQuicClientConnection::EnvoyPathValidationResultDelegate::EnvoyPathValidationResultDelegate(
270 : EnvoyQuicClientConnection& connection)
271 0 : : connection_(connection) {}
272 :
273 : void EnvoyQuicClientConnection::EnvoyPathValidationResultDelegate::OnPathValidationSuccess(
274 0 : std::unique_ptr<quic::QuicPathValidationContext> context, quic::QuicTime /*start_time*/) {
275 0 : connection_.onPathValidationSuccess(std::move(context));
276 0 : }
277 :
278 : void EnvoyQuicClientConnection::EnvoyPathValidationResultDelegate::OnPathValidationFailure(
279 0 : std::unique_ptr<quic::QuicPathValidationContext> context) {
280 0 : connection_.onPathValidationFailure(std::move(context));
281 0 : }
282 :
283 0 : void EnvoyQuicClientConnection::OnCanWrite() {
284 0 : quic::QuicConnection::OnCanWrite();
285 0 : onWriteEventDone();
286 0 : }
287 :
288 : void EnvoyQuicClientConnection::probeAndMigrateToServerPreferredAddress(
289 0 : const quic::QuicSocketAddress& server_preferred_address) {
290 0 : probeWithNewPort(server_preferred_address,
291 0 : quic::PathValidationReason::kServerPreferredAddressMigration);
292 0 : }
293 :
294 : } // namespace Quic
295 : } // namespace Envoy
|