-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
quiche: implement ActiveQuicListener #7896
Changes from 98 commits
3253b11
9914fb4
2ea58dd
a8aa262
6b5315b
02e4fd9
6f8c2e4
58f3842
8fec99d
15e5e33
72c2f84
25998ab
2fd6df0
aacc588
f68e5dd
12ef3c7
6029aeb
14f1e79
2fccff1
ef7cf28
5419162
117244f
1719fd0
732b3c8
f94f424
0d56de4
bacb5c5
f04a469
9ba2754
05daf8d
6eca9c6
b8fc520
f5e0c11
b881ae8
3db6847
1e55f68
ab474d5
5f7dd41
fcfe160
9dc44be
7a2395f
e9d66fa
3e4388b
2397898
0641fba
a5f8ba3
30d976b
a50fc1e
dbda7ef
ed15996
8b297e4
adbdee0
3dc7365
57144ed
c9abaa5
d278167
1b8117b
8375028
6361860
4e51861
44e262f
f2fc291
97b7d90
7361ca0
10e85f6
5f55a97
a4050dc
81e5f9a
109e9a9
9d545c5
4e91aae
7c863ef
c6f0763
bca0450
42b5df3
8891a14
9a5468e
1d23053
817c08d
5987586
3405bfc
548f8f5
b218f5a
3deb19e
be37cb1
d7c551f
bb1e435
799c5fa
c617a40
7618f8b
6a58153
c8428df
8e4d2a6
4b3d7be
11b653f
dcbb7d0
6ecaff1
fe5a5fa
dfc26fd
c7e416d
5b317be
32d0e78
2b0273f
2a6efb5
94dc9a3
ce75217
e31e933
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
syntax = "proto3"; | ||
|
||
package envoy.api.v2.listener; | ||
|
||
option java_outer_classname = "ListenerProto"; | ||
option java_multiple_files = true; | ||
option java_package = "io.envoyproxy.envoy.api.v2.listener"; | ||
option csharp_namespace = "Envoy.Api.V2.ListenerNS"; | ||
option ruby_package = "Envoy::Api::V2::ListenerNS"; | ||
|
||
import "google/protobuf/duration.proto"; | ||
|
||
// Configuration specific to the QUIC protocol. | ||
// Next id: 4 | ||
message QuicProtocolOptions { | ||
// Maximum number of streams that the client can negotiate per connection. 100 | ||
// if not specified. | ||
int32 max_concurrent_streams = 1; | ||
|
||
// Maximum number of milliseconds that connection will be alive when there is | ||
// no network activity. 300000ms if not specified. | ||
google.protobuf.Duration idle_timeout = 2; | ||
|
||
// Connection timeout in milliseconds before the crypto handshake is finished. | ||
// 20000ms if not specified. | ||
google.protobuf.Duration crypto_handshake_timeout = 3; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
#include "extensions/quic_listeners/quiche/active_quic_listener.h" | ||
|
||
#include "extensions/quic_listeners/quiche/envoy_quic_alarm_factory.h" | ||
#include "extensions/quic_listeners/quiche/envoy_quic_connection_helper.h" | ||
#include "extensions/quic_listeners/quiche/envoy_quic_dispatcher.h" | ||
#include "extensions/quic_listeners/quiche/envoy_quic_fake_proof_source.h" | ||
#include "extensions/quic_listeners/quiche/envoy_quic_packet_writer.h" | ||
#include "extensions/quic_listeners/quiche/envoy_quic_utils.h" | ||
|
||
namespace Envoy { | ||
namespace Quic { | ||
|
||
ActiveQuicListener::ActiveQuicListener(Event::Dispatcher& dispatcher, | ||
Network::ConnectionHandler& parent, spdlog::logger& logger, | ||
Network::ListenerConfig& listener_config, | ||
const quic::QuicConfig& quic_config) | ||
: ActiveQuicListener(dispatcher, parent, | ||
dispatcher.createUdpListener(listener_config.socket(), *this), logger, | ||
listener_config, quic_config) {} | ||
|
||
ActiveQuicListener::ActiveQuicListener(Event::Dispatcher& dispatcher, | ||
Network::ConnectionHandler& parent, | ||
Network::ListenerPtr&& listener, spdlog::logger& logger, | ||
Network::ListenerConfig& listener_config, | ||
const quic::QuicConfig& quic_config) | ||
: Server::ConnectionHandlerImpl::ActiveListenerImplBase(std::move(listener), listener_config), | ||
logger_(logger), dispatcher_(dispatcher), version_manager_(quic::CurrentSupportedVersions()) { | ||
quic::QuicRandom* const random = quic::QuicRandom::GetInstance(); | ||
random->RandBytes(random_seed_, sizeof(random_seed_)); | ||
crypto_config_ = std::make_unique<quic::QuicCryptoServerConfig>( | ||
quic::QuicStringPiece(reinterpret_cast<char*>(random_seed_), sizeof(random_seed_)), | ||
quic::QuicRandom::GetInstance(), std::make_unique<EnvoyQuicFakeProofSource>(), | ||
quic::KeyExchangeSource::Default()); | ||
auto connection_helper = std::make_unique<EnvoyQuicConnectionHelper>(dispatcher_); | ||
auto alarm_factory = | ||
std::make_unique<EnvoyQuicAlarmFactory>(dispatcher_, *connection_helper->GetClock()); | ||
quic_dispatcher_ = std::make_unique<EnvoyQuicDispatcher>( | ||
crypto_config_.get(), quic_config, &version_manager_, std::move(connection_helper), | ||
std::move(alarm_factory), quic::kQuicDefaultConnectionIdLength, parent, config_, stats_, | ||
dispatcher); | ||
quic_dispatcher_->InitializeWithWriter( | ||
new EnvoyQuicPacketWriter(*dynamic_cast<Network::UdpListener*>(listener_.get()))); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a TODO here to try to get rid of the dynamic cast? I will try to look at this myself and provide some guidance instead of just complaining. :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one is easy to remove actually. I modified Dispatcher interface and ActiveQuicListener constructor a bit. Done |
||
} | ||
|
||
void ActiveQuicListener::onListenerShutdown() { | ||
ENVOY_LOG_TO_LOGGER(logger_, info, "Listener with tag {} shutdown.", listenerTag()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think the listener tag is actually known to anyone. Name? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
quic_dispatcher_->Shutdown(); | ||
} | ||
|
||
void ActiveQuicListener::onData(Network::UdpRecvData& data) { | ||
quic::QuicSocketAddress peer_address(envoyAddressInstanceToQuicSocketAddress(data.peer_address_)); | ||
quic::QuicSocketAddress self_address( | ||
envoyAddressInstanceToQuicSocketAddress(data.local_address_)); | ||
quic::QuicTime timestamp = | ||
quic::QuicTime::Zero() + | ||
quic::QuicTime::Delta::FromMilliseconds(std::chrono::duration_cast<std::chrono::milliseconds>( | ||
data.receive_time_.time_since_epoch()) | ||
.count()); | ||
uint64_t num_slice = data.buffer_->getRawSlices(nullptr, 0); | ||
ASSERT(num_slice == 1); | ||
Buffer::RawSlice slice; | ||
data.buffer_->getRawSlices(&slice, 1); | ||
// TODO(danzh): pass in TTL and UDP header. | ||
quic::QuicReceivedPacket packet(reinterpret_cast<char*>(slice.mem_), slice.len_, timestamp, | ||
/*owns_buffer=*/false, /*ttl=*/0, /*ttl_valid=*/true, | ||
/*packet_headers=*/nullptr, /*headers_length=*/0, | ||
/*owns_header_buffer*/ false); | ||
quic_dispatcher_->ProcessPacket(self_address, peer_address, packet); | ||
} | ||
|
||
void ActiveQuicListener::onWriteReady(const Network::Socket& /*socket*/) { | ||
quic_dispatcher_->OnCanWrite(); | ||
} | ||
|
||
} // namespace Quic | ||
} // namespace Envoy |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
#pragma once | ||
|
||
#include "envoy/api/v2/listener/quic_config.pb.h" | ||
#include "envoy/network/connection_handler.h" | ||
#include "envoy/network/listener.h" | ||
|
||
#include "common/protobuf/utility.h" | ||
|
||
#include "server/connection_handler_impl.h" | ||
|
||
#include "extensions/quic_listeners/quiche/envoy_quic_dispatcher.h" | ||
|
||
namespace Envoy { | ||
namespace Quic { | ||
|
||
// QUIC specific UdpListenerCallbacks implemention which delegates incoming | ||
// packets, write signals and listener errors to QuicDispatcher. | ||
class ActiveQuicListener : public Network::UdpListenerCallbacks, | ||
public Server::ConnectionHandlerImpl::ActiveListenerImplBase, | ||
// Inherits below two interfaces just to have common | ||
mattklein123 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// interfaces. Not expected to support listener | ||
// filter. | ||
// TODO(danzh): clean up meaningless inheritance. | ||
public Network::UdpListenerFilterManager, | ||
public Network::UdpReadFilterCallbacks { | ||
public: | ||
ActiveQuicListener(Event::Dispatcher& dispatcher, Network::ConnectionHandler& parent, | ||
spdlog::logger& logger, Network::ListenerConfig& listener_config, | ||
const quic::QuicConfig& quic_config); | ||
|
||
ActiveQuicListener(Event::Dispatcher& dispatcher, Network::ConnectionHandler& parent, | ||
Network::ListenerPtr&& listener, spdlog::logger& logger, | ||
Network::ListenerConfig& listener_config, const quic::QuicConfig& quic_config); | ||
// TODO(#7465): Make this a callback. | ||
void onListenerShutdown(); | ||
|
||
// Network::UdpListenerCallbacks | ||
void onData(Network::UdpRecvData& data) override; | ||
void onWriteReady(const Network::Socket& socket) override; | ||
void onReceiveError(const Network::UdpListenerCallbacks::ErrorCode& /*error_code*/, | ||
Api::IoError::IoErrorCode /*err*/) override { | ||
// No-op. Quic can't do anything upon listener error. | ||
} | ||
|
||
// Network::UdpListenerFilterManager | ||
void addReadFilter(Network::UdpListenerReadFilterPtr&& /*filter*/) override { | ||
// QUIC doesn't support listener filter. | ||
NOT_REACHED_GCOVR_EXCL_LINE; | ||
} | ||
|
||
// Network::UdpReadFilterCallbacks | ||
Network::UdpListener& udpListener() override { NOT_REACHED_GCOVR_EXCL_LINE; } | ||
|
||
private: | ||
friend class ActiveQuicListenerPeer; | ||
|
||
uint8_t random_seed_[16]; | ||
std::unique_ptr<quic::QuicCryptoServerConfig> crypto_config_; | ||
spdlog::logger& logger_; | ||
Event::Dispatcher& dispatcher_; | ||
quic::QuicVersionManager version_manager_; | ||
std::unique_ptr<EnvoyQuicDispatcher> quic_dispatcher_; | ||
}; | ||
|
||
using ActiveQuicListenerPtr = std::unique_ptr<ActiveQuicListener>; | ||
|
||
// A factory to create ActiveQuicListener based on given config. | ||
class ActiveQuicListenerFactory : public Network::ActiveUdpListenerFactory { | ||
public: | ||
ActiveQuicListenerFactory(const envoy::api::v2::listener::QuicProtocolOptions& config) { | ||
uint64_t idle_network_timeout_ms = | ||
config.has_idle_timeout() ? DurationUtil::durationToMilliseconds(config.idle_timeout()) | ||
: 300000; | ||
quic_config_.SetIdleNetworkTimeout( | ||
quic::QuicTime::Delta::FromMilliseconds(idle_network_timeout_ms), | ||
quic::QuicTime::Delta::FromMilliseconds(idle_network_timeout_ms)); | ||
int32_t max_time_before_crypto_handshake_ms = | ||
config.has_crypto_handshake_timeout() | ||
? DurationUtil::durationToMilliseconds(config.crypto_handshake_timeout()) | ||
: 20000; | ||
quic_config_.set_max_time_before_crypto_handshake( | ||
quic::QuicTime::Delta::FromMilliseconds(max_time_before_crypto_handshake_ms)); | ||
int32_t max_streams = | ||
config.max_concurrent_streams() == 0 ? 100 : config.max_concurrent_streams(); | ||
quic_config_.SetMaxIncomingBidirectionalStreamsToSend(max_streams); | ||
quic_config_.SetMaxIncomingUnidirectionalStreamsToSend(max_streams); | ||
} | ||
|
||
Network::ConnectionHandler::ActiveListenerPtr | ||
createActiveUdpListener(Network::ConnectionHandler& parent, Event::Dispatcher& disptacher, | ||
spdlog::logger& logger, Network::ListenerConfig& config) const override { | ||
return std::make_unique<ActiveQuicListener>(disptacher, parent, logger, config, quic_config_); | ||
} | ||
|
||
private: | ||
friend class ActiveQuicListenerFactoryPeer; | ||
|
||
quic::QuicConfig quic_config_; | ||
}; | ||
|
||
} // namespace Quic | ||
} // namespace Envoy |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
#include "extensions/quic_listeners/quiche/active_quic_listener_config.h" | ||
|
||
#include "envoy/api/v2/listener/quic_config.pb.h" | ||
|
||
#include "server/well_known_names.h" | ||
|
||
#include "extensions/quic_listeners/quiche/active_quic_listener.h" | ||
|
||
namespace Envoy { | ||
namespace Quic { | ||
|
||
ProtobufTypes::MessagePtr ActiveQuicListenerConfigFactory::createEmptyConfigProto() { | ||
return std::make_unique<envoy::api::v2::listener::QuicProtocolOptions>(); | ||
} | ||
|
||
Network::ActiveUdpListenerFactoryPtr | ||
ActiveQuicListenerConfigFactory::createActiveUdpListenerFactory(const Protobuf::Message& message) { | ||
auto& config = dynamic_cast<const envoy::api::v2::listener::QuicProtocolOptions&>(message); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you use downcastAndValidate here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no validation annotation in quic_config.proto There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, thanks. We can add this later if needed. |
||
return std::make_unique<ActiveQuicListenerFactory>(config); | ||
} | ||
|
||
std::string ActiveQuicListenerConfigFactory::name() { return Server::UdpListenerNames::get().Quic; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The name can now be moved out of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure what inline means here. The string is also used in tests. So I added a statistic const in the .h file. |
||
|
||
REGISTER_FACTORY(ActiveQuicListenerConfigFactory, Server::ActiveUdpListenerConfigFactory); | ||
|
||
} // namespace Quic | ||
} // namespace Envoy |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
#pragma once | ||
|
||
#include "envoy/registry/registry.h" | ||
#include "envoy/server/active_udp_listener_config.h" | ||
|
||
namespace Envoy { | ||
namespace Quic { | ||
|
||
// A factory to create ActiveQuicListenerFactory based on given protobuf. | ||
class ActiveQuicListenerConfigFactory : public Server::ActiveUdpListenerConfigFactory { | ||
public: | ||
ProtobufTypes::MessagePtr createEmptyConfigProto() override; | ||
|
||
Network::ActiveUdpListenerFactoryPtr | ||
createActiveUdpListenerFactory(const Protobuf::Message&) override; | ||
|
||
std::string name() override; | ||
}; | ||
|
||
DECLARE_FACTORY(ActiveQuicListenerConfigFactory); | ||
|
||
} // namespace Quic | ||
} // namespace Envoy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use a UInt32 WKT here since we default to 100?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done