Implement FilterChainMatch algorithm (#25757)

* Implement FilterChainMatch logic

* Add tests for transport protocol too

* Tests for duplicate NACKing

* Introduce ConnectionManager as an interface for config fetchers

* Do not parameterize IncrementIfNonZero

* Some formatting

* Reviewer comments

* Add filter chain match information for duplicate match error

* Reviewer comments

* Some cleanup

* Reviewer comments

* Reviewer comments

* Reviewer comments

* Clang-tidy
pull/25821/head
Yash Tibrewal 4 years ago committed by GitHub
parent 10b17f3739
commit 53ba4a101e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 64
      CMakeLists.txt
  2. 17
      build_autogenerated.yaml
  3. 70
      src/core/ext/transport/chttp2/server/chttp2_server.cc
  4. 494
      src/core/ext/xds/xds_api.cc
  5. 137
      src/core/ext/xds/xds_api.h
  6. 435
      src/core/ext/xds/xds_server_config_fetcher.cc
  7. 6
      src/core/lib/gprpp/atomic.h
  8. 101
      src/core/lib/iomgr/sockaddr_utils.cc
  9. 22
      src/core/lib/iomgr/sockaddr_utils.h
  10. 18
      src/core/lib/surface/server.h
  11. 1
      src/proto/grpc/testing/xds/v3/BUILD
  12. 12
      src/proto/grpc/testing/xds/v3/address.proto
  13. 67
      src/proto/grpc/testing/xds/v3/listener.proto
  14. 3
      test/core/iomgr/BUILD
  15. 396
      test/core/iomgr/sockaddr_utils_test.cc
  16. 628
      test/cpp/end2end/xds_end2end_test.cc
  17. 48
      tools/run_tests/generated/tests.json

@ -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)

@ -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++

