1 /* 2 * 3 * Copyright 2018 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 #ifndef GRPC_CORE_EXT_XDS_XDS_API_H 20 #define GRPC_CORE_EXT_XDS_XDS_API_H 21 22 #include <grpc/support/port_platform.h> 23 24 #include <stdint.h> 25 26 #include <set> 27 28 #include "absl/container/inlined_vector.h" 29 #include "absl/types/optional.h" 30 #include "re2/re2.h" 31 32 #include "upb/def.hpp" 33 34 #include <grpc/slice_buffer.h> 35 36 #include "envoy/admin/v3/config_dump.upb.h" 37 #include "src/core/ext/filters/client_channel/server_address.h" 38 #include "src/core/ext/xds/xds_bootstrap.h" 39 #include "src/core/ext/xds/xds_client_stats.h" 40 #include "src/core/ext/xds/xds_http_filters.h" 41 #include "src/core/lib/matchers/matchers.h" 42 43 namespace grpc_core { 44 45 // TODO(yashykt): Check to see if xDS security is enabled. This will be 46 // removed once this feature is fully integration-tested and enabled by 47 // default. 48 bool XdsSecurityEnabled(); 49 50 class XdsClient; 51 52 class XdsApi { 53 public: 54 static const char* kLdsTypeUrl; 55 static const char* kRdsTypeUrl; 56 static const char* kCdsTypeUrl; 57 static const char* kEdsTypeUrl; 58 59 struct Duration { 60 int64_t seconds = 0; 61 int32_t nanos = 0; 62 bool operator==(const Duration& other) const { 63 return seconds == other.seconds && nanos == other.nanos; 64 } ToStringDuration65 std::string ToString() const { 66 return absl::StrFormat("Duration seconds: %ld, nanos %d", seconds, nanos); 67 } 68 }; 69 70 using TypedPerFilterConfig = 71 std::map<std::string, XdsHttpFilterImpl::FilterConfig>; 72 73 // TODO(donnadionne): When we can use absl::variant<>, consider using that 74 // for: PathMatcher, HeaderMatcher, cluster_name and weighted_clusters 75 struct Route { 76 // Matchers for this route. 77 struct Matchers { 78 StringMatcher path_matcher; 79 std::vector<HeaderMatcher> header_matchers; 80 absl::optional<uint32_t> fraction_per_million; 81 82 bool operator==(const Matchers& other) const { 83 return path_matcher == other.path_matcher && 84 header_matchers == other.header_matchers && 85 fraction_per_million == other.fraction_per_million; 86 } 87 std::string ToString() const; 88 }; 89 90 struct HashPolicy { 91 enum Type { HEADER, CHANNEL_ID }; 92 Type type; 93 bool terminal = false; 94 // Fields used for type HEADER. 95 std::string header_name; 96 std::unique_ptr<RE2> regex = nullptr; 97 std::string regex_substitution; 98 HashPolicyRoute::HashPolicy99 HashPolicy() {} 100 101 // Copyable. 102 HashPolicy(const HashPolicy& other); 103 HashPolicy& operator=(const HashPolicy& other); 104 105 // Moveable. 106 HashPolicy(HashPolicy&& other) noexcept; 107 HashPolicy& operator=(HashPolicy&& other) noexcept; 108 109 bool operator==(const HashPolicy& other) const; 110 std::string ToString() const; 111 }; 112 113 Matchers matchers; 114 std::vector<HashPolicy> hash_policies; 115 116 // Action for this route. 117 // TODO(roth): When we can use absl::variant<>, consider using that 118 // here, to enforce the fact that only one of the two fields can be set. 119 std::string cluster_name; 120 struct ClusterWeight { 121 std::string name; 122 uint32_t weight; 123 TypedPerFilterConfig typed_per_filter_config; 124 125 bool operator==(const ClusterWeight& other) const { 126 return name == other.name && weight == other.weight && 127 typed_per_filter_config == other.typed_per_filter_config; 128 } 129 std::string ToString() const; 130 }; 131 std::vector<ClusterWeight> weighted_clusters; 132 // Storing the timeout duration from route action: 133 // RouteAction.max_stream_duration.grpc_timeout_header_max or 134 // RouteAction.max_stream_duration.max_stream_duration if the former is 135 // not set. 136 absl::optional<Duration> max_stream_duration; 137 138 TypedPerFilterConfig typed_per_filter_config; 139 140 bool operator==(const Route& other) const { 141 return matchers == other.matchers && cluster_name == other.cluster_name && 142 weighted_clusters == other.weighted_clusters && 143 max_stream_duration == other.max_stream_duration && 144 typed_per_filter_config == other.typed_per_filter_config; 145 } 146 std::string ToString() const; 147 }; 148 149 struct RdsUpdate { 150 struct VirtualHost { 151 std::vector<std::string> domains; 152 std::vector<Route> routes; 153 TypedPerFilterConfig typed_per_filter_config; 154 155 bool operator==(const VirtualHost& other) const { 156 return domains == other.domains && routes == other.routes && 157 typed_per_filter_config == other.typed_per_filter_config; 158 } 159 }; 160 161 std::vector<VirtualHost> virtual_hosts; 162 163 bool operator==(const RdsUpdate& other) const { 164 return virtual_hosts == other.virtual_hosts; 165 } 166 std::string ToString() const; 167 VirtualHost* FindVirtualHostForDomain(const std::string& domain); 168 }; 169 170 struct CommonTlsContext { 171 struct CertificateValidationContext { 172 std::vector<StringMatcher> match_subject_alt_names; 173 174 bool operator==(const CertificateValidationContext& other) const { 175 return match_subject_alt_names == other.match_subject_alt_names; 176 } 177 178 std::string ToString() const; 179 bool Empty() const; 180 }; 181 182 struct CertificateProviderInstance { 183 std::string instance_name; 184 std::string certificate_name; 185 186 bool operator==(const CertificateProviderInstance& other) const { 187 return instance_name == other.instance_name && 188 certificate_name == other.certificate_name; 189 } 190 191 std::string ToString() const; 192 bool Empty() const; 193 }; 194 195 struct CombinedCertificateValidationContext { 196 CertificateValidationContext default_validation_context; 197 CertificateProviderInstance 198 validation_context_certificate_provider_instance; 199 200 bool operator==(const CombinedCertificateValidationContext& other) const { 201 return default_validation_context == other.default_validation_context && 202 validation_context_certificate_provider_instance == 203 other.validation_context_certificate_provider_instance; 204 } 205 206 std::string ToString() const; 207 bool Empty() const; 208 }; 209 210 CertificateProviderInstance tls_certificate_certificate_provider_instance; 211 CombinedCertificateValidationContext combined_validation_context; 212 213 bool operator==(const CommonTlsContext& other) const { 214 return tls_certificate_certificate_provider_instance == 215 other.tls_certificate_certificate_provider_instance && 216 combined_validation_context == other.combined_validation_context; 217 } 218 219 std::string ToString() const; 220 bool Empty() const; 221 }; 222 223 struct DownstreamTlsContext { 224 CommonTlsContext common_tls_context; 225 bool require_client_certificate = false; 226 227 bool operator==(const DownstreamTlsContext& other) const { 228 return common_tls_context == other.common_tls_context && 229 require_client_certificate == other.require_client_certificate; 230 } 231 232 std::string ToString() const; 233 bool Empty() const; 234 }; 235 236 // TODO(roth): When we can use absl::variant<>, consider using that 237 // here, to enforce the fact that only one of the two fields can be set. 238 struct LdsUpdate { 239 enum class ListenerType { 240 kTcpListener = 0, 241 kHttpApiListener, 242 } type; 243 244 struct HttpConnectionManager { 245 // The name to use in the RDS request. 246 std::string route_config_name; 247 // Storing the Http Connection Manager Common Http Protocol Option 248 // max_stream_duration 249 Duration http_max_stream_duration; 250 // The RouteConfiguration to use for this listener. 251 // Present only if it is inlined in the LDS response. 252 absl::optional<RdsUpdate> rds_update; 253 254 struct HttpFilter { 255 std::string name; 256 XdsHttpFilterImpl::FilterConfig config; 257 258 bool operator==(const HttpFilter& other) const { 259 return name == other.name && config == other.config; 260 } 261 262 std::string ToString() const; 263 }; 264 std::vector<HttpFilter> http_filters; 265 266 bool operator==(const HttpConnectionManager& other) const { 267 return route_config_name == other.route_config_name && 268 http_max_stream_duration == other.http_max_stream_duration && 269 rds_update == other.rds_update && 270 http_filters == other.http_filters; 271 } 272 273 std::string ToString() const; 274 }; 275 276 // Populated for type=kHttpApiListener. 277 HttpConnectionManager http_connection_manager; 278 279 // Populated for type=kTcpListener. 280 // host:port listening_address set when type is kTcpListener 281 std::string address; 282 283 struct FilterChainData { 284 DownstreamTlsContext downstream_tls_context; 285 // This is in principle the filter list. 286 // We currently require exactly one filter, which is the HCM. 287 HttpConnectionManager http_connection_manager; 288 289 bool operator==(const FilterChainData& other) const { 290 return downstream_tls_context == other.downstream_tls_context && 291 http_connection_manager == other.http_connection_manager; 292 } 293 294 std::string ToString() const; 295 } filter_chain_data; 296 297 // A multi-level map used to determine which filter chain to use for a given 298 // incoming connection. Determining the right filter chain for a given 299 // connection checks the following properties, in order: 300 // - destination port (never matched, so not present in map) 301 // - destination IP address 302 // - server name (never matched, so not present in map) 303 // - transport protocol (allows only "raw_buffer" or unset, prefers the 304 // former, so only one of those two types is present in map) 305 // - application protocol (never matched, so not present in map) 306 // - connection source type (any, local or external) 307 // - source IP address 308 // - source port 309 // https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/listener/v3/listener_components.proto#config-listener-v3-filterchainmatch 310 // for more details 311 struct FilterChainMap { 312 struct FilterChainDataSharedPtr { 313 std::shared_ptr<FilterChainData> data; 314 bool operator==(const FilterChainDataSharedPtr& other) const { 315 return *data == *other.data; 316 } 317 }; 318 struct CidrRange { 319 grpc_resolved_address address; 320 uint32_t prefix_len; 321 322 bool operator==(const CidrRange& other) const { 323 return memcmp(&address, &other.address, sizeof(address)) == 0 && 324 prefix_len == other.prefix_len; 325 } 326 327 std::string ToString() const; 328 }; 329 using SourcePortsMap = std::map<uint16_t, FilterChainDataSharedPtr>; 330 struct SourceIp { 331 absl::optional<CidrRange> prefix_range; 332 SourcePortsMap ports_map; 333 334 bool operator==(const SourceIp& other) const { 335 return prefix_range == other.prefix_range && 336 ports_map == other.ports_map; 337 } 338 }; 339 using SourceIpVector = std::vector<SourceIp>; 340 enum class ConnectionSourceType { 341 kAny = 0, 342 kSameIpOrLoopback, 343 kExternal 344 }; 345 using ConnectionSourceTypesArray = std::array<SourceIpVector, 3>; 346 struct DestinationIp { 347 absl::optional<CidrRange> prefix_range; 348 // We always fail match on server name, so those filter chains are not 349 // included here. 350 ConnectionSourceTypesArray source_types_array; 351 352 bool operator==(const DestinationIp& other) const { 353 return prefix_range == other.prefix_range && 354 source_types_array == other.source_types_array; 355 } 356 }; 357 // We always fail match on destination ports map 358 using DestinationIpVector = std::vector<DestinationIp>; 359 DestinationIpVector destination_ip_vector; 360 361 bool operator==(const FilterChainMap& other) const { 362 return destination_ip_vector == other.destination_ip_vector; 363 } 364 365 std::string ToString() const; 366 } filter_chain_map; 367 368 absl::optional<FilterChainData> default_filter_chain; 369 370 bool operator==(const LdsUpdate& other) const { 371 return http_connection_manager == other.http_connection_manager && 372 address == other.address && 373 filter_chain_map == other.filter_chain_map && 374 default_filter_chain == other.default_filter_chain; 375 } 376 377 std::string ToString() const; 378 }; 379 380 struct LdsResourceData { 381 LdsUpdate resource; 382 std::string serialized_proto; 383 }; 384 385 using LdsUpdateMap = std::map<std::string /*server_name*/, LdsResourceData>; 386 387 struct RdsResourceData { 388 RdsUpdate resource; 389 std::string serialized_proto; 390 }; 391 392 using RdsUpdateMap = 393 std::map<std::string /*route_config_name*/, RdsResourceData>; 394 395 struct CdsUpdate { 396 enum ClusterType { EDS, LOGICAL_DNS, AGGREGATE }; 397 ClusterType cluster_type; 398 // For cluster type EDS. 399 // The name to use in the EDS request. 400 // If empty, the cluster name will be used. 401 std::string eds_service_name; 402 // Tls Context used by clients 403 CommonTlsContext common_tls_context; 404 // The LRS server to use for load reporting. 405 // If not set, load reporting will be disabled. 406 // If set to the empty string, will use the same server we obtained the CDS 407 // data from. 408 absl::optional<std::string> lrs_load_reporting_server_name; 409 // The LB policy to use (e.g., "ROUND_ROBIN" or "RING_HASH"). 410 std::string lb_policy; 411 // Used for RING_HASH LB policy only. 412 uint64_t min_ring_size = 1024; 413 uint64_t max_ring_size = 8388608; 414 enum HashFunction { XX_HASH, MURMUR_HASH_2 }; 415 HashFunction hash_function; 416 // Maximum number of outstanding requests can be made to the upstream 417 // cluster. 418 uint32_t max_concurrent_requests = 1024; 419 // For cluster type AGGREGATE. 420 // The prioritized list of cluster names. 421 std::vector<std::string> prioritized_cluster_names; 422 423 bool operator==(const CdsUpdate& other) const { 424 return cluster_type == other.cluster_type && 425 eds_service_name == other.eds_service_name && 426 common_tls_context == other.common_tls_context && 427 lrs_load_reporting_server_name == 428 other.lrs_load_reporting_server_name && 429 prioritized_cluster_names == other.prioritized_cluster_names && 430 max_concurrent_requests == other.max_concurrent_requests; 431 } 432 433 std::string ToString() const; 434 }; 435 436 struct CdsResourceData { 437 CdsUpdate resource; 438 std::string serialized_proto; 439 }; 440 441 using CdsUpdateMap = std::map<std::string /*cluster_name*/, CdsResourceData>; 442 443 struct EdsUpdate { 444 struct Priority { 445 struct Locality { 446 RefCountedPtr<XdsLocalityName> name; 447 uint32_t lb_weight; 448 ServerAddressList endpoints; 449 450 bool operator==(const Locality& other) const { 451 return *name == *other.name && lb_weight == other.lb_weight && 452 endpoints == other.endpoints; 453 } 454 bool operator!=(const Locality& other) const { 455 return !(*this == other); 456 } 457 std::string ToString() const; 458 }; 459 460 std::map<XdsLocalityName*, Locality, XdsLocalityName::Less> localities; 461 462 bool operator==(const Priority& other) const; 463 std::string ToString() const; 464 }; 465 using PriorityList = absl::InlinedVector<Priority, 2>; 466 467 // There are two phases of accessing this class's content: 468 // 1. to initialize in the control plane combiner; 469 // 2. to use in the data plane combiner. 470 // So no additional synchronization is needed. 471 class DropConfig : public RefCounted<DropConfig> { 472 public: 473 struct DropCategory { 474 bool operator==(const DropCategory& other) const { 475 return name == other.name && 476 parts_per_million == other.parts_per_million; 477 } 478 479 std::string name; 480 const uint32_t parts_per_million; 481 }; 482 483 using DropCategoryList = absl::InlinedVector<DropCategory, 2>; 484 AddCategoryEdsUpdate485 void AddCategory(std::string name, uint32_t parts_per_million) { 486 drop_category_list_.emplace_back( 487 DropCategory{std::move(name), parts_per_million}); 488 if (parts_per_million == 1000000) drop_all_ = true; 489 } 490 491 // The only method invoked from outside the WorkSerializer (used in 492 // the data plane). 493 bool ShouldDrop(const std::string** category_name) const; 494 drop_category_listEdsUpdate495 const DropCategoryList& drop_category_list() const { 496 return drop_category_list_; 497 } 498 drop_allEdsUpdate499 bool drop_all() const { return drop_all_; } 500 501 bool operator==(const DropConfig& other) const { 502 return drop_category_list_ == other.drop_category_list_; 503 } 504 bool operator!=(const DropConfig& other) const { 505 return !(*this == other); 506 } 507 508 std::string ToString() const; 509 510 private: 511 DropCategoryList drop_category_list_; 512 bool drop_all_ = false; 513 }; 514 515 PriorityList priorities; 516 RefCountedPtr<DropConfig> drop_config; 517 518 bool operator==(const EdsUpdate& other) const { 519 return priorities == other.priorities && 520 *drop_config == *other.drop_config; 521 } 522 std::string ToString() const; 523 }; 524 525 struct EdsResourceData { 526 EdsUpdate resource; 527 std::string serialized_proto; 528 }; 529 530 using EdsUpdateMap = 531 std::map<std::string /*eds_service_name*/, EdsResourceData>; 532 533 struct ClusterLoadReport { 534 XdsClusterDropStats::Snapshot dropped_requests; 535 std::map<RefCountedPtr<XdsLocalityName>, XdsClusterLocalityStats::Snapshot, 536 XdsLocalityName::Less> 537 locality_stats; 538 grpc_millis load_report_interval; 539 }; 540 using ClusterLoadReportMap = std::map< 541 std::pair<std::string /*cluster_name*/, std::string /*eds_service_name*/>, 542 ClusterLoadReport>; 543 544 // The metadata of the xDS resource; used by the xDS config dump. 545 struct ResourceMetadata { 546 // Resource status from the view of a xDS client, which tells the 547 // synchronization status between the xDS client and the xDS server. 548 enum ClientResourceStatus { 549 // Client requested this resource but hasn't received any update from 550 // management server. The client will not fail requests, but will queue 551 // them 552 // until update arrives or the client times out waiting for the resource. 553 REQUESTED = 1, 554 // This resource has been requested by the client but has either not been 555 // delivered by the server or was previously delivered by the server and 556 // then subsequently removed from resources provided by the server. 557 DOES_NOT_EXIST, 558 // Client received this resource and replied with ACK. 559 ACKED, 560 // Client received this resource and replied with NACK. 561 NACKED 562 }; 563 564 // The client status of this resource. 565 ClientResourceStatus client_status = REQUESTED; 566 // The serialized bytes of the last successfully updated raw xDS resource. 567 std::string serialized_proto; 568 // The timestamp when the resource was last successfully updated. 569 grpc_millis update_time = 0; 570 // The last successfully updated version of the resource. 571 std::string version; 572 // The rejected version string of the last failed update attempt. 573 std::string failed_version; 574 // Details about the last failed update attempt. 575 std::string failed_details; 576 // Timestamp of the last failed update attempt. 577 grpc_millis failed_update_time = 0; 578 }; 579 using ResourceMetadataMap = 580 std::map<absl::string_view /*resource_name*/, const ResourceMetadata*>; 581 struct ResourceTypeMetadata { 582 absl::string_view version; 583 ResourceMetadataMap resource_metadata_map; 584 }; 585 using ResourceTypeMetadataMap = 586 std::map<absl::string_view /*type_url*/, ResourceTypeMetadata>; 587 static_assert(static_cast<ResourceMetadata::ClientResourceStatus>( 588 envoy_admin_v3_REQUESTED) == 589 ResourceMetadata::ClientResourceStatus::REQUESTED, 590 ""); 591 static_assert(static_cast<ResourceMetadata::ClientResourceStatus>( 592 envoy_admin_v3_DOES_NOT_EXIST) == 593 ResourceMetadata::ClientResourceStatus::DOES_NOT_EXIST, 594 ""); 595 static_assert(static_cast<ResourceMetadata::ClientResourceStatus>( 596 envoy_admin_v3_ACKED) == 597 ResourceMetadata::ClientResourceStatus::ACKED, 598 ""); 599 static_assert(static_cast<ResourceMetadata::ClientResourceStatus>( 600 envoy_admin_v3_NACKED) == 601 ResourceMetadata::ClientResourceStatus::NACKED, 602 ""); 603 604 // If the response can't be parsed at the top level, the resulting 605 // type_url will be empty. 606 // If there is any other type of validation error, the parse_error 607 // field will be set to something other than GRPC_ERROR_NONE and the 608 // resource_names_failed field will be populated. 609 // Otherwise, one of the *_update_map fields will be populated, based 610 // on the type_url field. 611 struct AdsParseResult { 612 grpc_error_handle parse_error = GRPC_ERROR_NONE; 613 std::string version; 614 std::string nonce; 615 std::string type_url; 616 LdsUpdateMap lds_update_map; 617 RdsUpdateMap rds_update_map; 618 CdsUpdateMap cds_update_map; 619 EdsUpdateMap eds_update_map; 620 std::set<std::string> resource_names_failed; 621 }; 622 623 XdsApi(XdsClient* client, TraceFlag* tracer, const XdsBootstrap::Node* node); 624 625 // Creates an ADS request. 626 // Takes ownership of \a error. 627 grpc_slice CreateAdsRequest(const XdsBootstrap::XdsServer& server, 628 const std::string& type_url, 629 const std::set<absl::string_view>& resource_names, 630 const std::string& version, 631 const std::string& nonce, grpc_error_handle error, 632 bool populate_node); 633 634 // Parses an ADS response. 635 AdsParseResult ParseAdsResponse( 636 const XdsBootstrap::XdsServer& server, const grpc_slice& encoded_response, 637 const std::set<absl::string_view>& expected_listener_names, 638 const std::set<absl::string_view>& expected_route_configuration_names, 639 const std::set<absl::string_view>& expected_cluster_names, 640 const std::set<absl::string_view>& expected_eds_service_names); 641 642 // Creates an initial LRS request. 643 grpc_slice CreateLrsInitialRequest(const XdsBootstrap::XdsServer& server); 644 645 // Creates an LRS request sending a client-side load report. 646 grpc_slice CreateLrsRequest(ClusterLoadReportMap cluster_load_report_map); 647 648 // Parses the LRS response and returns \a 649 // load_reporting_interval for client-side load reporting. If there is any 650 // error, the output config is invalid. 651 grpc_error_handle ParseLrsResponse(const grpc_slice& encoded_response, 652 bool* send_all_clusters, 653 std::set<std::string>* cluster_names, 654 grpc_millis* load_reporting_interval); 655 656 // Assemble the client config proto message and return the serialized result. 657 std::string AssembleClientConfig( 658 const ResourceTypeMetadataMap& resource_type_metadata_map); 659 660 private: 661 XdsClient* client_; 662 TraceFlag* tracer_; 663 const XdsBootstrap::Node* node_; // Do not own. 664 upb::SymbolTable symtab_; 665 const std::string build_version_; 666 const std::string user_agent_name_; 667 }; 668 669 } // namespace grpc_core 670 671 #endif /* GRPC_CORE_EXT_XDS_XDS_API_H */ 672