• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2018 gRPC authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //     http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 #include "src/core/xds/grpc/xds_route_config_parser.h"
18 
19 #include <grpc/status.h>
20 #include <grpc/support/port_platform.h>
21 #include <stddef.h>
22 #include <stdint.h>
23 
24 #include <limits>
25 #include <map>
26 #include <memory>
27 #include <set>
28 #include <string>
29 #include <utility>
30 #include <vector>
31 
32 #include "absl/log/check.h"
33 #include "absl/log/log.h"
34 #include "absl/status/status.h"
35 #include "absl/status/statusor.h"
36 #include "absl/strings/str_cat.h"
37 #include "absl/strings/str_format.h"
38 #include "absl/strings/str_join.h"
39 #include "absl/strings/str_split.h"
40 #include "absl/strings/string_view.h"
41 #include "absl/types/optional.h"
42 #include "absl/types/variant.h"
43 #include "envoy/config/core/v3/base.upb.h"
44 #include "envoy/config/core/v3/extension.upb.h"
45 #include "envoy/config/route/v3/route.upb.h"
46 #include "envoy/config/route/v3/route.upbdefs.h"
47 #include "envoy/config/route/v3/route_components.upb.h"
48 #include "envoy/type/matcher/v3/regex.upb.h"
49 #include "envoy/type/matcher/v3/string.upb.h"
50 #include "envoy/type/v3/percent.upb.h"
51 #include "envoy/type/v3/range.upb.h"
52 #include "google/protobuf/any.upb.h"
53 #include "google/protobuf/duration.upb.h"
54 #include "google/protobuf/wrappers.upb.h"
55 #include "re2/re2.h"
56 #include "src/core/config/core_configuration.h"
57 #include "src/core/lib/channel/status_util.h"
58 #include "src/core/lib/debug/trace.h"
59 #include "src/core/load_balancing/lb_policy_registry.h"
60 #include "src/core/util/env.h"
61 #include "src/core/util/json/json.h"
62 #include "src/core/util/json/json_writer.h"
63 #include "src/core/util/match.h"
64 #include "src/core/util/matchers.h"
65 #include "src/core/util/ref_counted_ptr.h"
66 #include "src/core/util/string.h"
67 #include "src/core/util/time.h"
68 #include "src/core/util/upb_utils.h"
69 #include "src/core/xds/grpc/xds_cluster_specifier_plugin.h"
70 #include "src/core/xds/grpc/xds_common_types.h"
71 #include "src/core/xds/grpc/xds_common_types_parser.h"
72 #include "src/core/xds/grpc/xds_http_filter.h"
73 #include "src/core/xds/grpc/xds_http_filter_registry.h"
74 #include "src/core/xds/grpc/xds_routing.h"
75 #include "src/core/xds/xds_client/xds_resource_type.h"
76 #include "upb/base/string_view.h"
77 #include "upb/message/map.h"
78 #include "upb/text/encode.h"
79 
80 namespace grpc_core {
81 
82 // TODO(apolcyn): remove this flag by the 1.58 release
XdsRlsEnabled()83 bool XdsRlsEnabled() {
84   auto value = GetEnv("GRPC_EXPERIMENTAL_XDS_RLS_LB");
85   if (!value.has_value()) return true;
86   bool parsed_value;
87   bool parse_succeeded = gpr_parse_bool_value(value->c_str(), &parsed_value);
88   return parse_succeeded && parsed_value;
89 }
90 
91 // TODO(roth): Remove this once the feature passes interop tests.
XdsAuthorityRewriteEnabled()92 bool XdsAuthorityRewriteEnabled() {
93   auto value = GetEnv("GRPC_EXPERIMENTAL_XDS_AUTHORITY_REWRITE");
94   if (!value.has_value()) return false;
95   bool parsed_value;
96   bool parse_succeeded = gpr_parse_bool_value(value->c_str(), &parsed_value);
97   return parse_succeeded && parsed_value;
98 }
99 
100 //
101 // XdsRouteConfigResourceParse()
102 //
103 
104 namespace {
105 
ClusterSpecifierPluginParse(const XdsResourceType::DecodeContext & context,const envoy_config_route_v3_RouteConfiguration * route_config,ValidationErrors * errors)106 XdsRouteConfigResource::ClusterSpecifierPluginMap ClusterSpecifierPluginParse(
107     const XdsResourceType::DecodeContext& context,
108     const envoy_config_route_v3_RouteConfiguration* route_config,
109     ValidationErrors* errors) {
110   XdsRouteConfigResource::ClusterSpecifierPluginMap
111       cluster_specifier_plugin_map;
112   const auto& cluster_specifier_plugin_registry =
113       static_cast<const GrpcXdsBootstrap&>(context.client->bootstrap())
114           .cluster_specifier_plugin_registry();
115   size_t num_cluster_specifier_plugins;
116   const envoy_config_route_v3_ClusterSpecifierPlugin* const*
117       cluster_specifier_plugin =
118           envoy_config_route_v3_RouteConfiguration_cluster_specifier_plugins(
119               route_config, &num_cluster_specifier_plugins);
120   for (size_t i = 0; i < num_cluster_specifier_plugins; ++i) {
121     bool is_optional = envoy_config_route_v3_ClusterSpecifierPlugin_is_optional(
122         cluster_specifier_plugin[i]);
123     ValidationErrors::ScopedField field(
124         errors, absl::StrCat(".cluster_specifier_plugins[", i, "].extension"));
125     const envoy_config_core_v3_TypedExtensionConfig* typed_extension_config =
126         envoy_config_route_v3_ClusterSpecifierPlugin_extension(
127             cluster_specifier_plugin[i]);
128     if (typed_extension_config == nullptr) {
129       errors->AddError("field not present");
130       continue;
131     }
132     std::string name = UpbStringToStdString(
133         envoy_config_core_v3_TypedExtensionConfig_name(typed_extension_config));
134     if (cluster_specifier_plugin_map.find(name) !=
135         cluster_specifier_plugin_map.end()) {
136       ValidationErrors::ScopedField field(errors, ".name");
137       errors->AddError(absl::StrCat("duplicate name \"", name, "\""));
138     } else {
139       // Add a sentinel entry in case we encounter an error later, just so we
140       // don't generate duplicate errors for each route that uses this plugin.
141       cluster_specifier_plugin_map[name] = "<sentinel>";
142     }
143     ValidationErrors::ScopedField field2(errors, ".typed_config");
144     const google_protobuf_Any* any =
145         envoy_config_core_v3_TypedExtensionConfig_typed_config(
146             typed_extension_config);
147     auto extension = ExtractXdsExtension(context, any, errors);
148     if (!extension.has_value()) continue;
149     const XdsClusterSpecifierPluginImpl* cluster_specifier_plugin_impl =
150         cluster_specifier_plugin_registry.GetPluginForType(extension->type);
151     if (cluster_specifier_plugin_impl == nullptr) {
152       if (is_optional) {
153         // Empty string indicates an optional plugin.
154         // This is used later when validating routes, and since we will skip
155         // any routes that refer to this plugin, we won't wind up including
156         // this plugin in the resource that we return to the watcher.
157         cluster_specifier_plugin_map[std::move(name)] = "";
158       } else {
159         // Not optional, report error.
160         errors->AddError("unsupported ClusterSpecifierPlugin type");
161       }
162       continue;
163     }
164     const size_t original_error_size = errors->size();
165     Json lb_policy_config =
166         cluster_specifier_plugin_impl->GenerateLoadBalancingPolicyConfig(
167             std::move(*extension), context.arena, context.symtab, errors);
168     if (errors->size() != original_error_size) continue;
169     auto config =
170         CoreConfiguration::Get().lb_policy_registry().ParseLoadBalancingConfig(
171             lb_policy_config);
172     if (!config.ok()) {
173       errors->AddError(absl::StrCat(
174           "ClusterSpecifierPlugin returned invalid LB policy config: ",
175           config.status().message()));
176     } else {
177       cluster_specifier_plugin_map[std::move(name)] =
178           JsonDump(lb_policy_config);
179     }
180   }
181   return cluster_specifier_plugin_map;
182 }
183 
RoutePathMatchParse(const envoy_config_route_v3_RouteMatch * match,ValidationErrors * errors)184 absl::optional<StringMatcher> RoutePathMatchParse(
185     const envoy_config_route_v3_RouteMatch* match, ValidationErrors* errors) {
186   bool case_sensitive =
187       ParseBoolValue(envoy_config_route_v3_RouteMatch_case_sensitive(match),
188                      /*default_value=*/true);
189   StringMatcher::Type type;
190   std::string match_string;
191   if (envoy_config_route_v3_RouteMatch_has_prefix(match)) {
192     absl::string_view prefix =
193         UpbStringToAbsl(envoy_config_route_v3_RouteMatch_prefix(match));
194     // For any prefix that cannot match a path of the form "/service/method",
195     // ignore the route.
196     if (!prefix.empty()) {
197       // Does not start with a slash.
198       if (prefix[0] != '/') return absl::nullopt;
199       std::vector<absl::string_view> prefix_elements =
200           absl::StrSplit(prefix.substr(1), absl::MaxSplits('/', 2));
201       // More than 2 slashes.
202       if (prefix_elements.size() > 2) return absl::nullopt;
203       // Two consecutive slashes.
204       if (prefix_elements.size() == 2 && prefix_elements[0].empty()) {
205         return absl::nullopt;
206       }
207     }
208     type = StringMatcher::Type::kPrefix;
209     match_string = std::string(prefix);
210   } else if (envoy_config_route_v3_RouteMatch_has_path(match)) {
211     absl::string_view path =
212         UpbStringToAbsl(envoy_config_route_v3_RouteMatch_path(match));
213     // For any path not of the form "/service/method", ignore the route.
214     // Empty path.
215     if (path.empty()) return absl::nullopt;
216     // Does not start with a slash.
217     if (path[0] != '/') return absl::nullopt;
218     std::vector<absl::string_view> path_elements =
219         absl::StrSplit(path.substr(1), absl::MaxSplits('/', 2));
220     // Number of slashes does not equal 2.
221     if (path_elements.size() != 2) return absl::nullopt;
222     // Empty service name.
223     if (path_elements[0].empty()) return absl::nullopt;
224     // Empty method name.
225     if (path_elements[1].empty()) return absl::nullopt;
226     type = StringMatcher::Type::kExact;
227     match_string = std::string(path);
228   } else if (envoy_config_route_v3_RouteMatch_has_safe_regex(match)) {
229     const envoy_type_matcher_v3_RegexMatcher* regex_matcher =
230         envoy_config_route_v3_RouteMatch_safe_regex(match);
231     CHECK_NE(regex_matcher, nullptr);
232     type = StringMatcher::Type::kSafeRegex;
233     match_string = UpbStringToStdString(
234         envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher));
235   } else {
236     errors->AddError("invalid path specifier");
237     return absl::nullopt;
238   }
239   absl::StatusOr<StringMatcher> string_matcher =
240       StringMatcher::Create(type, match_string, case_sensitive);
241   if (!string_matcher.ok()) {
242     errors->AddError(absl::StrCat("error creating path matcher: ",
243                                   string_matcher.status().message()));
244     return absl::nullopt;
245   }
246   return std::move(*string_matcher);
247 }
248 
RouteHeaderMatchersParse(const envoy_config_route_v3_RouteMatch * match,XdsRouteConfigResource::Route * route,ValidationErrors * errors)249 void RouteHeaderMatchersParse(const envoy_config_route_v3_RouteMatch* match,
250                               XdsRouteConfigResource::Route* route,
251                               ValidationErrors* errors) {
252   size_t size;
253   const envoy_config_route_v3_HeaderMatcher* const* headers =
254       envoy_config_route_v3_RouteMatch_headers(match, &size);
255   for (size_t i = 0; i < size; ++i) {
256     ValidationErrors::ScopedField field(errors,
257                                         absl::StrCat(".headers[", i, "]"));
258     const envoy_config_route_v3_HeaderMatcher* header = headers[i];
259     CHECK_NE(header, nullptr);
260     const std::string name =
261         UpbStringToStdString(envoy_config_route_v3_HeaderMatcher_name(header));
262     HeaderMatcher::Type type;
263     std::string match_string;
264     int64_t range_start = 0;
265     int64_t range_end = 0;
266     bool present_match = false;
267     bool case_sensitive = true;
268     if (envoy_config_route_v3_HeaderMatcher_has_exact_match(header)) {
269       type = HeaderMatcher::Type::kExact;
270       match_string = UpbStringToStdString(
271           envoy_config_route_v3_HeaderMatcher_exact_match(header));
272     } else if (envoy_config_route_v3_HeaderMatcher_has_prefix_match(header)) {
273       type = HeaderMatcher::Type::kPrefix;
274       match_string = UpbStringToStdString(
275           envoy_config_route_v3_HeaderMatcher_prefix_match(header));
276     } else if (envoy_config_route_v3_HeaderMatcher_has_suffix_match(header)) {
277       type = HeaderMatcher::Type::kSuffix;
278       match_string = UpbStringToStdString(
279           envoy_config_route_v3_HeaderMatcher_suffix_match(header));
280     } else if (envoy_config_route_v3_HeaderMatcher_has_contains_match(header)) {
281       type = HeaderMatcher::Type::kContains;
282       match_string = UpbStringToStdString(
283           envoy_config_route_v3_HeaderMatcher_contains_match(header));
284     } else if (envoy_config_route_v3_HeaderMatcher_has_safe_regex_match(
285                    header)) {
286       const envoy_type_matcher_v3_RegexMatcher* regex_matcher =
287           envoy_config_route_v3_HeaderMatcher_safe_regex_match(header);
288       CHECK_NE(regex_matcher, nullptr);
289       type = HeaderMatcher::Type::kSafeRegex;
290       match_string = UpbStringToStdString(
291           envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher));
292     } else if (envoy_config_route_v3_HeaderMatcher_has_range_match(header)) {
293       type = HeaderMatcher::Type::kRange;
294       const envoy_type_v3_Int64Range* range_matcher =
295           envoy_config_route_v3_HeaderMatcher_range_match(header);
296       CHECK_NE(range_matcher, nullptr);
297       range_start = envoy_type_v3_Int64Range_start(range_matcher);
298       range_end = envoy_type_v3_Int64Range_end(range_matcher);
299     } else if (envoy_config_route_v3_HeaderMatcher_has_present_match(header)) {
300       type = HeaderMatcher::Type::kPresent;
301       present_match = envoy_config_route_v3_HeaderMatcher_present_match(header);
302     } else if (envoy_config_route_v3_HeaderMatcher_has_string_match(header)) {
303       ValidationErrors::ScopedField field(errors, ".string_match");
304       const auto* matcher =
305           envoy_config_route_v3_HeaderMatcher_string_match(header);
306       CHECK_NE(matcher, nullptr);
307       if (envoy_type_matcher_v3_StringMatcher_has_exact(matcher)) {
308         type = HeaderMatcher::Type::kExact;
309         match_string = UpbStringToStdString(
310             envoy_type_matcher_v3_StringMatcher_exact(matcher));
311       } else if (envoy_type_matcher_v3_StringMatcher_has_prefix(matcher)) {
312         type = HeaderMatcher::Type::kPrefix;
313         match_string = UpbStringToStdString(
314             envoy_type_matcher_v3_StringMatcher_prefix(matcher));
315       } else if (envoy_type_matcher_v3_StringMatcher_has_suffix(matcher)) {
316         type = HeaderMatcher::Type::kSuffix;
317         match_string = UpbStringToStdString(
318             envoy_type_matcher_v3_StringMatcher_suffix(matcher));
319       } else if (envoy_type_matcher_v3_StringMatcher_has_contains(matcher)) {
320         type = HeaderMatcher::Type::kContains;
321         match_string = UpbStringToStdString(
322             envoy_type_matcher_v3_StringMatcher_contains(matcher));
323       } else if (envoy_type_matcher_v3_StringMatcher_has_safe_regex(matcher)) {
324         type = HeaderMatcher::Type::kSafeRegex;
325         const auto* regex_matcher =
326             envoy_type_matcher_v3_StringMatcher_safe_regex(matcher);
327         CHECK_NE(regex_matcher, nullptr);
328         match_string = UpbStringToStdString(
329             envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher));
330       } else {
331         errors->AddError("invalid string matcher");
332         continue;
333       }
334       case_sensitive =
335           !envoy_type_matcher_v3_StringMatcher_ignore_case(matcher);
336     } else {
337       errors->AddError("invalid header matcher");
338       continue;
339     }
340     bool invert_match =
341         envoy_config_route_v3_HeaderMatcher_invert_match(header);
342     absl::StatusOr<HeaderMatcher> header_matcher =
343         HeaderMatcher::Create(name, type, match_string, range_start, range_end,
344                               present_match, invert_match, case_sensitive);
345     if (!header_matcher.ok()) {
346       errors->AddError(absl::StrCat("cannot create header matcher: ",
347                                     header_matcher.status().message()));
348     } else {
349       route->matchers.header_matchers.emplace_back(std::move(*header_matcher));
350     }
351   }
352 }
353 
RouteRuntimeFractionParse(const envoy_config_route_v3_RouteMatch * match,XdsRouteConfigResource::Route * route,ValidationErrors * errors)354 void RouteRuntimeFractionParse(const envoy_config_route_v3_RouteMatch* match,
355                                XdsRouteConfigResource::Route* route,
356                                ValidationErrors* errors) {
357   const envoy_config_core_v3_RuntimeFractionalPercent* runtime_fraction =
358       envoy_config_route_v3_RouteMatch_runtime_fraction(match);
359   if (runtime_fraction != nullptr) {
360     const envoy_type_v3_FractionalPercent* fraction =
361         envoy_config_core_v3_RuntimeFractionalPercent_default_value(
362             runtime_fraction);
363     if (fraction != nullptr) {
364       uint32_t numerator = envoy_type_v3_FractionalPercent_numerator(fraction);
365       const uint32_t denominator =
366           envoy_type_v3_FractionalPercent_denominator(fraction);
367       // Normalize to million.
368       switch (denominator) {
369         case envoy_type_v3_FractionalPercent_HUNDRED:
370           numerator *= 10000;
371           break;
372         case envoy_type_v3_FractionalPercent_TEN_THOUSAND:
373           numerator *= 100;
374           break;
375         case envoy_type_v3_FractionalPercent_MILLION:
376           break;
377         default: {
378           ValidationErrors::ScopedField field(
379               errors, ".runtime_fraction.default_value.denominator");
380           errors->AddError("unknown denominator type");
381           return;
382         }
383       }
384       route->matchers.fraction_per_million = numerator;
385     }
386   }
387 }
388 
389 template <typename ParentType, typename EntryType>
ParseTypedPerFilterConfig(const XdsResourceType::DecodeContext & context,const ParentType * parent,const EntryType * (* entry_func)(const ParentType *,size_t *),upb_StringView (* key_func)(const EntryType *),const google_protobuf_Any * (* value_func)(const EntryType *),ValidationErrors * errors)390 XdsRouteConfigResource::TypedPerFilterConfig ParseTypedPerFilterConfig(
391     const XdsResourceType::DecodeContext& context, const ParentType* parent,
392     const EntryType* (*entry_func)(const ParentType*, size_t*),
393     upb_StringView (*key_func)(const EntryType*),
394     const google_protobuf_Any* (*value_func)(const EntryType*),
395     ValidationErrors* errors) {
396   XdsRouteConfigResource::TypedPerFilterConfig typed_per_filter_config;
397   size_t filter_it = kUpb_Map_Begin;
398   while (true) {
399     const auto* filter_entry = entry_func(parent, &filter_it);
400     if (filter_entry == nullptr) break;
401     absl::string_view key = UpbStringToAbsl(key_func(filter_entry));
402     ValidationErrors::ScopedField field(errors, absl::StrCat("[", key, "]"));
403     if (key.empty()) errors->AddError("filter name must be non-empty");
404     const google_protobuf_Any* any = value_func(filter_entry);
405     auto extension = ExtractXdsExtension(context, any, errors);
406     if (!extension.has_value()) continue;
407     auto* extension_to_use = &*extension;
408     absl::optional<XdsExtension> nested_extension;
409     bool is_optional = false;
410     if (extension->type == "envoy.config.route.v3.FilterConfig") {
411       absl::string_view* serialized_config =
412           absl::get_if<absl::string_view>(&extension->value);
413       if (serialized_config == nullptr) {
414         errors->AddError("could not parse FilterConfig");
415         continue;
416       }
417       const auto* filter_config = envoy_config_route_v3_FilterConfig_parse(
418           serialized_config->data(), serialized_config->size(), context.arena);
419       if (filter_config == nullptr) {
420         errors->AddError("could not parse FilterConfig");
421         continue;
422       }
423       is_optional =
424           envoy_config_route_v3_FilterConfig_is_optional(filter_config);
425       any = envoy_config_route_v3_FilterConfig_config(filter_config);
426       extension->validation_fields.emplace_back(errors, ".config");
427       nested_extension = ExtractXdsExtension(context, any, errors);
428       if (!nested_extension.has_value()) continue;
429       extension_to_use = &*nested_extension;
430     }
431     const auto& http_filter_registry =
432         static_cast<const GrpcXdsBootstrap&>(context.client->bootstrap())
433             .http_filter_registry();
434     const XdsHttpFilterImpl* filter_impl =
435         http_filter_registry.GetFilterForType(extension_to_use->type);
436     if (filter_impl == nullptr) {
437       if (!is_optional) errors->AddError("unsupported filter type");
438       continue;
439     }
440     absl::optional<XdsHttpFilterImpl::FilterConfig> filter_config =
441         filter_impl->GenerateFilterConfigOverride(
442             key, context, std::move(*extension_to_use), errors);
443     if (filter_config.has_value()) {
444       typed_per_filter_config[std::string(key)] = std::move(*filter_config);
445     }
446   }
447   return typed_per_filter_config;
448 }
449 
RetryPolicyParse(const XdsResourceType::DecodeContext & context,const envoy_config_route_v3_RetryPolicy * retry_policy_proto,ValidationErrors * errors)450 XdsRouteConfigResource::RetryPolicy RetryPolicyParse(
451     const XdsResourceType::DecodeContext& context,
452     const envoy_config_route_v3_RetryPolicy* retry_policy_proto,
453     ValidationErrors* errors) {
454   XdsRouteConfigResource::RetryPolicy retry_policy;
455   auto retry_on = UpbStringToStdString(
456       envoy_config_route_v3_RetryPolicy_retry_on(retry_policy_proto));
457   std::vector<absl::string_view> codes = absl::StrSplit(retry_on, ',');
458   for (const auto& code : codes) {
459     if (code == "cancelled") {
460       retry_policy.retry_on.Add(GRPC_STATUS_CANCELLED);
461     } else if (code == "deadline-exceeded") {
462       retry_policy.retry_on.Add(GRPC_STATUS_DEADLINE_EXCEEDED);
463     } else if (code == "internal") {
464       retry_policy.retry_on.Add(GRPC_STATUS_INTERNAL);
465     } else if (code == "resource-exhausted") {
466       retry_policy.retry_on.Add(GRPC_STATUS_RESOURCE_EXHAUSTED);
467     } else if (code == "unavailable") {
468       retry_policy.retry_on.Add(GRPC_STATUS_UNAVAILABLE);
469     } else {
470       if (GRPC_TRACE_FLAG_ENABLED_OBJ(*context.tracer)) {
471         LOG(INFO) << "Unsupported retry_on policy " << code;
472       }
473     }
474   }
475   retry_policy.num_retries =
476       ParseUInt32Value(
477           envoy_config_route_v3_RetryPolicy_num_retries(retry_policy_proto))
478           .value_or(1);
479   if (retry_policy.num_retries == 0) {
480     ValidationErrors::ScopedField field(errors, ".num_retries");
481     errors->AddError("must be greater than 0");
482   }
483   const envoy_config_route_v3_RetryPolicy_RetryBackOff* backoff =
484       envoy_config_route_v3_RetryPolicy_retry_back_off(retry_policy_proto);
485   if (backoff != nullptr) {
486     ValidationErrors::ScopedField field(errors, ".retry_back_off");
487     {
488       ValidationErrors::ScopedField field(errors, ".base_interval");
489       const google_protobuf_Duration* base_interval =
490           envoy_config_route_v3_RetryPolicy_RetryBackOff_base_interval(backoff);
491       if (base_interval == nullptr) {
492         errors->AddError("field not present");
493       } else {
494         retry_policy.retry_back_off.base_interval =
495             ParseDuration(base_interval, errors);
496       }
497     }
498     {
499       ValidationErrors::ScopedField field(errors, ".max_interval");
500       const google_protobuf_Duration* max_interval =
501           envoy_config_route_v3_RetryPolicy_RetryBackOff_max_interval(backoff);
502       Duration max;
503       if (max_interval != nullptr) {
504         max = ParseDuration(max_interval, errors);
505       } else {
506         // if max interval is not set, it is 10x the base.
507         max = 10 * retry_policy.retry_back_off.base_interval;
508       }
509       retry_policy.retry_back_off.max_interval = max;
510     }
511   } else {
512     retry_policy.retry_back_off.base_interval = Duration::Milliseconds(25);
513     retry_policy.retry_back_off.max_interval = Duration::Milliseconds(250);
514   }
515   return retry_policy;
516 }
517 
RouteActionParse(const XdsResourceType::DecodeContext & context,const envoy_config_route_v3_RouteAction * route_action_proto,const std::map<std::string,std::string> & cluster_specifier_plugin_map,ValidationErrors * errors)518 absl::optional<XdsRouteConfigResource::Route::RouteAction> RouteActionParse(
519     const XdsResourceType::DecodeContext& context,
520     const envoy_config_route_v3_RouteAction* route_action_proto,
521     const std::map<std::string /*cluster_specifier_plugin_name*/,
522                    std::string /*LB policy config*/>&
523         cluster_specifier_plugin_map,
524     ValidationErrors* errors) {
525   XdsRouteConfigResource::Route::RouteAction route_action;
526   // grpc_timeout_header_max or max_stream_duration
527   const auto* max_stream_duration =
528       envoy_config_route_v3_RouteAction_max_stream_duration(route_action_proto);
529   if (max_stream_duration != nullptr) {
530     ValidationErrors::ScopedField field(errors, ".max_stream_duration");
531     const google_protobuf_Duration* duration =
532         envoy_config_route_v3_RouteAction_MaxStreamDuration_grpc_timeout_header_max(
533             max_stream_duration);
534     if (duration != nullptr) {
535       ValidationErrors::ScopedField field(errors, ".grpc_timeout_header_max");
536       route_action.max_stream_duration = ParseDuration(duration, errors);
537     } else {
538       duration =
539           envoy_config_route_v3_RouteAction_MaxStreamDuration_max_stream_duration(
540               max_stream_duration);
541       if (duration != nullptr) {
542         ValidationErrors::ScopedField field(errors, ".max_stream_duration");
543         route_action.max_stream_duration = ParseDuration(duration, errors);
544       }
545     }
546   }
547   // hash_policy
548   size_t size = 0;
549   const envoy_config_route_v3_RouteAction_HashPolicy* const* hash_policies =
550       envoy_config_route_v3_RouteAction_hash_policy(route_action_proto, &size);
551   for (size_t i = 0; i < size; ++i) {
552     ValidationErrors::ScopedField field(errors,
553                                         absl::StrCat(".hash_policy[", i, "]"));
554     const auto* hash_policy = hash_policies[i];
555     XdsRouteConfigResource::Route::RouteAction::HashPolicy policy;
556     policy.terminal =
557         envoy_config_route_v3_RouteAction_HashPolicy_terminal(hash_policy);
558     const envoy_config_route_v3_RouteAction_HashPolicy_Header* header;
559     const envoy_config_route_v3_RouteAction_HashPolicy_FilterState*
560         filter_state;
561     if ((header = envoy_config_route_v3_RouteAction_HashPolicy_header(
562              hash_policy)) != nullptr) {
563       // header
564       ValidationErrors::ScopedField field(errors, ".header");
565       XdsRouteConfigResource::Route::RouteAction::HashPolicy::Header
566           header_policy;
567       header_policy.header_name = UpbStringToStdString(
568           envoy_config_route_v3_RouteAction_HashPolicy_Header_header_name(
569               header));
570       if (header_policy.header_name.empty()) {
571         ValidationErrors::ScopedField field(errors, ".header_name");
572         errors->AddError("must be non-empty");
573       }
574       // regex_rewrite
575       const auto* regex_rewrite =
576           envoy_config_route_v3_RouteAction_HashPolicy_Header_regex_rewrite(
577               header);
578       if (regex_rewrite != nullptr) {
579         ValidationErrors::ScopedField field(errors, ".regex_rewrite.pattern");
580         const auto* pattern =
581             envoy_type_matcher_v3_RegexMatchAndSubstitute_pattern(
582                 regex_rewrite);
583         if (pattern == nullptr) {
584           errors->AddError("field not present");
585           continue;
586         }
587         ValidationErrors::ScopedField field2(errors, ".regex");
588         std::string regex = UpbStringToStdString(
589             envoy_type_matcher_v3_RegexMatcher_regex(pattern));
590         if (regex.empty()) {
591           errors->AddError("field not present");
592           continue;
593         }
594         RE2::Options options;
595         header_policy.regex = std::make_unique<RE2>(regex, options);
596         if (!header_policy.regex->ok()) {
597           errors->AddError(absl::StrCat("errors compiling regex: ",
598                                         header_policy.regex->error()));
599           continue;
600         }
601         header_policy.regex_substitution = UpbStringToStdString(
602             envoy_type_matcher_v3_RegexMatchAndSubstitute_substitution(
603                 regex_rewrite));
604       }
605       policy.policy = std::move(header_policy);
606     } else if ((filter_state =
607                     envoy_config_route_v3_RouteAction_HashPolicy_filter_state(
608                         hash_policy)) != nullptr) {
609       // filter_state
610       std::string key = UpbStringToStdString(
611           envoy_config_route_v3_RouteAction_HashPolicy_FilterState_key(
612               filter_state));
613       if (key != "io.grpc.channel_id") continue;
614       policy.policy =
615           XdsRouteConfigResource::Route::RouteAction::HashPolicy::ChannelId();
616     } else {
617       // Unsupported hash policy type, ignore it.
618       continue;
619     }
620     route_action.hash_policies.emplace_back(std::move(policy));
621   }
622   // Get retry policy
623   const envoy_config_route_v3_RetryPolicy* retry_policy =
624       envoy_config_route_v3_RouteAction_retry_policy(route_action_proto);
625   if (retry_policy != nullptr) {
626     ValidationErrors::ScopedField field(errors, ".retry_policy");
627     route_action.retry_policy = RetryPolicyParse(context, retry_policy, errors);
628   }
629   // Host rewrite field.
630   if (XdsAuthorityRewriteEnabled() &&
631       DownCast<const GrpcXdsServer&>(context.server).TrustedXdsServer()) {
632     route_action.auto_host_rewrite =
633         ParseBoolValue(envoy_config_route_v3_RouteAction_auto_host_rewrite(
634             route_action_proto));
635   }
636   // Parse cluster specifier, which is one of several options.
637   if (envoy_config_route_v3_RouteAction_has_cluster(route_action_proto)) {
638     // Cluster name.
639     std::string cluster_name = UpbStringToStdString(
640         envoy_config_route_v3_RouteAction_cluster(route_action_proto));
641     if (cluster_name.empty()) {
642       ValidationErrors::ScopedField field(errors, ".cluster");
643       errors->AddError("must be non-empty");
644     }
645     route_action.action =
646         XdsRouteConfigResource::Route::RouteAction::ClusterName{
647             std::move(cluster_name)};
648   } else if (envoy_config_route_v3_RouteAction_has_weighted_clusters(
649                  route_action_proto)) {
650     // WeightedClusters.
651     ValidationErrors::ScopedField field(errors, ".weighted_clusters");
652     const envoy_config_route_v3_WeightedCluster* weighted_clusters_proto =
653         envoy_config_route_v3_RouteAction_weighted_clusters(route_action_proto);
654     CHECK_NE(weighted_clusters_proto, nullptr);
655     std::vector<XdsRouteConfigResource::Route::RouteAction::ClusterWeight>
656         action_weighted_clusters;
657     uint64_t total_weight = 0;
658     size_t clusters_size;
659     const envoy_config_route_v3_WeightedCluster_ClusterWeight* const* clusters =
660         envoy_config_route_v3_WeightedCluster_clusters(weighted_clusters_proto,
661                                                        &clusters_size);
662     for (size_t i = 0; i < clusters_size; ++i) {
663       ValidationErrors::ScopedField field(errors,
664                                           absl::StrCat(".clusters[", i, "]"));
665       const auto* cluster_proto = clusters[i];
666       XdsRouteConfigResource::Route::RouteAction::ClusterWeight cluster;
667       // typed_per_filter_config
668       {
669         ValidationErrors::ScopedField field(errors, ".typed_per_filter_config");
670         cluster.typed_per_filter_config = ParseTypedPerFilterConfig<
671             envoy_config_route_v3_WeightedCluster_ClusterWeight,
672             envoy_config_route_v3_WeightedCluster_ClusterWeight_TypedPerFilterConfigEntry>(
673             context, cluster_proto,
674             envoy_config_route_v3_WeightedCluster_ClusterWeight_typed_per_filter_config_next,
675             envoy_config_route_v3_WeightedCluster_ClusterWeight_TypedPerFilterConfigEntry_key,
676             envoy_config_route_v3_WeightedCluster_ClusterWeight_TypedPerFilterConfigEntry_value,
677             errors);
678       }
679       // name
680       cluster.name = UpbStringToStdString(
681           envoy_config_route_v3_WeightedCluster_ClusterWeight_name(
682               cluster_proto));
683       if (cluster.name.empty()) {
684         ValidationErrors::ScopedField field(errors, ".name");
685         errors->AddError("must be non-empty");
686       }
687       // weight
688       auto weight = ParseUInt32Value(
689           envoy_config_route_v3_WeightedCluster_ClusterWeight_weight(
690               cluster_proto));
691       if (!weight.has_value()) {
692         ValidationErrors::ScopedField field(errors, ".weight");
693         errors->AddError("field not present");
694       } else {
695         cluster.weight = *weight;
696         if (cluster.weight == 0) continue;
697         total_weight += cluster.weight;
698       }
699       // Add entry to WeightedClusters.
700       action_weighted_clusters.emplace_back(std::move(cluster));
701     }
702     if (action_weighted_clusters.empty()) {
703       errors->AddError("no valid clusters specified");
704     } else if (total_weight > std::numeric_limits<uint32_t>::max()) {
705       errors->AddError("sum of cluster weights exceeds uint32 max");
706     }
707     route_action.action = std::move(action_weighted_clusters);
708   } else if (XdsRlsEnabled() &&
709              envoy_config_route_v3_RouteAction_has_cluster_specifier_plugin(
710                  route_action_proto)) {
711     // ClusterSpecifierPlugin
712     ValidationErrors::ScopedField field(errors, ".cluster_specifier_plugin");
713     std::string plugin_name = UpbStringToStdString(
714         envoy_config_route_v3_RouteAction_cluster_specifier_plugin(
715             route_action_proto));
716     if (plugin_name.empty()) {
717       errors->AddError("must be non-empty");
718       return absl::nullopt;
719     }
720     auto it = cluster_specifier_plugin_map.find(plugin_name);
721     if (it == cluster_specifier_plugin_map.end()) {
722       errors->AddError(absl::StrCat("unknown cluster specifier plugin name \"",
723                                     plugin_name, "\""));
724     } else {
725       // If the cluster specifier config is empty, that means that the
726       // plugin was unsupported but optional.  In that case, skip this route.
727       if (it->second.empty()) return absl::nullopt;
728     }
729     route_action.action =
730         XdsRouteConfigResource::Route::RouteAction::ClusterSpecifierPluginName{
731             std::move(plugin_name)};
732   } else {
733     // Not a supported cluster specifier, so ignore this route.
734     return absl::nullopt;
735   }
736   return route_action;
737 }
738 
ParseRoute(const XdsResourceType::DecodeContext & context,const envoy_config_route_v3_Route * route_proto,const absl::optional<XdsRouteConfigResource::RetryPolicy> & virtual_host_retry_policy,const XdsRouteConfigResource::ClusterSpecifierPluginMap & cluster_specifier_plugin_map,std::set<absl::string_view> * cluster_specifier_plugins_not_seen,ValidationErrors * errors)739 absl::optional<XdsRouteConfigResource::Route> ParseRoute(
740     const XdsResourceType::DecodeContext& context,
741     const envoy_config_route_v3_Route* route_proto,
742     const absl::optional<XdsRouteConfigResource::RetryPolicy>&
743         virtual_host_retry_policy,
744     const XdsRouteConfigResource::ClusterSpecifierPluginMap&
745         cluster_specifier_plugin_map,
746     std::set<absl::string_view>* cluster_specifier_plugins_not_seen,
747     ValidationErrors* errors) {
748   XdsRouteConfigResource::Route route;
749   // Parse route match.
750   {
751     ValidationErrors::ScopedField field(errors, ".match");
752     const auto* match = envoy_config_route_v3_Route_match(route_proto);
753     if (match == nullptr) {
754       errors->AddError("field not present");
755       return absl::nullopt;
756     }
757     // Skip routes with query_parameters set.
758     size_t query_parameters_size;
759     static_cast<void>(envoy_config_route_v3_RouteMatch_query_parameters(
760         match, &query_parameters_size));
761     if (query_parameters_size > 0) return absl::nullopt;
762     // Parse matchers.
763     auto path_matcher = RoutePathMatchParse(match, errors);
764     if (!path_matcher.has_value()) return absl::nullopt;
765     route.matchers.path_matcher = std::move(*path_matcher);
766     RouteHeaderMatchersParse(match, &route, errors);
767     RouteRuntimeFractionParse(match, &route, errors);
768   }
769   // Parse route action.
770   const envoy_config_route_v3_RouteAction* route_action_proto =
771       envoy_config_route_v3_Route_route(route_proto);
772   if (route_action_proto != nullptr) {
773     ValidationErrors::ScopedField field(errors, ".route");
774     auto route_action = RouteActionParse(context, route_action_proto,
775                                          cluster_specifier_plugin_map, errors);
776     if (!route_action.has_value()) return absl::nullopt;
777     // If the route does not have a retry policy but the vhost does,
778     // use the vhost retry policy for this route.
779     if (!route_action->retry_policy.has_value()) {
780       route_action->retry_policy = virtual_host_retry_policy;
781     }
782     // Mark off plugins used in route action.
783     auto* cluster_specifier_action = absl::get_if<
784         XdsRouteConfigResource::Route::RouteAction::ClusterSpecifierPluginName>(
785         &route_action->action);
786     if (cluster_specifier_action != nullptr) {
787       cluster_specifier_plugins_not_seen->erase(
788           cluster_specifier_action->cluster_specifier_plugin_name);
789     }
790     route.action = std::move(*route_action);
791   } else if (envoy_config_route_v3_Route_has_non_forwarding_action(
792                  route_proto)) {
793     route.action = XdsRouteConfigResource::Route::NonForwardingAction();
794   } else {
795     // Leave route.action initialized to UnknownAction (its default).
796   }
797   // Parse typed_per_filter_config.
798   {
799     ValidationErrors::ScopedField field(errors, ".typed_per_filter_config");
800     route.typed_per_filter_config = ParseTypedPerFilterConfig<
801         envoy_config_route_v3_Route,
802         envoy_config_route_v3_Route_TypedPerFilterConfigEntry>(
803         context, route_proto,
804         envoy_config_route_v3_Route_typed_per_filter_config_next,
805         envoy_config_route_v3_Route_TypedPerFilterConfigEntry_key,
806         envoy_config_route_v3_Route_TypedPerFilterConfigEntry_value, errors);
807   }
808   return route;
809 }
810 
811 }  // namespace
812 
XdsRouteConfigResourceParse(const XdsResourceType::DecodeContext & context,const envoy_config_route_v3_RouteConfiguration * route_config,ValidationErrors * errors)813 std::shared_ptr<const XdsRouteConfigResource> XdsRouteConfigResourceParse(
814     const XdsResourceType::DecodeContext& context,
815     const envoy_config_route_v3_RouteConfiguration* route_config,
816     ValidationErrors* errors) {
817   auto rds_update = std::make_shared<XdsRouteConfigResource>();
818   // Get the cluster spcifier plugin map.
819   if (XdsRlsEnabled()) {
820     rds_update->cluster_specifier_plugin_map =
821         ClusterSpecifierPluginParse(context, route_config, errors);
822   }
823   // Build a set of configured cluster_specifier_plugin names to make sure
824   // each is actually referenced by a route action.
825   std::set<absl::string_view> cluster_specifier_plugins_not_seen;
826   for (auto& plugin : rds_update->cluster_specifier_plugin_map) {
827     cluster_specifier_plugins_not_seen.emplace(plugin.first);
828   }
829   // Get the virtual hosts.
830   size_t num_virtual_hosts;
831   const envoy_config_route_v3_VirtualHost* const* virtual_hosts =
832       envoy_config_route_v3_RouteConfiguration_virtual_hosts(
833           route_config, &num_virtual_hosts);
834   for (size_t i = 0; i < num_virtual_hosts; ++i) {
835     ValidationErrors::ScopedField field(
836         errors, absl::StrCat(".virtual_hosts[", i, "]"));
837     rds_update->virtual_hosts.emplace_back();
838     XdsRouteConfigResource::VirtualHost& vhost =
839         rds_update->virtual_hosts.back();
840     // Parse domains.
841     size_t domain_size;
842     upb_StringView const* domains = envoy_config_route_v3_VirtualHost_domains(
843         virtual_hosts[i], &domain_size);
844     for (size_t j = 0; j < domain_size; ++j) {
845       std::string domain_pattern = UpbStringToStdString(domains[j]);
846       if (!XdsRouting::IsValidDomainPattern(domain_pattern)) {
847         ValidationErrors::ScopedField field(errors,
848                                             absl::StrCat(".domains[", j, "]"));
849         errors->AddError(
850             absl::StrCat("invalid domain pattern \"", domain_pattern, "\""));
851       }
852       vhost.domains.emplace_back(std::move(domain_pattern));
853     }
854     if (vhost.domains.empty()) {
855       ValidationErrors::ScopedField field(errors, ".domains");
856       errors->AddError("must be non-empty");
857     }
858     // Parse typed_per_filter_config.
859     {
860       ValidationErrors::ScopedField field(errors, ".typed_per_filter_config");
861       vhost.typed_per_filter_config = ParseTypedPerFilterConfig<
862           envoy_config_route_v3_VirtualHost,
863           envoy_config_route_v3_VirtualHost_TypedPerFilterConfigEntry>(
864           context, virtual_hosts[i],
865           envoy_config_route_v3_VirtualHost_typed_per_filter_config_next,
866           envoy_config_route_v3_VirtualHost_TypedPerFilterConfigEntry_key,
867           envoy_config_route_v3_VirtualHost_TypedPerFilterConfigEntry_value,
868           errors);
869     }
870     // Parse retry policy.
871     absl::optional<XdsRouteConfigResource::RetryPolicy>
872         virtual_host_retry_policy;
873     const envoy_config_route_v3_RetryPolicy* retry_policy =
874         envoy_config_route_v3_VirtualHost_retry_policy(virtual_hosts[i]);
875     if (retry_policy != nullptr) {
876       ValidationErrors::ScopedField field(errors, ".retry_policy");
877       virtual_host_retry_policy =
878           RetryPolicyParse(context, retry_policy, errors);
879     }
880     // Parse routes.
881     ValidationErrors::ScopedField field2(errors, ".routes");
882     size_t num_routes;
883     const envoy_config_route_v3_Route* const* routes =
884         envoy_config_route_v3_VirtualHost_routes(virtual_hosts[i], &num_routes);
885     for (size_t j = 0; j < num_routes; ++j) {
886       ValidationErrors::ScopedField field(errors, absl::StrCat("[", j, "]"));
887       auto route = ParseRoute(context, routes[j], virtual_host_retry_policy,
888                               rds_update->cluster_specifier_plugin_map,
889                               &cluster_specifier_plugins_not_seen, errors);
890       if (route.has_value()) vhost.routes.emplace_back(std::move(*route));
891     }
892   }
893   // For cluster specifier plugins that were not used in any route action,
894   // delete them from the update, since they will never be used.
895   for (auto& unused_plugin : cluster_specifier_plugins_not_seen) {
896     rds_update->cluster_specifier_plugin_map.erase(std::string(unused_plugin));
897   }
898   return rds_update;
899 }
900 
901 //
902 // XdsRouteConfigResourceType
903 //
904 
905 namespace {
906 
MaybeLogRouteConfiguration(const XdsResourceType::DecodeContext & context,const envoy_config_route_v3_RouteConfiguration * route_config)907 void MaybeLogRouteConfiguration(
908     const XdsResourceType::DecodeContext& context,
909     const envoy_config_route_v3_RouteConfiguration* route_config) {
910   if (GRPC_TRACE_FLAG_ENABLED_OBJ(*context.tracer) && ABSL_VLOG_IS_ON(2)) {
911     const upb_MessageDef* msg_type =
912         envoy_config_route_v3_RouteConfiguration_getmsgdef(context.symtab);
913     char buf[10240];
914     upb_TextEncode(reinterpret_cast<const upb_Message*>(route_config), msg_type,
915                    nullptr, 0, buf, sizeof(buf));
916     VLOG(2) << "[xds_client " << context.client
917             << "] RouteConfiguration: " << buf;
918   }
919 }
920 
921 }  // namespace
922 
Decode(const XdsResourceType::DecodeContext & context,absl::string_view serialized_resource) const923 XdsResourceType::DecodeResult XdsRouteConfigResourceType::Decode(
924     const XdsResourceType::DecodeContext& context,
925     absl::string_view serialized_resource) const {
926   DecodeResult result;
927   // Parse serialized proto.
928   auto* resource = envoy_config_route_v3_RouteConfiguration_parse(
929       serialized_resource.data(), serialized_resource.size(), context.arena);
930   if (resource == nullptr) {
931     result.resource =
932         absl::InvalidArgumentError("Can't parse RouteConfiguration resource.");
933     return result;
934   }
935   MaybeLogRouteConfiguration(context, resource);
936   // Validate resource.
937   result.name = UpbStringToStdString(
938       envoy_config_route_v3_RouteConfiguration_name(resource));
939   ValidationErrors errors;
940   auto rds_update = XdsRouteConfigResourceParse(context, resource, &errors);
941   if (!errors.ok()) {
942     absl::Status status =
943         errors.status(absl::StatusCode::kInvalidArgument,
944                       "errors validating RouteConfiguration resource");
945     if (GRPC_TRACE_FLAG_ENABLED_OBJ(*context.tracer)) {
946       LOG(ERROR) << "[xds_client " << context.client
947                  << "] invalid RouteConfiguration " << *result.name << ": "
948                  << status;
949     }
950     result.resource = std::move(status);
951   } else {
952     if (GRPC_TRACE_FLAG_ENABLED_OBJ(*context.tracer)) {
953       LOG(INFO) << "[xds_client " << context.client
954                 << "] parsed RouteConfiguration " << *result.name << ": "
955                 << rds_update->ToString();
956     }
957     result.resource = std::move(rds_update);
958   }
959   return result;
960 }
961 
962 }  // namespace grpc_core
963