@ -93,7 +93,9 @@ class Chttp2ServerListener : public Server::ListenerInterface {
explicit ConfigFetcherWatcher(RefCountedPtr<Chttp2ServerListener> listener)
: listener_(std::move(listener)) {}
void UpdateConfig(grpc_channel_args* args) override;
void UpdateConnectionManager(
RefCountedPtr<grpc_server_config_fetcher::ConnectionManager>
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<grpc_server_config_fetcher::ConnectionManager>
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<grpc_server_config_fetcher::ConnectionManager>
connection_manager) {
RefCountedPtr<grpc_server_config_fetcher::ConnectionManager>
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<Chttp2ServerListener*>(arg);
grpc_channel_args* args = nullptr;
RefCountedPtr<grpc_server_config_fetcher::ConnectionManager>
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<grpc_channel_args*> 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<ActiveConnection>(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);
}

@ -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<XdsApi::LdsUpdate::FilterChainMap::CidrRange> prefix_ranges;
XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceType source_type =
XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceType::kAny;
std::vector<XdsApi::LdsUpdate::FilterChainMap::CidrRange>
source_prefix_ranges;
std::vector<uint32_t> source_ports;
std::vector<std::string> server_names;
std::string transport_protocol;
std::vector<std::string> application_protocols;
std::string ToString() const;
} filter_chain_match;
std::shared_ptr<XdsApi::LdsUpdate::FilterChainData> filter_chain_data;
};
std::string FilterChain::FilterChainMatch::ToString() const {
absl::InlinedVector<std::string, 8> 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<std::string> 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<std::string, 4> contents;
if (type == ListenerType::kTcpListener) {
contents.push_back(absl::StrCat("address=", address));
std::vector<std::string> 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<const grpc_sockaddr*>(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<XdsApi::LdsUpdate::FilterChainMap::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++) {
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<XdsApi::LdsUpdate::FilterChainData>();
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<std::string, XdsApi::LdsUpdate::FilterChainMap::SourceIp>;
using ConnectionSourceTypesArray = std::array<SourceIpMap, 3>;
struct DestinationIp {
absl::optional<XdsApi::LdsUpdate::FilterChainMap::CidrRange> prefix_range;
bool transport_protocol_raw_buffer_provided = false;
ConnectionSourceTypesArray source_types_array;
};
using DestinationIpMap = std::map<std::string, DestinationIp>;
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<unsigned int>(
filter_chain.filter_chain_match.source_type) < 3);
return AddFilterChainDataForSourceIpRange(
filter_chain, &destination_ip->source_types_array[static_cast<int>(
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<FilterChain>& 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<FilterChain> 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;

@ -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<CidrRange> prefix_ranges;
enum class ConnectionSourceType {
kAny = 0,
kSameIpOrLoopback,
kExternal
} source_type = ConnectionSourceType::kAny;
std::vector<CidrRange> source_prefix_ranges;
std::vector<uint32_t> source_ports;
std::vector<std::string> server_names;
std::string transport_protocol;
std::vector<std::string> 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<FilterChainData> 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<FilterChain> filter_chains;
absl::optional<FilterChain> default_filter_chain;
std::string ToString() const;
};
using SourcePortsMap = std::map<uint16_t, FilterChainDataSharedPtr>;
struct SourceIp {
absl::optional<CidrRange> 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<SourceIp>;
enum class ConnectionSourceType {
kAny = 0,
kSameIpOrLoopback,
kExternal
};
using ConnectionSourceTypesArray = std::array<SourceIpVector, 3>;
struct DestinationIp {
absl::optional<CidrRange> 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<DestinationIp>;
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<FilterChainData> 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;
}

@ -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<XdsClient> xds_client,
XdsApi::LdsUpdate::FilterChainMap filter_chain_map,
absl::optional<XdsApi::LdsUpdate::FilterChainData> 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<grpc_channel_args*> UpdateChannelArgsForConnection(
grpc_channel_args* args, grpc_endpoint* tcp) override;
const XdsApi::LdsUpdate::FilterChainMap& filter_chain_map() const {
return filter_chain_map_;
}
const absl::optional<XdsApi::LdsUpdate::FilterChainData>&
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<grpc_tls_certificate_provider> root;
RefCountedPtr<grpc_tls_certificate_provider> instance;
RefCountedPtr<XdsCertificateProvider> xds;
};
absl::StatusOr<RefCountedPtr<XdsCertificateProvider>>
CreateOrGetXdsCertificateProviderFromFilterChainData(
const XdsApi::LdsUpdate::FilterChainData* filter_chain);
const RefCountedPtr<XdsClient> xds_client_;
const XdsApi::LdsUpdate::FilterChainMap filter_chain_map_;
const absl::optional<XdsApi::LdsUpdate::FilterChainData>
default_filter_chain_;
Mutex mu_;
std::map<const XdsApi::LdsUpdate::FilterChainData*, CertificateProviders>
certificate_providers_map_ ABSL_GUARDED_BY(mu_);
};
bool IsLoopbackIp(const grpc_resolved_address* address) {
const grpc_sockaddr* sock_addr =
reinterpret_cast<const grpc_sockaddr*>(&address->addr);
if (sock_addr->sa_family == GRPC_AF_INET) {
const grpc_sockaddr_in* addr4 =
reinterpret_cast<const grpc_sockaddr_in*>(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<const grpc_sockaddr_in6*>(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<int>(
XdsApi::LdsUpdate::FilterChainMap::
ConnectionSourceType::kSameIpOrLoopback)]
.empty() &&
source_types_array[static_cast<int>(XdsApi::LdsUpdate::FilterChainMap::
ConnectionSourceType::kExternal)]
.empty()) {
return FindFilterChainDataForSourceIp(
source_types_array[static_cast<int>(
XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceType::kAny)],
&source_addr, port);
}
if (IsLoopbackIp(&source_addr) || host == destination_ip) {
return FindFilterChainDataForSourceIp(
source_types_array[static_cast<int>(
XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceType::
kSameIpOrLoopback)],
&source_addr, port);
} else {
return FindFilterChainDataForSourceIp(
source_types_array[static_cast<int>(
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<RefCountedPtr<XdsCertificateProvider>>
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<XdsCertificateProvider>();
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<grpc_channel_args*>
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<RefCountedPtr<XdsCertificateProvider>> result =
CreateOrGetXdsCertificateProviderFromFilterChainData(filter_chain);
if (!result.ok()) {
grpc_channel_args_destroy(args);
return result.status();
}
RefCountedPtr<XdsCertificateProvider> 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<XdsClient> 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<FilterChainMatchManager>(
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<XdsCertificateProvider>();
}
// 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<grpc_tls_certificate_provider> 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<grpc_tls_certificate_provider> 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<grpc_server_config_fetcher::WatcherInterface>
server_config_watcher_;
grpc_channel_args* args_;
RefCountedPtr<XdsClient> xds_client_;
grpc_server_xds_status_notifier serving_status_notifier_;
std::string listening_address_;
RefCountedPtr<grpc_tls_certificate_provider> root_certificate_provider_;
RefCountedPtr<grpc_tls_certificate_provider> identity_certificate_provider_;
RefCountedPtr<XdsCertificateProvider> xds_certificate_provider_;
bool have_resource_ = false;
RefCountedPtr<FilterChainMatchManager> filter_chain_match_manager_;
};
struct WatcherState {

@ -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<std::memory_order>(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;
}

@ -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<grpc_sockaddr_in6*>(out->addr);
grpc_sockaddr_in* addr4 = reinterpret_cast<grpc_sockaddr_in*>(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<grpc_sockaddr*>(address->addr);
if (addr->sa_family == GRPC_AF_INET) {
grpc_sockaddr_in* addr4 = reinterpret_cast<grpc_sockaddr_in*>(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<grpc_sockaddr_in6*>(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<const grpc_sockaddr*>(address->addr);
auto* subnet_addr =
reinterpret_cast<const grpc_sockaddr*>(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<grpc_sockaddr*>((&masked_address)->addr);
grpc_sockaddr_mask_bits(&masked_address, mask_bits);
if (addr->sa_family == GRPC_AF_INET) {
auto* addr4 = reinterpret_cast<const grpc_sockaddr_in*>(addr);
auto* subnet_addr4 = reinterpret_cast<const grpc_sockaddr_in*>(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<const grpc_sockaddr_in6*>(addr);
auto* subnet_addr6 =
reinterpret_cast<const grpc_sockaddr_in6*>(subnet_addr);
if (memcmp(&addr6->sin6_addr, &subnet_addr6->sin6_addr,
sizeof(addr6->sin6_addr)) == 0) {
return true;
}
}
return false;
}

@ -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 */

@ -22,6 +22,7 @@
#include <list>
#include <vector>
#include "absl/status/statusor.h"
#include "absl/types/optional.h"
#include <grpc/grpc.h>
@ -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<ConnectionManager> {
public:
// Ownership of \a args is transfered.
virtual absl::StatusOr<grpc_channel_args*> 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<ConnectionManager> manager) = 0;
// Implementations should stop serving when this is called. Serving should
// only resume when UpdateConfig() is invoked.
virtual void StopServing() = 0;

@ -26,6 +26,7 @@ grpc_proto_library(
srcs = [
"address.proto",
],
well_known_protos = True,
)
grpc_proto_library(

@ -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 <https://tools.ietf.org/html/rfc4632>`_ 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;
}

@ -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 <faq_how_to_setup_sni>` 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 <config_listener_filters_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.

@ -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",

@ -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 <errno.h>
#include <string.h>
#include <gtest/gtest.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/port_platform.h>
#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<grpc_sockaddr_in*>(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<grpc_sockaddr_in6*>(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<grpc_sockaddr_in6*>(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<grpc_sockaddr_in*>(&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<grpc_sockaddr_in*>(&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<grpc_sockaddr_in6*>(&wild6.addr);
ASSERT_TRUE(grpc_sockaddr_is_wildcard(&wild6, &port));
ASSERT_EQ(port, 555);
grpc_sockaddr_in6* wild6_addr =
reinterpret_cast<grpc_sockaddr_in6*>(&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<grpc_sockaddr_in6*>(&wild_mapped.addr);
ASSERT_TRUE(grpc_sockaddr_is_wildcard(&wild_mapped, &port));
ASSERT_EQ(port, 555);
grpc_sockaddr_in6* wild_mapped_addr =
reinterpret_cast<grpc_sockaddr_in6*>(&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<grpc_sockaddr*>(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<grpc_sockaddr*>(phony.addr);
grpc_sockaddr* phony_addr = reinterpret_cast<grpc_sockaddr*>(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<grpc_sockaddr*>(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;
}

@ -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(

@ -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,

Loading…
Cancel
Save