Skip to content
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

listen_socket: Support multiple options and allow socket options to be set before bind(). #2734

Merged
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
3109a2c
listen_socket: Allow socket options to be set before bind().
jrajahalme Mar 13, 2018
3495654
listen_socket: Support multiple socket options.
jrajahalme Mar 14, 2018
1b58b6f
listener: Add support for LDS API "transparent" option.
jrajahalme Mar 14, 2018
a8fed75
listen_socket: Use a vector instead of a list for options.
jrajahalme Mar 14, 2018
824d8c7
cluster_manager: Fix socket options passing.
jrajahalme Mar 14, 2018
fa23036
cluster_manager: Fix format.
jrajahalme Mar 15, 2018
66b1ca3
listener: Use enum for socket state, fix IP_TRANSPARENT handling.
jrajahalme Mar 15, 2018
7524e53
listener_manager: Fix nits.
jrajahalme Mar 15, 2018
d569fec
listener_manager: Fix errno value.
jrajahalme Mar 16, 2018
a71b544
listener_manager: Fix setting socket options for non-IP sockets.
jrajahalme Mar 16, 2018
d8664f5
listener_manager: Remove throw and clean up the option setting code.
jrajahalme Mar 16, 2018
5650236
Merge master, update envoy API reference
jrajahalme Mar 19, 2018
07792b7
listener_manager: Update to optional "transparent" option.
jrajahalme Mar 19, 2018
33dda97
listener_manager: Use absl::optional for 'transparent_', fix format.
jrajahalme Mar 20, 2018
8cf748f
Merge branch 'master' into socket-options-before-and-after-bind
jrajahalme Mar 20, 2018
13887b8
listen_socket: Rename Socket::setOption() as Socket::addOption().
jrajahalme Mar 20, 2018
4dc4b04
listen_socket: Transfer ownership of options to the Socket.
jrajahalme Mar 20, 2018
b154e73
test: Don't use get() on unique_ptr's when not needed.
jrajahalme Mar 20, 2018
14a9ec7
os_sys_calls: add setsockopt() and getsockopt().
jrajahalme Mar 21, 2018
da28038
listener_manager_impl_test: Fix build when IP_TRANSPARENT option is n…
jrajahalme Mar 21, 2018
a46328b
listener_manager: Remove 'transparent' handling from this PR.
jrajahalme Mar 23, 2018
d48f9b6
listen_socket: Fix comment.
jrajahalme Mar 23, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions include/envoy/api/os_sys_calls.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,17 @@ class OsSysCalls {
* @see man 2 stat
*/
virtual int stat(const char* pathname, struct stat* buf) PURE;

/**
* @see man 2 setsockopt
*/
virtual int setsockopt(int sockfd, int level, int optname, const void* optval,
socklen_t optlen) PURE;

/**
* @see man 2 getsockopt
*/
virtual int getsockopt(int sockfd, int level, int optname, void* optval, socklen_t* optlen) PURE;
};

typedef std::unique_ptr<OsSysCalls> OsSysCallsPtr;
Expand Down
30 changes: 18 additions & 12 deletions include/envoy/network/listen_socket.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <memory>
#include <vector>

