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 "src/core/ext/filters/client_channel/server_address.h"
37 #include "src/core/ext/xds/xds_bootstrap.h"
38 #include "src/core/ext/xds/xds_client_stats.h"
39 
40 namespace grpc_core {
41 
42 // TODO(yashykt): Check to see if xDS security is enabled. This will be
43 // removed once this feature is fully integration-tested and enabled by
44 // default.
45 bool XdsSecurityEnabled();
46 
47 class XdsClient;
48 
49 class XdsApi {
50  public:
51   static const char* kLdsTypeUrl;
52   static const char* kRdsTypeUrl;
53   static const char* kCdsTypeUrl;
54   static const char* kEdsTypeUrl;
55 
56   struct Duration {
57     int64_t seconds = 0;
58     int32_t nanos = 0;
59     bool operator==(const Duration& other) const {
60       return (seconds == other.seconds && nanos == other.nanos);
61     }
ToStringDuration62     std::string ToString() const {
63       return absl::StrFormat("Duration seconds: %ld, nanos %d", seconds, nanos);
64     }
65   };
66 
67   // TODO(donnadionne): When we can use absl::variant<>, consider using that
68   // for: PathMatcher, HeaderMatcher, cluster_name and weighted_clusters
69   struct Route {
70     // Matchers for this route.
71     struct Matchers {
72       struct PathMatcher {
73         enum class PathMatcherType {
74           PATH,    // path stored in string_matcher field
75           PREFIX,  // prefix stored in string_matcher field
76           REGEX,   // regex stored in regex_matcher field
77         };
78         PathMatcherType type;
79         std::string string_matcher;
80         std::unique_ptr<RE2> regex_matcher;
81         bool case_sensitive = true;
82 
83         PathMatcher() = default;
84         PathMatcher(const PathMatcher& other);
85         PathMatcher& operator=(const PathMatcher& other);
86         bool operator==(const PathMatcher& other) const;
87         std::string ToString() const;
88       };
89 
90       struct HeaderMatcher {
91         enum class HeaderMatcherType {
92           EXACT,    // value stored in string_matcher field
93           REGEX,    // uses regex_match field
94           RANGE,    // uses range_start and range_end fields
95           PRESENT,  // uses present_match field
96           PREFIX,   // prefix stored in string_matcher field
97           SUFFIX,   // suffix stored in string_matcher field
98         };
99         std::string name;
100         HeaderMatcherType type;
101         int64_t range_start;
102         int64_t range_end;
103         std::string string_matcher;
104         std::unique_ptr<RE2> regex_match;
105         bool present_match;
106         // invert_match field may or may not exisit, so initialize it to
107         // false.
108         bool invert_match = false;
109 
110         HeaderMatcher() = default;
111         HeaderMatcher(const HeaderMatcher& other);
112         HeaderMatcher& operator=(const HeaderMatcher& other);
113         bool operator==(const HeaderMatcher& other) const;
114         std::string ToString() const;
115       };
116 
117       PathMatcher path_matcher;
118       std::vector<HeaderMatcher> header_matchers;
119       absl::optional<uint32_t> fraction_per_million;
120 
121       bool operator==(const Matchers& other) const {
122         return (path_matcher == other.path_matcher &&
123                 header_matchers == other.header_matchers &&
124                 fraction_per_million == other.fraction_per_million);
125       }
126       std::string ToString() const;
127     };
128 
129     Matchers matchers;
130 
131     // Action for this route.
132     // TODO(roth): When we can use absl::variant<>, consider using that
133     // here, to enforce the fact that only one of the two fields can be set.
134     std::string cluster_name;
135     struct ClusterWeight {
136       std::string name;
137       uint32_t weight;
138       bool operator==(const ClusterWeight& other) const {
139         return (name == other.name && weight == other.weight);
140       }
141       std::string ToString() const;
142     };
143     std::vector<ClusterWeight> weighted_clusters;
144     // Storing the timeout duration from route action:
145     // RouteAction.max_stream_duration.grpc_timeout_header_max or
146     // RouteAction.max_stream_duration.max_stream_duration if the former is
147     // not set.
148     absl::optional<Duration> max_stream_duration;
149 
150     bool operator==(const Route& other) const {
151       return (matchers == other.matchers &&
152               cluster_name == other.cluster_name &&
153               weighted_clusters == other.weighted_clusters &&
154               max_stream_duration == other.max_stream_duration);
155     }
156     std::string ToString() const;
157   };
158 
159   struct RdsUpdate {
160     struct VirtualHost {
161       std::vector<std::string> domains;
162       std::vector<Route> routes;
163 
164       bool operator==(const VirtualHost& other) const {
165         return domains == other.domains && routes == other.routes;
166       }
167     };
168 
169     std::vector<VirtualHost> virtual_hosts;
170 
171     bool operator==(const RdsUpdate& other) const {
172       return virtual_hosts == other.virtual_hosts;
173     }
174     std::string ToString() const;
175     VirtualHost* FindVirtualHostForDomain(const std::string& domain);
176   };
177 
178   class StringMatcher {
179    public:
180     enum class StringMatcherType {
181       EXACT,       // value stored in string_matcher_ field
182       PREFIX,      // value stored in string_matcher_ field
183       SUFFIX,      // value stored in string_matcher_ field
184       SAFE_REGEX,  // pattern stored in regex_matcher_ field
185       CONTAINS,    // value stored in string_matcher_ field
186     };
187 
188     StringMatcher() = default;
189     StringMatcher(const StringMatcher& other);
190     StringMatcher(StringMatcherType type, const std::string& matcher,
191                   bool ignore_case = false);
192     StringMatcher& operator=(const StringMatcher& other);
193     bool operator==(const StringMatcher& other) const;
194 
195     bool Match(absl::string_view value) const;
196 
197     std::string ToString() const;
198 
type()199     StringMatcherType type() const { return type_; }
200 
201     // Valid for EXACT, PREFIX, SUFFIX and CONTAINS
string_matcher()202     const std::string& string_matcher() const { return string_matcher_; }
203 
204     // Valid for SAFE_REGEX
regex_matcher()205     RE2* regex_matcher() const { return regex_matcher_.get(); }
206 
207    private:
208     StringMatcherType type_ = StringMatcherType::EXACT;
209     std::string string_matcher_;
210     std::unique_ptr<RE2> regex_matcher_;
211     bool ignore_case_ = false;
212   };
213 
214   struct CommonTlsContext {
215     struct CertificateValidationContext {
216       std::vector<StringMatcher> match_subject_alt_names;
217 
218       bool operator==(const CertificateValidationContext& other) const {
219         return match_subject_alt_names == other.match_subject_alt_names;
220       }
221 
222       std::string ToString() const;
223       bool Empty() const;
224     };
225 
226     struct CertificateProviderInstance {
227       std::string instance_name;
228       std::string certificate_name;
229 
230       bool operator==(const CertificateProviderInstance& other) const {
231         return instance_name == other.instance_name &&
232                certificate_name == other.certificate_name;
233       }
234 
235       std::string ToString() const;
236       bool Empty() const;
237     };
238 
239     struct CombinedCertificateValidationContext {
240       CertificateValidationContext default_validation_context;
241       CertificateProviderInstance
242           validation_context_certificate_provider_instance;
243 
244       bool operator==(const CombinedCertificateValidationContext& other) const {
245         return default_validation_context == other.default_validation_context &&
246                validation_context_certificate_provider_instance ==
247                    other.validation_context_certificate_provider_instance;
248       }
249 
250       std::string ToString() const;
251       bool Empty() const;
252     };
253 
254     CertificateProviderInstance tls_certificate_certificate_provider_instance;
255     CombinedCertificateValidationContext combined_validation_context;
256 
257     bool operator==(const CommonTlsContext& other) const {
258       return tls_certificate_certificate_provider_instance ==
259                  other.tls_certificate_certificate_provider_instance &&
260              combined_validation_context == other.combined_validation_context;
261     }
262 
263     std::string ToString() const;
264     bool Empty() const;
265   };
266 
267   // TODO(roth): When we can use absl::variant<>, consider using that
268   // here, to enforce the fact that only one of the two fields can be set.
269   struct LdsUpdate {
270     // The name to use in the RDS request.
271     std::string route_config_name;
272     // Storing the Http Connection Manager Common Http Protocol Option
273     // max_stream_duration
274     Duration http_max_stream_duration;
275     // The RouteConfiguration to use for this listener.
276     // Present only if it is inlined in the LDS response.
277     absl::optional<RdsUpdate> rds_update;
278 
279     bool operator==(const LdsUpdate& other) const {
280       return route_config_name == other.route_config_name &&
281              rds_update == other.rds_update &&
282              http_max_stream_duration == other.http_max_stream_duration;
283     }
284   };
285 
286   using LdsUpdateMap = std::map<std::string /*server_name*/, LdsUpdate>;
287 
288   using RdsUpdateMap = std::map<std::string /*route_config_name*/, RdsUpdate>;
289 
290   struct CdsUpdate {
291     // The name to use in the EDS request.
292     // If empty, the cluster name will be used.
293     std::string eds_service_name;
294     // Tls Context used by clients
295     CommonTlsContext common_tls_context;
296     // The LRS server to use for load reporting.
297     // If not set, load reporting will be disabled.
298     // If set to the empty string, will use the same server we obtained the CDS
299     // data from.
300     absl::optional<std::string> lrs_load_reporting_server_name;
301     // Maximum number of outstanding requests can be made to the upstream
302     // cluster.
303     uint32_t max_concurrent_requests = 1024;
304 
305     bool operator==(const CdsUpdate& other) const {
306       return eds_service_name == other.eds_service_name &&
307              common_tls_context == other.common_tls_context &&
308              lrs_load_reporting_server_name ==
309                  other.lrs_load_reporting_server_name &&
310              max_concurrent_requests == other.max_concurrent_requests;
311     }
312 
313     std::string ToString() const;
314   };
315 
316   using CdsUpdateMap = std::map<std::string /*cluster_name*/, CdsUpdate>;
317 
318   struct EdsUpdate {
319     struct Priority {
320       struct Locality {
321         RefCountedPtr<XdsLocalityName> name;
322         uint32_t lb_weight;
323         ServerAddressList endpoints;
324 
325         bool operator==(const Locality& other) const {
326           return *name == *other.name && lb_weight == other.lb_weight &&
327                  endpoints == other.endpoints;
328         }
329         bool operator!=(const Locality& other) const {
330           return !(*this == other);
331         }
332         std::string ToString() const;
333       };
334 
335       std::map<XdsLocalityName*, Locality, XdsLocalityName::Less> localities;
336 
337       bool operator==(const Priority& other) const;
338       std::string ToString() const;
339     };
340     using PriorityList = absl::InlinedVector<Priority, 2>;
341 
342     // There are two phases of accessing this class's content:
343     // 1. to initialize in the control plane combiner;
344     // 2. to use in the data plane combiner.
345     // So no additional synchronization is needed.
346     class DropConfig : public RefCounted<DropConfig> {
347      public:
348       struct DropCategory {
349         bool operator==(const DropCategory& other) const {
350           return name == other.name &&
351                  parts_per_million == other.parts_per_million;
352         }
353 
354         std::string name;
355         const uint32_t parts_per_million;
356       };
357 
358       using DropCategoryList = absl::InlinedVector<DropCategory, 2>;
359 
AddCategoryEdsUpdate360       void AddCategory(std::string name, uint32_t parts_per_million) {
361         drop_category_list_.emplace_back(
362             DropCategory{std::move(name), parts_per_million});
363         if (parts_per_million == 1000000) drop_all_ = true;
364       }
365 
366       // The only method invoked from outside the WorkSerializer (used in
367       // the data plane).
368       bool ShouldDrop(const std::string** category_name) const;
369 
drop_category_listEdsUpdate370       const DropCategoryList& drop_category_list() const {
371         return drop_category_list_;
372       }
373 
drop_allEdsUpdate374       bool drop_all() const { return drop_all_; }
375 
376       bool operator==(const DropConfig& other) const {
377         return drop_category_list_ == other.drop_category_list_;
378       }
379       bool operator!=(const DropConfig& other) const {
380         return !(*this == other);
381       }
382 
383       std::string ToString() const;
384 
385      private:
386       DropCategoryList drop_category_list_;
387       bool drop_all_ = false;
388     };
389 
390     PriorityList priorities;
391     RefCountedPtr<DropConfig> drop_config;
392 
393     bool operator==(const EdsUpdate& other) const {
394       return priorities == other.priorities &&
395              *drop_config == *other.drop_config;
396     }
397     std::string ToString() const;
398   };
399 
400   using EdsUpdateMap = std::map<std::string /*eds_service_name*/, EdsUpdate>;
401 
402   struct ClusterLoadReport {
403     XdsClusterDropStats::Snapshot dropped_requests;
404     std::map<RefCountedPtr<XdsLocalityName>, XdsClusterLocalityStats::Snapshot,
405              XdsLocalityName::Less>
406         locality_stats;
407     grpc_millis load_report_interval;
408   };
409   using ClusterLoadReportMap = std::map<
410       std::pair<std::string /*cluster_name*/, std::string /*eds_service_name*/>,
411       ClusterLoadReport>;
412 
413   XdsApi(XdsClient* client, TraceFlag* tracer, const XdsBootstrap::Node* node);
414 
415   // Creates an ADS request.
416   // Takes ownership of \a error.
417   grpc_slice CreateAdsRequest(const XdsBootstrap::XdsServer& server,
418                               const std::string& type_url,
419                               const std::set<absl::string_view>& resource_names,
420                               const std::string& version,
421                               const std::string& nonce, grpc_error* error,
422                               bool populate_node);
423 
424   // Parses an ADS response.
425   // If the response can't be parsed at the top level, the resulting
426   // type_url will be empty.
427   struct AdsParseResult {
428     grpc_error* parse_error = GRPC_ERROR_NONE;
429     std::string version;
430     std::string nonce;
431     std::string type_url;
432     LdsUpdateMap lds_update_map;
433     RdsUpdateMap rds_update_map;
434     CdsUpdateMap cds_update_map;
435     EdsUpdateMap eds_update_map;
436   };
437   AdsParseResult ParseAdsResponse(
438       const grpc_slice& encoded_response,
439       const std::set<absl::string_view>& expected_listener_names,
440       const std::set<absl::string_view>& expected_route_configuration_names,
441       const std::set<absl::string_view>& expected_cluster_names,
442       const std::set<absl::string_view>& expected_eds_service_names);
443 
444   // Creates an initial LRS request.
445   grpc_slice CreateLrsInitialRequest(const XdsBootstrap::XdsServer& server);
446 
447   // Creates an LRS request sending a client-side load report.
448   grpc_slice CreateLrsRequest(ClusterLoadReportMap cluster_load_report_map);
449 
450   // Parses the LRS response and returns \a
451   // load_reporting_interval for client-side load reporting. If there is any
452   // error, the output config is invalid.
453   grpc_error* ParseLrsResponse(const grpc_slice& encoded_response,
454                                bool* send_all_clusters,
455                                std::set<std::string>* cluster_names,
456                                grpc_millis* load_reporting_interval);
457 
458  private:
459   XdsClient* client_;
460   TraceFlag* tracer_;
461   const XdsBootstrap::Node* node_;  // Do not own.
462   upb::SymbolTable symtab_;
463   const std::string build_version_;
464   const std::string user_agent_name_;
465 };
466 
467 }  // namespace grpc_core
468 
469 #endif /* GRPC_CORE_EXT_XDS_XDS_API_H */
470