diff --git a/CMakeLists.txt b/CMakeLists.txt index c3460501ab6..dd8453c4196 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -677,7 +677,6 @@ if(gRPC_BUILD_TESTS) add_dependencies(buildtests_c slice_buffer_test) add_dependencies(buildtests_c slice_string_helpers_test) add_dependencies(buildtests_c sockaddr_resolver_test) - add_dependencies(buildtests_c sockaddr_utils_test) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) add_dependencies(buildtests_c socket_utils_test) endif() @@ -929,6 +928,7 @@ if(gRPC_BUILD_TESTS) add_dependencies(buildtests_cxx settings_timeout_test) add_dependencies(buildtests_cxx shutdown_test) add_dependencies(buildtests_cxx simple_request_bad_client_test) + add_dependencies(buildtests_cxx sockaddr_utils_test) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) add_dependencies(buildtests_cxx stack_tracer_test) endif() @@ -6880,33 +6880,6 @@ target_link_libraries(sockaddr_resolver_test ) -endif() -if(gRPC_BUILD_TESTS) - -add_executable(sockaddr_utils_test - test/core/iomgr/sockaddr_utils_test.cc -) - -target_include_directories(sockaddr_utils_test - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/include - ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR} - ${_gRPC_RE2_INCLUDE_DIR} - ${_gRPC_SSL_INCLUDE_DIR} - ${_gRPC_UPB_GENERATED_DIR} - ${_gRPC_UPB_GRPC_GENERATED_DIR} - ${_gRPC_UPB_INCLUDE_DIR} - ${_gRPC_XXHASH_INCLUDE_DIR} - ${_gRPC_ZLIB_INCLUDE_DIR} -) - -target_link_libraries(sockaddr_utils_test - ${_gRPC_ALLTARGETS_LIBRARIES} - grpc_test_util -) - - endif() if(gRPC_BUILD_TESTS) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) @@ -13555,6 +13528,41 @@ target_link_libraries(simple_request_bad_client_test ) +endif() +if(gRPC_BUILD_TESTS) + +add_executable(sockaddr_utils_test + test/core/iomgr/sockaddr_utils_test.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc +) + +target_include_directories(sockaddr_utils_test + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR} + ${_gRPC_RE2_INCLUDE_DIR} + ${_gRPC_SSL_INCLUDE_DIR} + ${_gRPC_UPB_GENERATED_DIR} + ${_gRPC_UPB_GRPC_GENERATED_DIR} + ${_gRPC_UPB_INCLUDE_DIR} + ${_gRPC_XXHASH_INCLUDE_DIR} + ${_gRPC_ZLIB_INCLUDE_DIR} + third_party/googletest/googletest/include + third_party/googletest/googletest + third_party/googletest/googlemock/include + third_party/googletest/googlemock + ${_gRPC_PROTO_GENS_DIR} +) + +target_link_libraries(sockaddr_utils_test + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_test_util +) + + endif() if(gRPC_BUILD_TESTS) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index a7089e79270..c0829c11f11 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -3820,14 +3820,6 @@ targets: - test/core/client_channel/resolvers/sockaddr_resolver_test.cc deps: - grpc_test_util -- name: sockaddr_utils_test - build: test - language: c - headers: [] - src: - - test/core/iomgr/sockaddr_utils_test.cc - deps: - - grpc_test_util - name: socket_utils_test build: test language: c @@ -6180,6 +6172,15 @@ targets: - test/core/end2end/cq_verifier.cc deps: - grpc_test_util +- name: sockaddr_utils_test + gtest: true + build: test + language: c++ + headers: [] + src: + - test/core/iomgr/sockaddr_utils_test.cc + deps: + - grpc_test_util - name: ssl_server_fuzzer build: fuzzer language: c++ diff --git a/src/core/ext/transport/chttp2/server/chttp2_server.cc b/src/core/ext/transport/chttp2/server/chttp2_server.cc index 5079ffde0d1..ee01addc558 100644 --- a/src/core/ext/transport/chttp2/server/chttp2_server.cc +++ b/src/core/ext/transport/chttp2/server/chttp2_server.cc @@ -93,7 +93,9 @@ class Chttp2ServerListener : public Server::ListenerInterface { explicit ConfigFetcherWatcher(RefCountedPtr listener) : listener_(std::move(listener)) {} - void UpdateConfig(grpc_channel_args* args) override; + void UpdateConnectionManager( + RefCountedPtr + connection_manager) override; void StopServing() override; @@ -216,6 +218,8 @@ class Chttp2ServerListener : public Server::ListenerInterface { ConfigFetcherWatcher* config_fetcher_watcher_ = nullptr; Mutex channel_args_mu_; grpc_channel_args* args_ ABSL_GUARDED_BY(channel_args_mu_); + RefCountedPtr + connection_manager_ ABSL_GUARDED_BY(channel_args_mu_); Mutex mu_; // Signals whether grpc_tcp_server_start() has been called. bool started_ ABSL_GUARDED_BY(mu_) = false; @@ -236,22 +240,16 @@ class Chttp2ServerListener : public Server::ListenerInterface { // Chttp2ServerListener::ConfigFetcherWatcher // -void Chttp2ServerListener::ConfigFetcherWatcher::UpdateConfig( - grpc_channel_args* args) { - grpc_error* error = GRPC_ERROR_NONE; - args = listener_->args_modifier_(args, &error); - if (error != GRPC_ERROR_NONE) { - // TODO(yashykt): Set state to close down connections immediately - // after accepting. - GPR_ASSERT(0); - } - grpc_channel_args* args_to_destroy = nullptr; +void Chttp2ServerListener::ConfigFetcherWatcher::UpdateConnectionManager( + RefCountedPtr + connection_manager) { + RefCountedPtr + connection_manager_to_destroy; { MutexLock lock(&listener_->channel_args_mu_); - args_to_destroy = listener_->args_; - listener_->args_ = args; + connection_manager_to_destroy = listener_->connection_manager_; + listener_->connection_manager_ = std::move(connection_manager); } - grpc_channel_args_destroy(args_to_destroy); { MutexLock lock(&listener_->mu_); if (listener_->shutdown_) { @@ -261,8 +259,8 @@ void Chttp2ServerListener::ConfigFetcherWatcher::UpdateConfig( if (listener_->started_) return; } int port_temp; - error = grpc_tcp_server_add_port(listener_->tcp_server_, - &listener_->resolved_address_, &port_temp); + grpc_error* error = grpc_tcp_server_add_port( + listener_->tcp_server_, &listener_->resolved_address_, &port_temp); if (error != GRPC_ERROR_NONE) { GRPC_ERROR_UNREF(error); gpr_log(GPR_ERROR, "Error adding port to server: %s", @@ -705,9 +703,45 @@ void Chttp2ServerListener::OnAccept(void* arg, grpc_endpoint* tcp, grpc_tcp_server_acceptor* acceptor) { Chttp2ServerListener* self = static_cast(arg); grpc_channel_args* args = nullptr; + RefCountedPtr + connection_manager; { MutexLock lock(&self->channel_args_mu_); args = grpc_channel_args_copy(self->args_); + connection_manager = self->connection_manager_; + } + auto endpoint_cleanup = [&](grpc_error* error) { + grpc_endpoint_shutdown(tcp, error); + grpc_endpoint_destroy(tcp); + gpr_free(acceptor); + }; + if (self->server_->config_fetcher() != nullptr) { + if (connection_manager == nullptr) { + grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "No ConnectionManager configured. Closing connection."); + endpoint_cleanup(error); + grpc_channel_args_destroy(args); + return; + } + // TODO(yashykt): Maybe combine the following two arg modifiers into a + // single one. + absl::StatusOr args_result = + connection_manager->UpdateChannelArgsForConnection(args, tcp); + if (!args_result.ok()) { + gpr_log(GPR_DEBUG, "Closing connection: %s", + args_result.status().ToString().c_str()); + endpoint_cleanup(GRPC_ERROR_CREATE_FROM_COPIED_STRING( + args_result.status().ToString().c_str())); + return; + } + grpc_error* error = GRPC_ERROR_NONE; + args = self->args_modifier_(*args_result, &error); + if (error != GRPC_ERROR_NONE) { + gpr_log(GPR_DEBUG, "Closing connection: %s", grpc_error_string(error)); + endpoint_cleanup(error); + grpc_channel_args_destroy(args); + return; + } } auto connection = MakeOrphanable(accepting_pollset, acceptor, args); @@ -739,9 +773,7 @@ void Chttp2ServerListener::OnAccept(void* arg, grpc_endpoint* tcp, } } if (connection != nullptr) { - grpc_endpoint_shutdown(tcp, GRPC_ERROR_NONE); - grpc_endpoint_destroy(tcp); - gpr_free(acceptor); + endpoint_cleanup(GRPC_ERROR_NONE); } else { connection_ref->Start(std::move(listener_ref), tcp, args); } diff --git a/src/core/ext/xds/xds_api.cc b/src/core/ext/xds/xds_api.cc index 84e165d202c..6d8df2d3170 100644 --- a/src/core/ext/xds/xds_api.cc +++ b/src/core/ext/xds/xds_api.cc @@ -95,7 +95,9 @@ #include "src/core/lib/gpr/useful.h" #include "src/core/lib/gprpp/host_port.h" #include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/sockaddr.h" #include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/socket_utils.h" #include "src/core/lib/slice/slice_utils.h" namespace grpc_core { @@ -542,20 +544,49 @@ std::string XdsApi::LdsUpdate::HttpConnectionManager::HttpFilter::ToString() } // -// XdsApi::LdsUpdate::FilterChain::FilterChainMatch::CidrRange +// XdsApi::LdsUpdate::FilterChainData // -std::string -XdsApi::LdsUpdate::FilterChain::FilterChainMatch::CidrRange::ToString() const { - return absl::StrCat("{address_prefix=", address_prefix, - " prefix_len=", prefix_len, "}"); +std::string XdsApi::LdsUpdate::FilterChainData::ToString() const { + return absl::StrCat( + "{downstream_tls_context=", downstream_tls_context.ToString(), + " http_connection_manager=", http_connection_manager.ToString(), "}"); +} + +// +// XdsApi::LdsUpdate::FilterChainMap::CidrRange +// + +std::string XdsApi::LdsUpdate::FilterChainMap::CidrRange::ToString() const { + return absl::StrCat( + "{address_prefix=", grpc_sockaddr_to_string(&address, false), + ", prefix_len=", prefix_len, "}"); } // -// XdsApi::LdsUpdate::FilterChain::FilterChainMatch +// FilterChain // -std::string XdsApi::LdsUpdate::FilterChain::FilterChainMatch::ToString() const { +struct FilterChain { + struct FilterChainMatch { + uint32_t destination_port = 0; + std::vector prefix_ranges; + XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceType source_type = + XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceType::kAny; + std::vector + source_prefix_ranges; + std::vector source_ports; + std::vector server_names; + std::string transport_protocol; + std::vector application_protocols; + + std::string ToString() const; + } filter_chain_match; + + std::shared_ptr filter_chain_data; +}; + +std::string FilterChain::FilterChainMatch::ToString() const { absl::InlinedVector contents; if (destination_port != 0) { contents.push_back(absl::StrCat("destination_port=", destination_port)); @@ -568,10 +599,10 @@ std::string XdsApi::LdsUpdate::FilterChain::FilterChainMatch::ToString() const { contents.push_back(absl::StrCat( "prefix_ranges={", absl::StrJoin(prefix_ranges_content, ", "), "}")); } - if (source_type == XdsApi::LdsUpdate::FilterChain::FilterChainMatch:: - ConnectionSourceType::kSameIpOrLoopback) { + if (source_type == XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceType:: + kSameIpOrLoopback) { contents.push_back("source_type=SAME_IP_OR_LOOPBACK"); - } else if (source_type == XdsApi::LdsUpdate::FilterChain::FilterChainMatch:: + } else if (source_type == XdsApi::LdsUpdate::FilterChainMap:: ConnectionSourceType::kExternal) { contents.push_back("source_type=EXTERNAL"); } @@ -604,15 +635,40 @@ std::string XdsApi::LdsUpdate::FilterChain::FilterChainMatch::ToString() const { } // -// XdsApi::LdsUpdate::FilterChain +// XdsApi::LdsUpdate::FilterChainMap // -std::string XdsApi::LdsUpdate::FilterChain::ToString() const { - return absl::StrFormat( - "{filter_chain_match=%s, downstream_tls_context=%s, " - "http_connection_manager=%s}", - filter_chain_match.ToString(), downstream_tls_context.ToString(), - http_connection_manager.ToString()); +std::string XdsApi::LdsUpdate::FilterChainMap::ToString() const { + std::vector contents; + for (const auto& destination_ip : destination_ip_vector) { + for (int source_type = 0; source_type < 3; ++source_type) { + for (const auto& source_ip : + destination_ip.source_types_array[source_type]) { + for (const auto& source_port_pair : source_ip.ports_map) { + FilterChain::FilterChainMatch filter_chain_match; + if (destination_ip.prefix_range.has_value()) { + filter_chain_match.prefix_ranges.push_back( + *destination_ip.prefix_range); + } + filter_chain_match.source_type = static_cast< + XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceType>( + source_type); + if (source_ip.prefix_range.has_value()) { + filter_chain_match.source_prefix_ranges.push_back( + *source_ip.prefix_range); + } + if (source_port_pair.first != 0) { + filter_chain_match.source_ports.push_back(source_port_pair.first); + } + contents.push_back(absl::StrCat( + "{filter_chain_match=", filter_chain_match.ToString(), + ", filter_chain=", source_port_pair.second.data->ToString(), + "}")); + } + } + } + } + return absl::StrCat("{", absl::StrJoin(contents, ", "), "}"); } // @@ -623,12 +679,8 @@ std::string XdsApi::LdsUpdate::ToString() const { absl::InlinedVector contents; if (type == ListenerType::kTcpListener) { contents.push_back(absl::StrCat("address=", address)); - std::vector filter_chains_content; - for (const auto& filter_chain : filter_chains) { - filter_chains_content.push_back(filter_chain.ToString()); - } - contents.push_back(absl::StrCat( - "filter_chains={", absl::StrJoin(filter_chains_content, ", "), "}")); + contents.push_back( + absl::StrCat("filter_chain_map=", filter_chain_map.ToString())); if (default_filter_chain.has_value()) { contents.push_back(absl::StrCat("default_filter_chain=", default_filter_chain->ToString())); @@ -1974,74 +2026,6 @@ grpc_error* LdsResponseParseClient( &lds_update->http_connection_manager); } -XdsApi::LdsUpdate::FilterChain::FilterChainMatch::CidrRange CidrRangeParse( - const envoy_config_core_v3_CidrRange* cidr_range_proto) { - uint32_t prefix_len = 0; - auto* prefix_len_proto = - envoy_config_core_v3_CidrRange_prefix_len(cidr_range_proto); - if (prefix_len_proto != nullptr) { - prefix_len = google_protobuf_UInt32Value_value(prefix_len_proto); - } - return {UpbStringToStdString( - envoy_config_core_v3_CidrRange_address_prefix(cidr_range_proto)), - prefix_len}; -} - -XdsApi::LdsUpdate::FilterChain::FilterChainMatch FilterChainMatchParse( - const envoy_config_listener_v3_FilterChainMatch* filter_chain_match_proto) { - XdsApi::LdsUpdate::FilterChain::FilterChainMatch filter_chain_match; - auto* destination_port = - envoy_config_listener_v3_FilterChainMatch_destination_port( - filter_chain_match_proto); - if (destination_port != nullptr) { - filter_chain_match.destination_port = - google_protobuf_UInt32Value_value(destination_port); - } - size_t size = 0; - auto* prefix_ranges = envoy_config_listener_v3_FilterChainMatch_prefix_ranges( - filter_chain_match_proto, &size); - filter_chain_match.prefix_ranges.reserve(size); - for (size_t i = 0; i < size; i++) { - filter_chain_match.prefix_ranges.push_back( - CidrRangeParse(prefix_ranges[i])); - } - filter_chain_match.source_type = static_cast< - XdsApi::LdsUpdate::FilterChain::FilterChainMatch::ConnectionSourceType>( - envoy_config_listener_v3_FilterChainMatch_source_type( - filter_chain_match_proto)); - auto* source_prefix_ranges = - envoy_config_listener_v3_FilterChainMatch_source_prefix_ranges( - filter_chain_match_proto, &size); - filter_chain_match.source_prefix_ranges.reserve(size); - for (size_t i = 0; i < size; i++) { - filter_chain_match.source_prefix_ranges.push_back( - CidrRangeParse(source_prefix_ranges[i])); - } - auto* source_ports = envoy_config_listener_v3_FilterChainMatch_source_ports( - filter_chain_match_proto, &size); - filter_chain_match.source_ports.reserve(size); - for (size_t i = 0; i < size; i++) { - filter_chain_match.source_ports.push_back(source_ports[i]); - } - auto* server_names = envoy_config_listener_v3_FilterChainMatch_server_names( - filter_chain_match_proto, &size); - for (size_t i = 0; i < size; i++) { - filter_chain_match.server_names.push_back( - UpbStringToStdString(server_names[i])); - } - filter_chain_match.transport_protocol = UpbStringToStdString( - envoy_config_listener_v3_FilterChainMatch_transport_protocol( - filter_chain_match_proto)); - auto* application_protocols = - envoy_config_listener_v3_FilterChainMatch_application_protocols( - filter_chain_match_proto, &size); - for (size_t i = 0; i < size; i++) { - filter_chain_match.application_protocols.push_back( - UpbStringToStdString(application_protocols[i])); - } - return filter_chain_match; -} - grpc_error* DownstreamTlsContextParse( const EncodingContext& context, const envoy_config_core_v3_TransportSocket* transport_socket, @@ -2089,17 +2073,101 @@ grpc_error* DownstreamTlsContextParse( return GRPC_ERROR_NONE; } +grpc_error* CidrRangeParse( + const envoy_config_core_v3_CidrRange* cidr_range_proto, + XdsApi::LdsUpdate::FilterChainMap::CidrRange* cidr_range) { + std::string address_prefix = UpbStringToStdString( + envoy_config_core_v3_CidrRange_address_prefix(cidr_range_proto)); + grpc_error* error = grpc_string_to_sockaddr_new(&cidr_range->address, + address_prefix.c_str(), 0); + if (error != GRPC_ERROR_NONE) return error; + cidr_range->prefix_len = 0; + auto* prefix_len_proto = + envoy_config_core_v3_CidrRange_prefix_len(cidr_range_proto); + if (prefix_len_proto != nullptr) { + cidr_range->prefix_len = std::min( + google_protobuf_UInt32Value_value(prefix_len_proto), + (reinterpret_cast(cidr_range->address.addr)) + ->sa_family == GRPC_AF_INET + ? uint32_t(32) + : uint32_t(128)); + } + // Normalize the network address by masking it with prefix_len + grpc_sockaddr_mask_bits(&cidr_range->address, cidr_range->prefix_len); + return GRPC_ERROR_NONE; +} + +grpc_error* FilterChainMatchParse( + const envoy_config_listener_v3_FilterChainMatch* filter_chain_match_proto, + FilterChain::FilterChainMatch* filter_chain_match) { + auto* destination_port = + envoy_config_listener_v3_FilterChainMatch_destination_port( + filter_chain_match_proto); + if (destination_port != nullptr) { + filter_chain_match->destination_port = + google_protobuf_UInt32Value_value(destination_port); + } + size_t size = 0; + auto* prefix_ranges = envoy_config_listener_v3_FilterChainMatch_prefix_ranges( + filter_chain_match_proto, &size); + filter_chain_match->prefix_ranges.reserve(size); + for (size_t i = 0; i < size; i++) { + XdsApi::LdsUpdate::FilterChainMap::CidrRange cidr_range; + grpc_error* error = CidrRangeParse(prefix_ranges[i], &cidr_range); + if (error != GRPC_ERROR_NONE) return error; + filter_chain_match->prefix_ranges.push_back(cidr_range); + } + filter_chain_match->source_type = + static_cast( + envoy_config_listener_v3_FilterChainMatch_source_type( + filter_chain_match_proto)); + auto* source_prefix_ranges = + envoy_config_listener_v3_FilterChainMatch_source_prefix_ranges( + filter_chain_match_proto, &size); + filter_chain_match->source_prefix_ranges.reserve(size); + for (size_t i = 0; i < size; i++) { + XdsApi::LdsUpdate::FilterChainMap::CidrRange cidr_range; + grpc_error* error = CidrRangeParse(source_prefix_ranges[i], &cidr_range); + if (error != GRPC_ERROR_NONE) return error; + filter_chain_match->source_prefix_ranges.push_back(cidr_range); + } + auto* source_ports = envoy_config_listener_v3_FilterChainMatch_source_ports( + filter_chain_match_proto, &size); + filter_chain_match->source_ports.reserve(size); + for (size_t i = 0; i < size; i++) { + filter_chain_match->source_ports.push_back(source_ports[i]); + } + auto* server_names = envoy_config_listener_v3_FilterChainMatch_server_names( + filter_chain_match_proto, &size); + for (size_t i = 0; i < size; i++) { + filter_chain_match->server_names.push_back( + UpbStringToStdString(server_names[i])); + } + filter_chain_match->transport_protocol = UpbStringToStdString( + envoy_config_listener_v3_FilterChainMatch_transport_protocol( + filter_chain_match_proto)); + auto* application_protocols = + envoy_config_listener_v3_FilterChainMatch_application_protocols( + filter_chain_match_proto, &size); + for (size_t i = 0; i < size; i++) { + filter_chain_match->application_protocols.push_back( + UpbStringToStdString(application_protocols[i])); + } + return GRPC_ERROR_NONE; +} + grpc_error* FilterChainParse( const EncodingContext& context, const envoy_config_listener_v3_FilterChain* filter_chain_proto, bool is_v2, - XdsApi::LdsUpdate::FilterChain* filter_chain) { + FilterChain* filter_chain) { grpc_error* error = GRPC_ERROR_NONE; auto* filter_chain_match = envoy_config_listener_v3_FilterChain_filter_chain_match( filter_chain_proto); if (filter_chain_match != nullptr) { - filter_chain->filter_chain_match = - FilterChainMatchParse(filter_chain_match); + error = FilterChainMatchParse(filter_chain_match, + &filter_chain->filter_chain_match); + if (error != GRPC_ERROR_NONE) return error; } // Parse the filters list. Currently we only support HttpConnectionManager. size_t size = 0; @@ -2135,9 +2203,11 @@ grpc_error* FilterChainParse( "Could not parse HttpConnectionManager config from filter " "typed_config"); } - error = HttpConnectionManagerParse(false /* is_client */, context, - http_connection_manager, is_v2, - &filter_chain->http_connection_manager); + filter_chain->filter_chain_data = + std::make_shared(); + error = HttpConnectionManagerParse( + false /* is_client */, context, http_connection_manager, is_v2, + &filter_chain->filter_chain_data->http_connection_manager); if (error != GRPC_ERROR_NONE) return error; // Get the DownstreamTlsContext for the filter chain if (XdsSecurityEnabled()) { @@ -2145,8 +2215,9 @@ grpc_error* FilterChainParse( envoy_config_listener_v3_FilterChain_transport_socket( filter_chain_proto); if (transport_socket != nullptr) { - error = DownstreamTlsContextParse(context, transport_socket, - &filter_chain->downstream_tls_context); + error = DownstreamTlsContextParse( + context, transport_socket, + &filter_chain->filter_chain_data->downstream_tls_context); } } return error; @@ -2176,6 +2247,199 @@ grpc_error* AddressParse(const envoy_config_core_v3_Address* address_proto, return GRPC_ERROR_NONE; } +// An intermediate map for filter chains that we create to validate the list of +// filter chains received from the control plane and to finally create +// XdsApi::LdsUpdate::FilterChainMap +struct InternalFilterChainMap { + using SourceIpMap = + std::map; + using ConnectionSourceTypesArray = std::array; + struct DestinationIp { + absl::optional prefix_range; + bool transport_protocol_raw_buffer_provided = false; + ConnectionSourceTypesArray source_types_array; + }; + using DestinationIpMap = std::map; + DestinationIpMap destination_ip_map; +}; + +grpc_error* AddFilterChainDataForSourcePort( + const FilterChain& filter_chain, + XdsApi::LdsUpdate::FilterChainMap::SourcePortsMap* ports_map, + uint32_t port) { + auto insert_result = ports_map->emplace( + port, XdsApi::LdsUpdate::FilterChainMap::FilterChainDataSharedPtr{ + filter_chain.filter_chain_data}); + if (!insert_result.second) { + return GRPC_ERROR_CREATE_FROM_COPIED_STRING( + absl::StrCat( + "Duplicate matching rules detected when adding filter chain: ", + filter_chain.filter_chain_match.ToString()) + .c_str()); + } + return GRPC_ERROR_NONE; +} + +grpc_error* AddFilterChainDataForSourcePorts( + const FilterChain& filter_chain, + XdsApi::LdsUpdate::FilterChainMap::SourcePortsMap* ports_map) { + if (filter_chain.filter_chain_match.source_ports.empty()) { + return AddFilterChainDataForSourcePort(filter_chain, ports_map, 0); + } else { + for (uint32_t port : filter_chain.filter_chain_match.source_ports) { + grpc_error* error = + AddFilterChainDataForSourcePort(filter_chain, ports_map, port); + if (error != GRPC_ERROR_NONE) return error; + } + } + return GRPC_ERROR_NONE; +} + +grpc_error* AddFilterChainDataForSourceIpRange( + const FilterChain& filter_chain, + InternalFilterChainMap::SourceIpMap* source_ip_map) { + if (filter_chain.filter_chain_match.source_prefix_ranges.empty()) { + auto insert_result = source_ip_map->emplace( + "", XdsApi::LdsUpdate::FilterChainMap::SourceIp()); + return AddFilterChainDataForSourcePorts( + filter_chain, &insert_result.first->second.ports_map); + } else { + for (const auto& prefix_range : + filter_chain.filter_chain_match.source_prefix_ranges) { + auto insert_result = source_ip_map->emplace( + absl::StrCat(grpc_sockaddr_to_string(&prefix_range.address, false), + "/", prefix_range.prefix_len), + XdsApi::LdsUpdate::FilterChainMap::SourceIp()); + if (insert_result.second) { + insert_result.first->second.prefix_range.emplace(prefix_range); + } + grpc_error* error = AddFilterChainDataForSourcePorts( + filter_chain, &insert_result.first->second.ports_map); + if (error != GRPC_ERROR_NONE) return error; + } + } + return GRPC_ERROR_NONE; +} + +grpc_error* AddFilterChainDataForSourceType( + const FilterChain& filter_chain, + InternalFilterChainMap::DestinationIp* destination_ip) { + GPR_ASSERT(static_cast( + filter_chain.filter_chain_match.source_type) < 3); + return AddFilterChainDataForSourceIpRange( + filter_chain, &destination_ip->source_types_array[static_cast( + filter_chain.filter_chain_match.source_type)]); +} + +grpc_error* AddFilterChainDataForApplicationProtocols( + const FilterChain& filter_chain, + InternalFilterChainMap::DestinationIp* destination_ip) { + // Only allow filter chains that do not mention application protocols + if (!filter_chain.filter_chain_match.application_protocols.empty()) { + return GRPC_ERROR_NONE; + } + return AddFilterChainDataForSourceType(filter_chain, destination_ip); +} + +grpc_error* AddFilterChainDataForTransportProtocol( + const FilterChain& filter_chain, + InternalFilterChainMap::DestinationIp* destination_ip) { + const std::string& transport_protocol = + filter_chain.filter_chain_match.transport_protocol; + // Only allow filter chains with no transport protocol or "raw_buffer" + if (!transport_protocol.empty() && transport_protocol != "raw_buffer") { + return GRPC_ERROR_NONE; + } + // If for this configuration, we've already seen filter chains that mention + // the transport protocol as "raw_buffer", we will never match filter chains + // that do not mention it. + if (destination_ip->transport_protocol_raw_buffer_provided && + transport_protocol.empty()) { + return GRPC_ERROR_NONE; + } + if (!transport_protocol.empty() && + !destination_ip->transport_protocol_raw_buffer_provided) { + destination_ip->transport_protocol_raw_buffer_provided = true; + // Clear out the previous entries if any since those entries did not mention + // "raw_buffer" + destination_ip->source_types_array = + InternalFilterChainMap::ConnectionSourceTypesArray(); + } + return AddFilterChainDataForApplicationProtocols(filter_chain, + destination_ip); +} + +grpc_error* AddFilterChainDataForServerNames( + const FilterChain& filter_chain, + InternalFilterChainMap::DestinationIp* destination_ip) { + // Don't continue adding filter chains with server names mentioned + if (!filter_chain.filter_chain_match.server_names.empty()) { + return GRPC_ERROR_NONE; + } + return AddFilterChainDataForTransportProtocol(filter_chain, destination_ip); +} + +grpc_error* AddFilterChainDataForDestinationIpRange( + const FilterChain& filter_chain, + InternalFilterChainMap::DestinationIpMap* destination_ip_map) { + if (filter_chain.filter_chain_match.prefix_ranges.empty()) { + auto insert_result = destination_ip_map->emplace( + "", InternalFilterChainMap::DestinationIp()); + return AddFilterChainDataForServerNames(filter_chain, + &insert_result.first->second); + } else { + for (const auto& prefix_range : + filter_chain.filter_chain_match.prefix_ranges) { + auto insert_result = destination_ip_map->emplace( + absl::StrCat(grpc_sockaddr_to_string(&prefix_range.address, false), + "/", prefix_range.prefix_len), + InternalFilterChainMap::DestinationIp()); + if (insert_result.second) { + insert_result.first->second.prefix_range.emplace(prefix_range); + } + grpc_error* error = AddFilterChainDataForServerNames( + filter_chain, &insert_result.first->second); + if (error != GRPC_ERROR_NONE) return error; + } + } + return GRPC_ERROR_NONE; +} + +XdsApi::LdsUpdate::FilterChainMap BuildFromInternalFilterChainMap( + InternalFilterChainMap* internal_filter_chain_map) { + XdsApi::LdsUpdate::FilterChainMap filter_chain_map; + for (auto& destination_ip_pair : + internal_filter_chain_map->destination_ip_map) { + XdsApi::LdsUpdate::FilterChainMap::DestinationIp destination_ip; + destination_ip.prefix_range = destination_ip_pair.second.prefix_range; + for (int i = 0; i < 3; i++) { + auto& source_ip_map = destination_ip_pair.second.source_types_array[i]; + for (auto& source_ip_pair : source_ip_map) { + destination_ip.source_types_array[i].push_back( + std::move(source_ip_pair.second)); + } + } + filter_chain_map.destination_ip_vector.push_back(std::move(destination_ip)); + } + return filter_chain_map; +} + +grpc_error* BuildFilterChainMap( + const std::vector& filter_chains, + XdsApi::LdsUpdate::FilterChainMap* filter_chain_map) { + InternalFilterChainMap internal_filter_chain_map; + for (const auto& filter_chain : filter_chains) { + // Discard filter chain entries that specify destination port + if (filter_chain.filter_chain_match.destination_port != 0) continue; + grpc_error* error = AddFilterChainDataForDestinationIpRange( + filter_chain, &internal_filter_chain_map.destination_ip_map); + if (error != GRPC_ERROR_NONE) return error; + } + *filter_chain_map = + BuildFromInternalFilterChainMap(&internal_filter_chain_map); + return GRPC_ERROR_NONE; +} + grpc_error* LdsResponseParseServer( const EncodingContext& context, const envoy_config_listener_v3_Listener* listener, bool is_v2, @@ -2193,33 +2457,31 @@ grpc_error* LdsResponseParseServer( "Field \'use_original_dst\' is not supported."); } } - // TODO(yashykt): As part of this, we'll need to refactor the code to process - // the HttpConnectionManager config so that it is shared with the client-side - // parsing. size_t size = 0; auto* filter_chains = envoy_config_listener_v3_Listener_filter_chains(listener, &size); - // TODO(yashykt): Remove following if block when FilterChainMatch - // implementation is in - if (size == 0) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "At least one filter chain needed."); - } - lds_update->filter_chains.reserve(size); + std::vector parsed_filter_chains; + parsed_filter_chains.reserve(size); for (size_t i = 0; i < size; i++) { - XdsApi::LdsUpdate::FilterChain filter_chain; - error = FilterChainParse(context, filter_chains[0], is_v2, &filter_chain); + FilterChain filter_chain; + error = FilterChainParse(context, filter_chains[i], is_v2, &filter_chain); if (error != GRPC_ERROR_NONE) return error; - lds_update->filter_chains.push_back(std::move(filter_chain)); + parsed_filter_chains.push_back(std::move(filter_chain)); } + error = + BuildFilterChainMap(parsed_filter_chains, &lds_update->filter_chain_map); + if (error != GRPC_ERROR_NONE) return error; auto* default_filter_chain = envoy_config_listener_v3_Listener_default_filter_chain(listener); if (default_filter_chain != nullptr) { - XdsApi::LdsUpdate::FilterChain filter_chain; + FilterChain filter_chain; error = FilterChainParse(context, default_filter_chain, is_v2, &filter_chain); if (error != GRPC_ERROR_NONE) return error; - lds_update->default_filter_chain = std::move(filter_chain); + if (filter_chain.filter_chain_data != nullptr) { + lds_update->default_filter_chain = + std::move(*filter_chain.filter_chain_data); + } } if (size == 0 && default_filter_chain == nullptr) { return GRPC_ERROR_CREATE_FROM_STATIC_STRING("No filter chain provided."); @@ -2746,7 +3008,9 @@ grpc_error* ServerAddressParseAndAppend( } // Populate grpc_resolved_address. grpc_resolved_address addr; - grpc_string_to_sockaddr(&addr, address_str.c_str(), port); + grpc_error* error = + grpc_string_to_sockaddr_new(&addr, address_str.c_str(), port); + if (error != GRPC_ERROR_NONE) return error; // Append the address to the list. list->emplace_back(addr, nullptr); return GRPC_ERROR_NONE; diff --git a/src/core/ext/xds/xds_api.h b/src/core/ext/xds/xds_api.h index 72a47eb0f29..b8e46da18cd 100644 --- a/src/core/ext/xds/xds_api.h +++ b/src/core/ext/xds/xds_api.h @@ -273,77 +273,104 @@ class XdsApi { std::string ToString() const; }; - struct FilterChain { - struct FilterChainMatch { - uint32_t destination_port = 0; - - struct CidrRange { - std::string address_prefix; - uint32_t prefix_len; - - bool operator==(const CidrRange& other) const { - return address_prefix == other.address_prefix && - prefix_len == other.prefix_len; - } - - std::string ToString() const; - }; - - std::vector prefix_ranges; - - enum class ConnectionSourceType { - kAny = 0, - kSameIpOrLoopback, - kExternal - } source_type = ConnectionSourceType::kAny; - - std::vector source_prefix_ranges; - std::vector source_ports; - std::vector server_names; - std::string transport_protocol; - std::vector application_protocols; - - bool operator==(const FilterChainMatch& other) const { - return destination_port == other.destination_port && - prefix_ranges == other.prefix_ranges && - source_type == other.source_type && - source_prefix_ranges == other.source_prefix_ranges && - source_ports == other.source_ports && - server_names == other.server_names && - transport_protocol == other.transport_protocol && - application_protocols == other.application_protocols; - } + // Populated for type=kHttpApiListener. + HttpConnectionManager http_connection_manager; - std::string ToString() const; - } filter_chain_match; + // Populated for type=kTcpListener. + // host:port listening_address set when type is kTcpListener + std::string address; + struct FilterChainData { DownstreamTlsContext downstream_tls_context; - // This is in principle the filter list. // We currently require exactly one filter, which is the HCM. HttpConnectionManager http_connection_manager; - bool operator==(const FilterChain& other) const { - return filter_chain_match == other.filter_chain_match && - downstream_tls_context == other.downstream_tls_context && + bool operator==(const FilterChainData& other) const { + return downstream_tls_context == other.downstream_tls_context && http_connection_manager == other.http_connection_manager; } std::string ToString() const; - }; + } filter_chain_data; + + // A multi-level map used to determine which filter chain to use for a given + // incoming connection. Determining the right filter chain for a given + // connection checks the following properties, in order: + // - destination port (never matched, so not present in map) + // - destination IP address + // - server name (never matched, so not present in map) + // - transport protocol (allows only "raw_buffer" or unset, prefers the + // former, so only one of those two types is present in map) + // - application protocol (never matched, so not present in map) + // - connection source type (any, local or external) + // - source IP address + // - source port + // https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/listener/v3/listener_components.proto#config-listener-v3-filterchainmatch + // for more details + struct FilterChainMap { + struct FilterChainDataSharedPtr { + std::shared_ptr data; + bool operator==(const FilterChainDataSharedPtr& other) const { + return *data == *other.data; + } + }; + struct CidrRange { + grpc_resolved_address address; + uint32_t prefix_len; - // Populated for type=kHttpApiListener. - HttpConnectionManager http_connection_manager; + bool operator==(const CidrRange& other) const { + return memcmp(&address, &other.address, sizeof(address)) == 0 && + prefix_len == other.prefix_len; + } - // Populated for type=kTcpListener. - // host:port listening_address set when type is kTcpListener - std::string address; - std::vector filter_chains; - absl::optional default_filter_chain; + std::string ToString() const; + }; + using SourcePortsMap = std::map; + struct SourceIp { + absl::optional prefix_range; + SourcePortsMap ports_map; + + bool operator==(const SourceIp& other) const { + return prefix_range == other.prefix_range && + ports_map == other.ports_map; + } + }; + using SourceIpVector = std::vector; + enum class ConnectionSourceType { + kAny = 0, + kSameIpOrLoopback, + kExternal + }; + using ConnectionSourceTypesArray = std::array; + struct DestinationIp { + absl::optional prefix_range; + // We always fail match on server name, so those filter chains are not + // included here. + ConnectionSourceTypesArray source_types_array; + + bool operator==(const DestinationIp& other) const { + return prefix_range == other.prefix_range && + source_types_array == other.source_types_array; + } + }; + // We always fail match on destination ports map + using DestinationIpVector = std::vector; + DestinationIpVector destination_ip_vector; + + bool operator==(const FilterChainMap& other) const { + return destination_ip_vector == other.destination_ip_vector; + } + + std::string ToString() const; + } filter_chain_map; + + absl::optional default_filter_chain; bool operator==(const LdsUpdate& other) const { return http_connection_manager == other.http_connection_manager && - address == other.address && filter_chains == other.filter_chains && + address == other.address && + filter_chain_map == other.filter_chain_map && default_filter_chain == other.default_filter_chain; } diff --git a/src/core/ext/xds/xds_server_config_fetcher.cc b/src/core/ext/xds/xds_server_config_fetcher.cc index 7e6d34ee82b..5e7aba6a1ba 100644 --- a/src/core/ext/xds/xds_server_config_fetcher.cc +++ b/src/core/ext/xds/xds_server_config_fetcher.cc @@ -23,9 +23,14 @@ #include "src/core/ext/xds/xds_certificate_provider.h" #include "src/core/ext/xds/xds_client.h" #include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gprpp/host_port.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/socket_utils.h" #include "src/core/lib/security/credentials/xds/xds_credentials.h" #include "src/core/lib/surface/api_trace.h" #include "src/core/lib/surface/server.h" +#include "src/core/lib/uri/uri_parser.h" namespace grpc_core { @@ -34,6 +39,300 @@ TraceFlag grpc_xds_server_config_fetcher_trace(false, namespace { +class FilterChainMatchManager + : public grpc_server_config_fetcher::ConnectionManager { + public: + FilterChainMatchManager( + RefCountedPtr xds_client, + XdsApi::LdsUpdate::FilterChainMap filter_chain_map, + absl::optional default_filter_chain) + : xds_client_(xds_client), + filter_chain_map_(std::move(filter_chain_map)), + default_filter_chain_(std::move(default_filter_chain)) {} + + absl::StatusOr UpdateChannelArgsForConnection( + grpc_channel_args* args, grpc_endpoint* tcp) override; + + const XdsApi::LdsUpdate::FilterChainMap& filter_chain_map() const { + return filter_chain_map_; + } + + const absl::optional& + default_filter_chain() const { + return default_filter_chain_; + } + + private: + struct CertificateProviders { + // We need to save our own refs to the root and instance certificate + // providers since the xds certificate provider just stores a ref to their + // distributors. + RefCountedPtr root; + RefCountedPtr instance; + RefCountedPtr xds; + }; + + absl::StatusOr> + CreateOrGetXdsCertificateProviderFromFilterChainData( + const XdsApi::LdsUpdate::FilterChainData* filter_chain); + + const RefCountedPtr xds_client_; + const XdsApi::LdsUpdate::FilterChainMap filter_chain_map_; + const absl::optional + default_filter_chain_; + Mutex mu_; + std::map + certificate_providers_map_ ABSL_GUARDED_BY(mu_); +}; + +bool IsLoopbackIp(const grpc_resolved_address* address) { + const grpc_sockaddr* sock_addr = + reinterpret_cast(&address->addr); + if (sock_addr->sa_family == GRPC_AF_INET) { + const grpc_sockaddr_in* addr4 = + reinterpret_cast(sock_addr); + if (addr4->sin_addr.s_addr == grpc_htonl(INADDR_LOOPBACK)) { + return true; + } + } else if (sock_addr->sa_family == GRPC_AF_INET6) { + const grpc_sockaddr_in6* addr6 = + reinterpret_cast(sock_addr); + if (memcmp(&addr6->sin6_addr, &in6addr_loopback, + sizeof(in6addr_loopback)) == 0) { + return true; + } + } + return false; +} + +const XdsApi::LdsUpdate::FilterChainData* FindFilterChainDataForSourcePort( + const XdsApi::LdsUpdate::FilterChainMap::SourcePortsMap& source_ports_map, + absl::string_view port_str) { + int port = 0; + if (!absl::SimpleAtoi(port_str, &port)) return nullptr; + auto it = source_ports_map.find(port); + if (it != source_ports_map.end()) { + return it->second.data.get(); + } + // Search for the catch-all port 0 since we didn't get a direct match + it = source_ports_map.find(0); + if (it != source_ports_map.end()) { + return it->second.data.get(); + } + return nullptr; +} + +const XdsApi::LdsUpdate::FilterChainData* FindFilterChainDataForSourceIp( + const XdsApi::LdsUpdate::FilterChainMap::SourceIpVector& source_ip_vector, + const grpc_resolved_address* source_ip, absl::string_view port) { + const XdsApi::LdsUpdate::FilterChainMap::SourceIp* best_match = nullptr; + for (const auto& entry : source_ip_vector) { + // Special case for catch-all + if (!entry.prefix_range.has_value()) { + if (best_match == nullptr) { + best_match = &entry; + } + continue; + } + if (best_match != nullptr && best_match->prefix_range.has_value() && + best_match->prefix_range->prefix_len >= + entry.prefix_range->prefix_len) { + continue; + } + if (grpc_sockaddr_match_subnet(source_ip, &entry.prefix_range->address, + entry.prefix_range->prefix_len)) { + best_match = &entry; + } + } + if (best_match == nullptr) return nullptr; + return FindFilterChainDataForSourcePort(best_match->ports_map, port); +} + +const XdsApi::LdsUpdate::FilterChainData* FindFilterChainDataForSourceType( + const XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceTypesArray& + source_types_array, + grpc_endpoint* tcp, absl::string_view destination_ip) { + auto source_uri = URI::Parse(grpc_endpoint_get_peer(tcp)); + if (!source_uri.ok() || + (source_uri->scheme() != "ipv4" && source_uri->scheme() != "ipv6")) { + return nullptr; + } + std::string host; + std::string port; + if (!SplitHostPort(source_uri->path(), &host, &port)) { + return nullptr; + } + grpc_resolved_address source_addr; + grpc_string_to_sockaddr(&source_addr, host.c_str(), + 0 /* port doesn't matter here */); + // Use kAny only if kSameIporLoopback and kExternal are empty + if (source_types_array[static_cast( + XdsApi::LdsUpdate::FilterChainMap:: + ConnectionSourceType::kSameIpOrLoopback)] + .empty() && + source_types_array[static_cast(XdsApi::LdsUpdate::FilterChainMap:: + ConnectionSourceType::kExternal)] + .empty()) { + return FindFilterChainDataForSourceIp( + source_types_array[static_cast( + XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceType::kAny)], + &source_addr, port); + } + if (IsLoopbackIp(&source_addr) || host == destination_ip) { + return FindFilterChainDataForSourceIp( + source_types_array[static_cast( + XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceType:: + kSameIpOrLoopback)], + &source_addr, port); + } else { + return FindFilterChainDataForSourceIp( + source_types_array[static_cast( + XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceType:: + kExternal)], + &source_addr, port); + } +} + +const XdsApi::LdsUpdate::FilterChainData* FindFilterChainDataForDestinationIp( + const XdsApi::LdsUpdate::FilterChainMap::DestinationIpVector + destination_ip_vector, + grpc_endpoint* tcp) { + auto destination_uri = URI::Parse(grpc_endpoint_get_local_address(tcp)); + if (!destination_uri.ok() || (destination_uri->scheme() != "ipv4" && + destination_uri->scheme() != "ipv6")) { + return nullptr; + } + std::string host; + std::string port; + if (!SplitHostPort(destination_uri->path(), &host, &port)) { + return nullptr; + } + grpc_resolved_address destination_addr; + grpc_string_to_sockaddr(&destination_addr, host.c_str(), + 0 /* port doesn't matter here */); + const XdsApi::LdsUpdate::FilterChainMap::DestinationIp* best_match = nullptr; + for (const auto& entry : destination_ip_vector) { + // Special case for catch-all + if (!entry.prefix_range.has_value()) { + if (best_match == nullptr) { + best_match = &entry; + } + continue; + } + if (best_match != nullptr && best_match->prefix_range.has_value() && + best_match->prefix_range->prefix_len >= + entry.prefix_range->prefix_len) { + continue; + } + if (grpc_sockaddr_match_subnet(&destination_addr, + &entry.prefix_range->address, + entry.prefix_range->prefix_len)) { + best_match = &entry; + } + } + if (best_match == nullptr) return nullptr; + return FindFilterChainDataForSourceType(best_match->source_types_array, tcp, + host); +} + +absl::StatusOr> +FilterChainMatchManager::CreateOrGetXdsCertificateProviderFromFilterChainData( + const XdsApi::LdsUpdate::FilterChainData* filter_chain) { + MutexLock lock(&mu_); + auto it = certificate_providers_map_.find(filter_chain); + if (it != certificate_providers_map_.end()) { + return it->second.xds; + } + CertificateProviders certificate_providers; + // Configure root cert. + absl::string_view root_provider_instance_name = + filter_chain->downstream_tls_context.common_tls_context + .combined_validation_context + .validation_context_certificate_provider_instance.instance_name; + absl::string_view root_provider_cert_name = + filter_chain->downstream_tls_context.common_tls_context + .combined_validation_context + .validation_context_certificate_provider_instance.certificate_name; + if (!root_provider_instance_name.empty()) { + certificate_providers.root = + xds_client_->certificate_provider_store() + .CreateOrGetCertificateProvider(root_provider_instance_name); + if (certificate_providers.root == nullptr) { + return absl::NotFoundError( + absl::StrCat("Certificate provider instance name: \"", + root_provider_instance_name, "\" not recognized.")); + } + } + // Configure identity cert. + absl::string_view identity_provider_instance_name = + filter_chain->downstream_tls_context.common_tls_context + .tls_certificate_certificate_provider_instance.instance_name; + absl::string_view identity_provider_cert_name = + filter_chain->downstream_tls_context.common_tls_context + .tls_certificate_certificate_provider_instance.certificate_name; + if (!identity_provider_instance_name.empty()) { + certificate_providers.instance = + xds_client_->certificate_provider_store() + .CreateOrGetCertificateProvider(identity_provider_instance_name); + if (certificate_providers.instance == nullptr) { + return absl::NotFoundError( + absl::StrCat("Certificate provider instance name: \"", + identity_provider_instance_name, "\" not recognized.")); + } + } + certificate_providers.xds = MakeRefCounted(); + certificate_providers.xds->UpdateRootCertNameAndDistributor( + "", root_provider_cert_name, + certificate_providers.root == nullptr + ? nullptr + : certificate_providers.root->distributor()); + certificate_providers.xds->UpdateIdentityCertNameAndDistributor( + "", identity_provider_cert_name, + certificate_providers.instance == nullptr + ? nullptr + : certificate_providers.instance->distributor()); + certificate_providers.xds->UpdateRequireClientCertificate( + "", filter_chain->downstream_tls_context.require_client_certificate); + auto xds_certificate_provider = certificate_providers.xds; + certificate_providers_map_.emplace(filter_chain, + std::move(certificate_providers)); + return xds_certificate_provider; +} + +absl::StatusOr +FilterChainMatchManager::UpdateChannelArgsForConnection(grpc_channel_args* args, + grpc_endpoint* tcp) { + const auto* filter_chain = FindFilterChainDataForDestinationIp( + filter_chain_map_.destination_ip_vector, tcp); + if (filter_chain == nullptr && default_filter_chain_.has_value()) { + filter_chain = &default_filter_chain_.value(); + } + if (filter_chain == nullptr) { + grpc_channel_args_destroy(args); + return absl::UnavailableError("No matching filter chain found"); + } + // Nothing to update if credentials are not xDS. + grpc_server_credentials* server_creds = + grpc_find_server_credentials_in_args(args); + if (server_creds == nullptr || server_creds->type() != kCredentialsTypeXds) { + return args; + } + absl::StatusOr> result = + CreateOrGetXdsCertificateProviderFromFilterChainData(filter_chain); + if (!result.ok()) { + grpc_channel_args_destroy(args); + return result.status(); + } + RefCountedPtr xds_certificate_provider = + std::move(*result); + GPR_ASSERT(xds_certificate_provider != nullptr); + grpc_arg arg_to_add = xds_certificate_provider->MakeChannelArg(); + grpc_channel_args* updated_args = + grpc_channel_args_copy_and_add(args, &arg_to_add, 1); + grpc_channel_args_destroy(args); + return updated_args; +} + class XdsServerConfigFetcher : public grpc_server_config_fetcher { public: explicit XdsServerConfigFetcher(RefCountedPtr xds_client, @@ -113,18 +412,7 @@ class XdsServerConfigFetcher : public grpc_server_config_fetcher { "Address in LDS update does not match listening address")); return; } - grpc_error* error = GRPC_ERROR_NONE; - bool update_needed = UpdateXdsCertificateProvider(listener, &error); - if (error != GRPC_ERROR_NONE) { - OnError(error); - return; - } - // Only send an update, if something changed. - if (have_resource_ && !update_needed) { - return; - } - if (!have_resource_) { - have_resource_ = true; + if (filter_chain_match_manager_ == nullptr) { if (serving_status_notifier_.on_serving_status_change != nullptr) { serving_status_notifier_.on_serving_status_change( serving_status_notifier_.user_data, listening_address_.c_str(), @@ -135,18 +423,21 @@ class XdsServerConfigFetcher : public grpc_server_config_fetcher { listening_address_.c_str()); } } - grpc_channel_args* updated_args = nullptr; - if (xds_certificate_provider_ != nullptr) { - grpc_arg arg_to_add = xds_certificate_provider_->MakeChannelArg(); - updated_args = grpc_channel_args_copy_and_add(args_, &arg_to_add, 1); - } else { - updated_args = grpc_channel_args_copy(args_); + if (filter_chain_match_manager_ == nullptr || + !(listener.filter_chain_map == + filter_chain_match_manager_->filter_chain_map() && + listener.default_filter_chain == + filter_chain_match_manager_->default_filter_chain())) { + filter_chain_match_manager_ = MakeRefCounted( + xds_client_, std::move(listener.filter_chain_map), + std::move(listener.default_filter_chain)); + server_config_watcher_->UpdateConnectionManager( + filter_chain_match_manager_); } - server_config_watcher_->UpdateConfig(updated_args); } void OnError(grpc_error* error) override { - if (have_resource_) { + if (filter_chain_match_manager_ != nullptr) { gpr_log(GPR_ERROR, "ListenerWatcher:%p XdsClient reports error: %s for %s; " "ignoring in favor of existing resource", @@ -172,11 +463,11 @@ class XdsServerConfigFetcher : public grpc_server_config_fetcher { GPR_ERROR, "ListenerWatcher:%p Encountered fatal error %s; not serving on %s", this, status.ToString().c_str(), listening_address_.c_str()); - if (have_resource_) { + if (filter_chain_match_manager_ != nullptr) { // The server has started listening already, so we need to gracefully // stop serving. server_config_watcher_->StopServing(); - have_resource_ = false; + filter_chain_match_manager_.reset(); } if (serving_status_notifier_.on_serving_status_change != nullptr) { serving_status_notifier_.on_serving_status_change( @@ -191,111 +482,13 @@ class XdsServerConfigFetcher : public grpc_server_config_fetcher { } private: - // Returns true if the xds certificate provider changed in a way that - // required a new security connector to be created, false otherwise. - bool UpdateXdsCertificateProvider(const XdsApi::LdsUpdate& listener, - grpc_error** error) { - // Early out if channel is not configured to use xDS security. - grpc_server_credentials* server_creds = - grpc_find_server_credentials_in_args(args_); - if (server_creds == nullptr || - server_creds->type() != kCredentialsTypeXds) { - xds_certificate_provider_ = nullptr; - return false; - } - if (xds_certificate_provider_ == nullptr) { - xds_certificate_provider_ = MakeRefCounted(); - } - // Configure root cert. - absl::string_view root_provider_instance_name = - listener.filter_chains[0] - .downstream_tls_context.common_tls_context - .combined_validation_context - .validation_context_certificate_provider_instance.instance_name; - absl::string_view root_provider_cert_name = - listener.filter_chains[0] - .downstream_tls_context.common_tls_context - .combined_validation_context - .validation_context_certificate_provider_instance - .certificate_name; - RefCountedPtr new_root_provider; - if (!root_provider_instance_name.empty()) { - new_root_provider = - xds_client_->certificate_provider_store() - .CreateOrGetCertificateProvider(root_provider_instance_name); - if (new_root_provider == nullptr) { - *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING( - absl::StrCat("Certificate provider instance name: \"", - root_provider_instance_name, "\" not recognized.") - .c_str()); - return false; - } - } - // Configure identity cert. - absl::string_view identity_provider_instance_name = - listener.filter_chains[0] - .downstream_tls_context.common_tls_context - .tls_certificate_certificate_provider_instance.instance_name; - absl::string_view identity_provider_cert_name = - listener.filter_chains[0] - .downstream_tls_context.common_tls_context - .tls_certificate_certificate_provider_instance.certificate_name; - RefCountedPtr new_identity_provider; - if (!identity_provider_instance_name.empty()) { - new_identity_provider = xds_client_->certificate_provider_store() - .CreateOrGetCertificateProvider( - identity_provider_instance_name); - if (new_identity_provider == nullptr) { - *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING( - absl::StrCat("Certificate provider instance name: \"", - identity_provider_instance_name, - "\" not recognized.") - .c_str()); - return false; - } - } - bool security_connector_update_required = false; - if (((new_root_provider == nullptr) != - (root_certificate_provider_ == nullptr)) || - ((new_identity_provider == nullptr) != - (identity_certificate_provider_ == nullptr)) || - (listener.filter_chains[0] - .downstream_tls_context.require_client_certificate != - xds_certificate_provider_->GetRequireClientCertificate(""))) { - security_connector_update_required = true; - } - if (root_certificate_provider_ != new_root_provider) { - root_certificate_provider_ = std::move(new_root_provider); - } - if (identity_certificate_provider_ != new_identity_provider) { - identity_certificate_provider_ = std::move(new_identity_provider); - } - xds_certificate_provider_->UpdateRootCertNameAndDistributor( - "", root_provider_cert_name, - root_certificate_provider_ == nullptr - ? nullptr - : root_certificate_provider_->distributor()); - xds_certificate_provider_->UpdateIdentityCertNameAndDistributor( - "", identity_provider_cert_name, - identity_certificate_provider_ == nullptr - ? nullptr - : identity_certificate_provider_->distributor()); - xds_certificate_provider_->UpdateRequireClientCertificate( - "", listener.filter_chains[0] - .downstream_tls_context.require_client_certificate); - return security_connector_update_required; - } - std::unique_ptr server_config_watcher_; grpc_channel_args* args_; RefCountedPtr xds_client_; grpc_server_xds_status_notifier serving_status_notifier_; std::string listening_address_; - RefCountedPtr root_certificate_provider_; - RefCountedPtr identity_certificate_provider_; - RefCountedPtr xds_certificate_provider_; - bool have_resource_ = false; + RefCountedPtr filter_chain_match_manager_; }; struct WatcherState { diff --git a/src/core/lib/gprpp/atomic.h b/src/core/lib/gprpp/atomic.h index 4a53d2cfa55..dd99d9ed4d7 100644 --- a/src/core/lib/gprpp/atomic.h +++ b/src/core/lib/gprpp/atomic.h @@ -81,8 +81,8 @@ class Atomic { // Atomically increment a counter only if the counter value is not zero. // Returns true if increment took place; false if counter is zero. - bool IncrementIfNonzero(MemoryOrder load_order = MemoryOrder::ACQUIRE) { - T count = storage_.load(static_cast(load_order)); + bool IncrementIfNonzero() { + T count = storage_.load(std::memory_order_acquire); do { // If zero, we are done (without an increment). If not, we must do a CAS // to maintain the contract: do not increment the counter if it is already @@ -91,7 +91,7 @@ class Atomic { return false; } } while (!CompareExchangeWeak(&count, count + 1, MemoryOrder::ACQ_REL, - load_order)); + MemoryOrder::ACQUIRE)); return true; } diff --git a/src/core/lib/iomgr/sockaddr_utils.cc b/src/core/lib/iomgr/sockaddr_utils.cc index 333358abd48..4c36a0cc12b 100644 --- a/src/core/lib/iomgr/sockaddr_utils.cc +++ b/src/core/lib/iomgr/sockaddr_utils.cc @@ -215,6 +215,25 @@ void grpc_string_to_sockaddr(grpc_resolved_address* out, const char* addr, grpc_sockaddr_set_port(out, port); } +grpc_error* grpc_string_to_sockaddr_new(grpc_resolved_address* out, + const char* addr, int port) { + memset(out, 0, sizeof(grpc_resolved_address)); + grpc_sockaddr_in6* addr6 = reinterpret_cast(out->addr); + grpc_sockaddr_in* addr4 = reinterpret_cast(out->addr); + if (grpc_inet_pton(GRPC_AF_INET6, addr, &addr6->sin6_addr) == 1) { + addr6->sin6_family = GRPC_AF_INET6; + out->len = sizeof(grpc_sockaddr_in6); + } else if (grpc_inet_pton(GRPC_AF_INET, addr, &addr4->sin_addr) == 1) { + addr4->sin_family = GRPC_AF_INET; + out->len = sizeof(grpc_sockaddr_in); + } else { + return GRPC_ERROR_CREATE_FROM_COPIED_STRING( + absl::StrCat("Failed to parse address:", addr).c_str()); + } + grpc_sockaddr_set_port(out, port); + return GRPC_ERROR_NONE; +} + std::string grpc_sockaddr_to_uri(const grpc_resolved_address* resolved_addr) { if (resolved_addr->len == 0) return ""; grpc_resolved_address addr_normalized; @@ -313,3 +332,85 @@ std::string grpc_sockaddr_get_packed_host( GPR_ASSERT(false); } } + +void grpc_sockaddr_mask_bits(grpc_resolved_address* address, + uint32_t mask_bits) { + grpc_sockaddr* addr = reinterpret_cast(address->addr); + if (addr->sa_family == GRPC_AF_INET) { + grpc_sockaddr_in* addr4 = reinterpret_cast(addr); + if (mask_bits == 0) { + memset(&addr4->sin_addr, 0, sizeof(addr4->sin_addr)); + return; + } else if (mask_bits >= 32) { + return; + } + uint32_t mask_ip_addr = (~(uint32_t(0))) << (32 - mask_bits); + addr4->sin_addr.s_addr &= grpc_htonl(mask_ip_addr); + } else if (addr->sa_family == GRPC_AF_INET6) { + grpc_sockaddr_in6* addr6 = reinterpret_cast(addr); + if (mask_bits == 0) { + memset(&addr6->sin6_addr, 0, sizeof(addr6->sin6_addr)); + return; + } else if (mask_bits >= 128) { + return; + } + // We cannot use s6_addr32 since it is not defined on all platforms that we + // need it on. + uint32_t address_parts[4]; + GPR_ASSERT(sizeof(addr6->sin6_addr) == sizeof(address_parts)); + memcpy(address_parts, &addr6->sin6_addr, sizeof(grpc_in6_addr)); + if (mask_bits <= 32) { + uint32_t mask_ip_addr = (~(uint32_t(0))) << (32 - mask_bits); + address_parts[0] &= grpc_htonl(mask_ip_addr); + memset(&address_parts[1], 0, sizeof(uint32_t)); + memset(&address_parts[2], 0, sizeof(uint32_t)); + memset(&address_parts[3], 0, sizeof(uint32_t)); + } else if (mask_bits <= 64) { + mask_bits -= 32; + uint32_t mask_ip_addr = (~(uint32_t(0))) << (32 - mask_bits); + address_parts[1] &= grpc_htonl(mask_ip_addr); + memset(&address_parts[2], 0, sizeof(uint32_t)); + memset(&address_parts[3], 0, sizeof(uint32_t)); + } else if (mask_bits <= 96) { + mask_bits -= 64; + uint32_t mask_ip_addr = (~(uint32_t(0))) << (32 - mask_bits); + address_parts[2] &= grpc_htonl(mask_ip_addr); + memset(&address_parts[3], 0, sizeof(uint32_t)); + } else { + mask_bits -= 96; + uint32_t mask_ip_addr = (~(uint32_t(0))) << (32 - mask_bits); + address_parts[3] &= grpc_htonl(mask_ip_addr); + } + memcpy(&addr6->sin6_addr, address_parts, sizeof(grpc_in6_addr)); + } +} + +bool grpc_sockaddr_match_subnet(const grpc_resolved_address* address, + const grpc_resolved_address* subnet_address, + uint32_t mask_bits) { + auto* addr = reinterpret_cast(address->addr); + auto* subnet_addr = + reinterpret_cast(subnet_address->addr); + if (addr->sa_family != subnet_addr->sa_family) return false; + grpc_resolved_address masked_address; + memcpy(&masked_address, address, sizeof(grpc_resolved_address)); + addr = reinterpret_cast((&masked_address)->addr); + grpc_sockaddr_mask_bits(&masked_address, mask_bits); + if (addr->sa_family == GRPC_AF_INET) { + auto* addr4 = reinterpret_cast(addr); + auto* subnet_addr4 = reinterpret_cast(subnet_addr); + if (memcmp(&addr4->sin_addr, &subnet_addr4->sin_addr, + sizeof(addr4->sin_addr)) == 0) { + return true; + } + } else if (addr->sa_family == GRPC_AF_INET6) { + auto* addr6 = reinterpret_cast(addr); + auto* subnet_addr6 = + reinterpret_cast(subnet_addr); + if (memcmp(&addr6->sin6_addr, &subnet_addr6->sin6_addr, + sizeof(addr6->sin6_addr)) == 0) { + return true; + } + } + return false; +} diff --git a/src/core/lib/iomgr/sockaddr_utils.h b/src/core/lib/iomgr/sockaddr_utils.h index e5297c63e53..f0096920ed7 100644 --- a/src/core/lib/iomgr/sockaddr_utils.h +++ b/src/core/lib/iomgr/sockaddr_utils.h @@ -66,9 +66,16 @@ int grpc_sockaddr_set_port(grpc_resolved_address* addr, int port); std::string grpc_sockaddr_to_string(const grpc_resolved_address* addr, bool normalize); +// TODO(yashykt): Remove this function and replace usages with +// `grpc_string_to_sockaddr_new` void grpc_string_to_sockaddr(grpc_resolved_address* out, const char* addr, int port); +// Newer form of grpc_string_to_sockaddr which returns an error instead of +// crashing if \a addr is not IPv6/IPv6 +grpc_error* grpc_string_to_sockaddr_new(grpc_resolved_address* out, + const char* addr, int port); + /* Returns the URI string corresponding to \a addr */ std::string grpc_sockaddr_to_uri(const grpc_resolved_address* addr); @@ -80,4 +87,19 @@ int grpc_sockaddr_get_family(const grpc_resolved_address* resolved_addr); std::string grpc_sockaddr_get_packed_host( const grpc_resolved_address* resolved_addr); +// Applies a mask of \a mask_bits to IPv4/IPv6 addresses. Has no effect if the +// address type is not IPv4/IPv6. +void grpc_sockaddr_mask_bits(grpc_resolved_address* address, + uint32_t mask_bits); + +// If \a address is IPv4/IPv6, checks if the IP address falls in the CIDR +// specified by \a subnet_address and \a mask_bits. +// Returns false if \a address is not an IPv4/IPv6 address. The ports (if set) +// are ignored for matching purposes. Note that, \a subnet_address should be +// normalized, i.e., `grpc_sockaddr_mask_bits` should have been called on it if +// necessary. +bool grpc_sockaddr_match_subnet(const grpc_resolved_address* address, + const grpc_resolved_address* subnet_address, + uint32_t mask_bits); + #endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_UTILS_H */ diff --git a/src/core/lib/surface/server.h b/src/core/lib/surface/server.h index b0f422cf8df..ef8ad122c79 100644 --- a/src/core/lib/surface/server.h +++ b/src/core/lib/surface/server.h @@ -22,6 +22,7 @@ #include #include +#include "absl/status/statusor.h" #include "absl/types/optional.h" #include @@ -31,6 +32,7 @@ #include "src/core/lib/channel/channelz.h" #include "src/core/lib/debug/trace.h" #include "src/core/lib/gprpp/atomic.h" +#include "src/core/lib/iomgr/resolve_address.h" #include "src/core/lib/surface/completion_queue.h" #include "src/core/lib/transport/transport.h" @@ -456,13 +458,21 @@ struct grpc_server { // approaches here. struct grpc_server_config_fetcher { public: + class ConnectionManager : public grpc_core::RefCounted { + public: + // Ownership of \a args is transfered. + virtual absl::StatusOr UpdateChannelArgsForConnection( + grpc_channel_args* args, grpc_endpoint* tcp) = 0; + }; + class WatcherInterface { public: virtual ~WatcherInterface() = default; - // UpdateConfig() is invoked the config fetcher when a new config is - // available. Implementations should update the configuration and start - // serving if not already serving. Ownership of \a args is transferred. - virtual void UpdateConfig(grpc_channel_args* args) = 0; + // UpdateConnectionManager() is invoked by the config fetcher when a new + // config is available. Implementations should update the connection manager + // and start serving if not already serving. + virtual void UpdateConnectionManager( + grpc_core::RefCountedPtr manager) = 0; // Implementations should stop serving when this is called. Serving should // only resume when UpdateConfig() is invoked. virtual void StopServing() = 0; diff --git a/src/proto/grpc/testing/xds/v3/BUILD b/src/proto/grpc/testing/xds/v3/BUILD index 38a1c14b63b..f7a27c804e8 100644 --- a/src/proto/grpc/testing/xds/v3/BUILD +++ b/src/proto/grpc/testing/xds/v3/BUILD @@ -26,6 +26,7 @@ grpc_proto_library( srcs = [ "address.proto", ], + well_known_protos = True, ) grpc_proto_library( diff --git a/src/proto/grpc/testing/xds/v3/address.proto b/src/proto/grpc/testing/xds/v3/address.proto index abea8bf68a6..38592ccfda9 100644 --- a/src/proto/grpc/testing/xds/v3/address.proto +++ b/src/proto/grpc/testing/xds/v3/address.proto @@ -18,6 +18,8 @@ syntax = "proto3"; package envoy.config.core.v3; +import "google/protobuf/wrappers.proto"; + // [#protodoc-title: Network addresses] // [#next-free-field: 7] @@ -48,3 +50,13 @@ message Address { SocketAddress socket_address = 1; } } + +// CidrRange specifies an IP Address and a prefix length to construct +// the subnet mask for a `CIDR `_ range. +message CidrRange { + // IPv4 or IPv6 address, e.g. ``192.0.0.0`` or ``2001:db8::``. + string address_prefix = 1; + + // Length of prefix, e.g. 0, 32. + google.protobuf.UInt32Value prefix_len = 2; +} diff --git a/src/proto/grpc/testing/xds/v3/listener.proto b/src/proto/grpc/testing/xds/v3/listener.proto index 608e2f99576..df9ee71e725 100644 --- a/src/proto/grpc/testing/xds/v3/listener.proto +++ b/src/proto/grpc/testing/xds/v3/listener.proto @@ -57,6 +57,67 @@ message Filter { } message FilterChainMatch { + enum ConnectionSourceType { + // Any connection source matches. + ANY = 0; + + // Match a connection originating from the same host. + SAME_IP_OR_LOOPBACK = 1; + + // Match a connection originating from a different host. + EXTERNAL = 2; + } + + reserved 1; + + // Optional destination port to consider when use_original_dst is set on the + // listener in determining a filter chain match. + google.protobuf.UInt32Value destination_port = 8; + + // If non-empty, an IP address and prefix length to match addresses when the + // listener is bound to 0.0.0.0/:: or when use_original_dst is specified. + repeated core.v3.CidrRange prefix_ranges = 3; + + // Specifies the connection source IP match type. Can be any, local or external network. + ConnectionSourceType source_type = 12; + + // The criteria is satisfied if the source IP address of the downstream + // connection is contained in at least one of the specified subnets. If the + // parameter is not specified or the list is empty, the source IP address is + // ignored. + repeated core.v3.CidrRange source_prefix_ranges = 6; + + // The criteria is satisfied if the source port of the downstream connection + // is contained in at least one of the specified ports. If the parameter is + // not specified, the source port is ignored. + repeated uint32 source_ports = 7; + + // If non-empty, a list of server names (e.g. SNI for TLS protocol) to consider when determining + // a filter chain match. Those values will be compared against the server names of a new + // connection, when detected by one of the listener filters. + // + // The server name will be matched against all wildcard domains, i.e. ``www.example.com`` + // will be first matched against ``www.example.com``, then ``*.example.com``, then ``*.com``. + // + // Note that partial wildcards are not supported, and values like ``*w.example.com`` are invalid. + // + // .. attention:: + // + // See the :ref:`FAQ entry ` on how to configure SNI for more + // information. + repeated string server_names = 11; + + // If non-empty, a transport protocol to consider when determining a filter chain match. + // This value will be compared against the transport protocol of a new connection, when + // it's detected by one of the listener filters. + // + // Suggested values include: + // + // * ``raw_buffer`` - default, used when no transport protocol is detected, + // * ``tls`` - set by :ref:`envoy.filters.listener.tls_inspector ` + // when TLS protocol is detected. + string transport_protocol = 9; + // If non-empty, a list of application protocols (e.g. ALPN for TLS protocol) to consider when // determining a filter chain match. Those values will be compared against the application // protocols of a new connection, when detected by one of the listener filters. @@ -126,7 +187,11 @@ message Listener { // true, the listener hands off redirected connections to the listener associated with the // original destination address. If there is no listener associated with the original destination // address, the connection is handled by the listener that receives it. Defaults to false. - google.protobuf.BoolValue use_original_dst = 4; + google.protobuf.BoolValue use_original_dst = 4; + + // The default filter chain if none of the filter chain matches. If no default filter chain is supplied, + // the connection will be closed. The filter chain match is ignored in this field. + FilterChain default_filter_chain = 25; // Used to represent an API listener, which is used in non-proxy clients. The type of API // exposed to the non-proxy application depends on the type of API listener. diff --git a/test/core/iomgr/BUILD b/test/core/iomgr/BUILD index 55b6d0eb706..452a0c0f45e 100644 --- a/test/core/iomgr/BUILD +++ b/test/core/iomgr/BUILD @@ -236,6 +236,9 @@ grpc_cc_test( grpc_cc_test( name = "sockaddr_utils_test", srcs = ["sockaddr_utils_test.cc"], + external_deps = [ + "gtest", + ], language = "C++", deps = [ "//:gpr", diff --git a/test/core/iomgr/sockaddr_utils_test.cc b/test/core/iomgr/sockaddr_utils_test.cc index 6464933477f..14842bdc6b7 100644 --- a/test/core/iomgr/sockaddr_utils_test.cc +++ b/test/core/iomgr/sockaddr_utils_test.cc @@ -1,25 +1,23 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* With the addition of a libuv endpoint, sockaddr.h now includes uv.h when - using that endpoint. Because of various transitive includes in uv.h, - including windows.h on Windows, uv.h must be included before other system - headers. Therefore, sockaddr.h must always be included first */ +// +// Copyright 2015 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// With the addition of a libuv endpoint, sockaddr.h now includes uv.h when +// using that endpoint. Because of various transitive includes in uv.h, +// including windows.h on Windows, uv.h must be included before other system +// headers. Therefore, sockaddr.h must always be included first #include "src/core/lib/iomgr/sockaddr_utils.h" #include "src/core/lib/iomgr/sockaddr.h" #include "src/core/lib/iomgr/socket_utils.h" @@ -27,12 +25,16 @@ #include #include +#include + #include #include #include #include "test/core/util/test_config.h" -static grpc_resolved_address make_addr4(const uint8_t* data, size_t data_len) { +namespace { + +grpc_resolved_address MakeAddr4(const uint8_t* data, size_t data_len) { grpc_resolved_address resolved_addr4; grpc_sockaddr_in* addr4 = reinterpret_cast(resolved_addr4.addr); @@ -45,7 +47,7 @@ static grpc_resolved_address make_addr4(const uint8_t* data, size_t data_len) { return resolved_addr4; } -static grpc_resolved_address make_addr6(const uint8_t* data, size_t data_len) { +grpc_resolved_address MakeAddr6(const uint8_t* data, size_t data_len) { grpc_resolved_address resolved_addr6; grpc_sockaddr_in6* addr6 = reinterpret_cast(resolved_addr6.addr); @@ -58,216 +60,228 @@ static grpc_resolved_address make_addr6(const uint8_t* data, size_t data_len) { return resolved_addr6; } -static void set_addr6_scope_id(grpc_resolved_address* addr, uint32_t scope_id) { +void SetIPv6ScopeId(grpc_resolved_address* addr, uint32_t scope_id) { grpc_sockaddr_in6* addr6 = reinterpret_cast(addr->addr); - GPR_ASSERT(addr6->sin6_family == GRPC_AF_INET6); + ASSERT_EQ(addr6->sin6_family, GRPC_AF_INET6); addr6->sin6_scope_id = scope_id; } -static const uint8_t kMapped[] = {0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0xff, 0xff, 192, 0, 2, 1}; +const uint8_t kMapped[] = {0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0xff, 0xff, 192, 0, 2, 1}; -static const uint8_t kNotQuiteMapped[] = {0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0xff, 0xfe, 192, 0, 2, 99}; -static const uint8_t kIPv4[] = {192, 0, 2, 1}; +const uint8_t kNotQuiteMapped[] = {0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0xff, 0xfe, 192, 0, 2, 99}; +const uint8_t kIPv4[] = {192, 0, 2, 1}; -static const uint8_t kIPv6[] = {0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1}; +const uint8_t kIPv6[] = {0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1}; -static void test_sockaddr_is_v4mapped(void) { - grpc_resolved_address input4; - grpc_resolved_address input6; +TEST(SockAddrUtilsTest, SockAddrIsV4Mapped) { + // v4mapped input should succeed. + grpc_resolved_address input6 = MakeAddr6(kMapped, sizeof(kMapped)); + ASSERT_TRUE(grpc_sockaddr_is_v4mapped(&input6, nullptr)); grpc_resolved_address output4; - grpc_resolved_address expect4; - - gpr_log(GPR_INFO, "%s", "test_sockaddr_is_v4mapped"); - - /* v4mapped input should succeed. */ - input6 = make_addr6(kMapped, sizeof(kMapped)); - GPR_ASSERT(grpc_sockaddr_is_v4mapped(&input6, nullptr)); - GPR_ASSERT(grpc_sockaddr_is_v4mapped(&input6, &output4)); - expect4 = make_addr4(kIPv4, sizeof(kIPv4)); - GPR_ASSERT(memcmp(&expect4, &output4, sizeof(expect4)) == 0); - - /* Non-v4mapped input should fail. */ - input6 = make_addr6(kNotQuiteMapped, sizeof(kNotQuiteMapped)); - GPR_ASSERT(!grpc_sockaddr_is_v4mapped(&input6, nullptr)); - GPR_ASSERT(!grpc_sockaddr_is_v4mapped(&input6, &output4)); - /* Output is unchanged. */ - GPR_ASSERT(memcmp(&expect4, &output4, sizeof(expect4)) == 0); - - /* Plain IPv4 input should also fail. */ - input4 = make_addr4(kIPv4, sizeof(kIPv4)); - GPR_ASSERT(!grpc_sockaddr_is_v4mapped(&input4, nullptr)); + ASSERT_TRUE(grpc_sockaddr_is_v4mapped(&input6, &output4)); + grpc_resolved_address expect4 = MakeAddr4(kIPv4, sizeof(kIPv4)); + ASSERT_EQ(memcmp(&expect4, &output4, sizeof(expect4)), 0); + + // Non-v4mapped input should fail. + input6 = MakeAddr6(kNotQuiteMapped, sizeof(kNotQuiteMapped)); + ASSERT_FALSE(grpc_sockaddr_is_v4mapped(&input6, nullptr)); + ASSERT_FALSE(grpc_sockaddr_is_v4mapped(&input6, &output4)); + // Output is unchanged. + ASSERT_EQ(memcmp(&expect4, &output4, sizeof(expect4)), 0); + + // Plain IPv4 input should also fail. + grpc_resolved_address input4 = MakeAddr4(kIPv4, sizeof(kIPv4)); + ASSERT_FALSE(grpc_sockaddr_is_v4mapped(&input4, nullptr)); } -static void test_sockaddr_to_v4mapped(void) { - grpc_resolved_address input4; - grpc_resolved_address input6; +TEST(SockAddrUtilsTest, SockAddrToV4Mapped) { + // IPv4 input should succeed. + grpc_resolved_address input4 = MakeAddr4(kIPv4, sizeof(kIPv4)); grpc_resolved_address output6; - grpc_resolved_address expect6; - - gpr_log(GPR_INFO, "%s", "test_sockaddr_to_v4mapped"); - - /* IPv4 input should succeed. */ - input4 = make_addr4(kIPv4, sizeof(kIPv4)); - GPR_ASSERT(grpc_sockaddr_to_v4mapped(&input4, &output6)); - expect6 = make_addr6(kMapped, sizeof(kMapped)); - GPR_ASSERT(memcmp(&expect6, &output6, sizeof(output6)) == 0); - - /* IPv6 input should fail. */ - input6 = make_addr6(kIPv6, sizeof(kIPv6)); - GPR_ASSERT(!grpc_sockaddr_to_v4mapped(&input6, &output6)); - /* Output is unchanged. */ - GPR_ASSERT(memcmp(&expect6, &output6, sizeof(output6)) == 0); - - /* Already-v4mapped input should also fail. */ - input6 = make_addr6(kMapped, sizeof(kMapped)); - GPR_ASSERT(!grpc_sockaddr_to_v4mapped(&input6, &output6)); + ASSERT_TRUE(grpc_sockaddr_to_v4mapped(&input4, &output6)); + grpc_resolved_address expect6 = MakeAddr6(kMapped, sizeof(kMapped)); + ASSERT_EQ(memcmp(&expect6, &output6, sizeof(output6)), 0); + + // IPv6 input should fail. + grpc_resolved_address input6 = MakeAddr6(kIPv6, sizeof(kIPv6)); + ASSERT_TRUE(!grpc_sockaddr_to_v4mapped(&input6, &output6)); + // Output is unchanged. + ASSERT_EQ(memcmp(&expect6, &output6, sizeof(output6)), 0); + + // Already-v4mapped input should also fail. + input6 = MakeAddr6(kMapped, sizeof(kMapped)); + ASSERT_TRUE(!grpc_sockaddr_to_v4mapped(&input6, &output6)); } -static void test_sockaddr_is_wildcard(void) { +TEST(SockAddrUtilsTest, SockAddrIsWildCard) { + // Generate wildcards. grpc_resolved_address wild4; grpc_resolved_address wild6; - grpc_resolved_address wild_mapped; - grpc_resolved_address phony; - grpc_sockaddr_in* wild4_addr; - grpc_sockaddr_in6* wild6_addr; - grpc_sockaddr_in6* wild_mapped_addr; - int port; - - gpr_log(GPR_INFO, "%s", "test_sockaddr_is_wildcard"); - - /* Generate wildcards. */ grpc_sockaddr_make_wildcards(555, &wild4, &wild6); - GPR_ASSERT(grpc_sockaddr_to_v4mapped(&wild4, &wild_mapped)); - - /* Test 0.0.0.0:555 */ - port = -1; - GPR_ASSERT(grpc_sockaddr_is_wildcard(&wild4, &port)); - GPR_ASSERT(port == 555); - wild4_addr = reinterpret_cast(&wild4.addr); + grpc_resolved_address wild_mapped; + ASSERT_TRUE(grpc_sockaddr_to_v4mapped(&wild4, &wild_mapped)); + + // Test 0.0.0.0:555 + int port = -1; + ASSERT_TRUE(grpc_sockaddr_is_wildcard(&wild4, &port)); + ASSERT_TRUE(port == 555); + grpc_sockaddr_in* wild4_addr = + reinterpret_cast(&wild4.addr); memset(&wild4_addr->sin_addr.s_addr, 0xbd, 1); - GPR_ASSERT(!grpc_sockaddr_is_wildcard(&wild4, &port)); + ASSERT_FALSE(grpc_sockaddr_is_wildcard(&wild4, &port)); - /* Test [::]:555 */ + // Test [::]:555 port = -1; - GPR_ASSERT(grpc_sockaddr_is_wildcard(&wild6, &port)); - GPR_ASSERT(port == 555); - wild6_addr = reinterpret_cast(&wild6.addr); + ASSERT_TRUE(grpc_sockaddr_is_wildcard(&wild6, &port)); + ASSERT_EQ(port, 555); + grpc_sockaddr_in6* wild6_addr = + reinterpret_cast(&wild6.addr); memset(&wild6_addr->sin6_addr.s6_addr, 0xbd, 1); - GPR_ASSERT(!grpc_sockaddr_is_wildcard(&wild6, &port)); + ASSERT_FALSE(grpc_sockaddr_is_wildcard(&wild6, &port)); - /* Test [::ffff:0.0.0.0]:555 */ + // Test [::ffff:0.0.0.0]:555 port = -1; - GPR_ASSERT(grpc_sockaddr_is_wildcard(&wild_mapped, &port)); - GPR_ASSERT(port == 555); - wild_mapped_addr = reinterpret_cast(&wild_mapped.addr); + ASSERT_TRUE(grpc_sockaddr_is_wildcard(&wild_mapped, &port)); + ASSERT_EQ(port, 555); + grpc_sockaddr_in6* wild_mapped_addr = + reinterpret_cast(&wild_mapped.addr); memset(&wild_mapped_addr->sin6_addr.s6_addr, 0xbd, 1); - GPR_ASSERT(!grpc_sockaddr_is_wildcard(&wild_mapped, &port)); + ASSERT_FALSE(grpc_sockaddr_is_wildcard(&wild_mapped, &port)); - /* Test AF_UNSPEC. */ + // Test AF_UNSPEC. port = -1; + grpc_resolved_address phony; memset(&phony, 0, sizeof(phony)); - GPR_ASSERT(!grpc_sockaddr_is_wildcard(&phony, &port)); - GPR_ASSERT(port == -1); + ASSERT_FALSE(grpc_sockaddr_is_wildcard(&phony, &port)); + ASSERT_EQ(port, -1); } -static void expect_sockaddr_str(const char* expected, - grpc_resolved_address* addr, int normalize) { - gpr_log(GPR_INFO, " expect_sockaddr_str(%s)", expected); - std::string actual = grpc_sockaddr_to_string(addr, normalize); - GPR_ASSERT(actual == expected); -} - -static void expect_sockaddr_uri(const char* expected, - grpc_resolved_address* addr) { - gpr_log(GPR_INFO, " expect_sockaddr_uri(%s)", expected); - std::string actual = grpc_sockaddr_to_uri(addr); - GPR_ASSERT(actual == expected); -} - -static void test_sockaddr_to_string(void) { - grpc_resolved_address input4; - grpc_resolved_address input6; - grpc_resolved_address phony; - grpc_sockaddr* phony_addr; - - gpr_log(GPR_INFO, "%s", "test_sockaddr_to_string"); - +TEST(SockAddrUtilsTest, SockAddrToString) { errno = 0x7EADBEEF; - input4 = make_addr4(kIPv4, sizeof(kIPv4)); - expect_sockaddr_str("192.0.2.1:12345", &input4, 0); - expect_sockaddr_str("192.0.2.1:12345", &input4, 1); - expect_sockaddr_uri("ipv4:192.0.2.1:12345", &input4); - - input6 = make_addr6(kIPv6, sizeof(kIPv6)); - expect_sockaddr_str("[2001:db8::1]:12345", &input6, 0); - expect_sockaddr_str("[2001:db8::1]:12345", &input6, 1); - expect_sockaddr_uri("ipv6:[2001:db8::1]:12345", &input6); + grpc_resolved_address input4 = MakeAddr4(kIPv4, sizeof(kIPv4)); + EXPECT_EQ(grpc_sockaddr_to_string(&input4, false), "192.0.2.1:12345"); + EXPECT_EQ(grpc_sockaddr_to_string(&input4, true), "192.0.2.1:12345"); + EXPECT_EQ(grpc_sockaddr_to_uri(&input4), "ipv4:192.0.2.1:12345"); + + grpc_resolved_address input6 = MakeAddr6(kIPv6, sizeof(kIPv6)); + EXPECT_EQ(grpc_sockaddr_to_string(&input6, false), "[2001:db8::1]:12345"); + EXPECT_EQ(grpc_sockaddr_to_string(&input6, true), "[2001:db8::1]:12345"); + EXPECT_EQ(grpc_sockaddr_to_uri(&input6), "ipv6:[2001:db8::1]:12345"); + + SetIPv6ScopeId(&input6, 2); + EXPECT_EQ(grpc_sockaddr_to_string(&input6, false), "[2001:db8::1%252]:12345"); + EXPECT_EQ(grpc_sockaddr_to_string(&input6, true), "[2001:db8::1%252]:12345"); + EXPECT_EQ(grpc_sockaddr_to_uri(&input6), "ipv6:[2001:db8::1%252]:12345"); + + SetIPv6ScopeId(&input6, 101); + EXPECT_EQ(grpc_sockaddr_to_string(&input6, false), + "[2001:db8::1%25101]:12345"); + EXPECT_EQ(grpc_sockaddr_to_string(&input6, true), + "[2001:db8::1%25101]:12345"); + EXPECT_EQ(grpc_sockaddr_to_uri(&input6), "ipv6:[2001:db8::1%25101]:12345"); + + input6 = MakeAddr6(kMapped, sizeof(kMapped)); + EXPECT_EQ(grpc_sockaddr_to_string(&input6, false), + "[::ffff:192.0.2.1]:12345"); + EXPECT_EQ(grpc_sockaddr_to_string(&input6, true), "192.0.2.1:12345"); + EXPECT_EQ(grpc_sockaddr_to_uri(&input6), "ipv4:192.0.2.1:12345"); + + input6 = MakeAddr6(kNotQuiteMapped, sizeof(kNotQuiteMapped)); + EXPECT_EQ(grpc_sockaddr_to_string(&input6, false), "[::fffe:c000:263]:12345"); + EXPECT_EQ(grpc_sockaddr_to_string(&input6, true), "[::fffe:c000:263]:12345"); + EXPECT_EQ(grpc_sockaddr_to_uri(&input6), "ipv6:[::fffe:c000:263]:12345"); - set_addr6_scope_id(&input6, 2); - expect_sockaddr_str("[2001:db8::1%252]:12345", &input6, 0); - expect_sockaddr_str("[2001:db8::1%252]:12345", &input6, 1); - expect_sockaddr_uri("ipv6:[2001:db8::1%252]:12345", &input6); - - set_addr6_scope_id(&input6, 101); - expect_sockaddr_str("[2001:db8::1%25101]:12345", &input6, 0); - expect_sockaddr_str("[2001:db8::1%25101]:12345", &input6, 1); - expect_sockaddr_uri("ipv6:[2001:db8::1%25101]:12345", &input6); + grpc_resolved_address phony; + memset(&phony, 0, sizeof(phony)); + grpc_sockaddr* phony_addr = reinterpret_cast(phony.addr); + phony_addr->sa_family = 123; + EXPECT_EQ(grpc_sockaddr_to_string(&phony, false), "(sockaddr family=123)"); + EXPECT_EQ(grpc_sockaddr_to_string(&phony, true), "(sockaddr family=123)"); + EXPECT_TRUE(grpc_sockaddr_to_uri(&phony).empty()); +} - input6 = make_addr6(kMapped, sizeof(kMapped)); - expect_sockaddr_str("[::ffff:192.0.2.1]:12345", &input6, 0); - expect_sockaddr_str("192.0.2.1:12345", &input6, 1); - expect_sockaddr_uri("ipv4:192.0.2.1:12345", &input6); +TEST(SockAddrUtilsTest, SockAddrSetGetPort) { + grpc_resolved_address input4 = MakeAddr4(kIPv4, sizeof(kIPv4)); + ASSERT_EQ(grpc_sockaddr_get_port(&input4), 12345); + ASSERT_TRUE(grpc_sockaddr_set_port(&input4, 54321)); + ASSERT_EQ(grpc_sockaddr_get_port(&input4), 54321); - input6 = make_addr6(kNotQuiteMapped, sizeof(kNotQuiteMapped)); - expect_sockaddr_str("[::fffe:c000:263]:12345", &input6, 0); - expect_sockaddr_str("[::fffe:c000:263]:12345", &input6, 1); - expect_sockaddr_uri("ipv6:[::fffe:c000:263]:12345", &input6); + grpc_resolved_address input6 = MakeAddr6(kIPv6, sizeof(kIPv6)); + ASSERT_EQ(grpc_sockaddr_get_port(&input6), 12345); + ASSERT_TRUE(grpc_sockaddr_set_port(&input6, 54321)); + ASSERT_EQ(grpc_sockaddr_get_port(&input6), 54321); + grpc_resolved_address phony; memset(&phony, 0, sizeof(phony)); - phony_addr = reinterpret_cast(phony.addr); + grpc_sockaddr* phony_addr = reinterpret_cast(phony.addr); phony_addr->sa_family = 123; - expect_sockaddr_str("(sockaddr family=123)", &phony, 0); - expect_sockaddr_str("(sockaddr family=123)", &phony, 1); - GPR_ASSERT(grpc_sockaddr_to_uri(&phony).empty()); + ASSERT_EQ(grpc_sockaddr_get_port(&phony), false); + ASSERT_EQ(grpc_sockaddr_set_port(&phony, 1234), false); } -static void test_sockaddr_set_get_port(void) { - grpc_resolved_address input4; - grpc_resolved_address input6; - grpc_resolved_address phony; - grpc_sockaddr* phony_addr; - - gpr_log(GPR_DEBUG, "test_sockaddr_set_get_port"); +void VerifySocketAddressMatch(const std::string& ip_address, + const std::string& subnet, uint32_t mask_bits, + bool success) { + grpc_resolved_address addr; + grpc_string_to_sockaddr(&addr, ip_address.c_str(), false); + // Setting the port has no effect on the match. + grpc_sockaddr_set_port(&addr, 12345); + grpc_resolved_address subnet_addr; + grpc_string_to_sockaddr(&subnet_addr, subnet.c_str(), false); + grpc_sockaddr_mask_bits(&subnet_addr, mask_bits); + EXPECT_EQ(grpc_sockaddr_match_subnet(&addr, &subnet_addr, mask_bits), success) + << "IP=" << ip_address << " Subnet=" << subnet << " Mask=" << mask_bits; +} - input4 = make_addr4(kIPv4, sizeof(kIPv4)); - GPR_ASSERT(grpc_sockaddr_get_port(&input4) == 12345); - GPR_ASSERT(grpc_sockaddr_set_port(&input4, 54321)); - GPR_ASSERT(grpc_sockaddr_get_port(&input4) == 54321); +void VerifySocketAddressMatchSuccess(const std::string& ip_address, + const std::string& subnet, + uint32_t mask_bits) { + // If the IP address matches the subnet for a particular length, then it would + // match for all lengths [0, mask_bits] + for (uint32_t i = 0; i <= mask_bits; i++) { + VerifySocketAddressMatch(ip_address, subnet, i, true); + } +} - input6 = make_addr6(kIPv6, sizeof(kIPv6)); - GPR_ASSERT(grpc_sockaddr_get_port(&input6) == 12345); - GPR_ASSERT(grpc_sockaddr_set_port(&input6, 54321)); - GPR_ASSERT(grpc_sockaddr_get_port(&input6) == 54321); +void VerifySocketAddressMatchFailure(const std::string& ip_address, + const std::string& subnet, + uint32_t mask_bits) { + // If the IP address fails matching the subnet for a particular length, then + // it would also fail for all lengths [mask_bits, 128] + for (auto i = mask_bits; i <= 128; i++) { + VerifySocketAddressMatch(ip_address, subnet, i, false); + } +} - memset(&phony, 0, sizeof(phony)); - phony_addr = reinterpret_cast(phony.addr); - phony_addr->sa_family = 123; - GPR_ASSERT(grpc_sockaddr_get_port(&phony) == 0); - GPR_ASSERT(grpc_sockaddr_set_port(&phony, 1234) == 0); +TEST(SockAddrUtilsTest, SockAddrMatchSubnet) { + // IPv4 Tests + VerifySocketAddressMatchSuccess("192.168.1.1", "192.168.1.1", 32); + VerifySocketAddressMatchSuccess("255.255.255.255", "255.255.255.255", 32); + VerifySocketAddressMatchFailure("192.168.1.1", "192.168.1.2", 31); + VerifySocketAddressMatchFailure("192.168.1.1", "191.0.0.0", 8); + VerifySocketAddressMatchFailure("192.168.1.1", "0.0.0.0", 1); + // IPv6 Tests + VerifySocketAddressMatchSuccess("2001:db8::", "2001::", 16); + VerifySocketAddressMatchSuccess("2001:db8:cfe:134:3ab:3456:78:9", + "2001:db8:cfe:134:3ab:3456:78:9", 128); + VerifySocketAddressMatchSuccess("FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", + "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", + 128); + VerifySocketAddressMatchFailure("2001:db8:cfe:134:3ab:3456:78:9", + "3001:2:3:4:5:6:7:8", 4); + VerifySocketAddressMatchFailure("FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", + "::", 1); } +} // namespace + int main(int argc, char** argv) { grpc::testing::TestEnvironment env(argc, argv); - - test_sockaddr_is_v4mapped(); - test_sockaddr_to_v4mapped(); - test_sockaddr_is_wildcard(); - test_sockaddr_to_string(); - test_sockaddr_set_get_port(); - - return 0; + ::testing::InitGoogleTest(&argc, argv); + int retval = RUN_ALL_TESTS(); + return retval; } diff --git a/test/cpp/end2end/xds_end2end_test.cc b/test/cpp/end2end/xds_end2end_test.cc index 1aa45d4ff59..5bf243efe76 100644 --- a/test/cpp/end2end/xds_end2end_test.cc +++ b/test/cpp/end2end/xds_end2end_test.cc @@ -113,6 +113,7 @@ using ::envoy::config::cluster::v3::CustomClusterType; using ::envoy::config::cluster::v3::RoutingPriority; using ::envoy::config::endpoint::v3::ClusterLoadAssignment; using ::envoy::config::endpoint::v3::HealthStatus; +using ::envoy::config::listener::v3::FilterChainMatch; using ::envoy::config::listener::v3::Listener; using ::envoy::config::route::v3::RouteConfiguration; using ::envoy::extensions::clusters::aggregate::v3::ClusterConfig; @@ -7919,16 +7920,6 @@ class XdsEnabledServerStatusNotificationTest : public XdsServerSecurityTest { auto* socket_address = listener.mutable_address()->mutable_socket_address(); socket_address->set_address(ipv6_only_ ? "::1" : "127.0.0.1"); socket_address->set_port_value(backends_[0]->port()); - auto* filter_chain = listener.add_filter_chains(); - filter_chain->add_filters()->mutable_typed_config()->PackFrom( - HttpConnectionManager()); - auto* transport_socket = filter_chain->mutable_transport_socket(); - transport_socket->set_name("envoy.transport_sockets.tls"); - DownstreamTlsContext downstream_tls_context; - downstream_tls_context.mutable_common_tls_context() - ->mutable_tls_certificate_certificate_provider_instance() - ->set_instance_name("unknown"); - transport_socket->mutable_typed_config()->PackFrom(downstream_tls_context); balancers_[0]->ads_service()->SetLdsResource(listener); } @@ -8073,6 +8064,616 @@ TEST_P(XdsEnabledServerStatusNotificationTest, ExistingRpcsOnResourceDeletion) { } } +using XdsServerFilterChainMatchTest = XdsServerSecurityTest; + +TEST_P(XdsServerFilterChainMatchTest, + DefaultFilterChainUsedWhenNoFilterChainMentioned) { + Listener listener; + listener.set_name( + absl::StrCat("grpc/server?xds.resource.listening_address=", + ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port())); + auto* socket_address = listener.mutable_address()->mutable_socket_address(); + socket_address->set_address(ipv6_only_ ? "::1" : "127.0.0.1"); + socket_address->set_port_value(backends_[0]->port()); + listener.mutable_default_filter_chain() + ->add_filters() + ->mutable_typed_config() + ->PackFrom(HttpConnectionManager()); + balancers_[0]->ads_service()->SetLdsResource(listener); + SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); +} + +TEST_P(XdsServerFilterChainMatchTest, + DefaultFilterChainUsedWhenOtherFilterChainsDontMatch) { + Listener listener; + listener.set_name( + absl::StrCat("grpc/server?xds.resource.listening_address=", + ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port())); + auto* socket_address = listener.mutable_address()->mutable_socket_address(); + socket_address->set_address(ipv6_only_ ? "::1" : "127.0.0.1"); + socket_address->set_port_value(backends_[0]->port()); + // Add a filter chain that will never get matched + auto* filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + filter_chain->mutable_filter_chain_match() + ->mutable_destination_port() + ->set_value(8080); + // Add default filter chain that should get used + listener.mutable_default_filter_chain() + ->add_filters() + ->mutable_typed_config() + ->PackFrom(HttpConnectionManager()); + balancers_[0]->ads_service()->SetLdsResource(listener); + SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); +} + +TEST_P(XdsServerFilterChainMatchTest, + FilterChainsWithDestinationPortDontMatch) { + Listener listener; + listener.set_name( + absl::StrCat("grpc/server?xds.resource.listening_address=", + ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port())); + auto* socket_address = listener.mutable_address()->mutable_socket_address(); + socket_address->set_address(ipv6_only_ ? "::1" : "127.0.0.1"); + socket_address->set_port_value(backends_[0]->port()); + // Add filter chain with destination port that should never get matched + auto* filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + filter_chain->mutable_filter_chain_match() + ->mutable_destination_port() + ->set_value(8080); + balancers_[0]->ads_service()->SetLdsResource(listener); + // RPC should fail since no matching filter chain was found and no default + // filter chain is configured. + SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, + true /* test_expects_failure */); +} + +TEST_P(XdsServerFilterChainMatchTest, FilterChainsWithServerNamesDontMatch) { + Listener listener; + listener.set_name( + absl::StrCat("grpc/server?xds.resource.listening_address=", + ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port())); + auto* socket_address = listener.mutable_address()->mutable_socket_address(); + socket_address->set_address(ipv6_only_ ? "::1" : "127.0.0.1"); + socket_address->set_port_value(backends_[0]->port()); + // Add filter chain with server name that should never get matched + auto* filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + filter_chain->mutable_filter_chain_match()->add_server_names("server_name"); + balancers_[0]->ads_service()->SetLdsResource(listener); + // RPC should fail since no matching filter chain was found and no default + // filter chain is configured. + SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, + true /* test_expects_failure */); +} + +TEST_P(XdsServerFilterChainMatchTest, + FilterChainsWithTransportProtocolsOtherThanRawBufferDontMatch) { + Listener listener; + listener.set_name( + absl::StrCat("grpc/server?xds.resource.listening_address=", + ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port())); + auto* socket_address = listener.mutable_address()->mutable_socket_address(); + socket_address->set_address(ipv6_only_ ? "::1" : "127.0.0.1"); + socket_address->set_port_value(backends_[0]->port()); + // Add filter chain with transport protocol "tls" that should never match + auto* filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + filter_chain->mutable_filter_chain_match()->set_transport_protocol("tls"); + balancers_[0]->ads_service()->SetLdsResource(listener); + // RPC should fail since no matching filter chain was found and no default + // filter chain is configured. + SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, + true /* test_expects_failure */); +} + +TEST_P(XdsServerFilterChainMatchTest, + FilterChainsWithApplicationProtocolsDontMatch) { + Listener listener; + listener.set_name( + absl::StrCat("grpc/server?xds.resource.listening_address=", + ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port())); + auto* socket_address = listener.mutable_address()->mutable_socket_address(); + socket_address->set_address(ipv6_only_ ? "::1" : "127.0.0.1"); + socket_address->set_port_value(backends_[0]->port()); + // Add filter chain with application protocol that should never get matched + auto* filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + filter_chain->mutable_filter_chain_match()->add_application_protocols("h2"); + balancers_[0]->ads_service()->SetLdsResource(listener); + // RPC should fail since no matching filter chain was found and no default + // filter chain is configured. + SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, + true /* test_expects_failure */); +} + +TEST_P(XdsServerFilterChainMatchTest, + FilterChainsWithTransportProtocolRawBufferIsPreferred) { + Listener listener; + listener.set_name( + absl::StrCat("grpc/server?xds.resource.listening_address=", + ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port())); + auto* socket_address = listener.mutable_address()->mutable_socket_address(); + socket_address->set_address(ipv6_only_ ? "::1" : "127.0.0.1"); + socket_address->set_port_value(backends_[0]->port()); + // Add filter chain with "raw_buffer" transport protocol + auto* filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + filter_chain->mutable_filter_chain_match()->set_transport_protocol( + "raw_buffer"); + // Add another filter chain with no transport protocol set but application + // protocol set (fails match) + filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + filter_chain->mutable_filter_chain_match()->add_application_protocols("h2"); + balancers_[0]->ads_service()->SetLdsResource(listener); + // A successful RPC proves that filter chains that mention "raw_buffer" as the + // transport protocol are chosen as the best match in the round. + SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); +} + +TEST_P(XdsServerFilterChainMatchTest, + FilterChainsWithMoreSpecificDestinationPrefixRangesArePreferred) { + Listener listener; + listener.set_name( + absl::StrCat("grpc/server?xds.resource.listening_address=", + ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port())); + auto* socket_address = listener.mutable_address()->mutable_socket_address(); + socket_address->set_address(ipv6_only_ ? "::1" : "127.0.0.1"); + socket_address->set_port_value(backends_[0]->port()); + // Add filter chain with prefix range (length 4 and 16) but with server name + // mentioned. (Prefix range is matched first.) + auto* filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + auto* prefix_range = + filter_chain->mutable_filter_chain_match()->add_prefix_ranges(); + prefix_range->set_address_prefix(ipv6_only_ ? "[::1]" : "127.0.0.1"); + prefix_range->mutable_prefix_len()->set_value(4); + prefix_range = + filter_chain->mutable_filter_chain_match()->add_prefix_ranges(); + prefix_range->set_address_prefix(ipv6_only_ ? "[::1]" : "127.0.0.1"); + prefix_range->mutable_prefix_len()->set_value(16); + filter_chain->mutable_filter_chain_match()->add_server_names("server_name"); + // Add filter chain with two prefix ranges (length 8 and 24). Since 24 is the + // highest match, it should be chosen. + filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + prefix_range = + filter_chain->mutable_filter_chain_match()->add_prefix_ranges(); + prefix_range->set_address_prefix(ipv6_only_ ? "[::1]" : "127.0.0.1"); + prefix_range->mutable_prefix_len()->set_value(8); + prefix_range = + filter_chain->mutable_filter_chain_match()->add_prefix_ranges(); + prefix_range->set_address_prefix(ipv6_only_ ? "[::1]" : "127.0.0.1"); + prefix_range->mutable_prefix_len()->set_value(24); + // Add another filter chain with a non-matching prefix range (with length 30) + filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + prefix_range = + filter_chain->mutable_filter_chain_match()->add_prefix_ranges(); + prefix_range->set_address_prefix("192.168.1.1"); + prefix_range->mutable_prefix_len()->set_value(30); + filter_chain->mutable_filter_chain_match()->add_server_names("server_name"); + // Add another filter chain with no prefix range mentioned + filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + filter_chain->mutable_filter_chain_match()->add_server_names("server_name"); + balancers_[0]->ads_service()->SetLdsResource(listener); + // A successful RPC proves that the filter chain with the longest matching + // prefix range was the best match. + SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); +} + +TEST_P(XdsServerFilterChainMatchTest, + FilterChainsThatMentionSourceTypeArePreferred) { + Listener listener; + listener.set_name( + absl::StrCat("grpc/server?xds.resource.listening_address=", + ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port())); + auto* socket_address = listener.mutable_address()->mutable_socket_address(); + socket_address->set_address(ipv6_only_ ? "::1" : "127.0.0.1"); + socket_address->set_port_value(backends_[0]->port()); + // Add filter chain with the local source type (best match) + auto* filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + filter_chain->mutable_filter_chain_match()->set_source_type( + FilterChainMatch::SAME_IP_OR_LOOPBACK); + // Add filter chain with the external source type but bad source port. + // Note that backends_[0]->port() will never be a match for the source port + // because it is already being used by a backend. + filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + filter_chain->mutable_filter_chain_match()->set_source_type( + FilterChainMatch::EXTERNAL); + filter_chain->mutable_filter_chain_match()->add_source_ports( + backends_[0]->port()); + // Add filter chain with the default source type (ANY) but bad source port. + filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + filter_chain->mutable_filter_chain_match()->add_source_ports( + backends_[0]->port()); + balancers_[0]->ads_service()->SetLdsResource(listener); + // A successful RPC proves that the filter chain with the longest matching + // prefix range was the best match. + SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); +} + +TEST_P(XdsServerFilterChainMatchTest, + FilterChainsWithMoreSpecificSourcePrefixRangesArePreferred) { + Listener listener; + listener.set_name( + absl::StrCat("grpc/server?xds.resource.listening_address=", + ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port())); + auto* socket_address = listener.mutable_address()->mutable_socket_address(); + socket_address->set_address(ipv6_only_ ? "::1" : "127.0.0.1"); + socket_address->set_port_value(backends_[0]->port()); + // Add filter chain with source prefix range (length 16) but with a bad source + // port mentioned. (Prefix range is matched first.) + // Note that backends_[0]->port() will never be a match for the source port + // because it is already being used by a backend. + auto* filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + auto* source_prefix_range = + filter_chain->mutable_filter_chain_match()->add_source_prefix_ranges(); + source_prefix_range->set_address_prefix(ipv6_only_ ? "[::1]" : "127.0.0.1"); + source_prefix_range->mutable_prefix_len()->set_value(4); + source_prefix_range = + filter_chain->mutable_filter_chain_match()->add_source_prefix_ranges(); + source_prefix_range->set_address_prefix(ipv6_only_ ? "[::1]" : "127.0.0.1"); + source_prefix_range->mutable_prefix_len()->set_value(16); + filter_chain->mutable_filter_chain_match()->add_source_ports( + backends_[0]->port()); + // Add filter chain with two source prefix ranges (length 8 and 24). Since 24 + // is the highest match, it should be chosen. + filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + source_prefix_range = + filter_chain->mutable_filter_chain_match()->add_source_prefix_ranges(); + source_prefix_range->set_address_prefix(ipv6_only_ ? "[::1]" : "127.0.0.1"); + source_prefix_range->mutable_prefix_len()->set_value(8); + source_prefix_range = + filter_chain->mutable_filter_chain_match()->add_source_prefix_ranges(); + source_prefix_range->set_address_prefix(ipv6_only_ ? "[::1]" : "127.0.0.1"); + source_prefix_range->mutable_prefix_len()->set_value(24); + // Add another filter chain with a non-matching source prefix range (with + // length 30) and bad source port + filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + source_prefix_range = + filter_chain->mutable_filter_chain_match()->add_source_prefix_ranges(); + source_prefix_range->set_address_prefix("192.168.1.1"); + source_prefix_range->mutable_prefix_len()->set_value(30); + filter_chain->mutable_filter_chain_match()->add_source_ports( + backends_[0]->port()); + // Add another filter chain with no source prefix range mentioned and bad + // source port + filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + filter_chain->mutable_filter_chain_match()->add_source_ports( + backends_[0]->port()); + balancers_[0]->ads_service()->SetLdsResource(listener); + // A successful RPC proves that the filter chain with the longest matching + // source prefix range was the best match. + SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); +} + +TEST_P(XdsServerFilterChainMatchTest, + FilterChainsWithMoreSpecificSourcePortArePreferred) { + Listener listener; + listener.set_name( + absl::StrCat("grpc/server?xds.resource.listening_address=", + ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port())); + auto* socket_address = listener.mutable_address()->mutable_socket_address(); + socket_address->set_address(ipv6_only_ ? "::1" : "127.0.0.1"); + socket_address->set_port_value(backends_[0]->port()); + auto* filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + // Since we don't know which port will be used by the channel, just add all + // ports except for 0. + for (int i = 1; i < 65536; i++) { + filter_chain->mutable_filter_chain_match()->add_source_ports(i); + } + // Add another filter chain with no source prefix range mentioned with a bad + // DownstreamTlsContext configuration. + filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + auto* transport_socket = filter_chain->mutable_transport_socket(); + transport_socket->set_name("envoy.transport_sockets.tls"); + DownstreamTlsContext downstream_tls_context; + downstream_tls_context.mutable_common_tls_context() + ->mutable_tls_certificate_certificate_provider_instance() + ->set_instance_name("unknown"); + transport_socket->mutable_typed_config()->PackFrom(downstream_tls_context); + balancers_[0]->ads_service()->SetLdsResource(listener); + // A successful RPC proves that the filter chain with matching source port + // was chosen. + SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); +} + +TEST_P(XdsServerFilterChainMatchTest, DuplicateMatchNacked) { + Listener listener; + listener.set_name( + absl::StrCat("grpc/server?xds.resource.listening_address=", + ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port())); + auto* socket_address = listener.mutable_address()->mutable_socket_address(); + socket_address->set_address(ipv6_only_ ? "::1" : "127.0.0.1"); + socket_address->set_port_value(backends_[0]->port()); + // Add filter chain + auto* filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + // Add a duplicate filter chain + filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + balancers_[0]->ads_service()->SetLdsResource(listener); + do { + CheckRpcSendFailure(); + } while (balancers_[0]->ads_service()->lds_response_state().state == + AdsServiceImpl::ResponseState::SENT); + const auto response_state = + balancers_[0]->ads_service()->lds_response_state(); + EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED); + EXPECT_THAT( + response_state.error_message, + ::testing::HasSubstr( + "Duplicate matching rules detected when adding filter chain: {}")); +} + +TEST_P(XdsServerFilterChainMatchTest, DuplicateMatchOnPrefixRangesNacked) { + Listener listener; + listener.set_name( + absl::StrCat("grpc/server?xds.resource.listening_address=", + ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port())); + auto* socket_address = listener.mutable_address()->mutable_socket_address(); + socket_address->set_address(ipv6_only_ ? "::1" : "127.0.0.1"); + socket_address->set_port_value(backends_[0]->port()); + // Add filter chain with prefix range + auto* filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + auto* prefix_range = + filter_chain->mutable_filter_chain_match()->add_prefix_ranges(); + prefix_range->set_address_prefix(ipv6_only_ ? "[::1]" : "127.0.0.1"); + prefix_range->mutable_prefix_len()->set_value(16); + prefix_range = + filter_chain->mutable_filter_chain_match()->add_prefix_ranges(); + prefix_range->set_address_prefix(ipv6_only_ ? "[::1]" : "127.0.0.1"); + prefix_range->mutable_prefix_len()->set_value(24); + // Add a filter chain with a duplicate prefix range entry + filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + prefix_range = + filter_chain->mutable_filter_chain_match()->add_prefix_ranges(); + prefix_range->set_address_prefix(ipv6_only_ ? "[::1]" : "127.0.0.1"); + prefix_range->mutable_prefix_len()->set_value(16); + prefix_range = + filter_chain->mutable_filter_chain_match()->add_prefix_ranges(); + prefix_range->set_address_prefix(ipv6_only_ ? "[::1]" : "127.0.0.1"); + prefix_range->mutable_prefix_len()->set_value(32); + balancers_[0]->ads_service()->SetLdsResource(listener); + do { + CheckRpcSendFailure(); + } while (balancers_[0]->ads_service()->lds_response_state().state == + AdsServiceImpl::ResponseState::SENT); + const auto response_state = + balancers_[0]->ads_service()->lds_response_state(); + EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED); + EXPECT_THAT( + response_state.error_message, + ::testing::HasSubstr( + "Duplicate matching rules detected when adding filter chain: " + "{prefix_ranges={{address_prefix=127.0.0.0:0, prefix_len=16}, " + "{address_prefix=127.0.0.1:0, prefix_len=32}}}")); +} + +TEST_P(XdsServerFilterChainMatchTest, DuplicateMatchOnTransportProtocolNacked) { + Listener listener; + listener.set_name( + absl::StrCat("grpc/server?xds.resource.listening_address=", + ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port())); + auto* socket_address = listener.mutable_address()->mutable_socket_address(); + socket_address->set_address(ipv6_only_ ? "::1" : "127.0.0.1"); + socket_address->set_port_value(backends_[0]->port()); + // Add filter chain with "raw_buffer" transport protocol + auto* filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + filter_chain->mutable_filter_chain_match()->set_transport_protocol( + "raw_buffer"); + // Add a duplicate filter chain with the same "raw_buffer" transport protocol + // entry + filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + filter_chain->mutable_filter_chain_match()->set_transport_protocol( + "raw_buffer"); + balancers_[0]->ads_service()->SetLdsResource(listener); + do { + CheckRpcSendFailure(); + } while (balancers_[0]->ads_service()->lds_response_state().state == + AdsServiceImpl::ResponseState::SENT); + const auto response_state = + balancers_[0]->ads_service()->lds_response_state(); + EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED); + EXPECT_THAT( + response_state.error_message, + ::testing::HasSubstr("Duplicate matching rules detected when adding " + "filter chain: {transport_protocol=raw_buffer}")); +} + +TEST_P(XdsServerFilterChainMatchTest, DuplicateMatchOnLocalSourceTypeNacked) { + Listener listener; + listener.set_name( + absl::StrCat("grpc/server?xds.resource.listening_address=", + ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port())); + auto* socket_address = listener.mutable_address()->mutable_socket_address(); + socket_address->set_address(ipv6_only_ ? "::1" : "127.0.0.1"); + socket_address->set_port_value(backends_[0]->port()); + // Add filter chain with the local source type + auto* filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + filter_chain->mutable_filter_chain_match()->set_source_type( + FilterChainMatch::SAME_IP_OR_LOOPBACK); + // Add a duplicate filter chain with the same local source type entry + filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + filter_chain->mutable_filter_chain_match()->set_source_type( + FilterChainMatch::SAME_IP_OR_LOOPBACK); + balancers_[0]->ads_service()->SetLdsResource(listener); + do { + CheckRpcSendFailure(); + } while (balancers_[0]->ads_service()->lds_response_state().state == + AdsServiceImpl::ResponseState::SENT); + const auto response_state = + balancers_[0]->ads_service()->lds_response_state(); + EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED); + EXPECT_THAT( + response_state.error_message, + ::testing::HasSubstr("Duplicate matching rules detected when adding " + "filter chain: {source_type=SAME_IP_OR_LOOPBACK}")); +} + +TEST_P(XdsServerFilterChainMatchTest, + DuplicateMatchOnExternalSourceTypeNacked) { + Listener listener; + listener.set_name( + absl::StrCat("grpc/server?xds.resource.listening_address=", + ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port())); + auto* socket_address = listener.mutable_address()->mutable_socket_address(); + socket_address->set_address(ipv6_only_ ? "::1" : "127.0.0.1"); + socket_address->set_port_value(backends_[0]->port()); + // Add filter chain with the external source type + auto* filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + filter_chain->mutable_filter_chain_match()->set_source_type( + FilterChainMatch::EXTERNAL); + // Add a duplicate filter chain with the same external source type entry + filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + filter_chain->mutable_filter_chain_match()->set_source_type( + FilterChainMatch::EXTERNAL); + balancers_[0]->ads_service()->SetLdsResource(listener); + do { + CheckRpcSendFailure(); + } while (balancers_[0]->ads_service()->lds_response_state().state == + AdsServiceImpl::ResponseState::SENT); + const auto response_state = + balancers_[0]->ads_service()->lds_response_state(); + EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED); + EXPECT_THAT( + response_state.error_message, + ::testing::HasSubstr("Duplicate matching rules detected when adding " + "filter chain: {source_type=EXTERNAL}")); +} + +TEST_P(XdsServerFilterChainMatchTest, + DuplicateMatchOnSourcePrefixRangesNacked) { + Listener listener; + listener.set_name( + absl::StrCat("grpc/server?xds.resource.listening_address=", + ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port())); + auto* socket_address = listener.mutable_address()->mutable_socket_address(); + socket_address->set_address(ipv6_only_ ? "::1" : "127.0.0.1"); + socket_address->set_port_value(backends_[0]->port()); + // Add filter chain with source prefix range + auto* filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + auto* prefix_range = + filter_chain->mutable_filter_chain_match()->add_source_prefix_ranges(); + prefix_range->set_address_prefix(ipv6_only_ ? "[::1]" : "127.0.0.1"); + prefix_range->mutable_prefix_len()->set_value(16); + prefix_range = + filter_chain->mutable_filter_chain_match()->add_source_prefix_ranges(); + prefix_range->set_address_prefix(ipv6_only_ ? "[::1]" : "127.0.0.1"); + prefix_range->mutable_prefix_len()->set_value(24); + // Add a filter chain with a duplicate source prefix range entry + filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + prefix_range = + filter_chain->mutable_filter_chain_match()->add_source_prefix_ranges(); + prefix_range->set_address_prefix(ipv6_only_ ? "[::1]" : "127.0.0.1"); + prefix_range->mutable_prefix_len()->set_value(16); + prefix_range = + filter_chain->mutable_filter_chain_match()->add_source_prefix_ranges(); + prefix_range->set_address_prefix(ipv6_only_ ? "[::1]" : "127.0.0.1"); + prefix_range->mutable_prefix_len()->set_value(32); + balancers_[0]->ads_service()->SetLdsResource(listener); + do { + CheckRpcSendFailure(); + } while (balancers_[0]->ads_service()->lds_response_state().state == + AdsServiceImpl::ResponseState::SENT); + const auto response_state = + balancers_[0]->ads_service()->lds_response_state(); + EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED); + EXPECT_THAT( + response_state.error_message, + ::testing::HasSubstr( + "Duplicate matching rules detected when adding filter chain: " + "{source_prefix_ranges={{address_prefix=127.0.0.0:0, prefix_len=16}, " + "{address_prefix=127.0.0.1:0, prefix_len=32}}}")); +} + +TEST_P(XdsServerFilterChainMatchTest, DuplicateMatchOnSourcePortNacked) { + Listener listener; + listener.set_name( + absl::StrCat("grpc/server?xds.resource.listening_address=", + ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port())); + auto* socket_address = listener.mutable_address()->mutable_socket_address(); + socket_address->set_address(ipv6_only_ ? "::1" : "127.0.0.1"); + socket_address->set_port_value(backends_[0]->port()); + // Add filter chain with the external source type + auto* filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + filter_chain->mutable_filter_chain_match()->add_source_ports(8080); + // Add a duplicate filter chain with the same source port entry + filter_chain = listener.add_filter_chains(); + filter_chain->add_filters()->mutable_typed_config()->PackFrom( + HttpConnectionManager()); + filter_chain->mutable_filter_chain_match()->add_source_ports(8080); + balancers_[0]->ads_service()->SetLdsResource(listener); + do { + CheckRpcSendFailure(); + } while (balancers_[0]->ads_service()->lds_response_state().state == + AdsServiceImpl::ResponseState::SENT); + const auto response_state = + balancers_[0]->ads_service()->lds_response_state(); + EXPECT_EQ(response_state.state, AdsServiceImpl::ResponseState::NACKED); + EXPECT_THAT( + response_state.error_message, + ::testing::HasSubstr("Duplicate matching rules detected when adding " + "filter chain: {source_ports={8080}}")); +} + using EdsTest = BasicTest; // Tests that EDS client should send a NACK if the EDS update contains @@ -10605,6 +11206,13 @@ INSTANTIATE_TEST_SUITE_P(XdsTest, XdsEnabledServerStatusNotificationTest, .set_use_xds_credentials()), &TestTypeName); +// We are only testing the server here. +INSTANTIATE_TEST_SUITE_P(XdsTest, XdsServerFilterChainMatchTest, + ::testing::Values(TestType() + .set_use_fake_resolver() + .set_use_xds_credentials()), + &TestTypeName); + // EDS could be tested with or without XdsResolver, but the tests would // be the same either way, so we test it only with XdsResolver. INSTANTIATE_TEST_SUITE_P( diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json index cae9ef0526b..38ee036ccc2 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -2445,30 +2445,6 @@ ], "uses_polling": true }, - { - "args": [], - "benchmark": false, - "ci_platforms": [ - "linux", - "mac", - "posix", - "windows" - ], - "cpu_cost": 1.0, - "exclude_configs": [], - "exclude_iomgrs": [], - "flaky": false, - "gtest": false, - "language": "c", - "name": "sockaddr_utils_test", - "platforms": [ - "linux", - "mac", - "posix", - "windows" - ], - "uses_polling": true - }, { "args": [], "benchmark": false, @@ -5863,6 +5839,30 @@ ], "uses_polling": true }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": true, + "language": "c++", + "name": "sockaddr_utils_test", + "platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "uses_polling": true + }, { "args": [], "benchmark": false,