Skip to content
Merged
1 change: 1 addition & 0 deletions envoy/network/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ envoy_cc_library(
":proxy_protocol_options_lib",
"//envoy/buffer:buffer_interface",
"//envoy/ssl:connection_interface",
"//envoy/stream_info:filter_state_interface",
],
)

Expand Down
23 changes: 12 additions & 11 deletions envoy/network/transport_socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "envoy/network/post_io_action.h"
#include "envoy/network/proxy_protocol.h"
#include "envoy/ssl/connection.h"
#include "envoy/stream_info/filter_state.h"

#include "absl/types/optional.h"

Expand Down Expand Up @@ -220,13 +221,9 @@ class TransportSocketOptions {
virtual absl::optional<Network::ProxyProtocolData> proxyProtocolOptions() const PURE;

/**
* @param key supplies a vector of bytes to which the option should append hash key data that will
* be used to separate connections based on the option. Any data already in the key vector
* must not be modified.
* @param factory supplies the factor which will be used for creating the transport socket.
* @return filter state from the downstream request or connection.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What in the implementation makes this downstream-specific?
and aren't transport sockets per-connection?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It specifies which filter state to capture when creating transport socket options. That determines whether downstream requests/connections map to the same upstream connection once we use the filter state in hashKey. This is basically a generalization of ProxyProtocolData but for arbitrary filter state objects.

*/
virtual void hashKey(std::vector<uint8_t>& key,
const Network::TransportSocketFactory& factory) const PURE;
virtual const StreamInfo::FilterStateSharedPtr& filterState() const PURE;
};

using TransportSocketOptionsConstSharedPtr = std::shared_ptr<const TransportSocketOptions>;
Expand All @@ -250,16 +247,20 @@ class TransportSocketFactory {
virtual TransportSocketPtr
createTransportSocket(TransportSocketOptionsConstSharedPtr options) const PURE;

/**
* @return bool whether the transport socket will use proxy protocol options.
*/
virtual bool usesProxyProtocolOptions() const PURE;

/**
* Returns true if the transport socket created by this factory supports some form of ALPN
* negotiation.
*/
virtual bool supportsAlpn() const { return false; }

/**
* @param key supplies a vector of bytes to which the option should append hash key data that will
* be used to separate connections based on the option. Any data already in the key vector
* must not be modified.
* @param options supplies the transport socket options.
*/
virtual void hashKey(std::vector<uint8_t>& key,
TransportSocketOptionsConstSharedPtr options) const PURE;
};

using TransportSocketFactoryPtr = std::unique_ptr<TransportSocketFactory>;
Expand Down
1 change: 1 addition & 0 deletions source/common/network/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ envoy_cc_library(
"//source/common/buffer:buffer_lib",
"//source/common/common:empty_string",
"//source/common/http:headers_lib",
"//source/common/network:transport_socket_options_lib",
],
)

Expand Down
1 change: 1 addition & 0 deletions source/common/network/raw_buffer_socket.cc
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,6 @@ RawBufferSocketFactory::createTransportSocket(TransportSocketOptionsConstSharedP
}

bool RawBufferSocketFactory::implementsSecureTransport() const { return false; }

} // namespace Network
} // namespace Envoy
4 changes: 2 additions & 2 deletions source/common/network/raw_buffer_socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "envoy/network/transport_socket.h"

#include "source/common/common/logger.h"
#include "source/common/network/transport_socket_options_impl.h"