#include "envoy/common/pure.h"
#include "envoy/network/address.h"
Expand Down Expand Up @@ -30,36 +31,41 @@ class Socket {
*/
virtual void close() PURE;

enum class SocketState { PreBind, PostBind };

/**
* Visitor class for setting socket options.
*/
class Options {
class Option {
public:
virtual ~Options() {}
virtual ~Option() {}

/**
* @param socket the socket on which to apply options.
* @param state the current state of the socket. Significant for options that can only be
* set for some particular state of the socket.
* @return true if succeeded, false otherwise.
*/
virtual bool setOptions(Socket& socket) const PURE;
virtual bool setOption(Socket& socket, SocketState state) const PURE;

/**
* @return bits that can be used to separate connections based on the options. Should return
* zero if connections with different options can be pooled together. This is limited
* to 32 bits to allow these bits to be efficiently combined into a larger hash key
* used in connection pool lookups.
* @param vector of bits that can be used to separate connections based on the options. Should
Copy link
Member

Choose a reason for hiding this comment

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

Nit: this is now a singular option.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

* return zero if connections with different options can be pooled together. This is
Copy link
Member

Choose a reason for hiding this comment

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

There is no return now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Comment updated, thanks for noticing this!

* limited to 32 bits to allow these bits to be efficiently combined into a larger hash
* key used in connection pool lookups.
*/
virtual uint32_t hashKey() const PURE;
virtual void hashKey(std::vector<uint8_t>& key) const PURE;
};
typedef std::shared_ptr<Options> OptionsSharedPtr;
typedef std::unique_ptr<Option> OptionPtr;
typedef std::shared_ptr<std::vector<OptionPtr>> OptionsSharedPtr;

/**
* Set the socket options for later retrieval with options().
* Add a socket option visitor for later retrieval with options().
*/
virtual void setOptions(const OptionsSharedPtr&) PURE;
virtual void addOption(OptionPtr&&) PURE;

/**
* @return the socket options stored earlier with setOptions(), if any.
* @return the socket options stored earlier with addOption() calls, if any.
*/
virtual const OptionsSharedPtr& options() const PURE;
};
Expand Down
2 changes: 1 addition & 1 deletion include/envoy/server/filter_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ class ListenerFactoryContext : public FactoryContext {
/**
* Store socket options to be set on the listen socket before listening.
*/
virtual void setListenSocketOptions(const Network::Socket::OptionsSharedPtr& options) PURE;
virtual void addListenSocketOption(Network::Socket::OptionPtr&& option) PURE;
};

/**
Expand Down
4 changes: 3 additions & 1 deletion include/envoy/server/listener_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ class ListenerComponentFactory {
/**
* Creates a socket.
* @param address supplies the socket's address.
* @param options to be set on the created socket just before calling 'bind()'.
* @param bind_to_port supplies whether to actually bind the socket.
* @return Network::SocketSharedPtr an initialized and potentially bound socket.
*/
virtual Network::SocketSharedPtr
createListenSocket(Network::Address::InstanceConstSharedPtr address, bool bind_to_port) PURE;
createListenSocket(Network::Address::InstanceConstSharedPtr address,
const Network::Socket::OptionsSharedPtr& options, bool bind_to_port) PURE;

/**
* Creates a list of filter factories.
Expand Down
10 changes: 10 additions & 0 deletions source/common/api/os_sys_calls_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,15 @@ void* OsSysCallsImpl::mmap(void* addr, size_t length, int prot, int flags, int f

int OsSysCallsImpl::stat(const char* pathname, struct stat* buf) { return ::stat(pathname, buf); }

int OsSysCallsImpl::setsockopt(int sockfd, int level, int optname, const void* optval,
socklen_t optlen) {
return ::setsockopt(sockfd, level, optname, optval, optlen);
}

int OsSysCallsImpl::getsockopt(int sockfd, int level, int optname, void* optval,
socklen_t* optlen) {
return ::getsockopt(sockfd, level, optname, optval, optlen);
}

} // namespace Api
} // namespace Envoy
2 changes: 2 additions & 0 deletions source/common/api/os_sys_calls_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ class OsSysCallsImpl : public OsSysCalls {
int ftruncate(int fd, off_t length) override;
void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset) override;
int stat(const char* pathname, struct stat* buf) override;
int setsockopt(int sockfd, int level, int optname, const void* optval, socklen_t optlen) override;
int getsockopt(int sockfd, int level, int optname, void* optval, socklen_t* optlen) override;
};

typedef ThreadSafeSingleton<OsSysCallsImpl> OsSysCallsSingleton;
Expand Down
16 changes: 9 additions & 7 deletions source/common/network/connection_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -539,13 +539,15 @@ ClientConnectionImpl::ClientConnectionImpl(
: ConnectionImpl(dispatcher, std::make_unique<ClientSocketImpl>(remote_address),
std::move(transport_socket), false) {
if (options) {
if (!options->setOptions(*socket_)) {
// Set a special error state to ensure asynchronous close to give the owner of the
// ConnectionImpl a chance to add callbacks and detect the "disconnect".
immediate_error_event_ = ConnectionEvent::LocalClose;
// Trigger a write event to close this connection out-of-band.
file_event_->activate(Event::FileReadyType::Write);
return;
for (const auto& option : *options) {
if (!option->setOption(*socket_, Socket::SocketState::PreBind)) {
// Set a special error state to ensure asynchronous close to give the owner of the
// ConnectionImpl a chance to add callbacks and detect the "disconnect".
immediate_error_event_ = ConnectionEvent::LocalClose;
// Trigger a write event to close this connection out-of-band.
file_event_->activate(Event::FileReadyType::Write);
return;
}
}
}
if (source_address != nullptr) {
Expand Down
23 changes: 20 additions & 3 deletions source/common/network/listen_socket_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,38 @@ void ListenSocketImpl::doBind() {
}
}

TcpListenSocket::TcpListenSocket(const Address::InstanceConstSharedPtr& address, bool bind_to_port)
void ListenSocketImpl::setListenSocketOptions(const Network::Socket::OptionsSharedPtr& options) {
if (options) {
for (const auto& option : *options) {
if (!option->setOption(*this, SocketState::PreBind)) {
throw EnvoyException("ListenSocket: Setting socket options failed");
}
}
}
}

TcpListenSocket::TcpListenSocket(const Address::InstanceConstSharedPtr& address,
const Network::Socket::OptionsSharedPtr& options,
bool bind_to_port)
: ListenSocketImpl(address->socket(Address::SocketType::Stream), address) {
RELEASE_ASSERT(fd_ != -1);

int on = 1;
int rc = setsockopt(fd_, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
RELEASE_ASSERT(rc != -1);

setListenSocketOptions(options);

if (bind_to_port) {
doBind();
}
}

TcpListenSocket::TcpListenSocket(int fd, const Address::InstanceConstSharedPtr& address)
: ListenSocketImpl(fd, address) {}
TcpListenSocket::TcpListenSocket(int fd, const Address::InstanceConstSharedPtr& address,
const Network::Socket::OptionsSharedPtr& options)
: ListenSocketImpl(fd, address) {
setListenSocketOptions(options);
}

UdsListenSocket::UdsListenSocket(const Address::InstanceConstSharedPtr& address)
: ListenSocketImpl(address->socket(Address::SocketType::Stream), address) {
Expand Down
14 changes: 11 additions & 3 deletions source/common/network/listen_socket_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ class SocketImpl : public virtual Socket {
fd_ = -1;
}
}
void setOptions(const OptionsSharedPtr& options) override { options_ = options; }
void addOption(OptionPtr&& option) override {
if (!options_) {
options_ = std::make_shared<std::vector<OptionPtr>>();
}
options_->emplace_back(std::move(option));
}
const OptionsSharedPtr& options() const override { return options_; }

protected:
Expand All @@ -44,15 +49,18 @@ class ListenSocketImpl : public SocketImpl {
: SocketImpl(fd, local_address) {}

void doBind();
void setListenSocketOptions(const Network::Socket::OptionsSharedPtr& options);
};

/**
* Wraps a unix socket.
*/
class TcpListenSocket : public ListenSocketImpl {
public:
TcpListenSocket(const Address::InstanceConstSharedPtr& address, bool bind_to_port);
TcpListenSocket(int fd, const Address::InstanceConstSharedPtr& address);
TcpListenSocket(const Address::InstanceConstSharedPtr& address,
const Network::Socket::OptionsSharedPtr& options, bool bind_to_port);
TcpListenSocket(int fd, const Address::InstanceConstSharedPtr& address,
const Network::Socket::OptionsSharedPtr& options);
};

typedef std::unique_ptr<TcpListenSocket> TcpListenSocketPtr;
Expand Down
17 changes: 10 additions & 7 deletions source/common/upstream/cluster_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -813,28 +813,31 @@ ClusterManagerImpl::ThreadLocalClusterManagerImpl::ClusterEntry::connPool(
}

// Inherit socket options from downstream connection, if set.
absl::optional<uint32_t> hash_key;
std::vector<uint8_t> hash_key = {uint8_t(protocol), uint8_t(priority)};

// Use downstream connection socket options for computing connection pool hash key, if any.
// This allows socket options to control connection pooling so that connections with
// different options are not pooled together.
bool have_options = false;
if (context && context->downstreamConnection()) {
const Network::ConnectionSocket::OptionsSharedPtr& options =
context->downstreamConnection()->socketOptions();
if (options) {
hash_key = options->hashKey();
for (const auto& option : *options) {
have_options = true;
option->hashKey(hash_key);
}
}
}

ConnPoolsContainer& container = parent_.host_http_conn_pool_map_[host];
const auto key = container.key(priority, protocol, hash_key ? hash_key.value() : 0);
if (!container.pools_[key]) {
container.pools_[key] = parent_.parent_.factory_.allocateConnPool(
if (!container.pools_[hash_key]) {
container.pools_[hash_key] = parent_.parent_.factory_.allocateConnPool(
parent_.thread_local_dispatcher_, host, priority, protocol,
hash_key ? context->downstreamConnection()->socketOptions() : nullptr);
have_options ? context->downstreamConnection()->socketOptions() : nullptr);
}

return container.pools_[key].get();
return container.pools_[hash_key].get();
}

ClusterManagerPtr ProdClusterManagerFactory::clusterManagerFromProto(
Expand Down
14 changes: 2 additions & 12 deletions source/common/upstream/cluster_manager_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
#include <cstdint>
#include <functional>
#include <list>
#include <map>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>

#include "envoy/config/bootstrap/v2/bootstrap.pb.h"
Expand Down Expand Up @@ -199,17 +199,7 @@ class ClusterManagerImpl : public ClusterManager, Logger::Loggable<Logger::Id::u
*/
struct ThreadLocalClusterManagerImpl : public ThreadLocal::ThreadLocalObject {
struct ConnPoolsContainer {
typedef std::unordered_map<uint64_t, Http::ConnectionPool::InstancePtr> ConnPools;

uint64_t key(ResourcePriority priority, Http::Protocol protocol, uint32_t hash_key) {
// One bit needed for priority
static_assert(NumResourcePriorities == 2,
"Fix shifts below to match number of bits needed for 'priority'");
// Two bits needed for protocol
static_assert(Http::NumProtocols <= 4,
"Fix shifts below to match number of bits needed for 'protocol'");
return uint64_t(hash_key) << 3 | uint64_t(protocol) << 1 | uint64_t(priority);
}
typedef std::map<std::vector<uint8_t>, Http::ConnectionPool::InstancePtr> ConnPools;
Copy link
Contributor

Choose a reason for hiding this comment

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

Why switch from unordered to ordered? Is it just because you don't have a hash function?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right. std::map supports a vector as a key out of the box, for unordered map I'd need to provide the hash function, I guess.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Looked into possible hash support, boost::hash would be nice, as there is no std hash combiner. Or then just keep this as unordered map for now.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, I've run into this before too. I'm fine leaving it as std::map, although other reviewers may feel more strongly about it.

Copy link
Member

Choose a reason for hiding this comment

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

No strong opinion that this has to be unordered, unless it affect performance. std::hash does support string as key so if we need a hash function we can just reuse some from that?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, no strong preference. IMHO, the STL has a lot of inconsistency around how hashing and comparison works with containers and inbuilt types, let's fix it only when we need to for perf reasons and prefer simplicity otherwise.


ConnPools pools_;
uint64_t drains_remaining_{};
Expand Down
1 change: 1 addition & 0 deletions source/server/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ envoy_cc_library(
"//include/envoy/server:listener_manager_interface",
"//include/envoy/server:transport_socket_config_interface",
"//include/envoy/server:worker_interface",
"//source/common/api:os_sys_calls_lib",
"//source/common/config:utility_lib",
"//source/common/config:well_known_names",
"//source/common/network:listen_socket_lib",
Expand Down
1 change: 1 addition & 0 deletions source/server/config_validation/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ class ValidationInstance : Logger::Loggable<Logger::Id::main>,
return ProdListenerComponentFactory::createListenerFilterFactoryList_(filters, context);
}
Network::SocketSharedPtr createListenSocket(Network::Address::InstanceConstSharedPtr,
const Network::Socket::OptionsSharedPtr&,
bool) override {
// Returned sockets are not currently used so we can return nothing here safely vs. a
// validation mock.
Expand Down
2 changes: 1 addition & 1 deletion source/server/http/admin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -620,7 +620,7 @@ AdminImpl::AdminImpl(const std::string& access_log_path, const std::string& prof
Network::Address::InstanceConstSharedPtr address, Server::Instance& server,
Stats::ScopePtr&& listener_scope)
: server_(server), profile_path_(profile_path),
socket_(new Network::TcpListenSocket(address, true)),
socket_(new Network::TcpListenSocket(address, nullptr, true)),
stats_(Http::ConnectionManagerImpl::generateStats("http.admin.", server_.stats())),
tracing_stats_(Http::ConnectionManagerImpl::generateTracingStats("http.admin.tracing.",
server_.stats())),
Expand Down
Loading