diff --git a/src/core/ext/xds/xds_api.cc b/src/core/ext/xds/xds_api.cc index e8d4d24942b..7aaed85ea77 100644 --- a/src/core/ext/xds/xds_api.cc +++ b/src/core/ext/xds/xds_api.cc @@ -447,6 +447,78 @@ std::string XdsApi::LdsUpdate::HttpFilter::ToString() const { return absl::StrCat("{name=", name, ", config=", config.ToString(), "}"); } +// +// XdsApi::LdsUpdate::FilterChain::FilterChainMatch::CidrRange +// + +std::string +XdsApi::LdsUpdate::FilterChain::FilterChainMatch::CidrRange::ToString() const { + return absl::StrCat("{address_prefix=", address_prefix, + " prefix_len=", prefix_len, "}"); +} + +// +// XdsApi::LdsUpdate::FilterChain::FilterChainMatch +// + +std::string XdsApi::LdsUpdate::FilterChain::FilterChainMatch::ToString() const { + absl::InlinedVector contents; + if (destination_port != 0) { + contents.push_back(absl::StrCat("destination_port=", destination_port)); + } + if (!prefix_ranges.empty()) { + std::vector prefix_ranges_content; + for (const auto& range : prefix_ranges) { + prefix_ranges_content.push_back(range.ToString()); + } + contents.push_back(absl::StrCat( + "prefix_ranges={", absl::StrJoin(prefix_ranges_content, ", "), "}")); + } + if (source_type == XdsApi::LdsUpdate::FilterChain::FilterChainMatch:: + ConnectionSourceType::kSameIpOrLoopback) { + contents.push_back("source_type=SAME_IP_OR_LOOPBACK"); + } else if (source_type == XdsApi::LdsUpdate::FilterChain::FilterChainMatch:: + ConnectionSourceType::kExternal) { + contents.push_back("source_type=EXTERNAL"); + } + if (!source_prefix_ranges.empty()) { + std::vector source_prefix_ranges_content; + for (const auto& range : source_prefix_ranges) { + source_prefix_ranges_content.push_back(range.ToString()); + } + contents.push_back( + absl::StrCat("source_prefix_ranges={", + absl::StrJoin(source_prefix_ranges_content, ", "), "}")); + } + if (!source_ports.empty()) { + contents.push_back( + absl::StrCat("source_ports={", absl::StrJoin(source_ports, ", "), "}")); + } + if (!server_names.empty()) { + contents.push_back( + absl::StrCat("server_names={", absl::StrJoin(server_names, ", "), "}")); + } + if (!transport_protocol.empty()) { + contents.push_back(absl::StrCat("transport_protocol=", transport_protocol)); + } + if (!application_protocols.empty()) { + contents.push_back(absl::StrCat("application_protocols={", + absl::StrJoin(application_protocols, ", "), + "}")); + } + return absl::StrCat("{", absl::StrJoin(contents, ", "), "}"); +} + +// +// XdsApi::LdsUpdate::FilterChain +// + +std::string XdsApi::LdsUpdate::FilterChain::ToString() const { + return absl::StrFormat("{filter_chain_match=%s, downstream_tls_context=%s}", + filter_chain_match.ToString(), + downstream_tls_context.ToString()); +} + // // XdsApi::LdsUpdate // @@ -454,9 +526,15 @@ std::string XdsApi::LdsUpdate::HttpFilter::ToString() const { std::string XdsApi::LdsUpdate::ToString() const { absl::InlinedVector contents; if (type == ListenerType::kTcpListener) { - if (!downstream_tls_context.Empty()) { - contents.push_back(absl::StrFormat("downstream_tls_context=%s", - downstream_tls_context.ToString())); + std::vector filter_chains_content; + for (const auto& filter_chain : filter_chains) { + filter_chains_content.push_back(filter_chain.ToString()); + } + contents.push_back(absl::StrCat( + "filter_chains={", absl::StrJoin(filter_chains_content, ", "), "}")); + if (default_filter_chain.has_value()) { + contents.push_back(absl::StrCat("default_filter_chain=", + default_filter_chain->ToString())); } } else if (type == ListenerType::kHttpApiListener) { contents.push_back(absl::StrFormat( @@ -1677,71 +1755,183 @@ grpc_error* LdsResponseParseClient( return GRPC_ERROR_NONE; } +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, + XdsApi::DownstreamTlsContext* downstream_tls_context) { + absl::string_view name = UpbStringToAbsl( + envoy_config_core_v3_TransportSocket_name(transport_socket)); + if (name == "envoy.transport_sockets.tls") { + auto* typed_config = + envoy_config_core_v3_TransportSocket_typed_config(transport_socket); + if (typed_config != nullptr) { + const upb_strview encoded_downstream_tls_context = + google_protobuf_Any_value(typed_config); + auto* downstream_tls_context_proto = + envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_parse( + encoded_downstream_tls_context.data, + encoded_downstream_tls_context.size, context.arena); + if (downstream_tls_context_proto == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Can't decode downstream tls context."); + } + auto* common_tls_context = + envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_common_tls_context( + downstream_tls_context_proto); + if (common_tls_context != nullptr) { + grpc_error* error = CommonTlsContextParse( + common_tls_context, &downstream_tls_context->common_tls_context); + if (error != GRPC_ERROR_NONE) return error; + } + auto* require_client_certificate = + envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_require_client_certificate( + downstream_tls_context_proto); + if (require_client_certificate != nullptr) { + downstream_tls_context->require_client_certificate = + google_protobuf_BoolValue_value(require_client_certificate); + } + } + if (downstream_tls_context->common_tls_context + .tls_certificate_certificate_provider_instance.instance_name + .empty()) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "TLS configuration provided but no " + "tls_certificate_certificate_provider_instance found."); + } + } + return GRPC_ERROR_NONE; +} + +XdsApi::LdsUpdate::FilterChain FilterChainParse( + const EncodingContext& context, + const envoy_config_listener_v3_FilterChain* filter_chain_proto, + grpc_error** error) { + XdsApi::LdsUpdate::FilterChain filter_chain; + 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); + } + // Get the DownstreamTlsContext for the filter chain + if (XdsSecurityEnabled()) { + auto* transport_socket = + envoy_config_listener_v3_FilterChain_transport_socket( + filter_chain_proto); + if (transport_socket != nullptr) { + *error = DownstreamTlsContextParse(context, transport_socket, + &filter_chain.downstream_tls_context); + } + } + return filter_chain; +} + grpc_error* LdsResponseParseServer( const EncodingContext& context, const envoy_config_listener_v3_Listener* listener, XdsApi::LdsUpdate* lds_update) { lds_update->type = XdsApi::LdsUpdate::ListenerType::kTcpListener; - // TODO(yashykt): Support filter chain match. - // Right now, we are supporting and expecting only one entry in filter_chains. - // 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 + grpc_error* error = GRPC_ERROR_NONE; + // 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); - if (size != 1) { + // TODO(yashykt): Remove following if block when FilterChainMatch + // implementation is in + if (size == 0) { return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Only one filter_chain supported."); + "At least one filter chain needed."); } - // Get the DownstreamTlsContext from the match - if (XdsSecurityEnabled()) { - auto* transport_socket = - envoy_config_listener_v3_FilterChain_transport_socket(filter_chains[0]); - if (transport_socket != nullptr) { - absl::string_view name = UpbStringToAbsl( - envoy_config_core_v3_TransportSocket_name(transport_socket)); - if (name == "envoy.transport_sockets.tls") { - auto* typed_config = - envoy_config_core_v3_TransportSocket_typed_config(transport_socket); - if (typed_config != nullptr) { - const upb_strview encoded_downstream_tls_context = - google_protobuf_Any_value(typed_config); - auto* downstream_tls_context = - envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_parse( - encoded_downstream_tls_context.data, - encoded_downstream_tls_context.size, context.arena); - if (downstream_tls_context == nullptr) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Can't decode downstream tls context."); - } - auto* common_tls_context = - envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_common_tls_context( - downstream_tls_context); - if (common_tls_context != nullptr) { - grpc_error* error = CommonTlsContextParse( - common_tls_context, - &lds_update->downstream_tls_context.common_tls_context); - if (error != GRPC_ERROR_NONE) return error; - } - auto* require_client_certificate = - envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_require_client_certificate( - downstream_tls_context); - if (require_client_certificate != nullptr) { - lds_update->downstream_tls_context.require_client_certificate = - google_protobuf_BoolValue_value(require_client_certificate); - } - } - if (lds_update->downstream_tls_context.common_tls_context - .tls_certificate_certificate_provider_instance.instance_name - .empty()) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "TLS configuration provided but no " - "tls_certificate_certificate_provider_instance found."); - } - } + lds_update->filter_chains.reserve(size); + for (size_t i = 0; i < size; i++) { + lds_update->filter_chains.push_back( + FilterChainParse(context, filter_chains[0], &error)); + 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) { + lds_update->default_filter_chain = + FilterChainParse(context, default_filter_chain, &error); + if (error != GRPC_ERROR_NONE) { + return error; + } + } + if (size == 0 && default_filter_chain == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("No filter chain provided."); + } return GRPC_ERROR_NONE; } diff --git a/src/core/ext/xds/xds_api.h b/src/core/ext/xds/xds_api.h index d7f3d90b955..921274bc86a 100644 --- a/src/core/ext/xds/xds_api.h +++ b/src/core/ext/xds/xds_api.h @@ -219,7 +219,6 @@ class XdsApi { kTcpListener = 0, kHttpApiListener, } type; - DownstreamTlsContext downstream_tls_context; // The name to use in the RDS request. std::string route_config_name; // Storing the Http Connection Manager Common Http Protocol Option @@ -239,14 +238,73 @@ class XdsApi { std::string ToString() const; }; + std::vector http_filters; + struct FilterChain { + struct FilterChainMatch { + uint32_t destination_port = 0; + + struct CidrRange { + std::string address_prefix; + uint32_t prefix_len; + + bool operator==(const CidrRange& other) const { + return address_prefix == other.address_prefix && + prefix_len == other.prefix_len; + } + + std::string ToString() const; + }; + + std::vector prefix_ranges; + + enum class ConnectionSourceType { + kAny = 0, + kSameIpOrLoopback, + kExternal + } source_type = ConnectionSourceType::kAny; + + std::vector source_prefix_ranges; + std::vector source_ports; + std::vector server_names; + std::string transport_protocol; + std::vector application_protocols; + + bool operator==(const FilterChainMatch& other) const { + return destination_port == other.destination_port && + prefix_ranges == other.prefix_ranges && + source_type == other.source_type && + source_prefix_ranges == other.source_prefix_ranges && + source_ports == other.source_ports && + server_names == other.server_names && + transport_protocol == other.transport_protocol && + application_protocols == other.application_protocols; + } + + std::string ToString() const; + } filter_chain_match; + + DownstreamTlsContext downstream_tls_context; + + bool operator==(const FilterChain& other) const { + return filter_chain_match == other.filter_chain_match && + downstream_tls_context == other.downstream_tls_context; + } + + std::string ToString() const; + }; + + std::vector filter_chains; + absl::optional default_filter_chain; + bool operator==(const LdsUpdate& other) const { - return downstream_tls_context == other.downstream_tls_context && - route_config_name == other.route_config_name && + return route_config_name == other.route_config_name && rds_update == other.rds_update && http_max_stream_duration == other.http_max_stream_duration && - http_filters == other.http_filters; + http_filters == other.http_filters && + filter_chains == other.filter_chains && + default_filter_chain == other.default_filter_chain; } std::string ToString() const; diff --git a/src/core/ext/xds/xds_server_config_fetcher.cc b/src/core/ext/xds/xds_server_config_fetcher.cc index d1dae59e411..b276b5b292c 100644 --- a/src/core/ext/xds/xds_server_config_fetcher.cc +++ b/src/core/ext/xds/xds_server_config_fetcher.cc @@ -154,11 +154,13 @@ class XdsServerConfigFetcher : public grpc_server_config_fetcher { } // Configure root cert. absl::string_view root_provider_instance_name = - listener.downstream_tls_context.common_tls_context + 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.downstream_tls_context.common_tls_context + listener.filter_chains[0] + .downstream_tls_context.common_tls_context .combined_validation_context .validation_context_certificate_provider_instance .certificate_name; @@ -177,10 +179,12 @@ class XdsServerConfigFetcher : public grpc_server_config_fetcher { } // Configure identity cert. absl::string_view identity_provider_instance_name = - listener.downstream_tls_context.common_tls_context + 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.downstream_tls_context.common_tls_context + listener.filter_chains[0] + .downstream_tls_context.common_tls_context .tls_certificate_certificate_provider_instance.certificate_name; RefCountedPtr new_identity_provider; if (!identity_provider_instance_name.empty()) { @@ -201,7 +205,8 @@ class XdsServerConfigFetcher : public grpc_server_config_fetcher { (root_certificate_provider_ == nullptr)) || ((new_identity_provider == nullptr) != (identity_certificate_provider_ == nullptr)) || - (listener.downstream_tls_context.require_client_certificate != + (listener.filter_chains[0] + .downstream_tls_context.require_client_certificate != xds_certificate_provider_->GetRequireClientCertificate(""))) { security_connector_update_required = true; } @@ -222,7 +227,8 @@ class XdsServerConfigFetcher : public grpc_server_config_fetcher { ? nullptr : identity_certificate_provider_->distributor()); xds_certificate_provider_->UpdateRequireClientCertificate( - "", listener.downstream_tls_context.require_client_certificate); + "", listener.filter_chains[0] + .downstream_tls_context.require_client_certificate); return security_connector_update_required; }