diff --git a/api/envoy/config/core/v3/socket_option.proto b/api/envoy/config/core/v3/socket_option.proto index 44f1ce3890ad5..0b1c7dad9971b 100644 --- a/api/envoy/config/core/v3/socket_option.proto +++ b/api/envoy/config/core/v3/socket_option.proto @@ -36,7 +36,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // :ref:`admin's ` socket_options etc. // // It should be noted that the name or level may have different values on different platforms. -// [#next-free-field: 7] +// [#next-free-field: 8] message SocketOption { option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.core.SocketOption"; @@ -51,6 +51,17 @@ message SocketOption { STATE_LISTENING = 2; } + enum SocketType { + // Apply the socket option irrespective of the socket type. + NONE = 0; + + // Apply the socket option to the sequenced connection-oriented socket type. + STREAM = 1; + + // Apply the socket type to the datagram connectionless socket type. + DATAGRAM = 2; + } + // An optional name to give this socket option for debugging, etc. // Uniqueness is not required and no special meaning is assumed. string description = 1; @@ -74,6 +85,11 @@ message SocketOption { // The state in which the option will be applied. When used in BindConfig // STATE_PREBIND is currently the only valid value. SocketState state = 6 [(validate.rules).enum = {defined_only: true}]; + + // The socket type (see man 2 socket) to apply the socket option to. If + // this field is not specified, the option will be applied irrespective + // of the socket type. + SocketType type = 7 [(validate.rules).enum = {defined_only: true}]; } message SocketOptionsOverride { diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 67c350f83d8ed..38f37cc16fd3e 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -535,6 +535,10 @@ new_features: QUIC stream reset error code will be added to transport failure reason. This behavior can be reverted by setting the runtime flag ``envoy.reloadable_features.report_stream_reset_error_code`` to ``false``. +- area: sockets + change: | + Added socket ``type`` field for specifying a socket type to apply the socket option to under :ref:`SocketOption + `. deprecated: - area: tracing diff --git a/source/common/network/socket_option_factory.cc b/source/common/network/socket_option_factory.cc index cfe4bf6d9f64d..1541e3b934fee 100644 --- a/source/common/network/socket_option_factory.cc +++ b/source/common/network/socket_option_factory.cc @@ -98,12 +98,19 @@ std::unique_ptr SocketOptionFactory::buildLiteralOptions( socket_option.DebugString()); continue; } + + absl::optional socket_type = absl::nullopt; + if (socket_option.type() == envoy::config::core::v3::SocketOption::STREAM) { + socket_type = Network::Socket::Type::Stream; + } else if (socket_option.type() == envoy::config::core::v3::SocketOption::DATAGRAM) { + socket_type = Network::Socket::Type::Datagram; + } options->emplace_back(std::make_shared( socket_option.state(), Network::SocketOptionName( socket_option.level(), socket_option.name(), fmt::format("{}/{}", socket_option.level(), socket_option.name())), - buf)); + buf, socket_type)); } return options; } diff --git a/source/common/network/socket_option_impl.cc b/source/common/network/socket_option_impl.cc index 4c7f0b651bdd1..5754b36c4290c 100644 --- a/source/common/network/socket_option_impl.cc +++ b/source/common/network/socket_option_impl.cc @@ -61,6 +61,8 @@ SocketOptionImpl::getOptionDetails(const Socket&, bool SocketOptionImpl::isSupported() const { return optname_.hasValue(); } +absl::optional SocketOptionImpl::socketType() const { return socket_type_; } + Api::SysCallIntResult SocketOptionImpl::setSocketOption(Socket& socket, const Network::SocketOptionName& optname, const void* value, size_t size) { diff --git a/source/common/network/socket_option_impl.h b/source/common/network/socket_option_impl.h index cea24762ae7fd..a909a1aae79de 100644 --- a/source/common/network/socket_option_impl.h +++ b/source/common/network/socket_option_impl.h @@ -38,6 +38,14 @@ class SocketOptionImpl : public Socket::Option, Logger::Loggable socketType() const; + /** * Set the option on the given socket. * @param socket the socket on which to apply the option. diff --git a/test/common/network/socket_option_factory_test.cc b/test/common/network/socket_option_factory_test.cc index 993dd98fdc25b..0d31fe5cf04d9 100644 --- a/test/common/network/socket_option_factory_test.cc +++ b/test/common/network/socket_option_factory_test.cc @@ -148,6 +148,7 @@ TEST_F(SocketOptionFactoryTest, TestBuildLiteralOptions) { auto linger_option = absl::StrFormat(linger_option_format, SOL_SOCKET, SO_LINGER, linger_bstr_formatted); ASSERT_TRUE(parser.ParseFromString(linger_option, &socket_option_proto)); + *socket_options_proto.Add() = socket_option_proto; static const char keepalive_option_format[] = R"proto( state: STATE_PREBIND @@ -159,8 +160,44 @@ TEST_F(SocketOptionFactoryTest, TestBuildLiteralOptions) { ASSERT_TRUE(parser.ParseFromString(keepalive_option, &socket_option_proto)); *socket_options_proto.Add() = socket_option_proto; + static const char all_socket_type_option_format[] = R"proto( + state: STATE_PREBIND + level: %d + name: %d + int_value: 1 + type: ALL + )proto"; + auto all_socket_type_option = + absl::StrFormat(all_socket_type_option_format, SOL_SOCKET, SO_KEEPALIVE); + ASSERT_TRUE(parser.ParseFromString(all_socket_type_option, &socket_option_proto)); + *socket_options_proto.Add() = socket_option_proto; + + static const char stream_socket_type_option_format[] = R"proto( + state: STATE_PREBIND + level: %d + name: %d + int_value: 1 + type: STREAM + )proto"; + auto stream_socket_type_option = + absl::StrFormat(stream_socket_type_option_format, SOL_SOCKET, SO_KEEPALIVE); + ASSERT_TRUE(parser.ParseFromString(stream_socket_type_option, &socket_option_proto)); + *socket_options_proto.Add() = socket_option_proto; + + static const char datagram_socket_type_option_format[] = R"proto( + state: STATE_PREBIND + level: %d + name: %d + int_value: 1 + type: DATAGRAM + )proto"; + auto datagram_socket_type_option = + absl::StrFormat(datagram_socket_type_option_format, SOL_SOCKET, SO_KEEPALIVE); + ASSERT_TRUE(parser.ParseFromString(datagram_socket_type_option, &socket_option_proto)); + *socket_options_proto.Add() = socket_option_proto; + auto socket_options = SocketOptionFactory::buildLiteralOptions(socket_options_proto); - EXPECT_EQ(2, socket_options->size()); + EXPECT_EQ(5, socket_options->size()); auto option_details = socket_options->at(0)->getOptionDetails( socket_mock_, envoy::config::core::v3::SocketOption::STATE_PREBIND); EXPECT_TRUE(option_details.has_value()); @@ -176,6 +213,17 @@ TEST_F(SocketOptionFactoryTest, TestBuildLiteralOptions) { int value = 1; absl::string_view value_bstr{reinterpret_cast(&value), sizeof(int)}; EXPECT_EQ(value_bstr, option_details->value_); + + auto none_socket_option = dynamic_pointer_cast(socket_options->at(2)); + EXPECT_FALSE(none_socket_option->socketType().has_value()); + + auto stream_socket_option = dynamic_pointer_cast(socket_options->at(3)); + EXPECT_TRUE(stream_socket_option->socketType().has_value()); + EXPECT_EQ(Socket::Type::Stream, *stream_socket_option->socketType()); + + auto datagram_socket_option = dynamic_pointer_cast(socket_options->at(4)); + EXPECT_TRUE(datagram_socket_option->socketType().has_value()); + EXPECT_EQ(Socket::Type::Datagram, *datagram_socket_option->socketType()); } TEST_F(SocketOptionFactoryTest, TestBuildZeroSoLingerOptions) { diff --git a/test/common/network/socket_option_impl_test.cc b/test/common/network/socket_option_impl_test.cc index 6979b7b7f3fb9..92297196ea586 100644 --- a/test/common/network/socket_option_impl_test.cc +++ b/test/common/network/socket_option_impl_test.cc @@ -6,6 +6,8 @@ namespace Envoy { namespace Network { namespace { +using ::testing::Return; + class SocketOptionImplTest : public SocketOptionTest {}; TEST_F(SocketOptionImplTest, BadFd) { @@ -59,6 +61,24 @@ TEST_F(SocketOptionImplTest, SetOptionSuccessTrue) { socket_option.setOption(socket_, envoy::config::core::v3::SocketOption::STATE_PREBIND)); } +TEST_F(SocketOptionImplTest, SetDatagramOptionOnStreamSocketType) { + ON_CALL(socket_, socketType()).WillByDefault(Return(Socket::Type::Stream)); + SocketOptionImpl socket_option{envoy::config::core::v3::SocketOption::STATE_PREBIND, + ENVOY_MAKE_SOCKET_OPTION_NAME(5, 10), 1, Socket::Type::Datagram}; + EXPECT_CALL(socket_, setSocketOption(_, _, _, _)).Times(0); + EXPECT_TRUE( + socket_option.setOption(socket_, envoy::config::core::v3::SocketOption::STATE_PREBIND)); +} + +TEST_F(SocketOptionImplTest, SetStreamOptionOnDatagramSocketType) { + ON_CALL(socket_, socketType()).WillByDefault(Return(Socket::Type::Datagram)); + SocketOptionImpl socket_option{envoy::config::core::v3::SocketOption::STATE_PREBIND, + ENVOY_MAKE_SOCKET_OPTION_NAME(5, 10), 1, Socket::Type::Stream}; + EXPECT_CALL(socket_, setSocketOption(_, _, _, _)).Times(0); + EXPECT_TRUE( + socket_option.setOption(socket_, envoy::config::core::v3::SocketOption::STATE_PREBIND)); +} + TEST_F(SocketOptionImplTest, GetOptionDetailsCorrectState) { SocketOptionImpl socket_option{envoy::config::core::v3::SocketOption::STATE_PREBIND, ENVOY_MAKE_SOCKET_OPTION_NAME(5, 10), 1};