• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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