namespace Envoy {
namespace Network {
Expand All @@ -29,13 +30,12 @@ class RawBufferSocket : public TransportSocket, protected Logger::Loggable<Logge
bool shutdown_{};
};

class RawBufferSocketFactory : public TransportSocketFactory {
class RawBufferSocketFactory : public CommonTransportSocketFactory {
public:
// Network::TransportSocketFactory
TransportSocketPtr
createTransportSocket(TransportSocketOptionsConstSharedPtr options) const override;
bool implementsSecureTransport() const override;
bool usesProxyProtocolOptions() const override { return false; }
};

} // namespace Network
Expand Down
67 changes: 23 additions & 44 deletions source/common/network/transport_socket_options_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,92 +15,71 @@

namespace Envoy {
namespace Network {
namespace {
void commonHashKey(const TransportSocketOptions& options, std::vector<std::uint8_t>& key,
const Network::TransportSocketFactory& factory) {
const auto& server_name_overide = options.serverNameOverride();

void CommonTransportSocketFactory::hashKey(std::vector<uint8_t>& key,
TransportSocketOptionsConstSharedPtr options) const {
if (!options) {
return;
}
const auto& server_name_overide = options->serverNameOverride();
if (server_name_overide.has_value()) {
pushScalarToByteVector(StringUtil::CaseInsensitiveHash()(server_name_overide.value()), key);
}

const auto& verify_san_list = options.verifySubjectAltNameListOverride();
const auto& verify_san_list = options->verifySubjectAltNameListOverride();
for (const auto& san : verify_san_list) {
pushScalarToByteVector(StringUtil::CaseInsensitiveHash()(san), key);
}

const auto& alpn_list = options.applicationProtocolListOverride();
const auto& alpn_list = options->applicationProtocolListOverride();
for (const auto& protocol : alpn_list) {
pushScalarToByteVector(StringUtil::CaseInsensitiveHash()(protocol), key);
}

const auto& alpn_fallback = options.applicationProtocolFallback();
const auto& alpn_fallback = options->applicationProtocolFallback();
for (const auto& protocol : alpn_fallback) {
pushScalarToByteVector(StringUtil::CaseInsensitiveHash()(protocol), key);
}

// Proxy protocol options should only be included in the hash if the upstream
// socket intends to use them.
const auto& proxy_protocol_options = options.proxyProtocolOptions();
if (proxy_protocol_options.has_value() && factory.usesProxyProtocolOptions()) {
pushScalarToByteVector(
StringUtil::CaseInsensitiveHash()(proxy_protocol_options.value().asStringForHash()), key);
}
}
} // namespace

void AlpnDecoratingTransportSocketOptions::hashKey(
std::vector<uint8_t>& key, const Network::TransportSocketFactory& factory) const {
commonHashKey(*this, key, factory);
}

void TransportSocketOptionsImpl::hashKey(std::vector<uint8_t>& key,
const Network::TransportSocketFactory& factory) const {
commonHashKey(*this, key, factory);
}

TransportSocketOptionsConstSharedPtr
TransportSocketOptionsUtility::fromFilterState(const StreamInfo::FilterState& filter_state) {
TransportSocketOptionsConstSharedPtr TransportSocketOptionsUtility::fromFilterState(
const StreamInfo::FilterStateSharedPtr& filter_state) {
if (!filter_state) {
return nullptr;
}
absl::string_view server_name;
std::vector<std::string> application_protocols;
std::vector<std::string> subject_alt_names;
std::vector<std::string> alpn_fallback;
absl::optional<Network::ProxyProtocolData> proxy_protocol_options;

bool needs_transport_socket_options = false;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could easily be misremembering but I thought previously this being null vs non-null meant that connections would or would not be hashed together in connection pools. have you made sure it's safe to always return this?
cc @lizan who wrote this code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I assumed this is just some optimization but if nullptr is actually special, then I'll have to track its usage throughout the code base.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked over and I think there is no risk in returning an empty options struct vs nullptr. This is because the common hash iterates over the optionals and vectors to add to hash, and if they are empty, then it is effectively a no-op.

PS. I added some additional refactor to make it more clear the goal of this PR. I want to swap out hashKey definition site to the transport socket factory so that the factory can change the hash (rather that the options checking whether a factory is special).

if (auto typed_data = filter_state.getDataReadOnly<UpstreamServerName>(UpstreamServerName::key());
if (auto typed_data =
filter_state->getDataReadOnly<UpstreamServerName>(UpstreamServerName::key());
typed_data != nullptr) {
server_name = typed_data->value();
needs_transport_socket_options = true;
}

if (auto typed_data = filter_state.getDataReadOnly<Network::ApplicationProtocols>(
if (auto typed_data = filter_state->getDataReadOnly<Network::ApplicationProtocols>(
Network::ApplicationProtocols::key());
typed_data != nullptr) {
application_protocols = typed_data->value();
needs_transport_socket_options = true;
}

if (auto typed_data =
filter_state.getDataReadOnly<UpstreamSubjectAltNames>(UpstreamSubjectAltNames::key());
filter_state->getDataReadOnly<UpstreamSubjectAltNames>(UpstreamSubjectAltNames::key());
typed_data != nullptr) {
subject_alt_names = typed_data->value();
needs_transport_socket_options = true;
}

if (auto typed_data =
filter_state.getDataReadOnly<ProxyProtocolFilterState>(ProxyProtocolFilterState::key());
filter_state->getDataReadOnly<ProxyProtocolFilterState>(ProxyProtocolFilterState::key());
typed_data != nullptr) {
proxy_protocol_options.emplace(typed_data->value());
needs_transport_socket_options = true;
}

if (needs_transport_socket_options) {
return std::make_shared<Network::TransportSocketOptionsImpl>(
server_name, std::move(subject_alt_names), std::move(application_protocols),
std::move(alpn_fallback), proxy_protocol_options);
} else {
return nullptr;
}
return std::make_shared<Network::TransportSocketOptionsImpl>(
server_name, std::move(subject_alt_names), std::move(application_protocols),
std::move(alpn_fallback), proxy_protocol_options, filter_state);
}

} // namespace Network
Expand Down
25 changes: 18 additions & 7 deletions source/common/network/transport_socket_options_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ class AlpnDecoratingTransportSocketOptions : public TransportSocketOptions {
absl::optional<Network::ProxyProtocolData> proxyProtocolOptions() const override {
return inner_options_->proxyProtocolOptions();
}
void hashKey(std::vector<uint8_t>& key,
const Network::TransportSocketFactory& factory) const override;
const StreamInfo::FilterStateSharedPtr& filterState() const override {
return inner_options_->filterState();
}

private:
const std::vector<std::string> alpn_fallback_;
Expand All @@ -43,13 +44,14 @@ class TransportSocketOptionsImpl : public TransportSocketOptions {
absl::string_view override_server_name = "",
std::vector<std::string>&& override_verify_san_list = {},
std::vector<std::string>&& override_alpn = {}, std::vector<std::string>&& fallback_alpn = {},
absl::optional<Network::ProxyProtocolData> proxy_proto_options = absl::nullopt)
absl::optional<Network::ProxyProtocolData> proxy_proto_options = absl::nullopt,
const StreamInfo::FilterStateSharedPtr filter_state = nullptr)
: override_server_name_(override_server_name.empty()
? absl::nullopt
: absl::optional<std::string>(override_server_name)),
override_verify_san_list_{std::move(override_verify_san_list)},
override_alpn_list_{std::move(override_alpn)}, alpn_fallback_{std::move(fallback_alpn)},
proxy_protocol_options_(proxy_proto_options) {}
proxy_protocol_options_(proxy_proto_options), filter_state_(filter_state) {}

// Network::TransportSocketOptions
const absl::optional<std::string>& serverNameOverride() const override {
Expand All @@ -67,15 +69,15 @@ class TransportSocketOptionsImpl : public TransportSocketOptions {
absl::optional<Network::ProxyProtocolData> proxyProtocolOptions() const override {
return proxy_protocol_options_;
}
void hashKey(std::vector<uint8_t>& key,
const Network::TransportSocketFactory& factory) const override;
const StreamInfo::FilterStateSharedPtr& filterState() const override { return filter_state_; }

private:
const absl::optional<std::string> override_server_name_;
const std::vector<std::string> override_verify_san_list_;
const std::vector<std::string> override_alpn_list_;
const std::vector<std::string> alpn_fallback_;
const absl::optional<Network::ProxyProtocolData> proxy_protocol_options_;
const StreamInfo::FilterStateSharedPtr filter_state_;
};

class TransportSocketOptionsUtility {
Expand All @@ -87,7 +89,16 @@ class TransportSocketOptionsUtility {
* nullptr if nothing is in the filter state.
*/
static TransportSocketOptionsConstSharedPtr
fromFilterState(const StreamInfo::FilterState& stream_info);
fromFilterState(const StreamInfo::FilterStateSharedPtr& stream_info);
};

class CommonTransportSocketFactory : public TransportSocketFactory {
public:
/**
* Compute the generic hash key from the transport socket options.
*/
void hashKey(std::vector<uint8_t>& key,
TransportSocketOptionsConstSharedPtr options) const override;
};

} // namespace Network
Expand Down
1 change: 1 addition & 0 deletions source/common/quic/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,7 @@ envoy_cc_library(
"//envoy/server:transport_socket_config_interface",
"//envoy/ssl:context_config_interface",
"//source/common/common:assert_lib",
"//source/common/network:transport_socket_options_lib",
"//source/extensions/transport_sockets/tls:context_config_lib",
"//source/extensions/transport_sockets/tls:ssl_socket_lib",
"@envoy_api//envoy/extensions/transport_sockets/quic/v3:pkg_cc_proto",
Expand Down
4 changes: 2 additions & 2 deletions source/common/quic/quic_transport_socket_factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "envoy/ssl/context_config.h"

#include "source/common/common/assert.h"
#include "source/common/network/transport_socket_options_impl.h"
#include "source/extensions/transport_sockets/tls/ssl_socket.h"

namespace Envoy {
Expand Down Expand Up @@ -34,7 +35,7 @@ QuicTransportSocketFactoryStats generateStats(Stats::Scope& store, const std::st
// socket for QUIC in current implementation. This factory doesn't provides a
// transport socket, instead, its derived class provides TLS context config for
// server and client.
class QuicTransportSocketFactoryBase : public Network::TransportSocketFactory,
class QuicTransportSocketFactoryBase : public Network::CommonTransportSocketFactory,
protected Logger::Loggable<Logger::Id::quic> {
public:
QuicTransportSocketFactoryBase(Stats::Scope& store, const std::string& perspective)
Expand All @@ -49,7 +50,6 @@ class QuicTransportSocketFactoryBase : public Network::TransportSocketFactory,
PANIC("not implemented");
}
bool implementsSecureTransport() const override { return true; }
bool usesProxyProtocolOptions() const override { return false; }
bool supportsAlpn() const override { return true; }

protected:
Expand Down
2 changes: 1 addition & 1 deletion source/common/router/router.cc
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,7 @@ Http::FilterHeadersStatus Filter::decodeHeaders(Http::RequestHeaderMap& headers,
}

transport_socket_options_ = Network::TransportSocketOptionsUtility::fromFilterState(
*callbacks_->streamInfo().filterState());
callbacks_->streamInfo().filterState());

if (auto downstream_connection = downstreamConnection(); downstream_connection != nullptr) {
if (auto typed_state = downstream_connection->streamInfo()
Expand Down
2 changes: 1 addition & 1 deletion source/common/tcp_proxy/tcp_proxy.cc
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ Network::FilterStatus Filter::initializeUpstreamConnection() {
StreamInfo::FilterState::LifeSpan::Connection);
}
transport_socket_options_ = Network::TransportSocketOptionsUtility::fromFilterState(
downstream_connection->streamInfo().filterState());
read_callbacks_->connection().streamInfo().filterState());

if (auto typed_state = downstream_connection->streamInfo()
.filterState()
Expand Down
4 changes: 2 additions & 2 deletions source/common/upstream/cluster_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1651,7 +1651,7 @@ ClusterManagerImpl::ThreadLocalClusterManagerImpl::ClusterEntry::httpConnPoolImp

bool have_transport_socket_options = false;
if (context && context->upstreamTransportSocketOptions()) {
context->upstreamTransportSocketOptions()->hashKey(hash_key, host->transportSocketFactory());
host->transportSocketFactory().hashKey(hash_key, context->upstreamTransportSocketOptions());
have_transport_socket_options = true;
}

Expand Down Expand Up @@ -1752,7 +1752,7 @@ ClusterManagerImpl::ThreadLocalClusterManagerImpl::ClusterEntry::tcpConnPoolImpl
bool have_transport_socket_options = false;
if (context != nullptr && context->upstreamTransportSocketOptions() != nullptr) {
have_transport_socket_options = true;
context->upstreamTransportSocketOptions()->hashKey(hash_key, host->transportSocketFactory());
host->transportSocketFactory().hashKey(hash_key, context->upstreamTransportSocketOptions());
}

TcpConnPoolsContainer& container = parent_.host_tcp_conn_pool_map_[host];
Expand Down
1 change: 1 addition & 0 deletions source/extensions/transport_sockets/alts/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ envoy_cc_library(
"//source/common/common:empty_string",
"//source/common/common:enum_to_int",
"//source/common/network:raw_buffer_socket_lib",
"//source/common/network:transport_socket_options_lib",
"//source/common/protobuf:utility_lib",
],
)
Expand Down
5 changes: 3 additions & 2 deletions source/extensions/transport_sockets/alts/tsi_socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "source/common/buffer/buffer_impl.h"
#include "source/common/buffer/watermark_buffer.h"
#include "source/common/network/raw_buffer_socket.h"
#include "source/common/network/transport_socket_options_impl.h"
#include "source/extensions/transport_sockets/alts/noop_transport_socket_callbacks.h"
#include "source/extensions/transport_sockets/alts/tsi_frame_protector.h"
#include "source/extensions/transport_sockets/alts/tsi_handshaker.h"
Expand Down Expand Up @@ -129,12 +130,12 @@ class TsiSocket : public Network::TransportSocket,
/**
* An implementation of Network::TransportSocketFactory for TsiSocket
*/
class TsiSocketFactory : public Network::TransportSocketFactory {
class TsiSocketFactory : public Network::CommonTransportSocketFactory {
public:
TsiSocketFactory(HandshakerFactory handshaker_factory, HandshakeValidator handshake_validator);

bool implementsSecureTransport() const override;
bool usesProxyProtocolOptions() const override { return false; }

Network::TransportSocketPtr
createTransportSocket(Network::TransportSocketOptionsConstSharedPtr options) const override;

Expand Down
1 change: 1 addition & 0 deletions source/extensions/transport_sockets/common/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ envoy_cc_library(
"//envoy/network:connection_interface",
"//envoy/network:transport_socket_interface",
"//source/common/buffer:buffer_lib",
"//source/common/network:transport_socket_options_lib",
],
)
Loading