Skip to content

Commit

Permalink
# This is a combination of 3 commits.
Browse files Browse the repository at this point in the history
# This is the 1st commit message:

Allow for config-specified sockopts, suport for listeners.

Upstream support will follow.

Signed-off-by: Trevor Schroeder <[email protected]>

# This is the commit message envoyproxy#1:

Make value a oneof { int, bytes } as a convenience.

Signed-off-by: Trevor Schroeder <[email protected]>

# This is the commit message envoyproxy#2:

Clang-format cluster_manager_impl_test.cc

Signed-off-by: Trevor Schroeder <[email protected]>
  • Loading branch information
tschroed committed Jun 21, 2018
1 parent c75fb59 commit 1bfc776
Show file tree
Hide file tree
Showing 25 changed files with 296 additions and 130 deletions.
38 changes: 38 additions & 0 deletions api/envoy/api/v2/core/address.proto
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,40 @@ message TcpKeepalive {
google.protobuf.UInt32Value keepalive_interval = 3;
}

// Generic socket option message. This would be used to set socket options that
// might not exist in upstream kernels or precompiled Envoy binaries.
message SocketOption {
// An optional name to give this socket option
// No special meaning is assumed.
string readable_name = 1;
// Corresponding to the level value passed to setsockopt, such as IPPROTO_TCP
int32 level = 2;
// The numeric name as passed to setsockopt
int32 name = 3;
// The setsockopt option value. Integers should be encoded in network byte order.
oneof value {
option (validate.required) = true;

// Because many sockopts take an int value.
int32 int = 4;
// Otherwise it's a byte buffer.
bytes buf = 5;
}
enum SocketState {
option (gogoproto.goproto_enum_prefix) = false;
// Socket options are applied after socket creation but before binding the socket to a port
STATE_PREBIND = 0;
// Socket options are applied after binding the socket to a port but before calling listen()
STATE_BOUND = 1;
// Socket options are applied after calling listen()
STATE_LISTENING = 2;
}
// For listeners the option can only be used on sockets in the given state.
// For BindConfig, state is ignored.
SocketState state = 6
[(validate.rules).message.required = true, (validate.rules).enum.defined_only = true];
}

