From 78f832db10751bb803f6a339f7515d7e1d724c8f Mon Sep 17 00:00:00 2001 From: danzh Date: Mon, 7 Oct 2024 11:11:29 -0400 Subject: [PATCH] Reapply "udp: set Don't Fragment(DF) bit in IP packet header (#36341)" (#36437) Commit Message: removed the static_assert as it is expected that some platform, especially some iOS versions supports neither of the socket options. In this case, Envoy wont' set DF bit. Additional Description: reland #36341 Risk Level: low Testing: new unit tests Docs Changes: N/A Release Notes: Yes Platform Specific Features: N/A Runtime guard: envoy.reloadable_features.udp_set_do_not_fragment --------- Signed-off-by: Dan Zhang Co-authored-by: Dan Zhang --- changelogs/current.yaml | 4 + envoy/network/socket.h | 19 ++++ .../common/listener_manager/listener_impl.cc | 7 ++ .../common/network/socket_option_factory.cc | 27 ++++++ source/common/network/socket_option_factory.h | 6 ++ source/common/quic/envoy_quic_utils.cc | 31 +++++- source/common/runtime/runtime_features.cc | 1 + .../listener_manager_impl_quic_only_test.cc | 95 +++++++++++++------ test/common/quic/BUILD | 1 + .../client_connection_factory_impl_test.cc | 76 +++++++++++++++ test/common/quic/envoy_quic_utils_test.cc | 73 +++++++++++++- tools/spelling/spelling_dictionary.txt | 1 + 12 files changed, 306 insertions(+), 35 deletions(-) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 9b844af99473..20f1e7683b35 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -116,6 +116,10 @@ minor_behavior_changes: change: | Changes the default value of ``envoy.reloadable_features.http2_use_oghttp2`` to ``false``. This changes the codec used for HTTP/2 requests and responses to address to address stability concerns. This behavior can be reverted by setting the feature to ``true``. +- area: udp + change: | + Set Don't Fragment (DF) flag bit on IP packet header on UDP listener sockets and QUIC upstream connection sockets. This behavior + can be reverted by setting ``envoy.reloadable_features.udp_set_do_not_fragment`` to false. - area: access_log change: | Sanitize SNI for potential log injection. The invalid character will be replaced by ``_`` with an ``invalid:`` marker. If runtime diff --git a/envoy/network/socket.h b/envoy/network/socket.h index 39c492754964..bed7b7be35aa 100644 --- a/envoy/network/socket.h +++ b/envoy/network/socket.h @@ -175,6 +175,25 @@ static_assert(IP_RECVDSTADDR == IP_SENDSRCADDR); #define ENVOY_ATTACH_REUSEPORT_CBPF Network::SocketOptionName() #endif +#if !defined(ANDROID) && defined(__APPLE__) +// Only include TargetConditionals after testing ANDROID as some Android builds +// on the Mac have this header available and it's not needed unless the target +// is really an Apple platform. +#include +#if !defined(TARGET_OS_IPHONE) || !TARGET_OS_IPHONE +// MAC_OS +#define ENVOY_IP_DONTFRAG ENVOY_MAKE_SOCKET_OPTION_NAME(IPPROTO_IP, IP_DONTFRAG) +#define ENVOY_IPV6_DONTFRAG ENVOY_MAKE_SOCKET_OPTION_NAME(IPPROTO_IPV6, IPV6_DONTFRAG) +#endif +#endif + +#if !defined(ENVOY_IP_DONTFRAG) && defined(IP_PMTUDISC_DO) +#define ENVOY_IP_MTU_DISCOVER ENVOY_MAKE_SOCKET_OPTION_NAME(IPPROTO_IP, IP_MTU_DISCOVER) +#define ENVOY_IP_MTU_DISCOVER_VALUE IP_PMTUDISC_DO +#define ENVOY_IPV6_MTU_DISCOVER ENVOY_MAKE_SOCKET_OPTION_NAME(IPPROTO_IPV6, IPV6_MTU_DISCOVER) +#define ENVOY_IPV6_MTU_DISCOVER_VALUE IPV6_PMTUDISC_DO +#endif + /** * Interface representing a single filter chain info. */ diff --git a/source/common/listener_manager/listener_impl.cc b/source/common/listener_manager/listener_impl.cc index 1e2c8cd03e45..23350a0c2270 100644 --- a/source/common/listener_manager/listener_impl.cc +++ b/source/common/listener_manager/listener_impl.cc @@ -677,6 +677,13 @@ void ListenerImpl::buildListenSocketOptions( addListenSocketOptions(listen_socket_options_list_[i], Network::SocketOptionFactory::buildUdpGroOptions()); } + if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.udp_set_do_not_fragment")) { + addListenSocketOptions( + listen_socket_options_list_[i], + Network::SocketOptionFactory::buildDoNotFragmentOptions( + /*mapped_v6*/ addresses_[i]->ip()->version() == Network::Address::IpVersion::v6 && + !addresses_[i]->ip()->ipv6()->v6only())); + } // Additional factory specific options. ASSERT(udp_listener_config_->listener_factory_ != nullptr, diff --git a/source/common/network/socket_option_factory.cc b/source/common/network/socket_option_factory.cc index 094ebca07423..83fc9bfc3043 100644 --- a/source/common/network/socket_option_factory.cc +++ b/source/common/network/socket_option_factory.cc @@ -181,5 +181,32 @@ std::unique_ptr SocketOptionFactory::buildIpRecvTosOptions() { return options; } +std::unique_ptr +SocketOptionFactory::buildDoNotFragmentOptions(bool supports_v4_mapped_v6_addresses) { + auto options = std::make_unique(); +#ifdef ENVOY_IP_DONTFRAG + options->push_back(std::make_shared( + envoy::config::core::v3::SocketOption::STATE_PREBIND, ENVOY_IP_DONTFRAG, ENVOY_IPV6_DONTFRAG, + 1)); + // v4 mapped v6 addresses don't support ENVOY_IP_DONTFRAG on MAC OS. + (void)supports_v4_mapped_v6_addresses; +#elif defined(ENVOY_IP_MTU_DISCOVER) + options->push_back(std::make_shared( + envoy::config::core::v3::SocketOption::STATE_PREBIND, ENVOY_IP_MTU_DISCOVER, + ENVOY_IP_MTU_DISCOVER_VALUE, ENVOY_IPV6_MTU_DISCOVER, ENVOY_IPV6_MTU_DISCOVER_VALUE)); + + if (supports_v4_mapped_v6_addresses) { + ENVOY_LOG_MISC(trace, "Also apply the V4 option to v6 socket to support v4-mapped addresses."); + options->push_back( + std::make_shared(envoy::config::core::v3::SocketOption::STATE_PREBIND, + ENVOY_IP_MTU_DISCOVER, ENVOY_IP_MTU_DISCOVER_VALUE)); + } +#else + (void)supports_v4_mapped_v6_addresses; + ENVOY_LOG_MISC(trace, "Platform supports neither socket option IP_DONTFRAG nor IP_MTU_DISCOVER"); +#endif + return options; +} + } // namespace Network } // namespace Envoy diff --git a/source/common/network/socket_option_factory.h b/source/common/network/socket_option_factory.h index 90f521bc672b..94bfd5f83912 100644 --- a/source/common/network/socket_option_factory.h +++ b/source/common/network/socket_option_factory.h @@ -38,6 +38,12 @@ class SocketOptionFactory : Logger::Loggable { static std::unique_ptr buildUdpGroOptions(); static std::unique_ptr buildZeroSoLingerOptions(); static std::unique_ptr buildIpRecvTosOptions(); + /** + * @param supports_v4_mapped_v6_addresses true if this option is to be applied to a v6 socket with + * v4-mapped v6 address(i.e. ::ffff:172.21.0.6) support. + */ + static std::unique_ptr + buildDoNotFragmentOptions(bool supports_v4_mapped_v6_addresses); }; } // namespace Network } // namespace Envoy diff --git a/source/common/quic/envoy_quic_utils.cc b/source/common/quic/envoy_quic_utils.cc index 098abd6b795d..28d59302e5c1 100644 --- a/source/common/quic/envoy_quic_utils.cc +++ b/source/common/quic/envoy_quic_utils.cc @@ -17,11 +17,13 @@ namespace Quic { namespace { Network::Address::InstanceConstSharedPtr -getLoopbackAddress(const Network::Address::IpVersion version) { - if (version == Network::Address::IpVersion::v6) { - return std::make_shared("::1"); +getLoopbackAddress(Network::Address::InstanceConstSharedPtr peer_address) { + if (peer_address->ip()->version() == Network::Address::IpVersion::v6) { + return std::make_shared( + "::1", 0, &peer_address->socketInterface(), peer_address->ip()->ipv6()->v6only()); } - return std::make_shared("127.0.0.1"); + return std::make_shared("127.0.0.1", + &peer_address->socketInterface()); } } // namespace @@ -200,7 +202,7 @@ createConnectionSocket(const Network::Address::InstanceConstSharedPtr& peer_addr Network::Socket::Type::Datagram, // Use the loopback address if `local_addr` is null, to pass in the socket interface used to // create the IoHandle, without having to make the more expensive `getifaddrs` call. - local_addr ? local_addr : getLoopbackAddress(peer_addr->ip()->version()), peer_addr, + local_addr ? local_addr : getLoopbackAddress(peer_addr), peer_addr, Network::SocketCreationOptions{false, max_addresses_cache_size}); connection_socket->setDetectedTransportProtocol("quic"); if (!connection_socket->isOpen()) { @@ -215,6 +217,25 @@ createConnectionSocket(const Network::Address::InstanceConstSharedPtr& peer_addr if (prefer_gro && Api::OsSysCallsSingleton::get().supportsUdpGro()) { connection_socket->addOptions(Network::SocketOptionFactory::buildUdpGroOptions()); } + if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.udp_set_do_not_fragment")) { + int v6_only = 0; + if (connection_socket->ipVersion().has_value() && + connection_socket->ipVersion().value() == Network::Address::IpVersion::v6) { + socklen_t v6_only_len = sizeof(v6_only); + Api::SysCallIntResult result = + connection_socket->getSocketOption(IPPROTO_IPV6, IPV6_V6ONLY, &v6_only, &v6_only_len); + if (result.return_value_ != 0) { + ENVOY_LOG_MISC( + error, "Failed to get IPV6_V6ONLY socket option, getsockopt() returned {}, errno {}", + result.return_value_, result.errno_); + connection_socket->close(); + return connection_socket; + } + } + connection_socket->addOptions(Network::SocketOptionFactory::buildDoNotFragmentOptions( + /*mapped_v6*/ connection_socket->ipVersion().value() == Network::Address::IpVersion::v6 && + v6_only == 0)); + } if (options != nullptr) { connection_socket->addOptions(options); } diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index 6ec39cd77dd3..83069571415c 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -94,6 +94,7 @@ RUNTIME_GUARD(envoy_reloadable_features_skip_dns_lookup_for_proxied_requests); RUNTIME_GUARD(envoy_reloadable_features_strict_duration_validation); RUNTIME_GUARD(envoy_reloadable_features_tcp_tunneling_send_downstream_fin_on_upstream_trailers); RUNTIME_GUARD(envoy_reloadable_features_test_feature_true); +RUNTIME_GUARD(envoy_reloadable_features_udp_set_do_not_fragment); RUNTIME_GUARD(envoy_reloadable_features_udp_socket_apply_aggregated_read_limit); RUNTIME_GUARD(envoy_reloadable_features_uhv_allow_malformed_url_encoding); RUNTIME_GUARD(envoy_reloadable_features_upstream_remote_address_use_connection); diff --git a/test/common/listener_manager/listener_manager_impl_quic_only_test.cc b/test/common/listener_manager/listener_manager_impl_quic_only_test.cc index 05dd2cf623bb..f088a12dd0ab 100644 --- a/test/common/listener_manager/listener_manager_impl_quic_only_test.cc +++ b/test/common/listener_manager/listener_manager_impl_quic_only_test.cc @@ -24,6 +24,21 @@ class MockSupportsUdpGso : public Api::OsSysCallsImpl { class ListenerManagerImplQuicOnlyTest : public ListenerManagerImplTest { public: + size_t expectedNumSocketOptions() { + // SO_REUSEPORT, IP_PKTINFO and IP_MTU_DISCOVER/IP_DONTFRAG. + const size_t num_platform_independent_socket_options = + Runtime::runtimeFeatureEnabled("envoy.reloadable_features.udp_set_do_not_fragment") ? 3 : 2; + size_t num_platform_dependent_socket_options = 0; +#ifdef SO_RXQ_OVFL + ++num_platform_dependent_socket_options; +#endif + if (Api::OsSysCallsSingleton::get().supportsUdpGro()) { + // SO_REUSEPORT + ++num_platform_dependent_socket_options; + } + return num_platform_dependent_socket_options + num_platform_independent_socket_options; + } + NiceMock udp_gso_syscall_; TestThreadsafeSingletonInjector os_calls{&udp_gso_syscall_}; Api::OsSysCallsImpl os_sys_calls_actual_; @@ -106,13 +121,7 @@ TEST_P(ListenerManagerImplQuicOnlyTest, QuicListenerFactoryAndSslContext) { .WillByDefault(Return(os_sys_calls_actual_.supportsUdpGso())); EXPECT_CALL(server_.api_.random_, uuid()); expectCreateListenSocket(envoy::config::core::v3::SocketOption::STATE_PREBIND, -#ifdef SO_RXQ_OVFL // SO_REUSEPORT is on as configured - /* expected_num_options */ - Api::OsSysCallsSingleton::get().supportsUdpGro() ? 4 : 3, -#else - /* expected_num_options */ - Api::OsSysCallsSingleton::get().supportsUdpGro() ? 3 : 2, -#endif + expectedNumSocketOptions(), ListenerComponentFactory::BindType::ReusePort); expectSetsockopt(/* expected_sockopt_level */ IPPROTO_IP, @@ -138,6 +147,18 @@ TEST_P(ListenerManagerImplQuicOnlyTest, QuicListenerFactoryAndSslContext) { } #endif +#ifdef ENVOY_IP_DONTFRAG + expectSetsockopt(/* expected_sockopt_level */ IPPROTO_IP, + /* expected_sockopt_name */ IP_DONTFRAG, + /* expected_value */ 1, + /* expected_num_calls */ 1); +#else + expectSetsockopt(/* expected_sockopt_level */ IPPROTO_IP, + /* expected_sockopt_name */ IP_MTU_DISCOVER, + /* expected_value */ IP_PMTUDISC_DO, + /* expected_num_calls */ 1); +#endif + addOrUpdateListener(listener_proto); EXPECT_EQ(1u, manager_->listeners().size()); EXPECT_FALSE(manager_->listeners()[0] @@ -195,13 +216,7 @@ TEST_P(ListenerManagerImplQuicOnlyTest, QuicWriterFromConfig) { ON_CALL(udp_gso_syscall_, supportsUdpGso()).WillByDefault(Return(true)); EXPECT_CALL(server_.api_.random_, uuid()); expectCreateListenSocket(envoy::config::core::v3::SocketOption::STATE_PREBIND, -#ifdef SO_RXQ_OVFL // SO_REUSEPORT is on as configured - /* expected_num_options */ - Api::OsSysCallsSingleton::get().supportsUdpGro() ? 4 : 3, -#else - /* expected_num_options */ - Api::OsSysCallsSingleton::get().supportsUdpGro() ? 3 : 2, -#endif + expectedNumSocketOptions(), ListenerComponentFactory::BindType::ReusePort); expectSetsockopt(/* expected_sockopt_level */ IPPROTO_IP, @@ -227,6 +242,18 @@ TEST_P(ListenerManagerImplQuicOnlyTest, QuicWriterFromConfig) { } #endif +#ifdef ENVOY_IP_DONTFRAG + expectSetsockopt(/* expected_sockopt_level */ IPPROTO_IP, + /* expected_sockopt_name */ IP_DONTFRAG, + /* expected_value */ 1, + /* expected_num_calls */ 1); +#else + expectSetsockopt(/* expected_sockopt_level */ IPPROTO_IP, + /* expected_sockopt_name */ IP_MTU_DISCOVER, + /* expected_value */ IP_PMTUDISC_DO, + /* expected_num_calls */ 1); +#endif + addOrUpdateListener(listener_proto); EXPECT_EQ(1u, manager_->listeners().size()); EXPECT_FALSE(manager_->listeners()[0] @@ -303,13 +330,7 @@ TEST_P(ListenerManagerImplQuicOnlyTest, QuicListenerFactoryWithExplictConnection ON_CALL(udp_gso_syscall_, supportsUdpGso()).WillByDefault(Return(true)); EXPECT_CALL(server_.api_.random_, uuid()); expectCreateListenSocket(envoy::config::core::v3::SocketOption::STATE_PREBIND, -#ifdef SO_RXQ_OVFL // SO_REUSEPORT is on as configured - /* expected_num_options */ - Api::OsSysCallsSingleton::get().supportsUdpGro() ? 4 : 3, -#else - /* expected_num_options */ - Api::OsSysCallsSingleton::get().supportsUdpGro() ? 3 : 2, -#endif + expectedNumSocketOptions(), ListenerComponentFactory::BindType::ReusePort); expectSetsockopt(/* expected_sockopt_level */ IPPROTO_IP, @@ -335,6 +356,18 @@ TEST_P(ListenerManagerImplQuicOnlyTest, QuicListenerFactoryWithExplictConnection } #endif +#ifdef ENVOY_IP_DONTFRAG + expectSetsockopt(/* expected_sockopt_level */ IPPROTO_IP, + /* expected_sockopt_name */ IP_DONTFRAG, + /* expected_value */ 1, + /* expected_num_calls */ 1); +#else + expectSetsockopt(/* expected_sockopt_level */ IPPROTO_IP, + /* expected_sockopt_name */ IP_MTU_DISCOVER, + /* expected_value */ IP_PMTUDISC_DO, + /* expected_num_calls */ 1); +#endif + addOrUpdateListener(listener_proto); EXPECT_EQ(1u, manager_->listeners().size()); EXPECT_FALSE(manager_->listeners()[0] @@ -359,13 +392,7 @@ TEST_P(ListenerManagerImplQuicOnlyTest, QuicListenerFilterFromConfig) { ON_CALL(udp_gso_syscall_, supportsUdpGso()).WillByDefault(Return(true)); EXPECT_CALL(server_.api_.random_, uuid()); expectCreateListenSocket(envoy::config::core::v3::SocketOption::STATE_PREBIND, -#ifdef SO_RXQ_OVFL // SO_REUSEPORT is on as configured - /* expected_num_options */ - Api::OsSysCallsSingleton::get().supportsUdpGro() ? 4 : 3, -#else - /* expected_num_options */ - Api::OsSysCallsSingleton::get().supportsUdpGro() ? 3 : 2, -#endif + expectedNumSocketOptions(), ListenerComponentFactory::BindType::ReusePort); expectSetsockopt(/* expected_sockopt_level */ IPPROTO_IP, @@ -391,6 +418,18 @@ TEST_P(ListenerManagerImplQuicOnlyTest, QuicListenerFilterFromConfig) { } #endif +#ifdef ENVOY_IP_DONTFRAG + expectSetsockopt(/* expected_sockopt_level */ IPPROTO_IP, + /* expected_sockopt_name */ IP_DONTFRAG, + /* expected_value */ 1, + /* expected_num_calls */ 1); +#else + expectSetsockopt(/* expected_sockopt_level */ IPPROTO_IP, + /* expected_sockopt_name */ IP_MTU_DISCOVER, + /* expected_value */ IP_PMTUDISC_DO, + /* expected_num_calls */ 1); +#endif + addOrUpdateListener(listener_proto); EXPECT_EQ(1u, manager_->listeners().size()); // Verify that the right filter chain type is installed. diff --git a/test/common/quic/BUILD b/test/common/quic/BUILD index 2f1c560192b5..36c0a8f1b57c 100644 --- a/test/common/quic/BUILD +++ b/test/common/quic/BUILD @@ -323,6 +323,7 @@ envoy_cc_test( "//test/mocks/upstream:cluster_info_mocks", "//test/mocks/upstream:transport_socket_match_mocks", "//test/test_common:test_runtime_lib", + "//test/test_common:threadsafe_singleton_injector_lib", ], ) diff --git a/test/common/quic/client_connection_factory_impl_test.cc b/test/common/quic/client_connection_factory_impl_test.cc index 5f505584b97b..7b407eb46f2f 100644 --- a/test/common/quic/client_connection_factory_impl_test.cc +++ b/test/common/quic/client_connection_factory_impl_test.cc @@ -14,6 +14,7 @@ #include "test/test_common/environment.h" #include "test/test_common/network_utility.h" #include "test/test_common/simulated_time_system.h" +#include "test/test_common/threadsafe_singleton_injector.h" #include "quiche/quic/core/crypto/quic_client_session_cache.h" #include "quiche/quic/core/deterministic_connection_id_generator.h" @@ -134,6 +135,49 @@ TEST_P(QuicNetworkConnectionTest, SocketOptions) { client_connection->close(Network::ConnectionCloseType::NoFlush); } +TEST_P(QuicNetworkConnectionTest, PreBindSocketOptionsFailure) { + initialize(); + + auto socket_option = std::make_shared(); + auto socket_options = std::make_shared(); + socket_options->push_back(socket_option); + EXPECT_CALL(*socket_option, setOption(_, envoy::config::core::v3::SocketOption::STATE_PREBIND)) + .WillOnce(Return(false)); + + std::unique_ptr client_connection = createQuicNetworkConnection( + *quic_info_, crypto_config_, + quic::QuicServerId{factory_->clientContextConfig()->serverNameIndication(), PEER_PORT}, + dispatcher_, test_address_, test_address_, quic_stat_names_, {}, *store_.rootScope(), + socket_options, nullptr, connection_id_generator_, *factory_); + EnvoyQuicClientSession* session = static_cast(client_connection.get()); + session->Initialize(); + client_connection->connect(); + EXPECT_FALSE(session->connection()->connected()); + EXPECT_EQ(client_connection->state(), Network::Connection::State::Closed); +} + +TEST_P(QuicNetworkConnectionTest, PostBindSocketOptionsFailure) { + initialize(); + + auto socket_option = std::make_shared(); + auto socket_options = std::make_shared(); + socket_options->push_back(socket_option); + EXPECT_CALL(*socket_option, setOption(_, envoy::config::core::v3::SocketOption::STATE_PREBIND)); + EXPECT_CALL(*socket_option, setOption(_, envoy::config::core::v3::SocketOption::STATE_BOUND)) + .WillOnce(Return(false)); + + std::unique_ptr client_connection = createQuicNetworkConnection( + *quic_info_, crypto_config_, + quic::QuicServerId{factory_->clientContextConfig()->serverNameIndication(), PEER_PORT}, + dispatcher_, test_address_, test_address_, quic_stat_names_, {}, *store_.rootScope(), + socket_options, nullptr, connection_id_generator_, *factory_); + EnvoyQuicClientSession* session = static_cast(client_connection.get()); + session->Initialize(); + client_connection->connect(); + EXPECT_FALSE(session->connection()->connected()); + EXPECT_EQ(client_connection->state(), Network::Connection::State::Closed); +} + TEST_P(QuicNetworkConnectionTest, LocalAddress) { initialize(); Network::Address::InstanceConstSharedPtr local_addr = @@ -151,9 +195,41 @@ TEST_P(QuicNetworkConnectionTest, LocalAddress) { EXPECT_TRUE(client_connection->connecting()); EXPECT_EQ(Network::Connection::State::Open, client_connection->state()); EXPECT_THAT(client_connection->connectionInfoProvider().localAddress(), testing::NotNull()); + if (GetParam() == Network::Address::IpVersion::v6) { + EXPECT_TRUE(client_connection->connectionInfoProvider().localAddress()->ip()->ipv6()->v6only()); + } client_connection->close(Network::ConnectionCloseType::NoFlush); } +class MockGetSockOptSysCalls : public Api::OsSysCallsImpl { +public: + MOCK_METHOD(Api::SysCallIntResult, getsockopt, + (os_fd_t sockfd, int level, int optname, void* optval, socklen_t* optlen)); +}; + +TEST_P(QuicNetworkConnectionTest, GetV6OnlySocketOptionFailure) { + if (GetParam() == Network::Address::IpVersion::v4) { + return; + } + initialize(); + MockGetSockOptSysCalls os_sys_calls; + TestThreadsafeSingletonInjector singleton_injector{&os_sys_calls}; + + EXPECT_CALL(os_sys_calls, getsockopt(_, IPPROTO_IPV6, IPV6_V6ONLY, _, _)) + .WillOnce(Return(Api::SysCallIntResult{-1, SOCKET_ERROR_NOT_SUP})); + std::unique_ptr client_connection = createQuicNetworkConnection( + *quic_info_, crypto_config_, + quic::QuicServerId{factory_->clientContextConfig()->serverNameIndication(), PEER_PORT}, + dispatcher_, test_address_, test_address_, quic_stat_names_, {}, *store_.rootScope(), nullptr, + nullptr, connection_id_generator_, *factory_); + EnvoyQuicClientSession* session = static_cast(client_connection.get()); + session->Initialize(); + client_connection->connect(); + EXPECT_TRUE(client_connection->connecting()); + EXPECT_FALSE(session->connection()->connected()); + EXPECT_EQ(client_connection->state(), Network::Connection::State::Closed); +} + TEST_P(QuicNetworkConnectionTest, Srtt) { initialize(); diff --git a/test/common/quic/envoy_quic_utils_test.cc b/test/common/quic/envoy_quic_utils_test.cc index 3fbfe54e07f4..c3ea2ff3c402 100644 --- a/test/common/quic/envoy_quic_utils_test.cc +++ b/test/common/quic/envoy_quic_utils_test.cc @@ -297,15 +297,58 @@ TEST(EnvoyQuicUtilsTest, CreateConnectionSocket) { EXPECT_TRUE(connection_socket->isOpen()); EXPECT_TRUE(connection_socket->ioHandle().wasConnected()); EXPECT_EQ("127.0.0.1", no_local_addr->ip()->addressAsString()); + if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.udp_set_do_not_fragment")) { + int value = 0; + socklen_t val_length = sizeof(value); +#ifdef ENVOY_IP_DONTFRAG + RELEASE_ASSERT(connection_socket->getSocketOption(IPPROTO_IP, IP_DONTFRAG, &value, &val_length) + .return_value_ == 0, + "Failed getsockopt IP_DONTFRAG"); + EXPECT_EQ(value, 1); +#else + RELEASE_ASSERT( + connection_socket->getSocketOption(IPPROTO_IP, IP_MTU_DISCOVER, &value, &val_length) + .return_value_ == 0, + "Failed getsockopt IP_MTU_DISCOVER"); + EXPECT_EQ(value, IP_PMTUDISC_DO); +#endif + } connection_socket->close(); Network::Address::InstanceConstSharedPtr local_addr_v6 = - std::make_shared("::1"); + std::make_shared("::1", 0, nullptr, /*v6only*/ true); Network::Address::InstanceConstSharedPtr peer_addr_v6 = - std::make_shared("::1", 54321, nullptr); + std::make_shared("::1", 54321, nullptr, /*v6only*/ false); connection_socket = createConnectionSocket(peer_addr_v6, local_addr_v6, nullptr); EXPECT_TRUE(connection_socket->isOpen()); EXPECT_TRUE(connection_socket->ioHandle().wasConnected()); + EXPECT_TRUE(local_addr_v6->ip()->ipv6()->v6only()); + if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.udp_set_do_not_fragment")) { + int value = 0; + socklen_t val_length = sizeof(value); +#ifdef ENVOY_IP_DONTFRAG + RELEASE_ASSERT( + connection_socket->getSocketOption(IPPROTO_IPV6, IPV6_DONTFRAG, &value, &val_length) + .return_value_ == 0, + "Failed getsockopt IPV6_DONTFRAG"); + ; + EXPECT_EQ(value, 1); +#else + RELEASE_ASSERT( + connection_socket->getSocketOption(IPPROTO_IPV6, IPV6_MTU_DISCOVER, &value, &val_length) + .return_value_ == 0, + "Failed getsockopt IPV6_MTU_DISCOVER"); + EXPECT_EQ(value, IPV6_PMTUDISC_DO); + // The v4 socket option is not applied to v6-only socket. + value = 0; + val_length = sizeof(value); + RELEASE_ASSERT( + connection_socket->getSocketOption(IPPROTO_IP, IP_MTU_DISCOVER, &value, &val_length) + .return_value_ == 0, + "Failed getsockopt IP_MTU_DISCOVER"); + EXPECT_NE(value, IP_PMTUDISC_DO); +#endif + } connection_socket->close(); Network::Address::InstanceConstSharedPtr no_local_addr_v6 = nullptr; @@ -313,6 +356,32 @@ TEST(EnvoyQuicUtilsTest, CreateConnectionSocket) { EXPECT_TRUE(connection_socket->isOpen()); EXPECT_TRUE(connection_socket->ioHandle().wasConnected()); EXPECT_EQ("::1", no_local_addr_v6->ip()->addressAsString()); + EXPECT_FALSE(no_local_addr_v6->ip()->ipv6()->v6only()); + if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.udp_set_do_not_fragment")) { + int value = 0; + socklen_t val_length = sizeof(value); +#ifdef ENVOY_IP_DONTFRAG + RELEASE_ASSERT( + connection_socket->getSocketOption(IPPROTO_IPV6, IPV6_DONTFRAG, &value, &val_length) + .return_value_ == 0, + "Failed getsockopt IPV6_DONTFRAG"); + EXPECT_EQ(value, 1); +#else + RELEASE_ASSERT( + connection_socket->getSocketOption(IPPROTO_IPV6, IPV6_MTU_DISCOVER, &value, &val_length) + .return_value_ == 0, + "Failed getsockopt IPV6_MTU_DISCOVER"); + EXPECT_EQ(value, IPV6_PMTUDISC_DO); + // The v4 socket option is also applied to dual stack socket. + value = 0; + val_length = sizeof(value); + RELEASE_ASSERT( + connection_socket->getSocketOption(IPPROTO_IP, IP_MTU_DISCOVER, &value, &val_length) + .return_value_ == 0, + "Failed getsockopt IP_MTU_DISCOVER"); + EXPECT_EQ(value, IP_PMTUDISC_DO); +#endif + } connection_socket->close(); } diff --git a/tools/spelling/spelling_dictionary.txt b/tools/spelling/spelling_dictionary.txt index 452a48b9a23e..cd5d25bc3fbb 100644 --- a/tools/spelling/spelling_dictionary.txt +++ b/tools/spelling/spelling_dictionary.txt @@ -45,6 +45,7 @@ deadcode DFP Dynatrace DOM +DONTFRAG Gasd GiB IPTOS