message BindConfig {
// The address to bind to when creating a socket.
SocketAddress source_address = 1
Expand All @@ -87,6 +121,10 @@ message BindConfig {
// flag is not set (default), the socket is not modified, i.e. the option is
// neither enabled nor disabled.
google.protobuf.BoolValue freebind = 2;

// Additional socket options that may not be present in Envoy source code or
// precompiled binaries.
repeated SocketOption socket_options = 3;
}

// Addresses specify either a logical or physical address and port, which are
Expand Down
4 changes: 4 additions & 0 deletions api/envoy/api/v2/lds.proto
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ message Listener {
// nor disabled.
google.protobuf.BoolValue freebind = 11;

// Additional socket options that may not be present in Envoy source code or
// precompiled binaries.
repeated core.SocketOption socket_options = 13;

// Whether the listener should accept TCP Fast Open (TFO) connections.
// When this flag is set to a value greater than 0, the option TCP_FASTOPEN is enabled on
// the socket, with a queue length of the specified size
Expand Down
5 changes: 4 additions & 1 deletion include/envoy/network/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ envoy_cc_library(
envoy_cc_library(
name = "listen_socket_interface",
hdrs = ["listen_socket.h"],
deps = ["//include/envoy/network:address_interface"],
deps = [
"//include/envoy/network:address_interface",
"@envoy_api//envoy/api/v2/core:address_cc",
],
)

envoy_cc_library(
Expand Down
16 changes: 5 additions & 11 deletions include/envoy/network/listen_socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <memory>
#include <vector>

#include "envoy/api/v2/core/address.pb.h"
#include "envoy/common/pure.h"
#include "envoy/network/address.h"

Expand Down Expand Up @@ -33,15 +34,6 @@ class Socket {
*/
virtual void close() PURE;

enum class SocketState {
// Socket options are applied after socket creation but before binding the socket to a port
PreBind,
// Socket options are applied after binding the socket to a port but before calling listen()
PostBind,
// Socket options are applied after calling listen()
Listening,
};

/**
* Visitor class for setting socket options.
*/
Expand All @@ -55,7 +47,8 @@ class Socket {
* set for some particular state of the socket.
* @return true if succeeded, false otherwise.
*/
virtual bool setOption(Socket& socket, SocketState state) const PURE;
virtual bool setOption(Socket& socket,
envoy::api::v2::core::SocketOption::SocketState state) const PURE;

/**
* @param vector of bytes to which the option should append hash key data that will be used
Expand All @@ -73,7 +66,8 @@ class Socket {
return to;
}

static bool applyOptions(const OptionsSharedPtr& options, Socket& socket, SocketState state) {
static bool applyOptions(const OptionsSharedPtr& options, Socket& socket,
envoy::api::v2::core::SocketOption::SocketState state) {
if (options == nullptr) {
return true;
}
Expand Down
1 change: 1 addition & 0 deletions source/common/network/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ envoy_cc_library(
":socket_option_lib",
"//include/envoy/network:listen_socket_interface",
"//source/common/common:logger_lib",
"@envoy_api//envoy/api/v2/core:address_cc",
],
)

Expand Down
6 changes: 4 additions & 2 deletions source/common/network/addr_family_aware_socket_option_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@
namespace Envoy {
namespace Network {

bool AddrFamilyAwareSocketOptionImpl::setOption(Socket& socket, Socket::SocketState state) const {
bool AddrFamilyAwareSocketOptionImpl::setOption(
Socket& socket, envoy::api::v2::core::SocketOption::SocketState state) const {
return setIpSocketOption(socket, state, ipv4_option_, ipv6_option_);
}

bool AddrFamilyAwareSocketOptionImpl::setIpSocketOption(
Socket& socket, Socket::SocketState state, const std::unique_ptr<SocketOptionImpl>& ipv4_option,
Socket& socket, envoy::api::v2::core::SocketOption::SocketState state,
const std::unique_ptr<SocketOptionImpl>& ipv4_option,
const std::unique_ptr<SocketOptionImpl>& ipv6_option) {
// If this isn't IP, we're out of luck.
Address::InstanceConstSharedPtr address;
Expand Down
11 changes: 7 additions & 4 deletions source/common/network/addr_family_aware_socket_option_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ namespace Network {
class AddrFamilyAwareSocketOptionImpl : public Socket::Option,
Logger::Loggable<Logger::Id::connection> {
public:
AddrFamilyAwareSocketOptionImpl(Socket::SocketState in_state, SocketOptionName ipv4_optname,
SocketOptionName ipv6_optname, int value)
AddrFamilyAwareSocketOptionImpl(envoy::api::v2::core::SocketOption::SocketState in_state,
SocketOptionName ipv4_optname, SocketOptionName ipv6_optname,
int value)
: ipv4_option_(absl::make_unique<SocketOptionImpl>(in_state, ipv4_optname, value)),
ipv6_option_(absl::make_unique<SocketOptionImpl>(in_state, ipv6_optname, value)) {}

// Socket::Option
bool setOption(Socket& socket, Socket::SocketState state) const override;
bool setOption(Socket& socket,
envoy::api::v2::core::SocketOption::SocketState state) const override;
// The common socket options don't require a hash key.
void hashKey(std::vector<uint8_t>&) const override {}

Expand All @@ -42,7 +44,8 @@ class AddrFamilyAwareSocketOptionImpl : public Socket::Option,
* platform for fd after the above option level fallback semantics are taken into account or the
* socket is non-IP.
*/
static bool setIpSocketOption(Socket& socket, Socket::SocketState state,
static bool setIpSocketOption(Socket& socket,
envoy::api::v2::core::SocketOption::SocketState state,
const std::unique_ptr<SocketOptionImpl>& ipv4_option,
const std::unique_ptr<SocketOptionImpl>& ipv6_option);

Expand Down
3 changes: 2 additions & 1 deletion source/common/network/connection_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,8 @@ ClientConnectionImpl::ClientConnectionImpl(
const Network::ConnectionSocket::OptionsSharedPtr& options)
: ConnectionImpl(dispatcher, std::make_unique<ClientSocketImpl>(remote_address),
std::move(transport_socket), false) {
if (!Network::Socket::applyOptions(options, *socket_, Socket::SocketState::PreBind)) {
if (!Network::Socket::applyOptions(options, *socket_,
envoy::api::v2::core::SocketOption::STATE_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;
Expand Down
3 changes: 2 additions & 1 deletion source/common/network/listen_socket_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ void ListenSocketImpl::doBind() {
}

void ListenSocketImpl::setListenSocketOptions(const Network::Socket::OptionsSharedPtr& options) {
if (!Network::Socket::applyOptions(options, *this, Socket::SocketState::PreBind)) {
if (!Network::Socket::applyOptions(options, *this,
envoy::api::v2::core::SocketOption::STATE_PREBIND)) {
throw EnvoyException("ListenSocket: Setting socket options failed");
}
}
Expand Down
3 changes: 2 additions & 1 deletion source/common/network/listener_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ ListenerImpl::ListenerImpl(Event::DispatcherImpl& dispatcher, Socket& socket, Li
fmt::format("cannot listen on socket: {}", socket.localAddress()->asString()));
}

if (!Network::Socket::applyOptions(socket.options(), socket, Socket::SocketState::Listening)) {
if (!Network::Socket::applyOptions(socket.options(), socket,
envoy::api::v2::core::SocketOption::STATE_LISTENING)) {
throw CreateListenerException(fmt::format(
"cannot set post-listen socket option on socket: {}", socket.localAddress()->asString()));
}
Expand Down
46 changes: 37 additions & 9 deletions source/common/network/socket_option_factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,21 @@ std::unique_ptr<Socket::Options>
SocketOptionFactory::buildTcpKeepaliveOptions(Network::TcpKeepaliveConfig keepalive_config) {
std::unique_ptr<Socket::Options> options = absl::make_unique<Socket::Options>();
options->push_back(std::make_shared<Network::SocketOptionImpl>(
Network::Socket::SocketState::PreBind, ENVOY_SOCKET_SO_KEEPALIVE, 1));
envoy::api::v2::core::SocketOption::STATE_PREBIND, ENVOY_SOCKET_SO_KEEPALIVE, 1));

if (keepalive_config.keepalive_probes_.has_value()) {
options->push_back(std::make_shared<Network::SocketOptionImpl>(
Network::Socket::SocketState::PreBind, ENVOY_SOCKET_TCP_KEEPCNT,
envoy::api::v2::core::SocketOption::STATE_PREBIND, ENVOY_SOCKET_TCP_KEEPCNT,
keepalive_config.keepalive_probes_.value()));
}
if (keepalive_config.keepalive_interval_.has_value()) {
options->push_back(std::make_shared<Network::SocketOptionImpl>(
Network::Socket::SocketState::PreBind, ENVOY_SOCKET_TCP_KEEPINTVL,
envoy::api::v2::core::SocketOption::STATE_PREBIND, ENVOY_SOCKET_TCP_KEEPINTVL,
keepalive_config.keepalive_interval_.value()));
}
if (keepalive_config.keepalive_time_.has_value()) {
options->push_back(std::make_shared<Network::SocketOptionImpl>(
Network::Socket::SocketState::PreBind, ENVOY_SOCKET_TCP_KEEPIDLE,
envoy::api::v2::core::SocketOption::STATE_PREBIND, ENVOY_SOCKET_TCP_KEEPIDLE,
keepalive_config.keepalive_time_.value()));
}
return options;
Expand All @@ -33,27 +33,55 @@ SocketOptionFactory::buildTcpKeepaliveOptions(Network::TcpKeepaliveConfig keepal
std::unique_ptr<Socket::Options> SocketOptionFactory::buildIpFreebindOptions() {
std::unique_ptr<Socket::Options> options = absl::make_unique<Socket::Options>();
options->push_back(std::make_shared<Network::AddrFamilyAwareSocketOptionImpl>(
Network::Socket::SocketState::PreBind, ENVOY_SOCKET_IP_FREEBIND, ENVOY_SOCKET_IPV6_FREEBIND,
1));
envoy::api::v2::core::SocketOption::STATE_PREBIND, ENVOY_SOCKET_IP_FREEBIND,
ENVOY_SOCKET_IPV6_FREEBIND, 1));
return options;
}

std::unique_ptr<Socket::Options> SocketOptionFactory::buildIpTransparentOptions() {
std::unique_ptr<Socket::Options> options = absl::make_unique<Socket::Options>();
options->push_back(std::make_shared<Network::AddrFamilyAwareSocketOptionImpl>(
Network::Socket::SocketState::PreBind, ENVOY_SOCKET_IP_TRANSPARENT,
envoy::api::v2::core::SocketOption::STATE_PREBIND, ENVOY_SOCKET_IP_TRANSPARENT,
ENVOY_SOCKET_IPV6_TRANSPARENT, 1));
options->push_back(std::make_shared<Network::AddrFamilyAwareSocketOptionImpl>(
Network::Socket::SocketState::PostBind, ENVOY_SOCKET_IP_TRANSPARENT,
envoy::api::v2::core::SocketOption::STATE_BOUND, ENVOY_SOCKET_IP_TRANSPARENT,
ENVOY_SOCKET_IPV6_TRANSPARENT, 1));
return options;
}

std::unique_ptr<Socket::Options> SocketOptionFactory::buildLiteralOptions(
const Protobuf::RepeatedPtrField<envoy::api::v2::core::SocketOption>& socket_options) {
std::unique_ptr<Socket::Options> options = absl::make_unique<Socket::Options>();
std::string buf;
for (const auto& socket_option : socket_options) {
buf.clear();
int int_value;
switch (socket_option.value_case()) {
case envoy::api::v2::core::SocketOption::kIntValue:
int_value = socket_option.int_value();
buf.append(reinterpret_cast<char*>(&int_value), sizeof(int_value));
break;
case envoy::api::v2::core::SocketOption::kBufValue:
buf.append(socket_option.buf_value());
break;
default:
ENVOY_LOG(warn, "Socket option specified with no value: {}", socket_option.DebugString());
continue;
}
options->push_back(std::make_shared<Network::SocketOptionImpl>(
socket_option.state(),
Network::SocketOptionName(std::make_pair(socket_option.level(), socket_option.name())),
buf));
}
return options;
}

std::unique_ptr<Socket::Options>
SocketOptionFactory::buildTcpFastOpenOptions(uint32_t queue_length) {
std::unique_ptr<Socket::Options> options = absl::make_unique<Socket::Options>();
options->push_back(std::make_shared<Network::SocketOptionImpl>(
Network::Socket::SocketState::Listening, ENVOY_SOCKET_TCP_FASTOPEN, queue_length));
envoy::api::v2::core::SocketOption::STATE_LISTENING, ENVOY_SOCKET_TCP_FASTOPEN,
queue_length));
return options;
}

Expand Down
4 changes: 4 additions & 0 deletions source/common/network/socket_option_factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
#include <netinet/tcp.h>
#include <sys/socket.h>

#include "envoy/api/v2/core/address.pb.h"
#include "envoy/network/listen_socket.h"

#include "common/common/logger.h"
#include "common/protobuf/protobuf.h"

#include "absl/types/optional.h"

Expand All @@ -28,6 +30,8 @@ class SocketOptionFactory : Logger::Loggable<Logger::Id::connection> {
static std::unique_ptr<Socket::Options> buildIpFreebindOptions();
static std::unique_ptr<Socket::Options> buildIpTransparentOptions();
static std::unique_ptr<Socket::Options> buildTcpFastOpenOptions(uint32_t queue_length);
static std::unique_ptr<Socket::Options> buildLiteralOptions(
const Protobuf::RepeatedPtrField<envoy::api::v2::core::SocketOption>& socket_options);
};
} // namespace Network
} // namespace Envoy
9 changes: 5 additions & 4 deletions source/common/network/socket_option_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ namespace Envoy {
namespace Network {

// Socket::Option
bool SocketOptionImpl::setOption(Socket& socket, Socket::SocketState state) const {
bool SocketOptionImpl::setOption(Socket& socket,
envoy::api::v2::core::SocketOption::SocketState state) const {
if (in_state_ == state) {
const int error = SocketOptionImpl::setSocketOption(socket, optname_, value_);
if (error != 0) {
Expand All @@ -24,15 +25,15 @@ bool SocketOptionImpl::setOption(Socket& socket, Socket::SocketState state) cons
bool SocketOptionImpl::isSupported() const { return optname_.has_value(); }

int SocketOptionImpl::setSocketOption(Socket& socket, Network::SocketOptionName optname,
int value) {
const absl::string_view value) {

if (!optname.has_value()) {
errno = ENOTSUP;
return -1;
}
auto& os_syscalls = Api::OsSysCallsSingleton::get();
return os_syscalls.setsockopt(socket.fd(), optname.value().first, optname.value().second, &value,
sizeof(value));
return os_syscalls.setsockopt(socket.fd(), optname.value().first, optname.value().second,
value.data(), value.size());
}

} // namespace Network
Expand Down
18 changes: 13 additions & 5 deletions source/common/network/socket_option_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,23 +84,31 @@ typedef absl::optional<std::pair<int, int>> SocketOptionName;

class SocketOptionImpl : public Socket::Option, Logger::Loggable<Logger::Id::connection> {
public:
SocketOptionImpl(Socket::SocketState in_state, Network::SocketOptionName optname, int value)
SocketOptionImpl(envoy::api::v2::core::SocketOption::SocketState in_state,
Network::SocketOptionName optname, int value)
: SocketOptionImpl(in_state, optname,
absl::string_view(reinterpret_cast<char*>(&value), sizeof(value))) {}

SocketOptionImpl(envoy::api::v2::core::SocketOption::SocketState in_state,
Network::SocketOptionName optname, absl::string_view value)
: in_state_(in_state), optname_(optname), value_(value) {}

// Socket::Option
bool setOption(Socket& socket, Socket::SocketState state) const override;
bool setOption(Socket& socket,
envoy::api::v2::core::SocketOption::SocketState state) const override;

// The common socket options don't require a hash key.
void hashKey(std::vector<uint8_t>&) const override {}

bool isSupported() const;

static int setSocketOption(Socket& socket, Network::SocketOptionName optname, int value);
static int setSocketOption(Socket& socket, Network::SocketOptionName optname,
absl::string_view value);

private:
const Socket::SocketState in_state_;
const envoy::api::v2::core::SocketOption::SocketState in_state_;
const Network::SocketOptionName optname_;
const int value_;
const std::string value_;
};

} // namespace Network
Expand Down
Loading

0 comments on commit 1bfc776

Please sign in to comment.