• 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 #include <grpc/support/port_platform.h>
20 
21 #include <algorithm>
22 #include <cctype>
23 #include <cstdint>
24 #include <cstdlib>
25 #include <string>
26 
27 #include "absl/strings/str_cat.h"
28 #include "absl/strings/str_format.h"
29 #include "absl/strings/str_join.h"
30 #include "absl/strings/str_split.h"
31 #include "envoy/admin/v3/config_dump.upb.h"
32 #include "envoy/config/cluster/v3/circuit_breaker.upb.h"
33 #include "envoy/config/cluster/v3/cluster.upb.h"
34 #include "envoy/config/cluster/v3/cluster.upbdefs.h"
35 #include "envoy/config/core/v3/address.upb.h"
36 #include "envoy/config/core/v3/base.upb.h"
37 #include "envoy/config/core/v3/base.upbdefs.h"
38 #include "envoy/config/core/v3/config_source.upb.h"
39 #include "envoy/config/core/v3/health_check.upb.h"
40 #include "envoy/config/core/v3/protocol.upb.h"
41 #include "envoy/config/endpoint/v3/endpoint.upb.h"
42 #include "envoy/config/endpoint/v3/endpoint.upbdefs.h"
43 #include "envoy/config/endpoint/v3/endpoint_components.upb.h"
44 #include "envoy/config/endpoint/v3/load_report.upb.h"
45 #include "envoy/config/listener/v3/api_listener.upb.h"
46 #include "envoy/config/listener/v3/listener.upb.h"
47 #include "envoy/config/listener/v3/listener.upbdefs.h"
48 #include "envoy/config/listener/v3/listener_components.upb.h"
49 #include "envoy/config/route/v3/route.upb.h"
50 #include "envoy/config/route/v3/route.upbdefs.h"
51 #include "envoy/config/route/v3/route_components.upb.h"
52 #include "envoy/config/route/v3/route_components.upbdefs.h"
53 #include "envoy/extensions/clusters/aggregate/v3/cluster.upb.h"
54 #include "envoy/extensions/clusters/aggregate/v3/cluster.upbdefs.h"
55 #include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upb.h"
56 #include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upbdefs.h"
57 #include "envoy/extensions/transport_sockets/tls/v3/common.upb.h"
58 #include "envoy/extensions/transport_sockets/tls/v3/tls.upb.h"
59 #include "envoy/extensions/transport_sockets/tls/v3/tls.upbdefs.h"
60 #include "envoy/service/cluster/v3/cds.upb.h"
61 #include "envoy/service/cluster/v3/cds.upbdefs.h"
62 #include "envoy/service/discovery/v3/discovery.upb.h"
63 #include "envoy/service/discovery/v3/discovery.upbdefs.h"
64 #include "envoy/service/endpoint/v3/eds.upb.h"
65 #include "envoy/service/endpoint/v3/eds.upbdefs.h"
66 #include "envoy/service/listener/v3/lds.upb.h"
67 #include "envoy/service/load_stats/v3/lrs.upb.h"
68 #include "envoy/service/load_stats/v3/lrs.upbdefs.h"
69 #include "envoy/service/route/v3/rds.upb.h"
70 #include "envoy/service/route/v3/rds.upbdefs.h"
71 #include "envoy/service/status/v3/csds.upb.h"
72 #include "envoy/service/status/v3/csds.upbdefs.h"
73 #include "envoy/type/matcher/v3/regex.upb.h"
74 #include "envoy/type/matcher/v3/string.upb.h"
75 #include "envoy/type/v3/percent.upb.h"
76 #include "envoy/type/v3/range.upb.h"
77 #include "google/protobuf/any.upb.h"
78 #include "google/protobuf/duration.upb.h"
79 #include "google/protobuf/struct.upb.h"
80 #include "google/protobuf/timestamp.upb.h"
81 #include "google/protobuf/wrappers.upb.h"
82 #include "google/rpc/status.upb.h"
83 #include "udpa/type/v1/typed_struct.upb.h"
84 #include "upb/text_encode.h"
85 #include "upb/upb.h"
86 #include "upb/upb.hpp"
87 
88 #include <grpc/impl/codegen/log.h>
89 #include <grpc/support/alloc.h>
90 #include <grpc/support/string_util.h>
91 
92 #include "src/core/ext/xds/xds_api.h"
93 #include "src/core/lib/address_utils/sockaddr_utils.h"
94 #include "src/core/lib/gpr/env.h"
95 #include "src/core/lib/gpr/string.h"
96 #include "src/core/lib/gpr/useful.h"
97 #include "src/core/lib/gprpp/host_port.h"
98 #include "src/core/lib/iomgr/error.h"
99 #include "src/core/lib/iomgr/sockaddr.h"
100 #include "src/core/lib/iomgr/socket_utils.h"
101 #include "src/core/lib/slice/slice_utils.h"
102 
103 namespace grpc_core {
104 
105 // TODO(donnadionne): Check to see if cluster types aggregate_cluster and
106 // logical_dns are enabled, this will be
107 // removed once the cluster types are fully integration-tested and enabled by
108 // default.
XdsAggregateAndLogicalDnsClusterEnabled()109 bool XdsAggregateAndLogicalDnsClusterEnabled() {
110   char* value = gpr_getenv(
111       "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER");
112   bool parsed_value;
113   bool parse_succeeded = gpr_parse_bool_value(value, &parsed_value);
114   gpr_free(value);
115   return parse_succeeded && parsed_value;
116 }
117 
118 // TODO(donnadionne): Check to see if ring hash policy is enabled, this will be
119 // removed once ring hash policy is fully integration-tested and enabled by
120 // default.
XdsRingHashEnabled()121 bool XdsRingHashEnabled() {
122   char* value = gpr_getenv("GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH");
123   bool parsed_value;
124   bool parse_succeeded = gpr_parse_bool_value(value, &parsed_value);
125   gpr_free(value);
126   return parse_succeeded && parsed_value;
127 }
128 
129 // TODO(yashykt): Check to see if xDS security is enabled. This will be
130 // removed once this feature is fully integration-tested and enabled by
131 // default.
XdsSecurityEnabled()132 bool XdsSecurityEnabled() {
133   char* value = gpr_getenv("GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT");
134   bool parsed_value;
135   bool parse_succeeded = gpr_parse_bool_value(value, &parsed_value);
136   gpr_free(value);
137   return parse_succeeded && parsed_value;
138 }
139 
140 //
141 // XdsApi::Route::HashPolicy
142 //
143 
HashPolicy(const HashPolicy & other)144 XdsApi::Route::HashPolicy::HashPolicy(const HashPolicy& other)
145     : type(other.type),
146       header_name(other.header_name),
147       regex_substitution(other.regex_substitution) {
148   if (other.regex != nullptr) {
149     regex =
150         absl::make_unique<RE2>(other.regex->pattern(), other.regex->options());
151   }
152 }
153 
operator =(const HashPolicy & other)154 XdsApi::Route::HashPolicy& XdsApi::Route::HashPolicy::operator=(
155     const HashPolicy& other) {
156   type = other.type;
157   header_name = other.header_name;
158   if (other.regex != nullptr) {
159     regex =
160         absl::make_unique<RE2>(other.regex->pattern(), other.regex->options());
161   }
162   regex_substitution = other.regex_substitution;
163   return *this;
164 }
165 
HashPolicy(HashPolicy && other)166 XdsApi::Route::HashPolicy::HashPolicy(HashPolicy&& other) noexcept
167     : type(other.type),
168       header_name(std::move(other.header_name)),
169       regex(std::move(other.regex)),
170       regex_substitution(std::move(other.regex_substitution)) {}
171 
operator =(HashPolicy && other)172 XdsApi::Route::HashPolicy& XdsApi::Route::HashPolicy::operator=(
173     HashPolicy&& other) noexcept {
174   type = other.type;
175   header_name = std::move(other.header_name);
176   regex = std::move(other.regex);
177   regex_substitution = std::move(other.regex_substitution);
178   return *this;
179 }
180 
operator ==(const HashPolicy & other) const181 bool XdsApi::Route::HashPolicy::HashPolicy::operator==(
182     const HashPolicy& other) const {
183   if (type != other.type) return false;
184   if (type == Type::HEADER) {
185     if (regex == nullptr) {
186       if (other.regex != nullptr) return false;
187     } else {
188       if (other.regex == nullptr) return false;
189       return header_name == other.header_name &&
190              regex->pattern() == other.regex->pattern() &&
191              regex_substitution == other.regex_substitution;
192     }
193   }
194   return true;
195 }
196 
ToString() const197 std::string XdsApi::Route::HashPolicy::ToString() const {
198   std::vector<std::string> contents;
199   switch (type) {
200     case Type::HEADER:
201       contents.push_back("type=HEADER");
202       break;
203     case Type::CHANNEL_ID:
204       contents.push_back("type=CHANNEL_ID");
205       break;
206   }
207   contents.push_back(
208       absl::StrFormat("terminal=%s", terminal ? "true" : "false"));
209   if (type == Type::HEADER) {
210     contents.push_back(absl::StrFormat(
211         "Header %s:/%s/%s", header_name,
212         (regex == nullptr) ? "" : regex->pattern(), regex_substitution));
213   }
214   return absl::StrCat("{", absl::StrJoin(contents, ", "), "}");
215 }
216 
217 //
218 // XdsApi::Route
219 //
220 
ToString() const221 std::string XdsApi::Route::Matchers::ToString() const {
222   std::vector<std::string> contents;
223   contents.push_back(
224       absl::StrFormat("PathMatcher{%s}", path_matcher.ToString()));
225   for (const HeaderMatcher& header_matcher : header_matchers) {
226     contents.push_back(header_matcher.ToString());
227   }
228   if (fraction_per_million.has_value()) {
229     contents.push_back(absl::StrFormat("Fraction Per Million %d",
230                                        fraction_per_million.value()));
231   }
232   return absl::StrJoin(contents, "\n");
233 }
234 
ToString() const235 std::string XdsApi::Route::ClusterWeight::ToString() const {
236   std::vector<std::string> contents;
237   contents.push_back(absl::StrCat("cluster=", name));
238   contents.push_back(absl::StrCat("weight=", weight));
239   if (!typed_per_filter_config.empty()) {
240     std::vector<std::string> parts;
241     for (const auto& p : typed_per_filter_config) {
242       const std::string& key = p.first;
243       const auto& config = p.second;
244       parts.push_back(absl::StrCat(key, "=", config.ToString()));
245     }
246     contents.push_back(absl::StrCat("typed_per_filter_config={",
247                                     absl::StrJoin(parts, ", "), "}"));
248   }
249   return absl::StrCat("{", absl::StrJoin(contents, ", "), "}");
250 }
251 
ToString() const252 std::string XdsApi::Route::ToString() const {
253   std::vector<std::string> contents;
254   contents.push_back(matchers.ToString());
255   for (const HashPolicy& hash_policy : hash_policies) {
256     contents.push_back(absl::StrCat("hash_policy=", hash_policy.ToString()));
257   }
258   if (!cluster_name.empty()) {
259     contents.push_back(absl::StrFormat("Cluster name: %s", cluster_name));
260   }
261   for (const ClusterWeight& cluster_weight : weighted_clusters) {
262     contents.push_back(cluster_weight.ToString());
263   }
264   if (max_stream_duration.has_value()) {
265     contents.push_back(max_stream_duration->ToString());
266   }
267   if (!typed_per_filter_config.empty()) {
268     contents.push_back("typed_per_filter_config={");
269     for (const auto& p : typed_per_filter_config) {
270       const std::string& name = p.first;
271       const auto& config = p.second;
272       contents.push_back(absl::StrCat("  ", name, "=", config.ToString()));
273     }
274     contents.push_back("}");
275   }
276   return absl::StrJoin(contents, "\n");
277 }
278 
279 //
280 // XdsApi::RdsUpdate
281 //
282 
ToString() const283 std::string XdsApi::RdsUpdate::ToString() const {
284   std::vector<std::string> vhosts;
285   for (const VirtualHost& vhost : virtual_hosts) {
286     vhosts.push_back(
287         absl::StrCat("vhost={\n"
288                      "  domains=[",
289                      absl::StrJoin(vhost.domains, ", "),
290                      "]\n"
291                      "  routes=[\n"));
292     for (const XdsApi::Route& route : vhost.routes) {
293       vhosts.push_back("    {\n");
294       vhosts.push_back(route.ToString());
295       vhosts.push_back("\n    }\n");
296     }
297     vhosts.push_back("  ]\n");
298     vhosts.push_back("  typed_per_filter_config={\n");
299     for (const auto& p : vhost.typed_per_filter_config) {
300       const std::string& name = p.first;
301       const auto& config = p.second;
302       vhosts.push_back(
303           absl::StrCat("    ", name, "=", config.ToString(), "\n"));
304     }
305     vhosts.push_back("  }\n");
306     vhosts.push_back("]\n");
307   }
308   return absl::StrJoin(vhosts, "");
309 }
310 
311 namespace {
312 
313 // Better match type has smaller value.
314 enum MatchType {
315   EXACT_MATCH,
316   SUFFIX_MATCH,
317   PREFIX_MATCH,
318   UNIVERSE_MATCH,
319   INVALID_MATCH,
320 };
321 
322 // Returns true if match succeeds.
DomainMatch(MatchType match_type,const std::string & domain_pattern_in,const std::string & expected_host_name_in)323 bool DomainMatch(MatchType match_type, const std::string& domain_pattern_in,
324                  const std::string& expected_host_name_in) {
325   // Normalize the args to lower-case. Domain matching is case-insensitive.
326   std::string domain_pattern = domain_pattern_in;
327   std::string expected_host_name = expected_host_name_in;
328   std::transform(domain_pattern.begin(), domain_pattern.end(),
329                  domain_pattern.begin(),
330                  [](unsigned char c) { return std::tolower(c); });
331   std::transform(expected_host_name.begin(), expected_host_name.end(),
332                  expected_host_name.begin(),
333                  [](unsigned char c) { return std::tolower(c); });
334   if (match_type == EXACT_MATCH) {
335     return domain_pattern == expected_host_name;
336   } else if (match_type == SUFFIX_MATCH) {
337     // Asterisk must match at least one char.
338     if (expected_host_name.size() < domain_pattern.size()) return false;
339     absl::string_view pattern_suffix(domain_pattern.c_str() + 1);
340     absl::string_view host_suffix(expected_host_name.c_str() +
341                                   expected_host_name.size() -
342                                   pattern_suffix.size());
343     return pattern_suffix == host_suffix;
344   } else if (match_type == PREFIX_MATCH) {
345     // Asterisk must match at least one char.
346     if (expected_host_name.size() < domain_pattern.size()) return false;
347     absl::string_view pattern_prefix(domain_pattern.c_str(),
348                                      domain_pattern.size() - 1);
349     absl::string_view host_prefix(expected_host_name.c_str(),
350                                   pattern_prefix.size());
351     return pattern_prefix == host_prefix;
352   } else {
353     return match_type == UNIVERSE_MATCH;
354   }
355 }
356 
DomainPatternMatchType(const std::string & domain_pattern)357 MatchType DomainPatternMatchType(const std::string& domain_pattern) {
358   if (domain_pattern.empty()) return INVALID_MATCH;
359   if (domain_pattern.find('*') == std::string::npos) return EXACT_MATCH;
360   if (domain_pattern == "*") return UNIVERSE_MATCH;
361   if (domain_pattern[0] == '*') return SUFFIX_MATCH;
362   if (domain_pattern[domain_pattern.size() - 1] == '*') return PREFIX_MATCH;
363   return INVALID_MATCH;
364 }
365 
366 }  // namespace
367 
FindVirtualHostForDomain(const std::string & domain)368 XdsApi::RdsUpdate::VirtualHost* XdsApi::RdsUpdate::FindVirtualHostForDomain(
369     const std::string& domain) {
370   // Find the best matched virtual host.
371   // The search order for 4 groups of domain patterns:
372   //   1. Exact match.
373   //   2. Suffix match (e.g., "*ABC").
374   //   3. Prefix match (e.g., "ABC*").
375   //   4. Universe match (i.e., "*").
376   // Within each group, longest match wins.
377   // If the same best matched domain pattern appears in multiple virtual hosts,
378   // the first matched virtual host wins.
379   VirtualHost* target_vhost = nullptr;
380   MatchType best_match_type = INVALID_MATCH;
381   size_t longest_match = 0;
382   // Check each domain pattern in each virtual host to determine the best
383   // matched virtual host.
384   for (VirtualHost& vhost : virtual_hosts) {
385     for (const std::string& domain_pattern : vhost.domains) {
386       // Check the match type first. Skip the pattern if it's not better than
387       // current match.
388       const MatchType match_type = DomainPatternMatchType(domain_pattern);
389       // This should be caught by RouteConfigParse().
390       GPR_ASSERT(match_type != INVALID_MATCH);
391       if (match_type > best_match_type) continue;
392       if (match_type == best_match_type &&
393           domain_pattern.size() <= longest_match) {
394         continue;
395       }
396       // Skip if match fails.
397       if (!DomainMatch(match_type, domain_pattern, domain)) continue;
398       // Choose this match.
399       target_vhost = &vhost;
400       best_match_type = match_type;
401       longest_match = domain_pattern.size();
402       if (best_match_type == EXACT_MATCH) break;
403     }
404     if (best_match_type == EXACT_MATCH) break;
405   }
406   return target_vhost;
407 }
408 
409 //
410 // XdsApi::CommonTlsContext::CertificateValidationContext
411 //
412 
ToString() const413 std::string XdsApi::CommonTlsContext::CertificateValidationContext::ToString()
414     const {
415   std::vector<std::string> contents;
416   for (const auto& match : match_subject_alt_names) {
417     contents.push_back(match.ToString());
418   }
419   return absl::StrFormat("{match_subject_alt_names=[%s]}",
420                          absl::StrJoin(contents, ", "));
421 }
422 
Empty() const423 bool XdsApi::CommonTlsContext::CertificateValidationContext::Empty() const {
424   return match_subject_alt_names.empty();
425 }
426 
427 //
428 // XdsApi::CommonTlsContext::CertificateValidationContext
429 //
430 
ToString() const431 std::string XdsApi::CommonTlsContext::CertificateProviderInstance::ToString()
432     const {
433   absl::InlinedVector<std::string, 2> contents;
434   if (!instance_name.empty()) {
435     contents.push_back(absl::StrFormat("instance_name=%s", instance_name));
436   }
437   if (!certificate_name.empty()) {
438     contents.push_back(
439         absl::StrFormat("certificate_name=%s", certificate_name));
440   }
441   return absl::StrCat("{", absl::StrJoin(contents, ", "), "}");
442 }
443 
Empty() const444 bool XdsApi::CommonTlsContext::CertificateProviderInstance::Empty() const {
445   return instance_name.empty() && certificate_name.empty();
446 }
447 
448 //
449 // XdsApi::CommonTlsContext::CombinedCertificateValidationContext
450 //
451 
452 std::string
ToString() const453 XdsApi::CommonTlsContext::CombinedCertificateValidationContext::ToString()
454     const {
455   absl::InlinedVector<std::string, 2> contents;
456   if (!default_validation_context.Empty()) {
457     contents.push_back(absl::StrFormat("default_validation_context=%s",
458                                        default_validation_context.ToString()));
459   }
460   if (!validation_context_certificate_provider_instance.Empty()) {
461     contents.push_back(absl::StrFormat(
462         "validation_context_certificate_provider_instance=%s",
463         validation_context_certificate_provider_instance.ToString()));
464   }
465   return absl::StrCat("{", absl::StrJoin(contents, ", "), "}");
466 }
467 
Empty() const468 bool XdsApi::CommonTlsContext::CombinedCertificateValidationContext::Empty()
469     const {
470   return default_validation_context.Empty() &&
471          validation_context_certificate_provider_instance.Empty();
472 }
473 
474 //
475 // XdsApi::CommonTlsContext
476 //
477 
ToString() const478 std::string XdsApi::CommonTlsContext::ToString() const {
479   absl::InlinedVector<std::string, 2> contents;
480   if (!tls_certificate_certificate_provider_instance.Empty()) {
481     contents.push_back(absl::StrFormat(
482         "tls_certificate_certificate_provider_instance=%s",
483         tls_certificate_certificate_provider_instance.ToString()));
484   }
485   if (!combined_validation_context.Empty()) {
486     contents.push_back(absl::StrFormat("combined_validation_context=%s",
487                                        combined_validation_context.ToString()));
488   }
489   return absl::StrCat("{", absl::StrJoin(contents, ", "), "}");
490 }
491 
Empty() const492 bool XdsApi::CommonTlsContext::Empty() const {
493   return tls_certificate_certificate_provider_instance.Empty() &&
494          combined_validation_context.Empty();
495 }
496 
497 //
498 // XdsApi::DownstreamTlsContext
499 //
500 
ToString() const501 std::string XdsApi::DownstreamTlsContext::ToString() const {
502   return absl::StrFormat("common_tls_context=%s, require_client_certificate=%s",
503                          common_tls_context.ToString(),
504                          require_client_certificate ? "true" : "false");
505 }
506 
Empty() const507 bool XdsApi::DownstreamTlsContext::Empty() const {
508   return common_tls_context.Empty();
509 }
510 
511 //
512 // XdsApi::LdsUpdate::HttpConnectionManager
513 //
514 
ToString() const515 std::string XdsApi::LdsUpdate::HttpConnectionManager::ToString() const {
516   absl::InlinedVector<std::string, 4> contents;
517   contents.push_back(absl::StrFormat(
518       "route_config_name=%s",
519       !route_config_name.empty() ? route_config_name.c_str() : "<inlined>"));
520   contents.push_back(absl::StrFormat("http_max_stream_duration=%s",
521                                      http_max_stream_duration.ToString()));
522   if (rds_update.has_value()) {
523     contents.push_back(
524         absl::StrFormat("rds_update=%s", rds_update->ToString()));
525   }
526   if (!http_filters.empty()) {
527     std::vector<std::string> filter_strings;
528     for (const auto& http_filter : http_filters) {
529       filter_strings.push_back(http_filter.ToString());
530     }
531     contents.push_back(absl::StrCat("http_filters=[",
532                                     absl::StrJoin(filter_strings, ", "), "]"));
533   }
534   return absl::StrCat("{", absl::StrJoin(contents, ", "), "}");
535 }
536 
537 //
538 // XdsApi::LdsUpdate::HttpFilter
539 //
540 
ToString() const541 std::string XdsApi::LdsUpdate::HttpConnectionManager::HttpFilter::ToString()
542     const {
543   return absl::StrCat("{name=", name, ", config=", config.ToString(), "}");
544 }
545 
546 //
547 // XdsApi::LdsUpdate::FilterChainData
548 //
549 
ToString() const550 std::string XdsApi::LdsUpdate::FilterChainData::ToString() const {
551   return absl::StrCat(
552       "{downstream_tls_context=", downstream_tls_context.ToString(),
553       " http_connection_manager=", http_connection_manager.ToString(), "}");
554 }
555 
556 //
557 // XdsApi::LdsUpdate::FilterChainMap::CidrRange
558 //
559 
ToString() const560 std::string XdsApi::LdsUpdate::FilterChainMap::CidrRange::ToString() const {
561   return absl::StrCat(
562       "{address_prefix=", grpc_sockaddr_to_string(&address, false),
563       ", prefix_len=", prefix_len, "}");
564 }
565 
566 //
567 // FilterChain
568 //
569 
570 struct FilterChain {
571   struct FilterChainMatch {
572     uint32_t destination_port = 0;
573     std::vector<XdsApi::LdsUpdate::FilterChainMap::CidrRange> prefix_ranges;
574     XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceType source_type =
575         XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceType::kAny;
576     std::vector<XdsApi::LdsUpdate::FilterChainMap::CidrRange>
577         source_prefix_ranges;
578     std::vector<uint32_t> source_ports;
579     std::vector<std::string> server_names;
580     std::string transport_protocol;
581     std::vector<std::string> application_protocols;
582 
583     std::string ToString() const;
584   } filter_chain_match;
585 
586   std::shared_ptr<XdsApi::LdsUpdate::FilterChainData> filter_chain_data;
587 };
588 
ToString() const589 std::string FilterChain::FilterChainMatch::ToString() const {
590   absl::InlinedVector<std::string, 8> contents;
591   if (destination_port != 0) {
592     contents.push_back(absl::StrCat("destination_port=", destination_port));
593   }
594   if (!prefix_ranges.empty()) {
595     std::vector<std::string> prefix_ranges_content;
596     for (const auto& range : prefix_ranges) {
597       prefix_ranges_content.push_back(range.ToString());
598     }
599     contents.push_back(absl::StrCat(
600         "prefix_ranges={", absl::StrJoin(prefix_ranges_content, ", "), "}"));
601   }
602   if (source_type == XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceType::
603                          kSameIpOrLoopback) {
604     contents.push_back("source_type=SAME_IP_OR_LOOPBACK");
605   } else if (source_type == XdsApi::LdsUpdate::FilterChainMap::
606                                 ConnectionSourceType::kExternal) {
607     contents.push_back("source_type=EXTERNAL");
608   }
609   if (!source_prefix_ranges.empty()) {
610     std::vector<std::string> source_prefix_ranges_content;
611     for (const auto& range : source_prefix_ranges) {
612       source_prefix_ranges_content.push_back(range.ToString());
613     }
614     contents.push_back(
615         absl::StrCat("source_prefix_ranges={",
616                      absl::StrJoin(source_prefix_ranges_content, ", "), "}"));
617   }
618   if (!source_ports.empty()) {
619     contents.push_back(
620         absl::StrCat("source_ports={", absl::StrJoin(source_ports, ", "), "}"));
621   }
622   if (!server_names.empty()) {
623     contents.push_back(
624         absl::StrCat("server_names={", absl::StrJoin(server_names, ", "), "}"));
625   }
626   if (!transport_protocol.empty()) {
627     contents.push_back(absl::StrCat("transport_protocol=", transport_protocol));
628   }
629   if (!application_protocols.empty()) {
630     contents.push_back(absl::StrCat("application_protocols={",
631                                     absl::StrJoin(application_protocols, ", "),
632                                     "}"));
633   }
634   return absl::StrCat("{", absl::StrJoin(contents, ", "), "}");
635 }
636 
637 //
638 // XdsApi::LdsUpdate::FilterChainMap
639 //
640 
ToString() const641 std::string XdsApi::LdsUpdate::FilterChainMap::ToString() const {
642   std::vector<std::string> contents;
643   for (const auto& destination_ip : destination_ip_vector) {
644     for (int source_type = 0; source_type < 3; ++source_type) {
645       for (const auto& source_ip :
646            destination_ip.source_types_array[source_type]) {
647         for (const auto& source_port_pair : source_ip.ports_map) {
648           FilterChain::FilterChainMatch filter_chain_match;
649           if (destination_ip.prefix_range.has_value()) {
650             filter_chain_match.prefix_ranges.push_back(
651                 *destination_ip.prefix_range);
652           }
653           filter_chain_match.source_type = static_cast<
654               XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceType>(
655               source_type);
656           if (source_ip.prefix_range.has_value()) {
657             filter_chain_match.source_prefix_ranges.push_back(
658                 *source_ip.prefix_range);
659           }
660           if (source_port_pair.first != 0) {
661             filter_chain_match.source_ports.push_back(source_port_pair.first);
662           }
663           contents.push_back(absl::StrCat(
664               "{filter_chain_match=", filter_chain_match.ToString(),
665               ", filter_chain=", source_port_pair.second.data->ToString(),
666               "}"));
667         }
668       }
669     }
670   }
671   return absl::StrCat("{", absl::StrJoin(contents, ", "), "}");
672 }
673 
674 //
675 // XdsApi::LdsUpdate
676 //
677 
ToString() const678 std::string XdsApi::LdsUpdate::ToString() const {
679   absl::InlinedVector<std::string, 4> contents;
680   if (type == ListenerType::kTcpListener) {
681     contents.push_back(absl::StrCat("address=", address));
682     contents.push_back(
683         absl::StrCat("filter_chain_map=", filter_chain_map.ToString()));
684     if (default_filter_chain.has_value()) {
685       contents.push_back(absl::StrCat("default_filter_chain=",
686                                       default_filter_chain->ToString()));
687     }
688   } else if (type == ListenerType::kHttpApiListener) {
689     contents.push_back(absl::StrFormat("http_connection_manager=%s",
690                                        http_connection_manager.ToString()));
691   }
692   return absl::StrCat("{", absl::StrJoin(contents, ", "), "}");
693 }
694 
695 //
696 // XdsApi::CdsUpdate
697 //
698 
ToString() const699 std::string XdsApi::CdsUpdate::ToString() const {
700   absl::InlinedVector<std::string, 4> contents;
701   if (!eds_service_name.empty()) {
702     contents.push_back(
703         absl::StrFormat("eds_service_name=%s", eds_service_name));
704   }
705   if (!common_tls_context.Empty()) {
706     contents.push_back(absl::StrFormat("common_tls_context=%s",
707                                        common_tls_context.ToString()));
708   }
709   if (lrs_load_reporting_server_name.has_value()) {
710     contents.push_back(absl::StrFormat("lrs_load_reporting_server_name=%s",
711                                        lrs_load_reporting_server_name.value()));
712   }
713   contents.push_back(
714       absl::StrFormat("max_concurrent_requests=%d", max_concurrent_requests));
715   return absl::StrCat("{", absl::StrJoin(contents, ", "), "}");
716 }
717 
718 //
719 // XdsApi::EdsUpdate
720 //
721 
ToString() const722 std::string XdsApi::EdsUpdate::Priority::Locality::ToString() const {
723   std::vector<std::string> endpoint_strings;
724   for (const ServerAddress& endpoint : endpoints) {
725     endpoint_strings.emplace_back(endpoint.ToString());
726   }
727   return absl::StrCat("{name=", name->AsHumanReadableString(),
728                       ", lb_weight=", lb_weight, ", endpoints=[",
729                       absl::StrJoin(endpoint_strings, ", "), "]}");
730 }
731 
operator ==(const Priority & other) const732 bool XdsApi::EdsUpdate::Priority::operator==(const Priority& other) const {
733   if (localities.size() != other.localities.size()) return false;
734   auto it1 = localities.begin();
735   auto it2 = other.localities.begin();
736   while (it1 != localities.end()) {
737     if (*it1->first != *it2->first) return false;
738     if (it1->second != it2->second) return false;
739     ++it1;
740     ++it2;
741   }
742   return true;
743 }
744 
ToString() const745 std::string XdsApi::EdsUpdate::Priority::ToString() const {
746   std::vector<std::string> locality_strings;
747   for (const auto& p : localities) {
748     locality_strings.emplace_back(p.second.ToString());
749   }
750   return absl::StrCat("[", absl::StrJoin(locality_strings, ", "), "]");
751 }
752 
ShouldDrop(const std::string ** category_name) const753 bool XdsApi::EdsUpdate::DropConfig::ShouldDrop(
754     const std::string** category_name) const {
755   for (size_t i = 0; i < drop_category_list_.size(); ++i) {
756     const auto& drop_category = drop_category_list_[i];
757     // Generate a random number in [0, 1000000).
758     const uint32_t random = static_cast<uint32_t>(rand()) % 1000000;
759     if (random < drop_category.parts_per_million) {
760       *category_name = &drop_category.name;
761       return true;
762     }
763   }
764   return false;
765 }
766 
ToString() const767 std::string XdsApi::EdsUpdate::DropConfig::ToString() const {
768   std::vector<std::string> category_strings;
769   for (const DropCategory& category : drop_category_list_) {
770     category_strings.emplace_back(
771         absl::StrCat(category.name, "=", category.parts_per_million));
772   }
773   return absl::StrCat("{[", absl::StrJoin(category_strings, ", "),
774                       "], drop_all=", drop_all_, "}");
775 }
776 
ToString() const777 std::string XdsApi::EdsUpdate::ToString() const {
778   std::vector<std::string> priority_strings;
779   for (size_t i = 0; i < priorities.size(); ++i) {
780     const Priority& priority = priorities[i];
781     priority_strings.emplace_back(
782         absl::StrCat("priority ", i, ": ", priority.ToString()));
783   }
784   return absl::StrCat("priorities=[", absl::StrJoin(priority_strings, ", "),
785                       "], drop_config=", drop_config->ToString());
786 }
787 
788 //
789 // XdsApi
790 //
791 
792 const char* XdsApi::kLdsTypeUrl =
793     "type.googleapis.com/envoy.config.listener.v3.Listener";
794 const char* XdsApi::kRdsTypeUrl =
795     "type.googleapis.com/envoy.config.route.v3.RouteConfiguration";
796 const char* XdsApi::kCdsTypeUrl =
797     "type.googleapis.com/envoy.config.cluster.v3.Cluster";
798 const char* XdsApi::kEdsTypeUrl =
799     "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment";
800 
801 namespace {
802 
803 const char* kLdsV2TypeUrl = "type.googleapis.com/envoy.api.v2.Listener";
804 const char* kRdsV2TypeUrl =
805     "type.googleapis.com/envoy.api.v2.RouteConfiguration";
806 const char* kCdsV2TypeUrl = "type.googleapis.com/envoy.api.v2.Cluster";
807 const char* kEdsV2TypeUrl =
808     "type.googleapis.com/envoy.api.v2.ClusterLoadAssignment";
809 
IsLds(absl::string_view type_url,bool * is_v2=nullptr)810 bool IsLds(absl::string_view type_url, bool* is_v2 = nullptr) {
811   if (type_url == XdsApi::kLdsTypeUrl) return true;
812   if (type_url == kLdsV2TypeUrl) {
813     if (is_v2 != nullptr) *is_v2 = true;
814     return true;
815   }
816   return false;
817 }
818 
IsRds(absl::string_view type_url)819 bool IsRds(absl::string_view type_url) {
820   return type_url == XdsApi::kRdsTypeUrl || type_url == kRdsV2TypeUrl;
821 }
822 
IsCds(absl::string_view type_url)823 bool IsCds(absl::string_view type_url) {
824   return type_url == XdsApi::kCdsTypeUrl || type_url == kCdsV2TypeUrl;
825 }
826 
IsEds(absl::string_view type_url)827 bool IsEds(absl::string_view type_url) {
828   return type_url == XdsApi::kEdsTypeUrl || type_url == kEdsV2TypeUrl;
829 }
830 
831 }  // namespace
832 
XdsApi(XdsClient * client,TraceFlag * tracer,const XdsBootstrap::Node * node)833 XdsApi::XdsApi(XdsClient* client, TraceFlag* tracer,
834                const XdsBootstrap::Node* node)
835     : client_(client),
836       tracer_(tracer),
837       node_(node),
838       build_version_(absl::StrCat("gRPC C-core ", GPR_PLATFORM_STRING, " ",
839                                   grpc_version_string())),
840       user_agent_name_(absl::StrCat("gRPC C-core ", GPR_PLATFORM_STRING)) {
841   // Populate upb symtab with xDS proto messages that we want to print
842   // properly in logs.
843   // Note: This won't actually work properly until upb adds support for
844   // Any fields in textproto printing (internal b/178821188).
845   envoy_config_listener_v3_Listener_getmsgdef(symtab_.ptr());
846   envoy_config_route_v3_RouteConfiguration_getmsgdef(symtab_.ptr());
847   envoy_config_cluster_v3_Cluster_getmsgdef(symtab_.ptr());
848   envoy_extensions_clusters_aggregate_v3_ClusterConfig_getmsgdef(symtab_.ptr());
849   envoy_config_cluster_v3_Cluster_getmsgdef(symtab_.ptr());
850   envoy_config_endpoint_v3_ClusterLoadAssignment_getmsgdef(symtab_.ptr());
851   envoy_extensions_transport_sockets_tls_v3_UpstreamTlsContext_getmsgdef(
852       symtab_.ptr());
853   envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_getmsgdef(
854       symtab_.ptr());
855   // Load HTTP filter proto messages into the upb symtab.
856   XdsHttpFilterRegistry::PopulateSymtab(symtab_.ptr());
857 }
858 
859 namespace {
860 
861 struct EncodingContext {
862   XdsClient* client;
863   TraceFlag* tracer;
864   upb_symtab* symtab;
865   upb_arena* arena;
866   bool use_v3;
867 };
868 
869 // Works for both std::string and absl::string_view.
870 template <typename T>
StdStringToUpbString(const T & str)871 inline upb_strview StdStringToUpbString(const T& str) {
872   return upb_strview_make(str.data(), str.size());
873 }
874 
875 void PopulateMetadataValue(const EncodingContext& context,
876                            google_protobuf_Value* value_pb, const Json& value);
877 
PopulateListValue(const EncodingContext & context,google_protobuf_ListValue * list_value,const Json::Array & values)878 void PopulateListValue(const EncodingContext& context,
879                        google_protobuf_ListValue* list_value,
880                        const Json::Array& values) {
881   for (const auto& value : values) {
882     auto* value_pb =
883         google_protobuf_ListValue_add_values(list_value, context.arena);
884     PopulateMetadataValue(context, value_pb, value);
885   }
886 }
887 
PopulateMetadata(const EncodingContext & context,google_protobuf_Struct * metadata_pb,const Json::Object & metadata)888 void PopulateMetadata(const EncodingContext& context,
889                       google_protobuf_Struct* metadata_pb,
890                       const Json::Object& metadata) {
891   for (const auto& p : metadata) {
892     google_protobuf_Value* value = google_protobuf_Value_new(context.arena);
893     PopulateMetadataValue(context, value, p.second);
894     google_protobuf_Struct_fields_set(
895         metadata_pb, StdStringToUpbString(p.first), value, context.arena);
896   }
897 }
898 
PopulateMetadataValue(const EncodingContext & context,google_protobuf_Value * value_pb,const Json & value)899 void PopulateMetadataValue(const EncodingContext& context,
900                            google_protobuf_Value* value_pb, const Json& value) {
901   switch (value.type()) {
902     case Json::Type::JSON_NULL:
903       google_protobuf_Value_set_null_value(value_pb, 0);
904       break;
905     case Json::Type::NUMBER:
906       google_protobuf_Value_set_number_value(
907           value_pb, strtod(value.string_value().c_str(), nullptr));
908       break;
909     case Json::Type::STRING:
910       google_protobuf_Value_set_string_value(
911           value_pb, StdStringToUpbString(value.string_value()));
912       break;
913     case Json::Type::JSON_TRUE:
914       google_protobuf_Value_set_bool_value(value_pb, true);
915       break;
916     case Json::Type::JSON_FALSE:
917       google_protobuf_Value_set_bool_value(value_pb, false);
918       break;
919     case Json::Type::OBJECT: {
920       google_protobuf_Struct* struct_value =
921           google_protobuf_Value_mutable_struct_value(value_pb, context.arena);
922       PopulateMetadata(context, struct_value, value.object_value());
923       break;
924     }
925     case Json::Type::ARRAY: {
926       google_protobuf_ListValue* list_value =
927           google_protobuf_Value_mutable_list_value(value_pb, context.arena);
928       PopulateListValue(context, list_value, value.array_value());
929       break;
930     }
931   }
932 }
933 
934 // Helper functions to manually do protobuf string encoding, so that we
935 // can populate the node build_version field that was removed in v3.
EncodeVarint(uint64_t val)936 std::string EncodeVarint(uint64_t val) {
937   std::string data;
938   do {
939     uint8_t byte = val & 0x7fU;
940     val >>= 7;
941     if (val) byte |= 0x80U;
942     data += byte;
943   } while (val);
944   return data;
945 }
EncodeTag(uint32_t field_number,uint8_t wire_type)946 std::string EncodeTag(uint32_t field_number, uint8_t wire_type) {
947   return EncodeVarint((field_number << 3) | wire_type);
948 }
EncodeStringField(uint32_t field_number,const std::string & str)949 std::string EncodeStringField(uint32_t field_number, const std::string& str) {
950   static const uint8_t kDelimitedWireType = 2;
951   return EncodeTag(field_number, kDelimitedWireType) +
952          EncodeVarint(str.size()) + str;
953 }
954 
PopulateBuildVersion(const EncodingContext & context,envoy_config_core_v3_Node * node_msg,const std::string & build_version)955 void PopulateBuildVersion(const EncodingContext& context,
956                           envoy_config_core_v3_Node* node_msg,
957                           const std::string& build_version) {
958   std::string encoded_build_version = EncodeStringField(5, build_version);
959   // TODO(roth): This should use upb_msg_addunknown(), but that API is
960   // broken in the current version of upb, so we're using the internal
961   // API for now.  Change this once we upgrade to a version of upb that
962   // fixes this bug.
963   _upb_msg_addunknown(node_msg, encoded_build_version.data(),
964                       encoded_build_version.size(), context.arena);
965 }
966 
PopulateNode(const EncodingContext & context,const XdsBootstrap::Node * node,const std::string & build_version,const std::string & user_agent_name,envoy_config_core_v3_Node * node_msg)967 void PopulateNode(const EncodingContext& context,
968                   const XdsBootstrap::Node* node,
969                   const std::string& build_version,
970                   const std::string& user_agent_name,
971                   envoy_config_core_v3_Node* node_msg) {
972   if (node != nullptr) {
973     if (!node->id.empty()) {
974       envoy_config_core_v3_Node_set_id(node_msg,
975                                        StdStringToUpbString(node->id));
976     }
977     if (!node->cluster.empty()) {
978       envoy_config_core_v3_Node_set_cluster(
979           node_msg, StdStringToUpbString(node->cluster));
980     }
981     if (!node->metadata.object_value().empty()) {
982       google_protobuf_Struct* metadata =
983           envoy_config_core_v3_Node_mutable_metadata(node_msg, context.arena);
984       PopulateMetadata(context, metadata, node->metadata.object_value());
985     }
986     if (!node->locality_region.empty() || !node->locality_zone.empty() ||
987         !node->locality_sub_zone.empty()) {
988       envoy_config_core_v3_Locality* locality =
989           envoy_config_core_v3_Node_mutable_locality(node_msg, context.arena);
990       if (!node->locality_region.empty()) {
991         envoy_config_core_v3_Locality_set_region(
992             locality, StdStringToUpbString(node->locality_region));
993       }
994       if (!node->locality_zone.empty()) {
995         envoy_config_core_v3_Locality_set_zone(
996             locality, StdStringToUpbString(node->locality_zone));
997       }
998       if (!node->locality_sub_zone.empty()) {
999         envoy_config_core_v3_Locality_set_sub_zone(
1000             locality, StdStringToUpbString(node->locality_sub_zone));
1001       }
1002     }
1003   }
1004   if (!context.use_v3) {
1005     PopulateBuildVersion(context, node_msg, build_version);
1006   }
1007   envoy_config_core_v3_Node_set_user_agent_name(
1008       node_msg, StdStringToUpbString(user_agent_name));
1009   envoy_config_core_v3_Node_set_user_agent_version(
1010       node_msg, upb_strview_makez(grpc_version_string()));
1011   envoy_config_core_v3_Node_add_client_features(
1012       node_msg, upb_strview_makez("envoy.lb.does_not_support_overprovisioning"),
1013       context.arena);
1014 }
1015 
UpbStringToAbsl(const upb_strview & str)1016 inline absl::string_view UpbStringToAbsl(const upb_strview& str) {
1017   return absl::string_view(str.data, str.size);
1018 }
1019 
UpbStringToStdString(const upb_strview & str)1020 inline std::string UpbStringToStdString(const upb_strview& str) {
1021   return std::string(str.data, str.size);
1022 }
1023 
MaybeLogDiscoveryRequest(const EncodingContext & context,const envoy_service_discovery_v3_DiscoveryRequest * request)1024 void MaybeLogDiscoveryRequest(
1025     const EncodingContext& context,
1026     const envoy_service_discovery_v3_DiscoveryRequest* request) {
1027   if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) &&
1028       gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
1029     const upb_msgdef* msg_type =
1030         envoy_service_discovery_v3_DiscoveryRequest_getmsgdef(context.symtab);
1031     char buf[10240];
1032     upb_text_encode(request, msg_type, nullptr, 0, buf, sizeof(buf));
1033     gpr_log(GPR_DEBUG, "[xds_client %p] constructed ADS request: %s",
1034             context.client, buf);
1035   }
1036 }
1037 
SerializeDiscoveryRequest(const EncodingContext & context,envoy_service_discovery_v3_DiscoveryRequest * request)1038 grpc_slice SerializeDiscoveryRequest(
1039     const EncodingContext& context,
1040     envoy_service_discovery_v3_DiscoveryRequest* request) {
1041   size_t output_length;
1042   char* output = envoy_service_discovery_v3_DiscoveryRequest_serialize(
1043       request, context.arena, &output_length);
1044   return grpc_slice_from_copied_buffer(output, output_length);
1045 }
1046 
TypeUrlExternalToInternal(bool use_v3,const std::string & type_url)1047 absl::string_view TypeUrlExternalToInternal(bool use_v3,
1048                                             const std::string& type_url) {
1049   if (!use_v3) {
1050     if (type_url == XdsApi::kLdsTypeUrl) {
1051       return kLdsV2TypeUrl;
1052     }
1053     if (type_url == XdsApi::kRdsTypeUrl) {
1054       return kRdsV2TypeUrl;
1055     }
1056     if (type_url == XdsApi::kCdsTypeUrl) {
1057       return kCdsV2TypeUrl;
1058     }
1059     if (type_url == XdsApi::kEdsTypeUrl) {
1060       return kEdsV2TypeUrl;
1061     }
1062   }
1063   return type_url;
1064 }
1065 
1066 }  // namespace
1067 
CreateAdsRequest(const XdsBootstrap::XdsServer & server,const std::string & type_url,const std::set<absl::string_view> & resource_names,const std::string & version,const std::string & nonce,grpc_error_handle error,bool populate_node)1068 grpc_slice XdsApi::CreateAdsRequest(
1069     const XdsBootstrap::XdsServer& server, const std::string& type_url,
1070     const std::set<absl::string_view>& resource_names,
1071     const std::string& version, const std::string& nonce,
1072     grpc_error_handle error, bool populate_node) {
1073   upb::Arena arena;
1074   const EncodingContext context = {client_, tracer_, symtab_.ptr(), arena.ptr(),
1075                                    server.ShouldUseV3()};
1076   // Create a request.
1077   envoy_service_discovery_v3_DiscoveryRequest* request =
1078       envoy_service_discovery_v3_DiscoveryRequest_new(arena.ptr());
1079   // Set type_url.
1080   absl::string_view real_type_url =
1081       TypeUrlExternalToInternal(server.ShouldUseV3(), type_url);
1082   envoy_service_discovery_v3_DiscoveryRequest_set_type_url(
1083       request, StdStringToUpbString(real_type_url));
1084   // Set version_info.
1085   if (!version.empty()) {
1086     envoy_service_discovery_v3_DiscoveryRequest_set_version_info(
1087         request, StdStringToUpbString(version));
1088   }
1089   // Set nonce.
1090   if (!nonce.empty()) {
1091     envoy_service_discovery_v3_DiscoveryRequest_set_response_nonce(
1092         request, StdStringToUpbString(nonce));
1093   }
1094   // Set error_detail if it's a NACK.
1095   std::string error_string_storage;
1096   if (error != GRPC_ERROR_NONE) {
1097     google_rpc_Status* error_detail =
1098         envoy_service_discovery_v3_DiscoveryRequest_mutable_error_detail(
1099             request, arena.ptr());
1100     // Hard-code INVALID_ARGUMENT as the status code.
1101     // TODO(roth): If at some point we decide we care about this value,
1102     // we could attach a status code to the individual errors where we
1103     // generate them in the parsing code, and then use that here.
1104     google_rpc_Status_set_code(error_detail, GRPC_STATUS_INVALID_ARGUMENT);
1105     // Error description comes from the error that was passed in.
1106     error_string_storage = grpc_error_std_string(error);
1107     upb_strview error_description = StdStringToUpbString(error_string_storage);
1108     google_rpc_Status_set_message(error_detail, error_description);
1109     GRPC_ERROR_UNREF(error);
1110   }
1111   // Populate node.
1112   if (populate_node) {
1113     envoy_config_core_v3_Node* node_msg =
1114         envoy_service_discovery_v3_DiscoveryRequest_mutable_node(request,
1115                                                                  arena.ptr());
1116     PopulateNode(context, node_, build_version_, user_agent_name_, node_msg);
1117   }
1118   // Add resource_names.
1119   for (const auto& resource_name : resource_names) {
1120     envoy_service_discovery_v3_DiscoveryRequest_add_resource_names(
1121         request, StdStringToUpbString(resource_name), arena.ptr());
1122   }
1123   MaybeLogDiscoveryRequest(context, request);
1124   return SerializeDiscoveryRequest(context, request);
1125 }
1126 
1127 namespace {
1128 
MaybeLogDiscoveryResponse(const EncodingContext & context,const envoy_service_discovery_v3_DiscoveryResponse * response)1129 void MaybeLogDiscoveryResponse(
1130     const EncodingContext& context,
1131     const envoy_service_discovery_v3_DiscoveryResponse* response) {
1132   if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) &&
1133       gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
1134     const upb_msgdef* msg_type =
1135         envoy_service_discovery_v3_DiscoveryResponse_getmsgdef(context.symtab);
1136     char buf[10240];
1137     upb_text_encode(response, msg_type, nullptr, 0, buf, sizeof(buf));
1138     gpr_log(GPR_DEBUG, "[xds_client %p] received response: %s", context.client,
1139             buf);
1140   }
1141 }
1142 
MaybeLogHttpConnectionManager(const EncodingContext & context,const envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager * http_connection_manager_config)1143 void MaybeLogHttpConnectionManager(
1144     const EncodingContext& context,
1145     const envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager*
1146         http_connection_manager_config) {
1147   if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) &&
1148       gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
1149     const upb_msgdef* msg_type =
1150         envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_getmsgdef(
1151             context.symtab);
1152     char buf[10240];
1153     upb_text_encode(http_connection_manager_config, msg_type, nullptr, 0, buf,
1154                     sizeof(buf));
1155     gpr_log(GPR_DEBUG, "[xds_client %p] HttpConnectionManager: %s",
1156             context.client, buf);
1157   }
1158 }
1159 
MaybeLogRouteConfiguration(const EncodingContext & context,const envoy_config_route_v3_RouteConfiguration * route_config)1160 void MaybeLogRouteConfiguration(
1161     const EncodingContext& context,
1162     const envoy_config_route_v3_RouteConfiguration* route_config) {
1163   if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) &&
1164       gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
1165     const upb_msgdef* msg_type =
1166         envoy_config_route_v3_RouteConfiguration_getmsgdef(context.symtab);
1167     char buf[10240];
1168     upb_text_encode(route_config, msg_type, nullptr, 0, buf, sizeof(buf));
1169     gpr_log(GPR_DEBUG, "[xds_client %p] RouteConfiguration: %s", context.client,
1170             buf);
1171   }
1172 }
1173 
MaybeLogCluster(const EncodingContext & context,const envoy_config_cluster_v3_Cluster * cluster)1174 void MaybeLogCluster(const EncodingContext& context,
1175                      const envoy_config_cluster_v3_Cluster* cluster) {
1176   if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) &&
1177       gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
1178     const upb_msgdef* msg_type =
1179         envoy_config_cluster_v3_Cluster_getmsgdef(context.symtab);
1180     char buf[10240];
1181     upb_text_encode(cluster, msg_type, nullptr, 0, buf, sizeof(buf));
1182     gpr_log(GPR_DEBUG, "[xds_client %p] Cluster: %s", context.client, buf);
1183   }
1184 }
1185 
MaybeLogClusterLoadAssignment(const EncodingContext & context,const envoy_config_endpoint_v3_ClusterLoadAssignment * cla)1186 void MaybeLogClusterLoadAssignment(
1187     const EncodingContext& context,
1188     const envoy_config_endpoint_v3_ClusterLoadAssignment* cla) {
1189   if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) &&
1190       gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
1191     const upb_msgdef* msg_type =
1192         envoy_config_endpoint_v3_ClusterLoadAssignment_getmsgdef(
1193             context.symtab);
1194     char buf[10240];
1195     upb_text_encode(cla, msg_type, nullptr, 0, buf, sizeof(buf));
1196     gpr_log(GPR_DEBUG, "[xds_client %p] ClusterLoadAssignment: %s",
1197             context.client, buf);
1198   }
1199 }
1200 
RoutePathMatchParse(const envoy_config_route_v3_RouteMatch * match,XdsApi::Route * route,bool * ignore_route)1201 grpc_error_handle RoutePathMatchParse(
1202     const envoy_config_route_v3_RouteMatch* match, XdsApi::Route* route,
1203     bool* ignore_route) {
1204   auto* case_sensitive_ptr =
1205       envoy_config_route_v3_RouteMatch_case_sensitive(match);
1206   bool case_sensitive = true;
1207   if (case_sensitive_ptr != nullptr) {
1208     case_sensitive = google_protobuf_BoolValue_value(case_sensitive_ptr);
1209   }
1210   StringMatcher::Type type;
1211   std::string match_string;
1212   if (envoy_config_route_v3_RouteMatch_has_prefix(match)) {
1213     absl::string_view prefix =
1214         UpbStringToAbsl(envoy_config_route_v3_RouteMatch_prefix(match));
1215     // Empty prefix "" is accepted.
1216     if (!prefix.empty()) {
1217       // Prefix "/" is accepted.
1218       if (prefix[0] != '/') {
1219         // Prefix which does not start with a / will never match anything, so
1220         // ignore this route.
1221         *ignore_route = true;
1222         return GRPC_ERROR_NONE;
1223       }
1224       std::vector<absl::string_view> prefix_elements =
1225           absl::StrSplit(prefix.substr(1), absl::MaxSplits('/', 2));
1226       if (prefix_elements.size() > 2) {
1227         // Prefix cannot have more than 2 slashes.
1228         *ignore_route = true;
1229         return GRPC_ERROR_NONE;
1230       } else if (prefix_elements.size() == 2 && prefix_elements[0].empty()) {
1231         // Prefix contains empty string between the 2 slashes
1232         *ignore_route = true;
1233         return GRPC_ERROR_NONE;
1234       }
1235     }
1236     type = StringMatcher::Type::kPrefix;
1237     match_string = std::string(prefix);
1238   } else if (envoy_config_route_v3_RouteMatch_has_path(match)) {
1239     absl::string_view path =
1240         UpbStringToAbsl(envoy_config_route_v3_RouteMatch_path(match));
1241     if (path.empty()) {
1242       // Path that is empty will never match anything, so ignore this route.
1243       *ignore_route = true;
1244       return GRPC_ERROR_NONE;
1245     }
1246     if (path[0] != '/') {
1247       // Path which does not start with a / will never match anything, so
1248       // ignore this route.
1249       *ignore_route = true;
1250       return GRPC_ERROR_NONE;
1251     }
1252     std::vector<absl::string_view> path_elements =
1253         absl::StrSplit(path.substr(1), absl::MaxSplits('/', 2));
1254     if (path_elements.size() != 2) {
1255       // Path not in the required format of /service/method will never match
1256       // anything, so ignore this route.
1257       *ignore_route = true;
1258       return GRPC_ERROR_NONE;
1259     } else if (path_elements[0].empty()) {
1260       // Path contains empty service name will never match anything, so ignore
1261       // this route.
1262       *ignore_route = true;
1263       return GRPC_ERROR_NONE;
1264     } else if (path_elements[1].empty()) {
1265       // Path contains empty method name will never match anything, so ignore
1266       // this route.
1267       *ignore_route = true;
1268       return GRPC_ERROR_NONE;
1269     }
1270     type = StringMatcher::Type::kExact;
1271     match_string = std::string(path);
1272   } else if (envoy_config_route_v3_RouteMatch_has_safe_regex(match)) {
1273     const envoy_type_matcher_v3_RegexMatcher* regex_matcher =
1274         envoy_config_route_v3_RouteMatch_safe_regex(match);
1275     GPR_ASSERT(regex_matcher != nullptr);
1276     type = StringMatcher::Type::kSafeRegex;
1277     match_string = UpbStringToStdString(
1278         envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher));
1279   } else {
1280     return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1281         "Invalid route path specifier specified.");
1282   }
1283   absl::StatusOr<StringMatcher> string_matcher =
1284       StringMatcher::Create(type, match_string, case_sensitive);
1285   if (!string_matcher.ok()) {
1286     return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1287         absl::StrCat("path matcher: ", string_matcher.status().message())
1288             .c_str());
1289     ;
1290   }
1291   route->matchers.path_matcher = std::move(string_matcher.value());
1292   return GRPC_ERROR_NONE;
1293 }
1294 
RouteHeaderMatchersParse(const envoy_config_route_v3_RouteMatch * match,XdsApi::Route * route)1295 grpc_error_handle RouteHeaderMatchersParse(
1296     const envoy_config_route_v3_RouteMatch* match, XdsApi::Route* route) {
1297   size_t size;
1298   const envoy_config_route_v3_HeaderMatcher* const* headers =
1299       envoy_config_route_v3_RouteMatch_headers(match, &size);
1300   for (size_t i = 0; i < size; ++i) {
1301     const envoy_config_route_v3_HeaderMatcher* header = headers[i];
1302     const std::string name =
1303         UpbStringToStdString(envoy_config_route_v3_HeaderMatcher_name(header));
1304     HeaderMatcher::Type type;
1305     std::string match_string;
1306     int64_t range_start = 0;
1307     int64_t range_end = 0;
1308     bool present_match = false;
1309     if (envoy_config_route_v3_HeaderMatcher_has_exact_match(header)) {
1310       type = HeaderMatcher::Type::kExact;
1311       match_string = UpbStringToStdString(
1312           envoy_config_route_v3_HeaderMatcher_exact_match(header));
1313     } else if (envoy_config_route_v3_HeaderMatcher_has_safe_regex_match(
1314                    header)) {
1315       const envoy_type_matcher_v3_RegexMatcher* regex_matcher =
1316           envoy_config_route_v3_HeaderMatcher_safe_regex_match(header);
1317       GPR_ASSERT(regex_matcher != nullptr);
1318       type = HeaderMatcher::Type::kSafeRegex;
1319       match_string = UpbStringToStdString(
1320           envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher));
1321     } else if (envoy_config_route_v3_HeaderMatcher_has_range_match(header)) {
1322       type = HeaderMatcher::Type::kRange;
1323       const envoy_type_v3_Int64Range* range_matcher =
1324           envoy_config_route_v3_HeaderMatcher_range_match(header);
1325       range_start = envoy_type_v3_Int64Range_start(range_matcher);
1326       range_end = envoy_type_v3_Int64Range_end(range_matcher);
1327     } else if (envoy_config_route_v3_HeaderMatcher_has_present_match(header)) {
1328       type = HeaderMatcher::Type::kPresent;
1329       present_match = envoy_config_route_v3_HeaderMatcher_present_match(header);
1330     } else if (envoy_config_route_v3_HeaderMatcher_has_prefix_match(header)) {
1331       type = HeaderMatcher::Type::kPrefix;
1332       match_string = UpbStringToStdString(
1333           envoy_config_route_v3_HeaderMatcher_prefix_match(header));
1334     } else if (envoy_config_route_v3_HeaderMatcher_has_suffix_match(header)) {
1335       type = HeaderMatcher::Type::kSuffix;
1336       match_string = UpbStringToStdString(
1337           envoy_config_route_v3_HeaderMatcher_suffix_match(header));
1338     } else if (envoy_config_route_v3_HeaderMatcher_has_contains_match(header)) {
1339       type = HeaderMatcher::Type::kContains;
1340       match_string = UpbStringToStdString(
1341           envoy_config_route_v3_HeaderMatcher_contains_match(header));
1342     } else {
1343       return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1344           "Invalid route header matcher specified.");
1345     }
1346     bool invert_match =
1347         envoy_config_route_v3_HeaderMatcher_invert_match(header);
1348     absl::StatusOr<HeaderMatcher> header_matcher =
1349         HeaderMatcher::Create(name, type, match_string, range_start, range_end,
1350                               present_match, invert_match);
1351     if (!header_matcher.ok()) {
1352       return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1353           absl::StrCat("header matcher: ", header_matcher.status().message())
1354               .c_str());
1355     }
1356     route->matchers.header_matchers.emplace_back(
1357         std::move(header_matcher.value()));
1358   }
1359   return GRPC_ERROR_NONE;
1360 }
1361 
RouteRuntimeFractionParse(const envoy_config_route_v3_RouteMatch * match,XdsApi::Route * route)1362 grpc_error_handle RouteRuntimeFractionParse(
1363     const envoy_config_route_v3_RouteMatch* match, XdsApi::Route* route) {
1364   const envoy_config_core_v3_RuntimeFractionalPercent* runtime_fraction =
1365       envoy_config_route_v3_RouteMatch_runtime_fraction(match);
1366   if (runtime_fraction != nullptr) {
1367     const envoy_type_v3_FractionalPercent* fraction =
1368         envoy_config_core_v3_RuntimeFractionalPercent_default_value(
1369             runtime_fraction);
1370     if (fraction != nullptr) {
1371       uint32_t numerator = envoy_type_v3_FractionalPercent_numerator(fraction);
1372       const auto denominator =
1373           static_cast<envoy_type_v3_FractionalPercent_DenominatorType>(
1374               envoy_type_v3_FractionalPercent_denominator(fraction));
1375       // Normalize to million.
1376       switch (denominator) {
1377         case envoy_type_v3_FractionalPercent_HUNDRED:
1378           numerator *= 10000;
1379           break;
1380         case envoy_type_v3_FractionalPercent_TEN_THOUSAND:
1381           numerator *= 100;
1382           break;
1383         case envoy_type_v3_FractionalPercent_MILLION:
1384           break;
1385         default:
1386           return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1387               "Unknown denominator type");
1388       }
1389       route->matchers.fraction_per_million = numerator;
1390     }
1391   }
1392   return GRPC_ERROR_NONE;
1393 }
1394 
ExtractHttpFilterTypeName(const EncodingContext & context,const google_protobuf_Any * any,absl::string_view * filter_type)1395 grpc_error_handle ExtractHttpFilterTypeName(const EncodingContext& context,
1396                                             const google_protobuf_Any* any,
1397                                             absl::string_view* filter_type) {
1398   *filter_type = UpbStringToAbsl(google_protobuf_Any_type_url(any));
1399   if (*filter_type == "type.googleapis.com/udpa.type.v1.TypedStruct") {
1400     upb_strview any_value = google_protobuf_Any_value(any);
1401     const auto* typed_struct = udpa_type_v1_TypedStruct_parse(
1402         any_value.data, any_value.size, context.arena);
1403     if (typed_struct == nullptr) {
1404       return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1405           "could not parse TypedStruct from filter config");
1406     }
1407     *filter_type =
1408         UpbStringToAbsl(udpa_type_v1_TypedStruct_type_url(typed_struct));
1409   }
1410   *filter_type = absl::StripPrefix(*filter_type, "type.googleapis.com/");
1411   return GRPC_ERROR_NONE;
1412 }
1413 
1414 template <typename ParentType, typename EntryType>
ParseTypedPerFilterConfig(const EncodingContext & context,const ParentType * parent,const EntryType * (* entry_func)(const ParentType *,size_t *),upb_strview (* key_func)(const EntryType *),const google_protobuf_Any * (* value_func)(const EntryType *),XdsApi::TypedPerFilterConfig * typed_per_filter_config)1415 grpc_error_handle ParseTypedPerFilterConfig(
1416     const EncodingContext& context, const ParentType* parent,
1417     const EntryType* (*entry_func)(const ParentType*, size_t*),
1418     upb_strview (*key_func)(const EntryType*),
1419     const google_protobuf_Any* (*value_func)(const EntryType*),
1420     XdsApi::TypedPerFilterConfig* typed_per_filter_config) {
1421   size_t filter_it = UPB_MAP_BEGIN;
1422   while (true) {
1423     const auto* filter_entry = entry_func(parent, &filter_it);
1424     if (filter_entry == nullptr) break;
1425     absl::string_view key = UpbStringToAbsl(key_func(filter_entry));
1426     if (key.empty()) {
1427       return GRPC_ERROR_CREATE_FROM_STATIC_STRING("empty filter name in map");
1428     }
1429     const google_protobuf_Any* any = value_func(filter_entry);
1430     GPR_ASSERT(any != nullptr);
1431     absl::string_view filter_type =
1432         UpbStringToAbsl(google_protobuf_Any_type_url(any));
1433     if (filter_type.empty()) {
1434       return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1435           absl::StrCat("no filter config specified for filter name ", key)
1436               .c_str());
1437     }
1438     bool is_optional = false;
1439     if (filter_type ==
1440         "type.googleapis.com/envoy.config.route.v3.FilterConfig") {
1441       upb_strview any_value = google_protobuf_Any_value(any);
1442       const auto* filter_config = envoy_config_route_v3_FilterConfig_parse(
1443           any_value.data, any_value.size, context.arena);
1444       if (filter_config == nullptr) {
1445         return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1446             absl::StrCat("could not parse FilterConfig wrapper for ", key)
1447                 .c_str());
1448       }
1449       is_optional =
1450           envoy_config_route_v3_FilterConfig_is_optional(filter_config);
1451       any = envoy_config_route_v3_FilterConfig_config(filter_config);
1452       if (any == nullptr) {
1453         if (is_optional) continue;
1454         return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1455             absl::StrCat("no filter config specified for filter name ", key)
1456                 .c_str());
1457       }
1458     }
1459     grpc_error_handle error =
1460         ExtractHttpFilterTypeName(context, any, &filter_type);
1461     if (error != GRPC_ERROR_NONE) return error;
1462     const XdsHttpFilterImpl* filter_impl =
1463         XdsHttpFilterRegistry::GetFilterForType(filter_type);
1464     if (filter_impl == nullptr) {
1465       if (is_optional) continue;
1466       return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1467           absl::StrCat("no filter registered for config type ", filter_type)
1468               .c_str());
1469     }
1470     absl::StatusOr<XdsHttpFilterImpl::FilterConfig> filter_config =
1471         filter_impl->GenerateFilterConfigOverride(
1472             google_protobuf_Any_value(any), context.arena);
1473     if (!filter_config.ok()) {
1474       return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1475           absl::StrCat("filter config for type ", filter_type,
1476                        " failed to parse: ", filter_config.status().ToString())
1477               .c_str());
1478     }
1479     (*typed_per_filter_config)[std::string(key)] = std::move(*filter_config);
1480   }
1481   return GRPC_ERROR_NONE;
1482 }
1483 
RouteActionParse(const EncodingContext & context,const envoy_config_route_v3_Route * route_msg,XdsApi::Route * route,bool * ignore_route)1484 grpc_error_handle RouteActionParse(const EncodingContext& context,
1485                                    const envoy_config_route_v3_Route* route_msg,
1486                                    XdsApi::Route* route, bool* ignore_route) {
1487   if (!envoy_config_route_v3_Route_has_route(route_msg)) {
1488     return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1489         "No RouteAction found in route.");
1490   }
1491   const envoy_config_route_v3_RouteAction* route_action =
1492       envoy_config_route_v3_Route_route(route_msg);
1493   // Get the cluster or weighted_clusters in the RouteAction.
1494   if (envoy_config_route_v3_RouteAction_has_cluster(route_action)) {
1495     route->cluster_name = UpbStringToStdString(
1496         envoy_config_route_v3_RouteAction_cluster(route_action));
1497     if (route->cluster_name.empty()) {
1498       return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1499           "RouteAction cluster contains empty cluster name.");
1500     }
1501   } else if (envoy_config_route_v3_RouteAction_has_weighted_clusters(
1502                  route_action)) {
1503     const envoy_config_route_v3_WeightedCluster* weighted_cluster =
1504         envoy_config_route_v3_RouteAction_weighted_clusters(route_action);
1505     uint32_t total_weight = 100;
1506     const google_protobuf_UInt32Value* weight =
1507         envoy_config_route_v3_WeightedCluster_total_weight(weighted_cluster);
1508     if (weight != nullptr) {
1509       total_weight = google_protobuf_UInt32Value_value(weight);
1510     }
1511     size_t clusters_size;
1512     const envoy_config_route_v3_WeightedCluster_ClusterWeight* const* clusters =
1513         envoy_config_route_v3_WeightedCluster_clusters(weighted_cluster,
1514                                                        &clusters_size);
1515     uint32_t sum_of_weights = 0;
1516     for (size_t j = 0; j < clusters_size; ++j) {
1517       const envoy_config_route_v3_WeightedCluster_ClusterWeight*
1518           cluster_weight = clusters[j];
1519       XdsApi::Route::ClusterWeight cluster;
1520       cluster.name = UpbStringToStdString(
1521           envoy_config_route_v3_WeightedCluster_ClusterWeight_name(
1522               cluster_weight));
1523       if (cluster.name.empty()) {
1524         return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1525             "RouteAction weighted_cluster cluster contains empty cluster "
1526             "name.");
1527       }
1528       const google_protobuf_UInt32Value* weight =
1529           envoy_config_route_v3_WeightedCluster_ClusterWeight_weight(
1530               cluster_weight);
1531       if (weight == nullptr) {
1532         return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1533             "RouteAction weighted_cluster cluster missing weight");
1534       }
1535       cluster.weight = google_protobuf_UInt32Value_value(weight);
1536       if (cluster.weight == 0) continue;
1537       sum_of_weights += cluster.weight;
1538       if (context.use_v3) {
1539         grpc_error_handle error = ParseTypedPerFilterConfig<
1540             envoy_config_route_v3_WeightedCluster_ClusterWeight,
1541             envoy_config_route_v3_WeightedCluster_ClusterWeight_TypedPerFilterConfigEntry>(
1542             context, cluster_weight,
1543             envoy_config_route_v3_WeightedCluster_ClusterWeight_typed_per_filter_config_next,
1544             envoy_config_route_v3_WeightedCluster_ClusterWeight_TypedPerFilterConfigEntry_key,
1545             envoy_config_route_v3_WeightedCluster_ClusterWeight_TypedPerFilterConfigEntry_value,
1546             &cluster.typed_per_filter_config);
1547         if (error != GRPC_ERROR_NONE) return error;
1548       }
1549       route->weighted_clusters.emplace_back(std::move(cluster));
1550     }
1551     if (total_weight != sum_of_weights) {
1552       return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1553           "RouteAction weighted_cluster has incorrect total weight");
1554     }
1555     if (route->weighted_clusters.empty()) {
1556       return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1557           "RouteAction weighted_cluster has no valid clusters specified.");
1558     }
1559   } else {
1560     // No cluster or weighted_clusters found in RouteAction, ignore this route.
1561     *ignore_route = true;
1562   }
1563   if (!*ignore_route) {
1564     const envoy_config_route_v3_RouteAction_MaxStreamDuration*
1565         max_stream_duration =
1566             envoy_config_route_v3_RouteAction_max_stream_duration(route_action);
1567     if (max_stream_duration != nullptr) {
1568       const google_protobuf_Duration* duration =
1569           envoy_config_route_v3_RouteAction_MaxStreamDuration_grpc_timeout_header_max(
1570               max_stream_duration);
1571       if (duration == nullptr) {
1572         duration =
1573             envoy_config_route_v3_RouteAction_MaxStreamDuration_max_stream_duration(
1574                 max_stream_duration);
1575       }
1576       if (duration != nullptr) {
1577         XdsApi::Duration duration_in_route;
1578         duration_in_route.seconds = google_protobuf_Duration_seconds(duration);
1579         duration_in_route.nanos = google_protobuf_Duration_nanos(duration);
1580         route->max_stream_duration = duration_in_route;
1581       }
1582     }
1583   }
1584   // Get HashPolicy from RouteAction
1585   if (XdsRingHashEnabled()) {
1586     size_t size = 0;
1587     const envoy_config_route_v3_RouteAction_HashPolicy* const* hash_policies =
1588         envoy_config_route_v3_RouteAction_hash_policy(route_action, &size);
1589     for (size_t i = 0; i < size; ++i) {
1590       const envoy_config_route_v3_RouteAction_HashPolicy* hash_policy =
1591           hash_policies[i];
1592       XdsApi::Route::HashPolicy policy;
1593       policy.terminal =
1594           envoy_config_route_v3_RouteAction_HashPolicy_terminal(hash_policy);
1595       const envoy_config_route_v3_RouteAction_HashPolicy_Header* header;
1596       const envoy_config_route_v3_RouteAction_HashPolicy_FilterState*
1597           filter_state;
1598       if ((header = envoy_config_route_v3_RouteAction_HashPolicy_header(
1599                hash_policy)) != nullptr) {
1600         policy.type = XdsApi::Route::HashPolicy::Type::HEADER;
1601         policy.header_name = UpbStringToStdString(
1602             envoy_config_route_v3_RouteAction_HashPolicy_Header_header_name(
1603                 header));
1604         const struct envoy_type_matcher_v3_RegexMatchAndSubstitute*
1605             regex_rewrite =
1606                 envoy_config_route_v3_RouteAction_HashPolicy_Header_regex_rewrite(
1607                     header);
1608         if (regex_rewrite == nullptr) {
1609           gpr_log(
1610               GPR_DEBUG,
1611               "RouteAction HashPolicy contains policy specifier Header with "
1612               "RegexMatchAndSubstitution but Regex is missing");
1613           continue;
1614         }
1615         const envoy_type_matcher_v3_RegexMatcher* regex_matcher =
1616             envoy_type_matcher_v3_RegexMatchAndSubstitute_pattern(
1617                 regex_rewrite);
1618         if (regex_matcher == nullptr) {
1619           gpr_log(
1620               GPR_DEBUG,
1621               "RouteAction HashPolicy contains policy specifier Header with "
1622               "RegexMatchAndSubstitution but RegexMatcher pattern is "
1623               "missing");
1624           continue;
1625         }
1626         RE2::Options options;
1627         policy.regex = absl::make_unique<RE2>(
1628             UpbStringToStdString(
1629                 envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher)),
1630             options);
1631         if (!policy.regex->ok()) {
1632           gpr_log(
1633               GPR_DEBUG,
1634               "RouteAction HashPolicy contains policy specifier Header with "
1635               "RegexMatchAndSubstitution but RegexMatcher pattern does not "
1636               "compile");
1637           continue;
1638         }
1639         policy.regex_substitution = UpbStringToStdString(
1640             envoy_type_matcher_v3_RegexMatchAndSubstitute_substitution(
1641                 regex_rewrite));
1642       } else if ((filter_state =
1643                       envoy_config_route_v3_RouteAction_HashPolicy_filter_state(
1644                           hash_policy)) != nullptr) {
1645         std::string key = UpbStringToStdString(
1646             envoy_config_route_v3_RouteAction_HashPolicy_FilterState_key(
1647                 filter_state));
1648         if (key == "io.grpc.channel_id") {
1649           policy.type = XdsApi::Route::HashPolicy::Type::CHANNEL_ID;
1650         } else {
1651           gpr_log(GPR_DEBUG,
1652                   "RouteAction HashPolicy contains policy specifier "
1653                   "FilterState but "
1654                   "key is not io.grpc.channel_id.");
1655           continue;
1656         }
1657       } else {
1658         gpr_log(
1659             GPR_DEBUG,
1660             "RouteAction HashPolicy contains unsupported policy specifier.");
1661         continue;
1662       }
1663       route->hash_policies.emplace_back(std::move(policy));
1664     }
1665   }
1666   return GRPC_ERROR_NONE;
1667 }
1668 
RouteConfigParse(const EncodingContext & context,const envoy_config_route_v3_RouteConfiguration * route_config,XdsApi::RdsUpdate * rds_update)1669 grpc_error_handle RouteConfigParse(
1670     const EncodingContext& context,
1671     const envoy_config_route_v3_RouteConfiguration* route_config,
1672     XdsApi::RdsUpdate* rds_update) {
1673   MaybeLogRouteConfiguration(context, route_config);
1674   // Get the virtual hosts.
1675   size_t num_virtual_hosts;
1676   const envoy_config_route_v3_VirtualHost* const* virtual_hosts =
1677       envoy_config_route_v3_RouteConfiguration_virtual_hosts(
1678           route_config, &num_virtual_hosts);
1679   for (size_t i = 0; i < num_virtual_hosts; ++i) {
1680     rds_update->virtual_hosts.emplace_back();
1681     XdsApi::RdsUpdate::VirtualHost& vhost = rds_update->virtual_hosts.back();
1682     // Parse domains.
1683     size_t domain_size;
1684     upb_strview const* domains = envoy_config_route_v3_VirtualHost_domains(
1685         virtual_hosts[i], &domain_size);
1686     for (size_t j = 0; j < domain_size; ++j) {
1687       std::string domain_pattern = UpbStringToStdString(domains[j]);
1688       const MatchType match_type = DomainPatternMatchType(domain_pattern);
1689       if (match_type == INVALID_MATCH) {
1690         return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1691             absl::StrCat("Invalid domain pattern \"", domain_pattern, "\".")
1692                 .c_str());
1693       }
1694       vhost.domains.emplace_back(std::move(domain_pattern));
1695     }
1696     if (vhost.domains.empty()) {
1697       return GRPC_ERROR_CREATE_FROM_STATIC_STRING("VirtualHost has no domains");
1698     }
1699     // Parse typed_per_filter_config.
1700     if (context.use_v3) {
1701       grpc_error_handle error = ParseTypedPerFilterConfig<
1702           envoy_config_route_v3_VirtualHost,
1703           envoy_config_route_v3_VirtualHost_TypedPerFilterConfigEntry>(
1704           context, virtual_hosts[i],
1705           envoy_config_route_v3_VirtualHost_typed_per_filter_config_next,
1706           envoy_config_route_v3_VirtualHost_TypedPerFilterConfigEntry_key,
1707           envoy_config_route_v3_VirtualHost_TypedPerFilterConfigEntry_value,
1708           &vhost.typed_per_filter_config);
1709       if (error != GRPC_ERROR_NONE) return error;
1710     }
1711     // Parse routes.
1712     size_t num_routes;
1713     const envoy_config_route_v3_Route* const* routes =
1714         envoy_config_route_v3_VirtualHost_routes(virtual_hosts[i], &num_routes);
1715     if (num_routes < 1) {
1716       return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1717           "No route found in the virtual host.");
1718     }
1719     // Loop over the whole list of routes
1720     for (size_t j = 0; j < num_routes; ++j) {
1721       const envoy_config_route_v3_RouteMatch* match =
1722           envoy_config_route_v3_Route_match(routes[j]);
1723       if (match == nullptr) {
1724         return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Match can't be null.");
1725       }
1726       size_t query_parameters_size;
1727       static_cast<void>(envoy_config_route_v3_RouteMatch_query_parameters(
1728           match, &query_parameters_size));
1729       if (query_parameters_size > 0) {
1730         continue;
1731       }
1732       XdsApi::Route route;
1733       bool ignore_route = false;
1734       grpc_error_handle error =
1735           RoutePathMatchParse(match, &route, &ignore_route);
1736       if (error != GRPC_ERROR_NONE) return error;
1737       if (ignore_route) continue;
1738       error = RouteHeaderMatchersParse(match, &route);
1739       if (error != GRPC_ERROR_NONE) return error;
1740       error = RouteRuntimeFractionParse(match, &route);
1741       if (error != GRPC_ERROR_NONE) return error;
1742       error = RouteActionParse(context, routes[j], &route, &ignore_route);
1743       if (error != GRPC_ERROR_NONE) return error;
1744       if (ignore_route) continue;
1745       if (context.use_v3) {
1746         grpc_error_handle error = ParseTypedPerFilterConfig<
1747             envoy_config_route_v3_Route,
1748             envoy_config_route_v3_Route_TypedPerFilterConfigEntry>(
1749             context, routes[j],
1750             envoy_config_route_v3_Route_typed_per_filter_config_next,
1751             envoy_config_route_v3_Route_TypedPerFilterConfigEntry_key,
1752             envoy_config_route_v3_Route_TypedPerFilterConfigEntry_value,
1753             &route.typed_per_filter_config);
1754         if (error != GRPC_ERROR_NONE) return error;
1755       }
1756       vhost.routes.emplace_back(std::move(route));
1757     }
1758     if (vhost.routes.empty()) {
1759       return GRPC_ERROR_CREATE_FROM_STATIC_STRING("No valid routes specified.");
1760     }
1761   }
1762   return GRPC_ERROR_NONE;
1763 }
1764 
1765 XdsApi::CommonTlsContext::CertificateProviderInstance
CertificateProviderInstanceParse(const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance * certificate_provider_instance_proto)1766 CertificateProviderInstanceParse(
1767     const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance*
1768         certificate_provider_instance_proto) {
1769   return {
1770       UpbStringToStdString(
1771           envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance_instance_name(
1772               certificate_provider_instance_proto)),
1773       UpbStringToStdString(
1774           envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance_certificate_name(
1775               certificate_provider_instance_proto))};
1776 }
1777 
1778 grpc_error_handle CommonTlsContextParse(
1779     const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext*
1780         common_tls_context_proto,
1781     XdsApi::CommonTlsContext* common_tls_context) GRPC_MUST_USE_RESULT;
CommonTlsContextParse(const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext * common_tls_context_proto,XdsApi::CommonTlsContext * common_tls_context)1782 grpc_error_handle CommonTlsContextParse(
1783     const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext*
1784         common_tls_context_proto,
1785     XdsApi::CommonTlsContext* common_tls_context) {
1786   auto* combined_validation_context =
1787       envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_combined_validation_context(
1788           common_tls_context_proto);
1789   if (combined_validation_context != nullptr) {
1790     auto* default_validation_context =
1791         envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CombinedCertificateValidationContext_default_validation_context(
1792             combined_validation_context);
1793     if (default_validation_context != nullptr) {
1794       size_t len = 0;
1795       auto* subject_alt_names_matchers =
1796           envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_match_subject_alt_names(
1797               default_validation_context, &len);
1798       for (size_t i = 0; i < len; ++i) {
1799         StringMatcher::Type type;
1800         std::string matcher;
1801         if (envoy_type_matcher_v3_StringMatcher_has_exact(
1802                 subject_alt_names_matchers[i])) {
1803           type = StringMatcher::Type::kExact;
1804           matcher =
1805               UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_exact(
1806                   subject_alt_names_matchers[i]));
1807         } else if (envoy_type_matcher_v3_StringMatcher_has_prefix(
1808                        subject_alt_names_matchers[i])) {
1809           type = StringMatcher::Type::kPrefix;
1810           matcher =
1811               UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_prefix(
1812                   subject_alt_names_matchers[i]));
1813         } else if (envoy_type_matcher_v3_StringMatcher_has_suffix(
1814                        subject_alt_names_matchers[i])) {
1815           type = StringMatcher::Type::kSuffix;
1816           matcher =
1817               UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_suffix(
1818                   subject_alt_names_matchers[i]));
1819         } else if (envoy_type_matcher_v3_StringMatcher_has_contains(
1820                        subject_alt_names_matchers[i])) {
1821           type = StringMatcher::Type::kContains;
1822           matcher =
1823               UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_contains(
1824                   subject_alt_names_matchers[i]));
1825         } else if (envoy_type_matcher_v3_StringMatcher_has_safe_regex(
1826                        subject_alt_names_matchers[i])) {
1827           type = StringMatcher::Type::kSafeRegex;
1828           auto* regex_matcher = envoy_type_matcher_v3_StringMatcher_safe_regex(
1829               subject_alt_names_matchers[i]);
1830           matcher = UpbStringToStdString(
1831               envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher));
1832         } else {
1833           return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1834               "Invalid StringMatcher specified");
1835         }
1836         bool ignore_case = envoy_type_matcher_v3_StringMatcher_ignore_case(
1837             subject_alt_names_matchers[i]);
1838         absl::StatusOr<StringMatcher> string_matcher =
1839             StringMatcher::Create(type, matcher,
1840                                   /*case_sensitive=*/!ignore_case);
1841         if (!string_matcher.ok()) {
1842           return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1843               absl::StrCat("string matcher: ",
1844                            string_matcher.status().message())
1845                   .c_str());
1846         }
1847         if (type == StringMatcher::Type::kSafeRegex && ignore_case) {
1848           return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1849               "StringMatcher: ignore_case has no effect for SAFE_REGEX.");
1850         }
1851         common_tls_context->combined_validation_context
1852             .default_validation_context.match_subject_alt_names.push_back(
1853                 std::move(string_matcher.value()));
1854       }
1855     }
1856     auto* validation_context_certificate_provider_instance =
1857         envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CombinedCertificateValidationContext_validation_context_certificate_provider_instance(
1858             combined_validation_context);
1859     if (validation_context_certificate_provider_instance != nullptr) {
1860       common_tls_context->combined_validation_context
1861           .validation_context_certificate_provider_instance =
1862           CertificateProviderInstanceParse(
1863               validation_context_certificate_provider_instance);
1864     }
1865   }
1866   auto* tls_certificate_certificate_provider_instance =
1867       envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_tls_certificate_certificate_provider_instance(
1868           common_tls_context_proto);
1869   if (tls_certificate_certificate_provider_instance != nullptr) {
1870     common_tls_context->tls_certificate_certificate_provider_instance =
1871         CertificateProviderInstanceParse(
1872             tls_certificate_certificate_provider_instance);
1873   }
1874   return GRPC_ERROR_NONE;
1875 }
1876 
HttpConnectionManagerParse(bool is_client,const EncodingContext & context,const envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager * http_connection_manager_proto,bool is_v2,XdsApi::LdsUpdate::HttpConnectionManager * http_connection_manager)1877 grpc_error_handle HttpConnectionManagerParse(
1878     bool is_client, const EncodingContext& context,
1879     const envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager*
1880         http_connection_manager_proto,
1881     bool is_v2,
1882     XdsApi::LdsUpdate::HttpConnectionManager* http_connection_manager) {
1883   MaybeLogHttpConnectionManager(context, http_connection_manager_proto);
1884   // Obtain max_stream_duration from Http Protocol Options.
1885   const envoy_config_core_v3_HttpProtocolOptions* options =
1886       envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_common_http_protocol_options(
1887           http_connection_manager_proto);
1888   if (options != nullptr) {
1889     const google_protobuf_Duration* duration =
1890         envoy_config_core_v3_HttpProtocolOptions_max_stream_duration(options);
1891     if (duration != nullptr) {
1892       http_connection_manager->http_max_stream_duration.seconds =
1893           google_protobuf_Duration_seconds(duration);
1894       http_connection_manager->http_max_stream_duration.nanos =
1895           google_protobuf_Duration_nanos(duration);
1896     }
1897   }
1898   // Parse filters.
1899   if (!is_v2) {
1900     size_t num_filters = 0;
1901     const auto* http_filters =
1902         envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_http_filters(
1903             http_connection_manager_proto, &num_filters);
1904     std::set<absl::string_view> names_seen;
1905     for (size_t i = 0; i < num_filters; ++i) {
1906       const auto* http_filter = http_filters[i];
1907       absl::string_view name = UpbStringToAbsl(
1908           envoy_extensions_filters_network_http_connection_manager_v3_HttpFilter_name(
1909               http_filter));
1910       if (name.empty()) {
1911         return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1912             absl::StrCat("empty filter name at index ", i).c_str());
1913       }
1914       if (names_seen.find(name) != names_seen.end()) {
1915         return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1916             absl::StrCat("duplicate HTTP filter name: ", name).c_str());
1917       }
1918       names_seen.insert(name);
1919       const bool is_optional =
1920           envoy_extensions_filters_network_http_connection_manager_v3_HttpFilter_is_optional(
1921               http_filter);
1922       const google_protobuf_Any* any =
1923           envoy_extensions_filters_network_http_connection_manager_v3_HttpFilter_typed_config(
1924               http_filter);
1925       if (any == nullptr) {
1926         if (is_optional) continue;
1927         return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1928             absl::StrCat("no filter config specified for filter name ", name)
1929                 .c_str());
1930       }
1931       absl::string_view filter_type;
1932       grpc_error_handle error =
1933           ExtractHttpFilterTypeName(context, any, &filter_type);
1934       if (error != GRPC_ERROR_NONE) return error;
1935       const XdsHttpFilterImpl* filter_impl =
1936           XdsHttpFilterRegistry::GetFilterForType(filter_type);
1937       if (filter_impl == nullptr) {
1938         if (is_optional) continue;
1939         return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1940             absl::StrCat("no filter registered for config type ", filter_type)
1941                 .c_str());
1942       }
1943       if ((is_client && !filter_impl->IsSupportedOnClients()) ||
1944           (!is_client && !filter_impl->IsSupportedOnServers())) {
1945         if (is_optional) continue;
1946         return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1947             absl::StrFormat("Filter %s is not supported on %s", filter_type,
1948                             is_client ? "clients" : "servers")
1949                 .c_str());
1950       }
1951       absl::StatusOr<XdsHttpFilterImpl::FilterConfig> filter_config =
1952           filter_impl->GenerateFilterConfig(google_protobuf_Any_value(any),
1953                                             context.arena);
1954       if (!filter_config.ok()) {
1955         return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
1956             absl::StrCat(
1957                 "filter config for type ", filter_type,
1958                 " failed to parse: ", filter_config.status().ToString())
1959                 .c_str());
1960       }
1961       http_connection_manager->http_filters.emplace_back(
1962           XdsApi::LdsUpdate::HttpConnectionManager::HttpFilter{
1963               std::string(name), std::move(*filter_config)});
1964     }
1965   } else {
1966     // If using a v2 config, we just hard-code a list containing only the
1967     // router filter without actually looking at the config.  This ensures
1968     // that the right thing happens in the xds resolver without having
1969     // to expose whether the resource we received was v2 or v3.
1970     http_connection_manager->http_filters.emplace_back(
1971         XdsApi::LdsUpdate::HttpConnectionManager::HttpFilter{
1972             "router", {kXdsHttpRouterFilterConfigName, Json()}});
1973   }
1974   if (is_client) {
1975     // Found inlined route_config. Parse it to find the cluster_name.
1976     if (envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_has_route_config(
1977             http_connection_manager_proto)) {
1978       const envoy_config_route_v3_RouteConfiguration* route_config =
1979           envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_route_config(
1980               http_connection_manager_proto);
1981       XdsApi::RdsUpdate rds_update;
1982       grpc_error_handle error =
1983           RouteConfigParse(context, route_config, &rds_update);
1984       if (error != GRPC_ERROR_NONE) return error;
1985       http_connection_manager->rds_update = std::move(rds_update);
1986       return GRPC_ERROR_NONE;
1987     }
1988     // Validate that RDS must be used to get the route_config dynamically.
1989     const envoy_extensions_filters_network_http_connection_manager_v3_Rds* rds =
1990         envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_rds(
1991             http_connection_manager_proto);
1992     if (rds == nullptr) {
1993       return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
1994           "HttpConnectionManager neither has inlined route_config nor RDS.");
1995     }
1996     // Check that the ConfigSource specifies ADS.
1997     const envoy_config_core_v3_ConfigSource* config_source =
1998         envoy_extensions_filters_network_http_connection_manager_v3_Rds_config_source(
1999             rds);
2000     if (config_source == nullptr) {
2001       return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
2002           "HttpConnectionManager missing config_source for RDS.");
2003     }
2004     if (!envoy_config_core_v3_ConfigSource_has_ads(config_source)) {
2005       return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
2006           "HttpConnectionManager ConfigSource for RDS does not specify ADS.");
2007     }
2008     // Get the route_config_name.
2009     http_connection_manager->route_config_name = UpbStringToStdString(
2010         envoy_extensions_filters_network_http_connection_manager_v3_Rds_route_config_name(
2011             rds));
2012   }
2013   return GRPC_ERROR_NONE;
2014 }
2015 
LdsResponseParseClient(const EncodingContext & context,const envoy_config_listener_v3_ApiListener * api_listener,bool is_v2,XdsApi::LdsUpdate * lds_update)2016 grpc_error_handle LdsResponseParseClient(
2017     const EncodingContext& context,
2018     const envoy_config_listener_v3_ApiListener* api_listener, bool is_v2,
2019     XdsApi::LdsUpdate* lds_update) {
2020   lds_update->type = XdsApi::LdsUpdate::ListenerType::kHttpApiListener;
2021   const upb_strview encoded_api_listener = google_protobuf_Any_value(
2022       envoy_config_listener_v3_ApiListener_api_listener(api_listener));
2023   const auto* http_connection_manager =
2024       envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_parse(
2025           encoded_api_listener.data, encoded_api_listener.size, context.arena);
2026   if (http_connection_manager == nullptr) {
2027     return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
2028         "Could not parse HttpConnectionManager config from ApiListener");
2029   }
2030   return HttpConnectionManagerParse(true /* is_client */, context,
2031                                     http_connection_manager, is_v2,
2032                                     &lds_update->http_connection_manager);
2033 }
2034 
DownstreamTlsContextParse(const EncodingContext & context,const envoy_config_core_v3_TransportSocket * transport_socket,XdsApi::DownstreamTlsContext * downstream_tls_context)2035 grpc_error_handle DownstreamTlsContextParse(
2036     const EncodingContext& context,
2037     const envoy_config_core_v3_TransportSocket* transport_socket,
2038     XdsApi::DownstreamTlsContext* downstream_tls_context) {
2039   absl::string_view name = UpbStringToAbsl(
2040       envoy_config_core_v3_TransportSocket_name(transport_socket));
2041   if (name == "envoy.transport_sockets.tls") {
2042     auto* typed_config =
2043         envoy_config_core_v3_TransportSocket_typed_config(transport_socket);
2044     if (typed_config != nullptr) {
2045       const upb_strview encoded_downstream_tls_context =
2046           google_protobuf_Any_value(typed_config);
2047       auto* downstream_tls_context_proto =
2048           envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_parse(
2049               encoded_downstream_tls_context.data,
2050               encoded_downstream_tls_context.size, context.arena);
2051       if (downstream_tls_context_proto == nullptr) {
2052         return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
2053             "Can't decode downstream tls context.");
2054       }
2055       auto* common_tls_context =
2056           envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_common_tls_context(
2057               downstream_tls_context_proto);
2058       if (common_tls_context != nullptr) {
2059         grpc_error_handle error = CommonTlsContextParse(
2060             common_tls_context, &downstream_tls_context->common_tls_context);
2061         if (error != GRPC_ERROR_NONE) return error;
2062       }
2063       auto* require_client_certificate =
2064           envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_require_client_certificate(
2065               downstream_tls_context_proto);
2066       if (require_client_certificate != nullptr) {
2067         downstream_tls_context->require_client_certificate =
2068             google_protobuf_BoolValue_value(require_client_certificate);
2069       }
2070     }
2071     if (downstream_tls_context->common_tls_context
2072             .tls_certificate_certificate_provider_instance.instance_name
2073             .empty()) {
2074       return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
2075           "TLS configuration provided but no "
2076           "tls_certificate_certificate_provider_instance found.");
2077     }
2078   }
2079   return GRPC_ERROR_NONE;
2080 }
2081 
CidrRangeParse(const envoy_config_core_v3_CidrRange * cidr_range_proto,XdsApi::LdsUpdate::FilterChainMap::CidrRange * cidr_range)2082 grpc_error_handle CidrRangeParse(
2083     const envoy_config_core_v3_CidrRange* cidr_range_proto,
2084     XdsApi::LdsUpdate::FilterChainMap::CidrRange* cidr_range) {
2085   std::string address_prefix = UpbStringToStdString(
2086       envoy_config_core_v3_CidrRange_address_prefix(cidr_range_proto));
2087   grpc_error_handle error =
2088       grpc_string_to_sockaddr(&cidr_range->address, address_prefix.c_str(), 0);
2089   if (error != GRPC_ERROR_NONE) return error;
2090   cidr_range->prefix_len = 0;
2091   auto* prefix_len_proto =
2092       envoy_config_core_v3_CidrRange_prefix_len(cidr_range_proto);
2093   if (prefix_len_proto != nullptr) {
2094     cidr_range->prefix_len = std::min(
2095         google_protobuf_UInt32Value_value(prefix_len_proto),
2096         (reinterpret_cast<const grpc_sockaddr*>(cidr_range->address.addr))
2097                     ->sa_family == GRPC_AF_INET
2098             ? uint32_t(32)
2099             : uint32_t(128));
2100   }
2101   // Normalize the network address by masking it with prefix_len
2102   grpc_sockaddr_mask_bits(&cidr_range->address, cidr_range->prefix_len);
2103   return GRPC_ERROR_NONE;
2104 }
2105 
FilterChainMatchParse(const envoy_config_listener_v3_FilterChainMatch * filter_chain_match_proto,FilterChain::FilterChainMatch * filter_chain_match)2106 grpc_error_handle FilterChainMatchParse(
2107     const envoy_config_listener_v3_FilterChainMatch* filter_chain_match_proto,
2108     FilterChain::FilterChainMatch* filter_chain_match) {
2109   auto* destination_port =
2110       envoy_config_listener_v3_FilterChainMatch_destination_port(
2111           filter_chain_match_proto);
2112   if (destination_port != nullptr) {
2113     filter_chain_match->destination_port =
2114         google_protobuf_UInt32Value_value(destination_port);
2115   }
2116   size_t size = 0;
2117   auto* prefix_ranges = envoy_config_listener_v3_FilterChainMatch_prefix_ranges(
2118       filter_chain_match_proto, &size);
2119   filter_chain_match->prefix_ranges.reserve(size);
2120   for (size_t i = 0; i < size; i++) {
2121     XdsApi::LdsUpdate::FilterChainMap::CidrRange cidr_range;
2122     grpc_error_handle error = CidrRangeParse(prefix_ranges[i], &cidr_range);
2123     if (error != GRPC_ERROR_NONE) return error;
2124     filter_chain_match->prefix_ranges.push_back(cidr_range);
2125   }
2126   filter_chain_match->source_type =
2127       static_cast<XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceType>(
2128           envoy_config_listener_v3_FilterChainMatch_source_type(
2129               filter_chain_match_proto));
2130   auto* source_prefix_ranges =
2131       envoy_config_listener_v3_FilterChainMatch_source_prefix_ranges(
2132           filter_chain_match_proto, &size);
2133   filter_chain_match->source_prefix_ranges.reserve(size);
2134   for (size_t i = 0; i < size; i++) {
2135     XdsApi::LdsUpdate::FilterChainMap::CidrRange cidr_range;
2136     grpc_error_handle error =
2137         CidrRangeParse(source_prefix_ranges[i], &cidr_range);
2138     if (error != GRPC_ERROR_NONE) return error;
2139     filter_chain_match->source_prefix_ranges.push_back(cidr_range);
2140   }
2141   auto* source_ports = envoy_config_listener_v3_FilterChainMatch_source_ports(
2142       filter_chain_match_proto, &size);
2143   filter_chain_match->source_ports.reserve(size);
2144   for (size_t i = 0; i < size; i++) {
2145     filter_chain_match->source_ports.push_back(source_ports[i]);
2146   }
2147   auto* server_names = envoy_config_listener_v3_FilterChainMatch_server_names(
2148       filter_chain_match_proto, &size);
2149   for (size_t i = 0; i < size; i++) {
2150     filter_chain_match->server_names.push_back(
2151         UpbStringToStdString(server_names[i]));
2152   }
2153   filter_chain_match->transport_protocol = UpbStringToStdString(
2154       envoy_config_listener_v3_FilterChainMatch_transport_protocol(
2155           filter_chain_match_proto));
2156   auto* application_protocols =
2157       envoy_config_listener_v3_FilterChainMatch_application_protocols(
2158           filter_chain_match_proto, &size);
2159   for (size_t i = 0; i < size; i++) {
2160     filter_chain_match->application_protocols.push_back(
2161         UpbStringToStdString(application_protocols[i]));
2162   }
2163   return GRPC_ERROR_NONE;
2164 }
2165 
FilterChainParse(const EncodingContext & context,const envoy_config_listener_v3_FilterChain * filter_chain_proto,bool is_v2,FilterChain * filter_chain)2166 grpc_error_handle FilterChainParse(
2167     const EncodingContext& context,
2168     const envoy_config_listener_v3_FilterChain* filter_chain_proto, bool is_v2,
2169     FilterChain* filter_chain) {
2170   grpc_error_handle error = GRPC_ERROR_NONE;
2171   auto* filter_chain_match =
2172       envoy_config_listener_v3_FilterChain_filter_chain_match(
2173           filter_chain_proto);
2174   if (filter_chain_match != nullptr) {
2175     error = FilterChainMatchParse(filter_chain_match,
2176                                   &filter_chain->filter_chain_match);
2177     if (error != GRPC_ERROR_NONE) return error;
2178   }
2179   // Parse the filters list. Currently we only support HttpConnectionManager.
2180   size_t size = 0;
2181   auto* filters =
2182       envoy_config_listener_v3_FilterChain_filters(filter_chain_proto, &size);
2183   if (size != 1) {
2184     return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
2185         "FilterChain should have exactly one filter: HttpConnectionManager; no "
2186         "other filter is supported at the moment");
2187   }
2188   auto* typed_config = envoy_config_listener_v3_Filter_typed_config(filters[0]);
2189   if (typed_config == nullptr) {
2190     return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
2191         "No typed_config found in filter.");
2192   }
2193   absl::string_view type_url =
2194       UpbStringToAbsl(google_protobuf_Any_type_url(typed_config));
2195   if (type_url !=
2196       "type.googleapis.com/"
2197       "envoy.extensions.filters.network.http_connection_manager.v3."
2198       "HttpConnectionManager") {
2199     return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2200         absl::StrCat("Unsupported filter type ", type_url).c_str());
2201   }
2202   const upb_strview encoded_http_connection_manager =
2203       google_protobuf_Any_value(typed_config);
2204   const auto* http_connection_manager =
2205       envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_parse(
2206           encoded_http_connection_manager.data,
2207           encoded_http_connection_manager.size, context.arena);
2208   if (http_connection_manager == nullptr) {
2209     return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
2210         "Could not parse HttpConnectionManager config from filter "
2211         "typed_config");
2212   }
2213   filter_chain->filter_chain_data =
2214       std::make_shared<XdsApi::LdsUpdate::FilterChainData>();
2215   error = HttpConnectionManagerParse(
2216       false /* is_client */, context, http_connection_manager, is_v2,
2217       &filter_chain->filter_chain_data->http_connection_manager);
2218   if (error != GRPC_ERROR_NONE) return error;
2219   // Get the DownstreamTlsContext for the filter chain
2220   if (XdsSecurityEnabled()) {
2221     auto* transport_socket =
2222         envoy_config_listener_v3_FilterChain_transport_socket(
2223             filter_chain_proto);
2224     if (transport_socket != nullptr) {
2225       error = DownstreamTlsContextParse(
2226           context, transport_socket,
2227           &filter_chain->filter_chain_data->downstream_tls_context);
2228     }
2229   }
2230   return error;
2231 }
2232 
AddressParse(const envoy_config_core_v3_Address * address_proto,std::string * address)2233 grpc_error_handle AddressParse(
2234     const envoy_config_core_v3_Address* address_proto, std::string* address) {
2235   const auto* socket_address =
2236       envoy_config_core_v3_Address_socket_address(address_proto);
2237   if (socket_address == nullptr) {
2238     return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2239         "Address does not have socket_address");
2240   }
2241   if (envoy_config_core_v3_SocketAddress_protocol(socket_address) !=
2242       envoy_config_core_v3_SocketAddress_TCP) {
2243     return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
2244         "SocketAddress protocol is not TCP");
2245   }
2246   uint32_t port = envoy_config_core_v3_SocketAddress_port_value(socket_address);
2247   if (port > 65535) {
2248     return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Invalid port");
2249   }
2250   *address = JoinHostPort(
2251       UpbStringToAbsl(
2252           envoy_config_core_v3_SocketAddress_address(socket_address)),
2253       port);
2254   return GRPC_ERROR_NONE;
2255 }
2256 
2257 // An intermediate map for filter chains that we create to validate the list of
2258 // filter chains received from the control plane and to finally create
2259 // XdsApi::LdsUpdate::FilterChainMap
2260 struct InternalFilterChainMap {
2261   using SourceIpMap =
2262       std::map<std::string, XdsApi::LdsUpdate::FilterChainMap::SourceIp>;
2263   using ConnectionSourceTypesArray = std::array<SourceIpMap, 3>;
2264   struct DestinationIp {
2265     absl::optional<XdsApi::LdsUpdate::FilterChainMap::CidrRange> prefix_range;
2266     bool transport_protocol_raw_buffer_provided = false;
2267     ConnectionSourceTypesArray source_types_array;
2268   };
2269   using DestinationIpMap = std::map<std::string, DestinationIp>;
2270   DestinationIpMap destination_ip_map;
2271 };
2272 
AddFilterChainDataForSourcePort(const FilterChain & filter_chain,XdsApi::LdsUpdate::FilterChainMap::SourcePortsMap * ports_map,uint32_t port)2273 grpc_error_handle AddFilterChainDataForSourcePort(
2274     const FilterChain& filter_chain,
2275     XdsApi::LdsUpdate::FilterChainMap::SourcePortsMap* ports_map,
2276     uint32_t port) {
2277   auto insert_result = ports_map->emplace(
2278       port, XdsApi::LdsUpdate::FilterChainMap::FilterChainDataSharedPtr{
2279                 filter_chain.filter_chain_data});
2280   if (!insert_result.second) {
2281     return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2282         absl::StrCat(
2283             "Duplicate matching rules detected when adding filter chain: ",
2284             filter_chain.filter_chain_match.ToString())
2285             .c_str());
2286   }
2287   return GRPC_ERROR_NONE;
2288 }
2289 
AddFilterChainDataForSourcePorts(const FilterChain & filter_chain,XdsApi::LdsUpdate::FilterChainMap::SourcePortsMap * ports_map)2290 grpc_error_handle AddFilterChainDataForSourcePorts(
2291     const FilterChain& filter_chain,
2292     XdsApi::LdsUpdate::FilterChainMap::SourcePortsMap* ports_map) {
2293   if (filter_chain.filter_chain_match.source_ports.empty()) {
2294     return AddFilterChainDataForSourcePort(filter_chain, ports_map, 0);
2295   } else {
2296     for (uint32_t port : filter_chain.filter_chain_match.source_ports) {
2297       grpc_error_handle error =
2298           AddFilterChainDataForSourcePort(filter_chain, ports_map, port);
2299       if (error != GRPC_ERROR_NONE) return error;
2300     }
2301   }
2302   return GRPC_ERROR_NONE;
2303 }
2304 
AddFilterChainDataForSourceIpRange(const FilterChain & filter_chain,InternalFilterChainMap::SourceIpMap * source_ip_map)2305 grpc_error_handle AddFilterChainDataForSourceIpRange(
2306     const FilterChain& filter_chain,
2307     InternalFilterChainMap::SourceIpMap* source_ip_map) {
2308   if (filter_chain.filter_chain_match.source_prefix_ranges.empty()) {
2309     auto insert_result = source_ip_map->emplace(
2310         "", XdsApi::LdsUpdate::FilterChainMap::SourceIp());
2311     return AddFilterChainDataForSourcePorts(
2312         filter_chain, &insert_result.first->second.ports_map);
2313   } else {
2314     for (const auto& prefix_range :
2315          filter_chain.filter_chain_match.source_prefix_ranges) {
2316       auto insert_result = source_ip_map->emplace(
2317           absl::StrCat(grpc_sockaddr_to_string(&prefix_range.address, false),
2318                        "/", prefix_range.prefix_len),
2319           XdsApi::LdsUpdate::FilterChainMap::SourceIp());
2320       if (insert_result.second) {
2321         insert_result.first->second.prefix_range.emplace(prefix_range);
2322       }
2323       grpc_error_handle error = AddFilterChainDataForSourcePorts(
2324           filter_chain, &insert_result.first->second.ports_map);
2325       if (error != GRPC_ERROR_NONE) return error;
2326     }
2327   }
2328   return GRPC_ERROR_NONE;
2329 }
2330 
AddFilterChainDataForSourceType(const FilterChain & filter_chain,InternalFilterChainMap::DestinationIp * destination_ip)2331 grpc_error_handle AddFilterChainDataForSourceType(
2332     const FilterChain& filter_chain,
2333     InternalFilterChainMap::DestinationIp* destination_ip) {
2334   GPR_ASSERT(static_cast<unsigned int>(
2335                  filter_chain.filter_chain_match.source_type) < 3);
2336   return AddFilterChainDataForSourceIpRange(
2337       filter_chain, &destination_ip->source_types_array[static_cast<int>(
2338                         filter_chain.filter_chain_match.source_type)]);
2339 }
2340 
AddFilterChainDataForApplicationProtocols(const FilterChain & filter_chain,InternalFilterChainMap::DestinationIp * destination_ip)2341 grpc_error_handle AddFilterChainDataForApplicationProtocols(
2342     const FilterChain& filter_chain,
2343     InternalFilterChainMap::DestinationIp* destination_ip) {
2344   // Only allow filter chains that do not mention application protocols
2345   if (!filter_chain.filter_chain_match.application_protocols.empty()) {
2346     return GRPC_ERROR_NONE;
2347   }
2348   return AddFilterChainDataForSourceType(filter_chain, destination_ip);
2349 }
2350 
AddFilterChainDataForTransportProtocol(const FilterChain & filter_chain,InternalFilterChainMap::DestinationIp * destination_ip)2351 grpc_error_handle AddFilterChainDataForTransportProtocol(
2352     const FilterChain& filter_chain,
2353     InternalFilterChainMap::DestinationIp* destination_ip) {
2354   const std::string& transport_protocol =
2355       filter_chain.filter_chain_match.transport_protocol;
2356   // Only allow filter chains with no transport protocol or "raw_buffer"
2357   if (!transport_protocol.empty() && transport_protocol != "raw_buffer") {
2358     return GRPC_ERROR_NONE;
2359   }
2360   // If for this configuration, we've already seen filter chains that mention
2361   // the transport protocol as "raw_buffer", we will never match filter chains
2362   // that do not mention it.
2363   if (destination_ip->transport_protocol_raw_buffer_provided &&
2364       transport_protocol.empty()) {
2365     return GRPC_ERROR_NONE;
2366   }
2367   if (!transport_protocol.empty() &&
2368       !destination_ip->transport_protocol_raw_buffer_provided) {
2369     destination_ip->transport_protocol_raw_buffer_provided = true;
2370     // Clear out the previous entries if any since those entries did not mention
2371     // "raw_buffer"
2372     destination_ip->source_types_array =
2373         InternalFilterChainMap::ConnectionSourceTypesArray();
2374   }
2375   return AddFilterChainDataForApplicationProtocols(filter_chain,
2376                                                    destination_ip);
2377 }
2378 
AddFilterChainDataForServerNames(const FilterChain & filter_chain,InternalFilterChainMap::DestinationIp * destination_ip)2379 grpc_error_handle AddFilterChainDataForServerNames(
2380     const FilterChain& filter_chain,
2381     InternalFilterChainMap::DestinationIp* destination_ip) {
2382   // Don't continue adding filter chains with server names mentioned
2383   if (!filter_chain.filter_chain_match.server_names.empty()) {
2384     return GRPC_ERROR_NONE;
2385   }
2386   return AddFilterChainDataForTransportProtocol(filter_chain, destination_ip);
2387 }
2388 
AddFilterChainDataForDestinationIpRange(const FilterChain & filter_chain,InternalFilterChainMap::DestinationIpMap * destination_ip_map)2389 grpc_error_handle AddFilterChainDataForDestinationIpRange(
2390     const FilterChain& filter_chain,
2391     InternalFilterChainMap::DestinationIpMap* destination_ip_map) {
2392   if (filter_chain.filter_chain_match.prefix_ranges.empty()) {
2393     auto insert_result = destination_ip_map->emplace(
2394         "", InternalFilterChainMap::DestinationIp());
2395     return AddFilterChainDataForServerNames(filter_chain,
2396                                             &insert_result.first->second);
2397   } else {
2398     for (const auto& prefix_range :
2399          filter_chain.filter_chain_match.prefix_ranges) {
2400       auto insert_result = destination_ip_map->emplace(
2401           absl::StrCat(grpc_sockaddr_to_string(&prefix_range.address, false),
2402                        "/", prefix_range.prefix_len),
2403           InternalFilterChainMap::DestinationIp());
2404       if (insert_result.second) {
2405         insert_result.first->second.prefix_range.emplace(prefix_range);
2406       }
2407       grpc_error_handle error = AddFilterChainDataForServerNames(
2408           filter_chain, &insert_result.first->second);
2409       if (error != GRPC_ERROR_NONE) return error;
2410     }
2411   }
2412   return GRPC_ERROR_NONE;
2413 }
2414 
BuildFromInternalFilterChainMap(InternalFilterChainMap * internal_filter_chain_map)2415 XdsApi::LdsUpdate::FilterChainMap BuildFromInternalFilterChainMap(
2416     InternalFilterChainMap* internal_filter_chain_map) {
2417   XdsApi::LdsUpdate::FilterChainMap filter_chain_map;
2418   for (auto& destination_ip_pair :
2419        internal_filter_chain_map->destination_ip_map) {
2420     XdsApi::LdsUpdate::FilterChainMap::DestinationIp destination_ip;
2421     destination_ip.prefix_range = destination_ip_pair.second.prefix_range;
2422     for (int i = 0; i < 3; i++) {
2423       auto& source_ip_map = destination_ip_pair.second.source_types_array[i];
2424       for (auto& source_ip_pair : source_ip_map) {
2425         destination_ip.source_types_array[i].push_back(
2426             std::move(source_ip_pair.second));
2427       }
2428     }
2429     filter_chain_map.destination_ip_vector.push_back(std::move(destination_ip));
2430   }
2431   return filter_chain_map;
2432 }
2433 
BuildFilterChainMap(const std::vector<FilterChain> & filter_chains,XdsApi::LdsUpdate::FilterChainMap * filter_chain_map)2434 grpc_error_handle BuildFilterChainMap(
2435     const std::vector<FilterChain>& filter_chains,
2436     XdsApi::LdsUpdate::FilterChainMap* filter_chain_map) {
2437   InternalFilterChainMap internal_filter_chain_map;
2438   for (const auto& filter_chain : filter_chains) {
2439     // Discard filter chain entries that specify destination port
2440     if (filter_chain.filter_chain_match.destination_port != 0) continue;
2441     grpc_error_handle error = AddFilterChainDataForDestinationIpRange(
2442         filter_chain, &internal_filter_chain_map.destination_ip_map);
2443     if (error != GRPC_ERROR_NONE) return error;
2444   }
2445   *filter_chain_map =
2446       BuildFromInternalFilterChainMap(&internal_filter_chain_map);
2447   return GRPC_ERROR_NONE;
2448 }
2449 
LdsResponseParseServer(const EncodingContext & context,const envoy_config_listener_v3_Listener * listener,bool is_v2,XdsApi::LdsUpdate * lds_update)2450 grpc_error_handle LdsResponseParseServer(
2451     const EncodingContext& context,
2452     const envoy_config_listener_v3_Listener* listener, bool is_v2,
2453     XdsApi::LdsUpdate* lds_update) {
2454   lds_update->type = XdsApi::LdsUpdate::ListenerType::kTcpListener;
2455   grpc_error_handle error =
2456       AddressParse(envoy_config_listener_v3_Listener_address(listener),
2457                    &lds_update->address);
2458   if (error != GRPC_ERROR_NONE) return error;
2459   const auto* use_original_dst =
2460       envoy_config_listener_v3_Listener_use_original_dst(listener);
2461   if (use_original_dst != nullptr) {
2462     if (google_protobuf_BoolValue_value(use_original_dst)) {
2463       return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
2464           "Field \'use_original_dst\' is not supported.");
2465     }
2466   }
2467   size_t size = 0;
2468   auto* filter_chains =
2469       envoy_config_listener_v3_Listener_filter_chains(listener, &size);
2470   std::vector<FilterChain> parsed_filter_chains;
2471   parsed_filter_chains.reserve(size);
2472   for (size_t i = 0; i < size; i++) {
2473     FilterChain filter_chain;
2474     error = FilterChainParse(context, filter_chains[i], is_v2, &filter_chain);
2475     if (error != GRPC_ERROR_NONE) return error;
2476     parsed_filter_chains.push_back(std::move(filter_chain));
2477   }
2478   error =
2479       BuildFilterChainMap(parsed_filter_chains, &lds_update->filter_chain_map);
2480   if (error != GRPC_ERROR_NONE) return error;
2481   auto* default_filter_chain =
2482       envoy_config_listener_v3_Listener_default_filter_chain(listener);
2483   if (default_filter_chain != nullptr) {
2484     FilterChain filter_chain;
2485     error =
2486         FilterChainParse(context, default_filter_chain, is_v2, &filter_chain);
2487     if (error != GRPC_ERROR_NONE) return error;
2488     if (filter_chain.filter_chain_data != nullptr) {
2489       lds_update->default_filter_chain =
2490           std::move(*filter_chain.filter_chain_data);
2491     }
2492   }
2493   if (size == 0 && default_filter_chain == nullptr) {
2494     return GRPC_ERROR_CREATE_FROM_STATIC_STRING("No filter chain provided.");
2495   }
2496   return GRPC_ERROR_NONE;
2497 }
2498 
LdsResponseParse(const EncodingContext & context,const envoy_service_discovery_v3_DiscoveryResponse * response,const std::set<absl::string_view> & expected_listener_names,XdsApi::LdsUpdateMap * lds_update_map,std::set<std::string> * resource_names_failed)2499 grpc_error_handle LdsResponseParse(
2500     const EncodingContext& context,
2501     const envoy_service_discovery_v3_DiscoveryResponse* response,
2502     const std::set<absl::string_view>& expected_listener_names,
2503     XdsApi::LdsUpdateMap* lds_update_map,
2504     std::set<std::string>* resource_names_failed) {
2505   std::vector<grpc_error_handle> errors;
2506   // Get the resources from the response.
2507   size_t size;
2508   const google_protobuf_Any* const* resources =
2509       envoy_service_discovery_v3_DiscoveryResponse_resources(response, &size);
2510   for (size_t i = 0; i < size; ++i) {
2511     // Check the type_url of the resource.
2512     absl::string_view type_url =
2513         UpbStringToAbsl(google_protobuf_Any_type_url(resources[i]));
2514     bool is_v2 = false;
2515     if (!IsLds(type_url, &is_v2)) {
2516       errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2517           absl::StrCat("resource index ", i, ": Resource is not LDS.")
2518               .c_str()));
2519       continue;
2520     }
2521     // Decode the listener.
2522     const upb_strview encoded_listener =
2523         google_protobuf_Any_value(resources[i]);
2524     const envoy_config_listener_v3_Listener* listener =
2525         envoy_config_listener_v3_Listener_parse(
2526             encoded_listener.data, encoded_listener.size, context.arena);
2527     if (listener == nullptr) {
2528       errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2529           absl::StrCat("resource index ", i, ": Can't decode listener.")
2530               .c_str()));
2531       continue;
2532     }
2533     // Check listener name. Ignore unexpected listeners.
2534     std::string listener_name =
2535         UpbStringToStdString(envoy_config_listener_v3_Listener_name(listener));
2536     if (expected_listener_names.find(listener_name) ==
2537         expected_listener_names.end()) {
2538       continue;
2539     }
2540     // Fail if listener name is duplicated.
2541     if (lds_update_map->find(listener_name) != lds_update_map->end()) {
2542       errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2543           absl::StrCat("duplicate listener name \"", listener_name, "\"")
2544               .c_str()));
2545       resource_names_failed->insert(listener_name);
2546       continue;
2547     }
2548     // Serialize into JSON and store it in the LdsUpdateMap
2549     XdsApi::LdsResourceData& lds_resource_data =
2550         (*lds_update_map)[listener_name];
2551     XdsApi::LdsUpdate& lds_update = lds_resource_data.resource;
2552     lds_resource_data.serialized_proto = UpbStringToStdString(encoded_listener);
2553     // Check whether it's a client or server listener.
2554     const envoy_config_listener_v3_ApiListener* api_listener =
2555         envoy_config_listener_v3_Listener_api_listener(listener);
2556     const envoy_config_core_v3_Address* address =
2557         envoy_config_listener_v3_Listener_address(listener);
2558     if (api_listener != nullptr && address != nullptr) {
2559       errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2560           absl::StrCat(listener_name,
2561                        ": Listener has both address and ApiListener")
2562               .c_str()));
2563       resource_names_failed->insert(listener_name);
2564       continue;
2565     }
2566     if (api_listener == nullptr && address == nullptr) {
2567       errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2568           absl::StrCat(listener_name,
2569                        ": Listener has neither address nor ApiListener")
2570               .c_str()));
2571       resource_names_failed->insert(listener_name);
2572       continue;
2573     }
2574     grpc_error_handle error = GRPC_ERROR_NONE;
2575     if (api_listener != nullptr) {
2576       error = LdsResponseParseClient(context, api_listener, is_v2, &lds_update);
2577     } else {
2578       error = LdsResponseParseServer(context, listener, is_v2, &lds_update);
2579     }
2580     if (error != GRPC_ERROR_NONE) {
2581       errors.push_back(grpc_error_add_child(
2582           GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2583               absl::StrCat(listener_name, ": validation error").c_str()),
2584           error));
2585       resource_names_failed->insert(listener_name);
2586     }
2587   }
2588   return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing LDS response", &errors);
2589 }
2590 
RdsResponseParse(const EncodingContext & context,const envoy_service_discovery_v3_DiscoveryResponse * response,const std::set<absl::string_view> & expected_route_configuration_names,XdsApi::RdsUpdateMap * rds_update_map,std::set<std::string> * resource_names_failed)2591 grpc_error_handle RdsResponseParse(
2592     const EncodingContext& context,
2593     const envoy_service_discovery_v3_DiscoveryResponse* response,
2594     const std::set<absl::string_view>& expected_route_configuration_names,
2595     XdsApi::RdsUpdateMap* rds_update_map,
2596     std::set<std::string>* resource_names_failed) {
2597   std::vector<grpc_error_handle> errors;
2598   // Get the resources from the response.
2599   size_t size;
2600   const google_protobuf_Any* const* resources =
2601       envoy_service_discovery_v3_DiscoveryResponse_resources(response, &size);
2602   for (size_t i = 0; i < size; ++i) {
2603     // Check the type_url of the resource.
2604     absl::string_view type_url =
2605         UpbStringToAbsl(google_protobuf_Any_type_url(resources[i]));
2606     if (!IsRds(type_url)) {
2607       errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2608           absl::StrCat("resource index ", i, ": Resource is not RDS.")
2609               .c_str()));
2610       continue;
2611     }
2612     // Decode the route_config.
2613     const upb_strview encoded_route_config =
2614         google_protobuf_Any_value(resources[i]);
2615     const envoy_config_route_v3_RouteConfiguration* route_config =
2616         envoy_config_route_v3_RouteConfiguration_parse(
2617             encoded_route_config.data, encoded_route_config.size,
2618             context.arena);
2619     if (route_config == nullptr) {
2620       errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2621           absl::StrCat("resource index ", i, ": Can't decode route_config.")
2622               .c_str()));
2623       continue;
2624     }
2625     // Check route_config_name. Ignore unexpected route_config.
2626     std::string route_config_name = UpbStringToStdString(
2627         envoy_config_route_v3_RouteConfiguration_name(route_config));
2628     if (expected_route_configuration_names.find(route_config_name) ==
2629         expected_route_configuration_names.end()) {
2630       continue;
2631     }
2632     // Fail if route config name is duplicated.
2633     if (rds_update_map->find(route_config_name) != rds_update_map->end()) {
2634       errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2635           absl::StrCat("duplicate route config name \"", route_config_name,
2636                        "\"")
2637               .c_str()));
2638       resource_names_failed->insert(route_config_name);
2639       continue;
2640     }
2641     // Serialize into JSON and store it in the RdsUpdateMap
2642     XdsApi::RdsResourceData& rds_resource_data =
2643         (*rds_update_map)[route_config_name];
2644     XdsApi::RdsUpdate& rds_update = rds_resource_data.resource;
2645     rds_resource_data.serialized_proto =
2646         UpbStringToStdString(encoded_route_config);
2647     // Parse the route_config.
2648     grpc_error_handle error =
2649         RouteConfigParse(context, route_config, &rds_update);
2650     if (error != GRPC_ERROR_NONE) {
2651       errors.push_back(grpc_error_add_child(
2652           GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2653               absl::StrCat(route_config_name, ": validation error").c_str()),
2654           error));
2655       resource_names_failed->insert(route_config_name);
2656     }
2657   }
2658   return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing RDS response", &errors);
2659 }
2660 
CdsResponseParse(const EncodingContext & context,const envoy_service_discovery_v3_DiscoveryResponse * response,const std::set<absl::string_view> & expected_cluster_names,XdsApi::CdsUpdateMap * cds_update_map,std::set<std::string> * resource_names_failed)2661 grpc_error_handle CdsResponseParse(
2662     const EncodingContext& context,
2663     const envoy_service_discovery_v3_DiscoveryResponse* response,
2664     const std::set<absl::string_view>& expected_cluster_names,
2665     XdsApi::CdsUpdateMap* cds_update_map,
2666     std::set<std::string>* resource_names_failed) {
2667   std::vector<grpc_error_handle> errors;
2668   // Get the resources from the response.
2669   size_t size;
2670   const google_protobuf_Any* const* resources =
2671       envoy_service_discovery_v3_DiscoveryResponse_resources(response, &size);
2672   // Parse all the resources in the CDS response.
2673   for (size_t i = 0; i < size; ++i) {
2674     // Check the type_url of the resource.
2675     absl::string_view type_url =
2676         UpbStringToAbsl(google_protobuf_Any_type_url(resources[i]));
2677     if (!IsCds(type_url)) {
2678       errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2679           absl::StrCat("resource index ", i, ": Resource is not CDS.")
2680               .c_str()));
2681       continue;
2682     }
2683     // Decode the cluster.
2684     const upb_strview encoded_cluster = google_protobuf_Any_value(resources[i]);
2685     const envoy_config_cluster_v3_Cluster* cluster =
2686         envoy_config_cluster_v3_Cluster_parse(
2687             encoded_cluster.data, encoded_cluster.size, context.arena);
2688     if (cluster == nullptr) {
2689       errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2690           absl::StrCat("resource index ", i, ": Can't decode cluster.")
2691               .c_str()));
2692       continue;
2693     }
2694     MaybeLogCluster(context, cluster);
2695     // Ignore unexpected cluster names.
2696     std::string cluster_name =
2697         UpbStringToStdString(envoy_config_cluster_v3_Cluster_name(cluster));
2698     if (expected_cluster_names.find(cluster_name) ==
2699         expected_cluster_names.end()) {
2700       continue;
2701     }
2702     // Fail on duplicate resources.
2703     if (cds_update_map->find(cluster_name) != cds_update_map->end()) {
2704       errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2705           absl::StrCat("duplicate resource name \"", cluster_name, "\"")
2706               .c_str()));
2707       resource_names_failed->insert(cluster_name);
2708       continue;
2709     }
2710     // Serialize into JSON and store it in the CdsUpdateMap
2711     XdsApi::CdsResourceData& cds_resource_data =
2712         (*cds_update_map)[cluster_name];
2713     XdsApi::CdsUpdate& cds_update = cds_resource_data.resource;
2714     cds_resource_data.serialized_proto = UpbStringToStdString(encoded_cluster);
2715     // Check the cluster_discovery_type.
2716     if (!envoy_config_cluster_v3_Cluster_has_type(cluster) &&
2717         !envoy_config_cluster_v3_Cluster_has_cluster_type(cluster)) {
2718       errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2719           absl::StrCat(cluster_name, ": DiscoveryType not found.").c_str()));
2720       resource_names_failed->insert(cluster_name);
2721       continue;
2722     }
2723     if (envoy_config_cluster_v3_Cluster_type(cluster) ==
2724         envoy_config_cluster_v3_Cluster_EDS) {
2725       cds_update.cluster_type = XdsApi::CdsUpdate::ClusterType::EDS;
2726       // Check the EDS config source.
2727       const envoy_config_cluster_v3_Cluster_EdsClusterConfig*
2728           eds_cluster_config =
2729               envoy_config_cluster_v3_Cluster_eds_cluster_config(cluster);
2730       const envoy_config_core_v3_ConfigSource* eds_config =
2731           envoy_config_cluster_v3_Cluster_EdsClusterConfig_eds_config(
2732               eds_cluster_config);
2733       if (!envoy_config_core_v3_ConfigSource_has_ads(eds_config)) {
2734         errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2735             absl::StrCat(cluster_name, ": EDS ConfigSource is not ADS.")
2736                 .c_str()));
2737         resource_names_failed->insert(cluster_name);
2738         continue;
2739       }
2740       // Record EDS service_name (if any).
2741       upb_strview service_name =
2742           envoy_config_cluster_v3_Cluster_EdsClusterConfig_service_name(
2743               eds_cluster_config);
2744       if (service_name.size != 0) {
2745         cds_update.eds_service_name = UpbStringToStdString(service_name);
2746       }
2747     } else if (!XdsAggregateAndLogicalDnsClusterEnabled()) {
2748       errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2749           absl::StrCat(cluster_name, ": DiscoveryType is not valid.").c_str()));
2750       resource_names_failed->insert(cluster_name);
2751       continue;
2752     } else if (envoy_config_cluster_v3_Cluster_type(cluster) ==
2753                envoy_config_cluster_v3_Cluster_LOGICAL_DNS) {
2754       cds_update.cluster_type = XdsApi::CdsUpdate::ClusterType::LOGICAL_DNS;
2755     } else {
2756       if (envoy_config_cluster_v3_Cluster_has_cluster_type(cluster)) {
2757         const envoy_config_cluster_v3_Cluster_CustomClusterType*
2758             custom_cluster_type =
2759                 envoy_config_cluster_v3_Cluster_cluster_type(cluster);
2760         upb_strview type_name =
2761             envoy_config_cluster_v3_Cluster_CustomClusterType_name(
2762                 custom_cluster_type);
2763         if (UpbStringToAbsl(type_name) == "envoy.clusters.aggregate") {
2764           cds_update.cluster_type = XdsApi::CdsUpdate::ClusterType::AGGREGATE;
2765           // Retrieve aggregate clusters.
2766           const google_protobuf_Any* typed_config =
2767               envoy_config_cluster_v3_Cluster_CustomClusterType_typed_config(
2768                   custom_cluster_type);
2769           const upb_strview aggregate_cluster_config_upb_strview =
2770               google_protobuf_Any_value(typed_config);
2771           const envoy_extensions_clusters_aggregate_v3_ClusterConfig*
2772               aggregate_cluster_config =
2773                   envoy_extensions_clusters_aggregate_v3_ClusterConfig_parse(
2774                       aggregate_cluster_config_upb_strview.data,
2775                       aggregate_cluster_config_upb_strview.size, context.arena);
2776           if (aggregate_cluster_config == nullptr) {
2777             errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2778                 absl::StrCat(cluster_name, ": Can't parse aggregate cluster.")
2779                     .c_str()));
2780             resource_names_failed->insert(cluster_name);
2781             continue;
2782           }
2783           size_t size;
2784           const upb_strview* clusters =
2785               envoy_extensions_clusters_aggregate_v3_ClusterConfig_clusters(
2786                   aggregate_cluster_config, &size);
2787           for (size_t i = 0; i < size; ++i) {
2788             const upb_strview cluster = clusters[i];
2789             cds_update.prioritized_cluster_names.emplace_back(
2790                 UpbStringToStdString(cluster));
2791           }
2792         } else {
2793           errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2794               absl::StrCat(cluster_name, ": DiscoveryType is not valid.")
2795                   .c_str()));
2796           resource_names_failed->insert(cluster_name);
2797           continue;
2798         }
2799       } else {
2800         errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2801             absl::StrCat(cluster_name, ": DiscoveryType is not valid.")
2802                 .c_str()));
2803         resource_names_failed->insert(cluster_name);
2804         continue;
2805       }
2806     }
2807     // Check the LB policy.
2808     if (envoy_config_cluster_v3_Cluster_lb_policy(cluster) ==
2809         envoy_config_cluster_v3_Cluster_ROUND_ROBIN) {
2810       cds_update.lb_policy = "ROUND_ROBIN";
2811     } else if (XdsRingHashEnabled() &&
2812                envoy_config_cluster_v3_Cluster_lb_policy(cluster) ==
2813                    envoy_config_cluster_v3_Cluster_RING_HASH) {
2814       cds_update.lb_policy = "RING_HASH";
2815       // Record ring hash lb config
2816       auto* ring_hash_config =
2817           envoy_config_cluster_v3_Cluster_ring_hash_lb_config(cluster);
2818       if (ring_hash_config == nullptr) {
2819         errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2820             absl::StrCat(cluster_name,
2821                          ": ring hash lb config required but not present.")
2822                 .c_str()));
2823         resource_names_failed->insert(cluster_name);
2824         continue;
2825       }
2826       const google_protobuf_UInt64Value* max_ring_size =
2827           envoy_config_cluster_v3_Cluster_RingHashLbConfig_maximum_ring_size(
2828               ring_hash_config);
2829       if (max_ring_size != nullptr) {
2830         cds_update.max_ring_size =
2831             google_protobuf_UInt64Value_value(max_ring_size);
2832         if (cds_update.max_ring_size > 8388608 ||
2833             cds_update.max_ring_size == 0) {
2834           errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2835               absl::StrCat(
2836                   cluster_name,
2837                   ": max_ring_size is not in the range of 1 to 8388608.")
2838                   .c_str()));
2839           resource_names_failed->insert(cluster_name);
2840           continue;
2841         }
2842       }
2843       const google_protobuf_UInt64Value* min_ring_size =
2844           envoy_config_cluster_v3_Cluster_RingHashLbConfig_minimum_ring_size(
2845               ring_hash_config);
2846       if (min_ring_size != nullptr) {
2847         cds_update.min_ring_size =
2848             google_protobuf_UInt64Value_value(min_ring_size);
2849         if (cds_update.min_ring_size > 8388608 ||
2850             cds_update.min_ring_size == 0) {
2851           errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2852               absl::StrCat(
2853                   cluster_name,
2854                   ": min_ring_size is not in the range of 1 to 8388608.")
2855                   .c_str()));
2856           resource_names_failed->insert(cluster_name);
2857           continue;
2858         }
2859         if (cds_update.min_ring_size > cds_update.max_ring_size) {
2860           errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2861               absl::StrCat(
2862                   cluster_name,
2863                   ": min_ring_size cannot be greater than max_ring_size.")
2864                   .c_str()));
2865           resource_names_failed->insert(cluster_name);
2866           continue;
2867         }
2868       }
2869       if (envoy_config_cluster_v3_Cluster_RingHashLbConfig_hash_function(
2870               ring_hash_config) ==
2871           envoy_config_cluster_v3_Cluster_RingHashLbConfig_XX_HASH) {
2872         cds_update.hash_function = XdsApi::CdsUpdate::HashFunction::XX_HASH;
2873       } else if (
2874           envoy_config_cluster_v3_Cluster_RingHashLbConfig_hash_function(
2875               ring_hash_config) ==
2876           envoy_config_cluster_v3_Cluster_RingHashLbConfig_MURMUR_HASH_2) {
2877         cds_update.hash_function =
2878             XdsApi::CdsUpdate::HashFunction::MURMUR_HASH_2;
2879       } else {
2880         errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2881             absl::StrCat(cluster_name,
2882                          ": ring hash lb config has invalid hash function.")
2883                 .c_str()));
2884         resource_names_failed->insert(cluster_name);
2885         continue;
2886       }
2887     } else {
2888       errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2889           absl::StrCat(cluster_name, ": LB policy is not supported.").c_str()));
2890       resource_names_failed->insert(cluster_name);
2891       continue;
2892     }
2893     if (XdsSecurityEnabled()) {
2894       // Record Upstream tls context
2895       auto* transport_socket =
2896           envoy_config_cluster_v3_Cluster_transport_socket(cluster);
2897       if (transport_socket != nullptr) {
2898         absl::string_view name = UpbStringToAbsl(
2899             envoy_config_core_v3_TransportSocket_name(transport_socket));
2900         if (name == "envoy.transport_sockets.tls") {
2901           auto* typed_config =
2902               envoy_config_core_v3_TransportSocket_typed_config(
2903                   transport_socket);
2904           if (typed_config != nullptr) {
2905             const upb_strview encoded_upstream_tls_context =
2906                 google_protobuf_Any_value(typed_config);
2907             auto* upstream_tls_context =
2908                 envoy_extensions_transport_sockets_tls_v3_UpstreamTlsContext_parse(
2909                     encoded_upstream_tls_context.data,
2910                     encoded_upstream_tls_context.size, context.arena);
2911             if (upstream_tls_context == nullptr) {
2912               errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2913                   absl::StrCat(cluster_name,
2914                                ": Can't decode upstream tls context.")
2915                       .c_str()));
2916               resource_names_failed->insert(cluster_name);
2917               continue;
2918             }
2919             auto* common_tls_context =
2920                 envoy_extensions_transport_sockets_tls_v3_UpstreamTlsContext_common_tls_context(
2921                     upstream_tls_context);
2922             if (common_tls_context != nullptr) {
2923               grpc_error_handle error = CommonTlsContextParse(
2924                   common_tls_context, &cds_update.common_tls_context);
2925               if (error != GRPC_ERROR_NONE) {
2926                 errors.push_back(grpc_error_add_child(
2927                     GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2928                         absl::StrCat(cluster_name, ": error in TLS context")
2929                             .c_str()),
2930                     error));
2931                 resource_names_failed->insert(cluster_name);
2932                 continue;
2933               }
2934             }
2935           }
2936           if (cds_update.common_tls_context.combined_validation_context
2937                   .validation_context_certificate_provider_instance
2938                   .instance_name.empty()) {
2939             errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2940                 absl::StrCat(cluster_name,
2941                              "TLS configuration provided but no "
2942                              "validation_context_certificate_provider_instance "
2943                              "found.")
2944                     .c_str()));
2945             resource_names_failed->insert(cluster_name);
2946             continue;
2947           }
2948         }
2949       }
2950     }
2951     // Record LRS server name (if any).
2952     const envoy_config_core_v3_ConfigSource* lrs_server =
2953         envoy_config_cluster_v3_Cluster_lrs_server(cluster);
2954     if (lrs_server != nullptr) {
2955       if (!envoy_config_core_v3_ConfigSource_has_self(lrs_server)) {
2956         errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
2957             absl::StrCat(cluster_name, ": LRS ConfigSource is not self.")
2958                 .c_str()));
2959         resource_names_failed->insert(cluster_name);
2960         continue;
2961       }
2962       cds_update.lrs_load_reporting_server_name.emplace("");
2963     }
2964     // The Cluster resource encodes the circuit breaking parameters in a list of
2965     // Thresholds messages, where each message specifies the parameters for a
2966     // particular RoutingPriority. we will look only at the first entry in the
2967     // list for priority DEFAULT and default to 1024 if not found.
2968     if (envoy_config_cluster_v3_Cluster_has_circuit_breakers(cluster)) {
2969       const envoy_config_cluster_v3_CircuitBreakers* circuit_breakers =
2970           envoy_config_cluster_v3_Cluster_circuit_breakers(cluster);
2971       size_t num_thresholds;
2972       const envoy_config_cluster_v3_CircuitBreakers_Thresholds* const*
2973           thresholds = envoy_config_cluster_v3_CircuitBreakers_thresholds(
2974               circuit_breakers, &num_thresholds);
2975       for (size_t i = 0; i < num_thresholds; ++i) {
2976         const auto* threshold = thresholds[i];
2977         if (envoy_config_cluster_v3_CircuitBreakers_Thresholds_priority(
2978                 threshold) == envoy_config_core_v3_DEFAULT) {
2979           const google_protobuf_UInt32Value* max_requests =
2980               envoy_config_cluster_v3_CircuitBreakers_Thresholds_max_requests(
2981                   threshold);
2982           if (max_requests != nullptr) {
2983             cds_update.max_concurrent_requests =
2984                 google_protobuf_UInt32Value_value(max_requests);
2985           }
2986           break;
2987         }
2988       }
2989     }
2990   }
2991   return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing CDS response", &errors);
2992 }
2993 
ServerAddressParseAndAppend(const envoy_config_endpoint_v3_LbEndpoint * lb_endpoint,ServerAddressList * list)2994 grpc_error_handle ServerAddressParseAndAppend(
2995     const envoy_config_endpoint_v3_LbEndpoint* lb_endpoint,
2996     ServerAddressList* list) {
2997   // If health_status is not HEALTHY or UNKNOWN, skip this endpoint.
2998   const int32_t health_status =
2999       envoy_config_endpoint_v3_LbEndpoint_health_status(lb_endpoint);
3000   if (health_status != envoy_config_core_v3_UNKNOWN &&
3001       health_status != envoy_config_core_v3_HEALTHY) {
3002     return GRPC_ERROR_NONE;
3003   }
3004   // Find the ip:port.
3005   const envoy_config_endpoint_v3_Endpoint* endpoint =
3006       envoy_config_endpoint_v3_LbEndpoint_endpoint(lb_endpoint);
3007   const envoy_config_core_v3_Address* address =
3008       envoy_config_endpoint_v3_Endpoint_address(endpoint);
3009   const envoy_config_core_v3_SocketAddress* socket_address =
3010       envoy_config_core_v3_Address_socket_address(address);
3011   std::string address_str = UpbStringToStdString(
3012       envoy_config_core_v3_SocketAddress_address(socket_address));
3013   uint32_t port = envoy_config_core_v3_SocketAddress_port_value(socket_address);
3014   if (GPR_UNLIKELY(port >> 16) != 0) {
3015     return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Invalid port.");
3016   }
3017   // Populate grpc_resolved_address.
3018   grpc_resolved_address addr;
3019   grpc_error_handle error =
3020       grpc_string_to_sockaddr(&addr, address_str.c_str(), port);
3021   if (error != GRPC_ERROR_NONE) return error;
3022   // Append the address to the list.
3023   list->emplace_back(addr, nullptr);
3024   return GRPC_ERROR_NONE;
3025 }
3026 
LocalityParse(const envoy_config_endpoint_v3_LocalityLbEndpoints * locality_lb_endpoints,XdsApi::EdsUpdate::Priority::Locality * output_locality,size_t * priority)3027 grpc_error_handle LocalityParse(
3028     const envoy_config_endpoint_v3_LocalityLbEndpoints* locality_lb_endpoints,
3029     XdsApi::EdsUpdate::Priority::Locality* output_locality, size_t* priority) {
3030   // Parse LB weight.
3031   const google_protobuf_UInt32Value* lb_weight =
3032       envoy_config_endpoint_v3_LocalityLbEndpoints_load_balancing_weight(
3033           locality_lb_endpoints);
3034   // If LB weight is not specified, it means this locality is assigned no load.
3035   // TODO(juanlishen): When we support CDS to configure the inter-locality
3036   // policy, we should change the LB weight handling.
3037   output_locality->lb_weight =
3038       lb_weight != nullptr ? google_protobuf_UInt32Value_value(lb_weight) : 0;
3039   if (output_locality->lb_weight == 0) return GRPC_ERROR_NONE;
3040   // Parse locality name.
3041   const envoy_config_core_v3_Locality* locality =
3042       envoy_config_endpoint_v3_LocalityLbEndpoints_locality(
3043           locality_lb_endpoints);
3044   if (locality == nullptr) {
3045     return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Empty locality.");
3046   }
3047   std::string region =
3048       UpbStringToStdString(envoy_config_core_v3_Locality_region(locality));
3049   std::string zone =
3050       UpbStringToStdString(envoy_config_core_v3_Locality_region(locality));
3051   std::string sub_zone =
3052       UpbStringToStdString(envoy_config_core_v3_Locality_sub_zone(locality));
3053   output_locality->name = MakeRefCounted<XdsLocalityName>(
3054       std::move(region), std::move(zone), std::move(sub_zone));
3055   // Parse the addresses.
3056   size_t size;
3057   const envoy_config_endpoint_v3_LbEndpoint* const* lb_endpoints =
3058       envoy_config_endpoint_v3_LocalityLbEndpoints_lb_endpoints(
3059           locality_lb_endpoints, &size);
3060   for (size_t i = 0; i < size; ++i) {
3061     grpc_error_handle error = ServerAddressParseAndAppend(
3062         lb_endpoints[i], &output_locality->endpoints);
3063     if (error != GRPC_ERROR_NONE) return error;
3064   }
3065   // Parse the priority.
3066   *priority = envoy_config_endpoint_v3_LocalityLbEndpoints_priority(
3067       locality_lb_endpoints);
3068   return GRPC_ERROR_NONE;
3069 }
3070 
DropParseAndAppend(const envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_DropOverload * drop_overload,XdsApi::EdsUpdate::DropConfig * drop_config)3071 grpc_error_handle DropParseAndAppend(
3072     const envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_DropOverload*
3073         drop_overload,
3074     XdsApi::EdsUpdate::DropConfig* drop_config) {
3075   // Get the category.
3076   std::string category = UpbStringToStdString(
3077       envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_DropOverload_category(
3078           drop_overload));
3079   if (category.empty()) {
3080     return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Empty drop category name");
3081   }
3082   // Get the drop rate (per million).
3083   const envoy_type_v3_FractionalPercent* drop_percentage =
3084       envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_DropOverload_drop_percentage(
3085           drop_overload);
3086   uint32_t numerator =
3087       envoy_type_v3_FractionalPercent_numerator(drop_percentage);
3088   const auto denominator =
3089       static_cast<envoy_type_v3_FractionalPercent_DenominatorType>(
3090           envoy_type_v3_FractionalPercent_denominator(drop_percentage));
3091   // Normalize to million.
3092   switch (denominator) {
3093     case envoy_type_v3_FractionalPercent_HUNDRED:
3094       numerator *= 10000;
3095       break;
3096     case envoy_type_v3_FractionalPercent_TEN_THOUSAND:
3097       numerator *= 100;
3098       break;
3099     case envoy_type_v3_FractionalPercent_MILLION:
3100       break;
3101     default:
3102       return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Unknown denominator type");
3103   }
3104   // Cap numerator to 1000000.
3105   numerator = GPR_MIN(numerator, 1000000);
3106   drop_config->AddCategory(std::move(category), numerator);
3107   return GRPC_ERROR_NONE;
3108 }
3109 
EdsResponseParse(const EncodingContext & context,const envoy_service_discovery_v3_DiscoveryResponse * response,const std::set<absl::string_view> & expected_eds_service_names,XdsApi::EdsUpdateMap * eds_update_map,std::set<std::string> * resource_names_failed)3110 grpc_error_handle EdsResponseParse(
3111     const EncodingContext& context,
3112     const envoy_service_discovery_v3_DiscoveryResponse* response,
3113     const std::set<absl::string_view>& expected_eds_service_names,
3114     XdsApi::EdsUpdateMap* eds_update_map,
3115     std::set<std::string>* resource_names_failed) {
3116   std::vector<grpc_error_handle> errors;
3117   // Get the resources from the response.
3118   size_t size;
3119   const google_protobuf_Any* const* resources =
3120       envoy_service_discovery_v3_DiscoveryResponse_resources(response, &size);
3121   for (size_t i = 0; i < size; ++i) {
3122     // Check the type_url of the resource.
3123     absl::string_view type_url =
3124         UpbStringToAbsl(google_protobuf_Any_type_url(resources[i]));
3125     if (!IsEds(type_url)) {
3126       errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
3127           absl::StrCat("resource index ", i, ": Resource is not EDS.")
3128               .c_str()));
3129       continue;
3130     }
3131     // Get the cluster_load_assignment.
3132     upb_strview encoded_cluster_load_assignment =
3133         google_protobuf_Any_value(resources[i]);
3134     envoy_config_endpoint_v3_ClusterLoadAssignment* cluster_load_assignment =
3135         envoy_config_endpoint_v3_ClusterLoadAssignment_parse(
3136             encoded_cluster_load_assignment.data,
3137             encoded_cluster_load_assignment.size, context.arena);
3138     if (cluster_load_assignment == nullptr) {
3139       errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
3140           absl::StrCat("resource index ", i,
3141                        ": Can't parse cluster_load_assignment.")
3142               .c_str()));
3143       continue;
3144     }
3145     MaybeLogClusterLoadAssignment(context, cluster_load_assignment);
3146     // Check the EDS service name.  Ignore unexpected names.
3147     std::string eds_service_name = UpbStringToStdString(
3148         envoy_config_endpoint_v3_ClusterLoadAssignment_cluster_name(
3149             cluster_load_assignment));
3150     if (expected_eds_service_names.find(eds_service_name) ==
3151         expected_eds_service_names.end()) {
3152       continue;
3153     }
3154     // Fail on duplicate resources.
3155     if (eds_update_map->find(eds_service_name) != eds_update_map->end()) {
3156       errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
3157           absl::StrCat("duplicate resource name \"", eds_service_name, "\"")
3158               .c_str()));
3159       resource_names_failed->insert(eds_service_name);
3160       continue;
3161     }
3162     // Serialize into JSON and store it in the EdsUpdateMap
3163     XdsApi::EdsResourceData& eds_resource_data =
3164         (*eds_update_map)[eds_service_name];
3165     XdsApi::EdsUpdate& eds_update = eds_resource_data.resource;
3166     eds_resource_data.serialized_proto =
3167         UpbStringToStdString(encoded_cluster_load_assignment);
3168     // Get the endpoints.
3169     size_t locality_size;
3170     const envoy_config_endpoint_v3_LocalityLbEndpoints* const* endpoints =
3171         envoy_config_endpoint_v3_ClusterLoadAssignment_endpoints(
3172             cluster_load_assignment, &locality_size);
3173     grpc_error_handle error = GRPC_ERROR_NONE;
3174     for (size_t j = 0; j < locality_size; ++j) {
3175       size_t priority;
3176       XdsApi::EdsUpdate::Priority::Locality locality;
3177       error = LocalityParse(endpoints[j], &locality, &priority);
3178       if (error != GRPC_ERROR_NONE) break;
3179       // Filter out locality with weight 0.
3180       if (locality.lb_weight == 0) continue;
3181       // Make sure prorities is big enough. Note that they might not
3182       // arrive in priority order.
3183       while (eds_update.priorities.size() < priority + 1) {
3184         eds_update.priorities.emplace_back();
3185       }
3186       eds_update.priorities[priority].localities.emplace(locality.name.get(),
3187                                                          std::move(locality));
3188     }
3189     if (error != GRPC_ERROR_NONE) {
3190       errors.push_back(grpc_error_add_child(
3191           GRPC_ERROR_CREATE_FROM_COPIED_STRING(
3192               absl::StrCat(eds_service_name, ": locality validation error")
3193                   .c_str()),
3194           error));
3195       resource_names_failed->insert(eds_service_name);
3196       continue;
3197     }
3198     for (const auto& priority : eds_update.priorities) {
3199       if (priority.localities.empty()) {
3200         errors.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
3201             absl::StrCat(eds_service_name, ": sparse priority list").c_str()));
3202         resource_names_failed->insert(eds_service_name);
3203         continue;
3204       }
3205     }
3206     // Get the drop config.
3207     eds_update.drop_config = MakeRefCounted<XdsApi::EdsUpdate::DropConfig>();
3208     const envoy_config_endpoint_v3_ClusterLoadAssignment_Policy* policy =
3209         envoy_config_endpoint_v3_ClusterLoadAssignment_policy(
3210             cluster_load_assignment);
3211     if (policy != nullptr) {
3212       size_t drop_size;
3213       const envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_DropOverload* const*
3214           drop_overload =
3215               envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_drop_overloads(
3216                   policy, &drop_size);
3217       for (size_t j = 0; j < drop_size; ++j) {
3218         error =
3219             DropParseAndAppend(drop_overload[j], eds_update.drop_config.get());
3220         if (error != GRPC_ERROR_NONE) break;
3221       }
3222       if (error != GRPC_ERROR_NONE) {
3223         errors.push_back(grpc_error_add_child(
3224             GRPC_ERROR_CREATE_FROM_COPIED_STRING(
3225                 absl::StrCat(eds_service_name, ": drop config validation error")
3226                     .c_str()),
3227             error));
3228         resource_names_failed->insert(eds_service_name);
3229         continue;
3230       }
3231     }
3232   }
3233   return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing EDS response", &errors);
3234 }
3235 
TypeUrlInternalToExternal(absl::string_view type_url)3236 std::string TypeUrlInternalToExternal(absl::string_view type_url) {
3237   if (type_url == kLdsV2TypeUrl) {
3238     return XdsApi::kLdsTypeUrl;
3239   } else if (type_url == kRdsV2TypeUrl) {
3240     return XdsApi::kRdsTypeUrl;
3241   } else if (type_url == kCdsV2TypeUrl) {
3242     return XdsApi::kCdsTypeUrl;
3243   } else if (type_url == kEdsV2TypeUrl) {
3244     return XdsApi::kEdsTypeUrl;
3245   }
3246   return std::string(type_url);
3247 }
3248 
3249 template <typename UpdateMap>
MoveUpdatesToFailedSet(UpdateMap * update_map,std::set<std::string> * resource_names_failed)3250 void MoveUpdatesToFailedSet(UpdateMap* update_map,
3251                             std::set<std::string>* resource_names_failed) {
3252   for (const auto& p : *update_map) {
3253     resource_names_failed->insert(p.first);
3254   }
3255   update_map->clear();
3256 }
3257 
3258 }  // namespace
3259 
ParseAdsResponse(const XdsBootstrap::XdsServer & server,const grpc_slice & encoded_response,const std::set<absl::string_view> & expected_listener_names,const std::set<absl::string_view> & expected_route_configuration_names,const std::set<absl::string_view> & expected_cluster_names,const std::set<absl::string_view> & expected_eds_service_names)3260 XdsApi::AdsParseResult XdsApi::ParseAdsResponse(
3261     const XdsBootstrap::XdsServer& server, const grpc_slice& encoded_response,
3262     const std::set<absl::string_view>& expected_listener_names,
3263     const std::set<absl::string_view>& expected_route_configuration_names,
3264     const std::set<absl::string_view>& expected_cluster_names,
3265     const std::set<absl::string_view>& expected_eds_service_names) {
3266   AdsParseResult result;
3267   upb::Arena arena;
3268   const EncodingContext context = {client_, tracer_, symtab_.ptr(), arena.ptr(),
3269                                    server.ShouldUseV3()};
3270   // Decode the response.
3271   const envoy_service_discovery_v3_DiscoveryResponse* response =
3272       envoy_service_discovery_v3_DiscoveryResponse_parse(
3273           reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(encoded_response)),
3274           GRPC_SLICE_LENGTH(encoded_response), arena.ptr());
3275   // If decoding fails, output an empty type_url and return.
3276   if (response == nullptr) {
3277     result.parse_error =
3278         GRPC_ERROR_CREATE_FROM_STATIC_STRING("Can't decode DiscoveryResponse.");
3279     return result;
3280   }
3281   MaybeLogDiscoveryResponse(context, response);
3282   // Record the type_url, the version_info, and the nonce of the response.
3283   result.type_url = TypeUrlInternalToExternal(UpbStringToAbsl(
3284       envoy_service_discovery_v3_DiscoveryResponse_type_url(response)));
3285   result.version = UpbStringToStdString(
3286       envoy_service_discovery_v3_DiscoveryResponse_version_info(response));
3287   result.nonce = UpbStringToStdString(
3288       envoy_service_discovery_v3_DiscoveryResponse_nonce(response));
3289   // Parse the response according to the resource type.
3290   if (IsLds(result.type_url)) {
3291     result.parse_error =
3292         LdsResponseParse(context, response, expected_listener_names,
3293                          &result.lds_update_map, &result.resource_names_failed);
3294     if (result.parse_error != GRPC_ERROR_NONE) {
3295       MoveUpdatesToFailedSet(&result.lds_update_map,
3296                              &result.resource_names_failed);
3297     }
3298   } else if (IsRds(result.type_url)) {
3299     result.parse_error =
3300         RdsResponseParse(context, response, expected_route_configuration_names,
3301                          &result.rds_update_map, &result.resource_names_failed);
3302     if (result.parse_error != GRPC_ERROR_NONE) {
3303       MoveUpdatesToFailedSet(&result.rds_update_map,
3304                              &result.resource_names_failed);
3305     }
3306   } else if (IsCds(result.type_url)) {
3307     result.parse_error =
3308         CdsResponseParse(context, response, expected_cluster_names,
3309                          &result.cds_update_map, &result.resource_names_failed);
3310     if (result.parse_error != GRPC_ERROR_NONE) {
3311       MoveUpdatesToFailedSet(&result.cds_update_map,
3312                              &result.resource_names_failed);
3313     }
3314   } else if (IsEds(result.type_url)) {
3315     result.parse_error =
3316         EdsResponseParse(context, response, expected_eds_service_names,
3317                          &result.eds_update_map, &result.resource_names_failed);
3318     if (result.parse_error != GRPC_ERROR_NONE) {
3319       MoveUpdatesToFailedSet(&result.eds_update_map,
3320                              &result.resource_names_failed);
3321     }
3322   }
3323   return result;
3324 }
3325 
3326 namespace {
3327 
MaybeLogLrsRequest(const EncodingContext & context,const envoy_service_load_stats_v3_LoadStatsRequest * request)3328 void MaybeLogLrsRequest(
3329     const EncodingContext& context,
3330     const envoy_service_load_stats_v3_LoadStatsRequest* request) {
3331   if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) &&
3332       gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
3333     const upb_msgdef* msg_type =
3334         envoy_service_load_stats_v3_LoadStatsRequest_getmsgdef(context.symtab);
3335     char buf[10240];
3336     upb_text_encode(request, msg_type, nullptr, 0, buf, sizeof(buf));
3337     gpr_log(GPR_DEBUG, "[xds_client %p] constructed LRS request: %s",
3338             context.client, buf);
3339   }
3340 }
3341 
SerializeLrsRequest(const EncodingContext & context,const envoy_service_load_stats_v3_LoadStatsRequest * request)3342 grpc_slice SerializeLrsRequest(
3343     const EncodingContext& context,
3344     const envoy_service_load_stats_v3_LoadStatsRequest* request) {
3345   size_t output_length;
3346   char* output = envoy_service_load_stats_v3_LoadStatsRequest_serialize(
3347       request, context.arena, &output_length);
3348   return grpc_slice_from_copied_buffer(output, output_length);
3349 }
3350 
3351 }  // namespace
3352 
CreateLrsInitialRequest(const XdsBootstrap::XdsServer & server)3353 grpc_slice XdsApi::CreateLrsInitialRequest(
3354     const XdsBootstrap::XdsServer& server) {
3355   upb::Arena arena;
3356   const EncodingContext context = {client_, tracer_, symtab_.ptr(), arena.ptr(),
3357                                    server.ShouldUseV3()};
3358   // Create a request.
3359   envoy_service_load_stats_v3_LoadStatsRequest* request =
3360       envoy_service_load_stats_v3_LoadStatsRequest_new(arena.ptr());
3361   // Populate node.
3362   envoy_config_core_v3_Node* node_msg =
3363       envoy_service_load_stats_v3_LoadStatsRequest_mutable_node(request,
3364                                                                 arena.ptr());
3365   PopulateNode(context, node_, build_version_, user_agent_name_, node_msg);
3366   envoy_config_core_v3_Node_add_client_features(
3367       node_msg, upb_strview_makez("envoy.lrs.supports_send_all_clusters"),
3368       arena.ptr());
3369   MaybeLogLrsRequest(context, request);
3370   return SerializeLrsRequest(context, request);
3371 }
3372 
3373 namespace {
3374 
LocalityStatsPopulate(const EncodingContext & context,envoy_config_endpoint_v3_UpstreamLocalityStats * output,const XdsLocalityName & locality_name,const XdsClusterLocalityStats::Snapshot & snapshot)3375 void LocalityStatsPopulate(
3376     const EncodingContext& context,
3377     envoy_config_endpoint_v3_UpstreamLocalityStats* output,
3378     const XdsLocalityName& locality_name,
3379     const XdsClusterLocalityStats::Snapshot& snapshot) {
3380   // Set locality.
3381   envoy_config_core_v3_Locality* locality =
3382       envoy_config_endpoint_v3_UpstreamLocalityStats_mutable_locality(
3383           output, context.arena);
3384   if (!locality_name.region().empty()) {
3385     envoy_config_core_v3_Locality_set_region(
3386         locality, StdStringToUpbString(locality_name.region()));
3387   }
3388   if (!locality_name.zone().empty()) {
3389     envoy_config_core_v3_Locality_set_zone(
3390         locality, StdStringToUpbString(locality_name.zone()));
3391   }
3392   if (!locality_name.sub_zone().empty()) {
3393     envoy_config_core_v3_Locality_set_sub_zone(
3394         locality, StdStringToUpbString(locality_name.sub_zone()));
3395   }
3396   // Set total counts.
3397   envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_successful_requests(
3398       output, snapshot.total_successful_requests);
3399   envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_requests_in_progress(
3400       output, snapshot.total_requests_in_progress);
3401   envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_error_requests(
3402       output, snapshot.total_error_requests);
3403   envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_issued_requests(
3404       output, snapshot.total_issued_requests);
3405   // Add backend metrics.
3406   for (const auto& p : snapshot.backend_metrics) {
3407     const std::string& metric_name = p.first;
3408     const XdsClusterLocalityStats::BackendMetric& metric_value = p.second;
3409     envoy_config_endpoint_v3_EndpointLoadMetricStats* load_metric =
3410         envoy_config_endpoint_v3_UpstreamLocalityStats_add_load_metric_stats(
3411             output, context.arena);
3412     envoy_config_endpoint_v3_EndpointLoadMetricStats_set_metric_name(
3413         load_metric, StdStringToUpbString(metric_name));
3414     envoy_config_endpoint_v3_EndpointLoadMetricStats_set_num_requests_finished_with_metric(
3415         load_metric, metric_value.num_requests_finished_with_metric);
3416     envoy_config_endpoint_v3_EndpointLoadMetricStats_set_total_metric_value(
3417         load_metric, metric_value.total_metric_value);
3418   }
3419 }
3420 
3421 }  // namespace
3422 
CreateLrsRequest(ClusterLoadReportMap cluster_load_report_map)3423 grpc_slice XdsApi::CreateLrsRequest(
3424     ClusterLoadReportMap cluster_load_report_map) {
3425   upb::Arena arena;
3426   const EncodingContext context = {client_, tracer_, symtab_.ptr(), arena.ptr(),
3427                                    false};
3428   // Create a request.
3429   envoy_service_load_stats_v3_LoadStatsRequest* request =
3430       envoy_service_load_stats_v3_LoadStatsRequest_new(arena.ptr());
3431   for (auto& p : cluster_load_report_map) {
3432     const std::string& cluster_name = p.first.first;
3433     const std::string& eds_service_name = p.first.second;
3434     const ClusterLoadReport& load_report = p.second;
3435     // Add cluster stats.
3436     envoy_config_endpoint_v3_ClusterStats* cluster_stats =
3437         envoy_service_load_stats_v3_LoadStatsRequest_add_cluster_stats(
3438             request, arena.ptr());
3439     // Set the cluster name.
3440     envoy_config_endpoint_v3_ClusterStats_set_cluster_name(
3441         cluster_stats, StdStringToUpbString(cluster_name));
3442     // Set EDS service name, if non-empty.
3443     if (!eds_service_name.empty()) {
3444       envoy_config_endpoint_v3_ClusterStats_set_cluster_service_name(
3445           cluster_stats, StdStringToUpbString(eds_service_name));
3446     }
3447     // Add locality stats.
3448     for (const auto& p : load_report.locality_stats) {
3449       const XdsLocalityName& locality_name = *p.first;
3450       const auto& snapshot = p.second;
3451       envoy_config_endpoint_v3_UpstreamLocalityStats* locality_stats =
3452           envoy_config_endpoint_v3_ClusterStats_add_upstream_locality_stats(
3453               cluster_stats, arena.ptr());
3454       LocalityStatsPopulate(context, locality_stats, locality_name, snapshot);
3455     }
3456     // Add dropped requests.
3457     uint64_t total_dropped_requests = 0;
3458     for (const auto& p : load_report.dropped_requests.categorized_drops) {
3459       const std::string& category = p.first;
3460       const uint64_t count = p.second;
3461       envoy_config_endpoint_v3_ClusterStats_DroppedRequests* dropped_requests =
3462           envoy_config_endpoint_v3_ClusterStats_add_dropped_requests(
3463               cluster_stats, arena.ptr());
3464       envoy_config_endpoint_v3_ClusterStats_DroppedRequests_set_category(
3465           dropped_requests, StdStringToUpbString(category));
3466       envoy_config_endpoint_v3_ClusterStats_DroppedRequests_set_dropped_count(
3467           dropped_requests, count);
3468       total_dropped_requests += count;
3469     }
3470     total_dropped_requests += load_report.dropped_requests.uncategorized_drops;
3471     // Set total dropped requests.
3472     envoy_config_endpoint_v3_ClusterStats_set_total_dropped_requests(
3473         cluster_stats, total_dropped_requests);
3474     // Set real load report interval.
3475     gpr_timespec timespec =
3476         grpc_millis_to_timespec(load_report.load_report_interval, GPR_TIMESPAN);
3477     google_protobuf_Duration* load_report_interval =
3478         envoy_config_endpoint_v3_ClusterStats_mutable_load_report_interval(
3479             cluster_stats, arena.ptr());
3480     google_protobuf_Duration_set_seconds(load_report_interval, timespec.tv_sec);
3481     google_protobuf_Duration_set_nanos(load_report_interval, timespec.tv_nsec);
3482   }
3483   MaybeLogLrsRequest(context, request);
3484   return SerializeLrsRequest(context, request);
3485 }
3486 
ParseLrsResponse(const grpc_slice & encoded_response,bool * send_all_clusters,std::set<std::string> * cluster_names,grpc_millis * load_reporting_interval)3487 grpc_error_handle XdsApi::ParseLrsResponse(
3488     const grpc_slice& encoded_response, bool* send_all_clusters,
3489     std::set<std::string>* cluster_names,
3490     grpc_millis* load_reporting_interval) {
3491   upb::Arena arena;
3492   // Decode the response.
3493   const envoy_service_load_stats_v3_LoadStatsResponse* decoded_response =
3494       envoy_service_load_stats_v3_LoadStatsResponse_parse(
3495           reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(encoded_response)),
3496           GRPC_SLICE_LENGTH(encoded_response), arena.ptr());
3497   // Parse the response.
3498   if (decoded_response == nullptr) {
3499     return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Can't decode response.");
3500   }
3501   // Check send_all_clusters.
3502   if (envoy_service_load_stats_v3_LoadStatsResponse_send_all_clusters(
3503           decoded_response)) {
3504     *send_all_clusters = true;
3505   } else {
3506     // Store the cluster names.
3507     size_t size;
3508     const upb_strview* clusters =
3509         envoy_service_load_stats_v3_LoadStatsResponse_clusters(decoded_response,
3510                                                                &size);
3511     for (size_t i = 0; i < size; ++i) {
3512       cluster_names->emplace(UpbStringToStdString(clusters[i]));
3513     }
3514   }
3515   // Get the load report interval.
3516   const google_protobuf_Duration* load_reporting_interval_duration =
3517       envoy_service_load_stats_v3_LoadStatsResponse_load_reporting_interval(
3518           decoded_response);
3519   gpr_timespec timespec{
3520       google_protobuf_Duration_seconds(load_reporting_interval_duration),
3521       google_protobuf_Duration_nanos(load_reporting_interval_duration),
3522       GPR_TIMESPAN};
3523   *load_reporting_interval = gpr_time_to_millis(timespec);
3524   return GRPC_ERROR_NONE;
3525 }
3526 
3527 namespace {
GrpcMillisToTimestamp(const EncodingContext & context,grpc_millis value)3528 google_protobuf_Timestamp* GrpcMillisToTimestamp(const EncodingContext& context,
3529                                                  grpc_millis value) {
3530   google_protobuf_Timestamp* timestamp =
3531       google_protobuf_Timestamp_new(context.arena);
3532   gpr_timespec timespec = grpc_millis_to_timespec(value, GPR_CLOCK_REALTIME);
3533   google_protobuf_Timestamp_set_seconds(timestamp, timespec.tv_sec);
3534   google_protobuf_Timestamp_set_nanos(timestamp, timespec.tv_nsec);
3535   return timestamp;
3536 }
3537 
CreateUpdateFailureStateUpb(const EncodingContext & context,const XdsApi::ResourceMetadata * resource_metadata)3538 envoy_admin_v3_UpdateFailureState* CreateUpdateFailureStateUpb(
3539     const EncodingContext& context,
3540     const XdsApi::ResourceMetadata* resource_metadata) {
3541   auto* update_failure_state =
3542       envoy_admin_v3_UpdateFailureState_new(context.arena);
3543   envoy_admin_v3_UpdateFailureState_set_details(
3544       update_failure_state,
3545       StdStringToUpbString(resource_metadata->failed_details));
3546   envoy_admin_v3_UpdateFailureState_set_version_info(
3547       update_failure_state,
3548       StdStringToUpbString(resource_metadata->failed_version));
3549   envoy_admin_v3_UpdateFailureState_set_last_update_attempt(
3550       update_failure_state,
3551       GrpcMillisToTimestamp(context, resource_metadata->failed_update_time));
3552   return update_failure_state;
3553 }
3554 
DumpLdsConfig(const EncodingContext & context,const XdsApi::ResourceTypeMetadata & resource_type_metadata,envoy_service_status_v3_PerXdsConfig * per_xds_config)3555 void DumpLdsConfig(const EncodingContext& context,
3556                    const XdsApi::ResourceTypeMetadata& resource_type_metadata,
3557                    envoy_service_status_v3_PerXdsConfig* per_xds_config) {
3558   upb_strview kLdsTypeUrlUpb = upb_strview_makez(XdsApi::kLdsTypeUrl);
3559   auto* listener_config_dump =
3560       envoy_service_status_v3_PerXdsConfig_mutable_listener_config(
3561           per_xds_config, context.arena);
3562   envoy_admin_v3_ListenersConfigDump_set_version_info(
3563       listener_config_dump,
3564       StdStringToUpbString(resource_type_metadata.version));
3565   for (auto& p : resource_type_metadata.resource_metadata_map) {
3566     absl::string_view name = p.first;
3567     const XdsApi::ResourceMetadata* meta = p.second;
3568     const upb_strview name_upb = StdStringToUpbString(name);
3569     auto* dynamic_listener =
3570         envoy_admin_v3_ListenersConfigDump_add_dynamic_listeners(
3571             listener_config_dump, context.arena);
3572     envoy_admin_v3_ListenersConfigDump_DynamicListener_set_name(
3573         dynamic_listener, name_upb);
3574     envoy_admin_v3_ListenersConfigDump_DynamicListener_set_client_status(
3575         dynamic_listener, meta->client_status);
3576     if (!meta->serialized_proto.empty()) {
3577       // Set in-effective listeners
3578       auto* dynamic_listener_state =
3579           envoy_admin_v3_ListenersConfigDump_DynamicListener_mutable_active_state(
3580               dynamic_listener, context.arena);
3581       envoy_admin_v3_ListenersConfigDump_DynamicListenerState_set_version_info(
3582           dynamic_listener_state, StdStringToUpbString(meta->version));
3583       envoy_admin_v3_ListenersConfigDump_DynamicListenerState_set_last_updated(
3584           dynamic_listener_state,
3585           GrpcMillisToTimestamp(context, meta->update_time));
3586       auto* listener_any =
3587           envoy_admin_v3_ListenersConfigDump_DynamicListenerState_mutable_listener(
3588               dynamic_listener_state, context.arena);
3589       google_protobuf_Any_set_type_url(listener_any, kLdsTypeUrlUpb);
3590       google_protobuf_Any_set_value(
3591           listener_any, StdStringToUpbString(meta->serialized_proto));
3592     }
3593     if (meta->client_status == XdsApi::ResourceMetadata::NACKED) {
3594       // Set error_state if NACKED
3595       envoy_admin_v3_ListenersConfigDump_DynamicListener_set_error_state(
3596           dynamic_listener, CreateUpdateFailureStateUpb(context, meta));
3597     }
3598   }
3599 }
3600 
DumpRdsConfig(const EncodingContext & context,const XdsApi::ResourceTypeMetadata & resource_type_metadata,envoy_service_status_v3_PerXdsConfig * per_xds_config)3601 void DumpRdsConfig(const EncodingContext& context,
3602                    const XdsApi::ResourceTypeMetadata& resource_type_metadata,
3603                    envoy_service_status_v3_PerXdsConfig* per_xds_config) {
3604   upb_strview kRdsTypeUrlUpb = upb_strview_makez(XdsApi::kRdsTypeUrl);
3605   auto* route_config_dump =
3606       envoy_service_status_v3_PerXdsConfig_mutable_route_config(per_xds_config,
3607                                                                 context.arena);
3608   for (auto& p : resource_type_metadata.resource_metadata_map) {
3609     absl::string_view name = p.first;
3610     const XdsApi::ResourceMetadata* meta = p.second;
3611     const upb_strview name_upb = StdStringToUpbString(name);
3612     auto* dynamic_route_config =
3613         envoy_admin_v3_RoutesConfigDump_add_dynamic_route_configs(
3614             route_config_dump, context.arena);
3615     envoy_admin_v3_RoutesConfigDump_DynamicRouteConfig_set_client_status(
3616         dynamic_route_config, meta->client_status);
3617     auto* route_config_any =
3618         envoy_admin_v3_RoutesConfigDump_DynamicRouteConfig_mutable_route_config(
3619             dynamic_route_config, context.arena);
3620     if (!meta->serialized_proto.empty()) {
3621       // Set in-effective route configs
3622       envoy_admin_v3_RoutesConfigDump_DynamicRouteConfig_set_version_info(
3623           dynamic_route_config, StdStringToUpbString(meta->version));
3624       envoy_admin_v3_RoutesConfigDump_DynamicRouteConfig_set_last_updated(
3625           dynamic_route_config,
3626           GrpcMillisToTimestamp(context, meta->update_time));
3627       google_protobuf_Any_set_type_url(route_config_any, kRdsTypeUrlUpb);
3628       google_protobuf_Any_set_value(
3629           route_config_any, StdStringToUpbString(meta->serialized_proto));
3630     } else {
3631       // If there isn't a working route config, we still need to print the
3632       // name.
3633       auto* route_config =
3634           envoy_config_route_v3_RouteConfiguration_new(context.arena);
3635       envoy_config_route_v3_RouteConfiguration_set_name(route_config, name_upb);
3636       size_t length;
3637       char* bytes = envoy_config_route_v3_RouteConfiguration_serialize(
3638           route_config, context.arena, &length);
3639       google_protobuf_Any_set_type_url(route_config_any, kRdsTypeUrlUpb);
3640       google_protobuf_Any_set_value(route_config_any,
3641                                     upb_strview_make(bytes, length));
3642     }
3643     if (meta->client_status == XdsApi::ResourceMetadata::NACKED) {
3644       // Set error_state if NACKED
3645       envoy_admin_v3_RoutesConfigDump_DynamicRouteConfig_set_error_state(
3646           dynamic_route_config, CreateUpdateFailureStateUpb(context, meta));
3647     }
3648   }
3649 }
3650 
DumpCdsConfig(const EncodingContext & context,const XdsApi::ResourceTypeMetadata & resource_type_metadata,envoy_service_status_v3_PerXdsConfig * per_xds_config)3651 void DumpCdsConfig(const EncodingContext& context,
3652                    const XdsApi::ResourceTypeMetadata& resource_type_metadata,
3653                    envoy_service_status_v3_PerXdsConfig* per_xds_config) {
3654   upb_strview kCdsTypeUrlUpb = upb_strview_makez(XdsApi::kCdsTypeUrl);
3655   auto* cluster_config_dump =
3656       envoy_service_status_v3_PerXdsConfig_mutable_cluster_config(
3657           per_xds_config, context.arena);
3658   envoy_admin_v3_ClustersConfigDump_set_version_info(
3659       cluster_config_dump,
3660       StdStringToUpbString(resource_type_metadata.version));
3661   for (auto& p : resource_type_metadata.resource_metadata_map) {
3662     absl::string_view name = p.first;
3663     const XdsApi::ResourceMetadata* meta = p.second;
3664     const upb_strview name_upb = StdStringToUpbString(name);
3665     auto* dynamic_cluster =
3666         envoy_admin_v3_ClustersConfigDump_add_dynamic_active_clusters(
3667             cluster_config_dump, context.arena);
3668     envoy_admin_v3_ClustersConfigDump_DynamicCluster_set_client_status(
3669         dynamic_cluster, meta->client_status);
3670     auto* cluster_any =
3671         envoy_admin_v3_ClustersConfigDump_DynamicCluster_mutable_cluster(
3672             dynamic_cluster, context.arena);
3673     if (!meta->serialized_proto.empty()) {
3674       // Set in-effective clusters
3675       envoy_admin_v3_ClustersConfigDump_DynamicCluster_set_version_info(
3676           dynamic_cluster, StdStringToUpbString(meta->version));
3677       envoy_admin_v3_ClustersConfigDump_DynamicCluster_set_last_updated(
3678           dynamic_cluster, GrpcMillisToTimestamp(context, meta->update_time));
3679       google_protobuf_Any_set_type_url(cluster_any, kCdsTypeUrlUpb);
3680       google_protobuf_Any_set_value(
3681           cluster_any, StdStringToUpbString(meta->serialized_proto));
3682     } else {
3683       // If there isn't a working cluster, we still need to print the name.
3684       auto* cluster = envoy_config_cluster_v3_Cluster_new(context.arena);
3685       envoy_config_cluster_v3_Cluster_set_name(cluster, name_upb);
3686       size_t length;
3687       char* bytes = envoy_config_cluster_v3_Cluster_serialize(
3688           cluster, context.arena, &length);
3689       google_protobuf_Any_set_type_url(cluster_any, kCdsTypeUrlUpb);
3690       google_protobuf_Any_set_value(cluster_any,
3691                                     upb_strview_make(bytes, length));
3692     }
3693     if (meta->client_status == XdsApi::ResourceMetadata::NACKED) {
3694       // Set error_state if NACKED
3695       envoy_admin_v3_ClustersConfigDump_DynamicCluster_set_error_state(
3696           dynamic_cluster, CreateUpdateFailureStateUpb(context, meta));
3697     }
3698   }
3699 }
3700 
DumpEdsConfig(const EncodingContext & context,const XdsApi::ResourceTypeMetadata & resource_type_metadata,envoy_service_status_v3_PerXdsConfig * per_xds_config)3701 void DumpEdsConfig(const EncodingContext& context,
3702                    const XdsApi::ResourceTypeMetadata& resource_type_metadata,
3703                    envoy_service_status_v3_PerXdsConfig* per_xds_config) {
3704   upb_strview kEdsTypeUrlUpb = upb_strview_makez(XdsApi::kEdsTypeUrl);
3705   auto* endpoint_config_dump =
3706       envoy_service_status_v3_PerXdsConfig_mutable_endpoint_config(
3707           per_xds_config, context.arena);
3708   for (auto& p : resource_type_metadata.resource_metadata_map) {
3709     absl::string_view name = p.first;
3710     const XdsApi::ResourceMetadata* meta = p.second;
3711     const upb_strview name_upb = StdStringToUpbString(name);
3712     auto* dynamic_endpoint =
3713         envoy_admin_v3_EndpointsConfigDump_add_dynamic_endpoint_configs(
3714             endpoint_config_dump, context.arena);
3715     envoy_admin_v3_EndpointsConfigDump_DynamicEndpointConfig_set_client_status(
3716         dynamic_endpoint, meta->client_status);
3717     auto* endpoint_any =
3718         envoy_admin_v3_EndpointsConfigDump_DynamicEndpointConfig_mutable_endpoint_config(
3719             dynamic_endpoint, context.arena);
3720     if (!meta->serialized_proto.empty()) {
3721       // Set in-effective endpoints
3722       envoy_admin_v3_EndpointsConfigDump_DynamicEndpointConfig_set_version_info(
3723           dynamic_endpoint, StdStringToUpbString(meta->version));
3724       envoy_admin_v3_EndpointsConfigDump_DynamicEndpointConfig_set_last_updated(
3725           dynamic_endpoint, GrpcMillisToTimestamp(context, meta->update_time));
3726       google_protobuf_Any_set_type_url(endpoint_any, kEdsTypeUrlUpb);
3727       google_protobuf_Any_set_value(
3728           endpoint_any, StdStringToUpbString(meta->serialized_proto));
3729     } else {
3730       // If there isn't a working endpoint, we still need to print the name.
3731       auto* cluster_load_assignment =
3732           envoy_config_endpoint_v3_ClusterLoadAssignment_new(context.arena);
3733       envoy_config_endpoint_v3_ClusterLoadAssignment_set_cluster_name(
3734           cluster_load_assignment, name_upb);
3735       size_t length;
3736       char* bytes = envoy_config_endpoint_v3_ClusterLoadAssignment_serialize(
3737           cluster_load_assignment, context.arena, &length);
3738       google_protobuf_Any_set_type_url(endpoint_any, kEdsTypeUrlUpb);
3739       google_protobuf_Any_set_value(endpoint_any,
3740                                     upb_strview_make(bytes, length));
3741     }
3742     if (meta->client_status == XdsApi::ResourceMetadata::NACKED) {
3743       // Set error_state if NACKED
3744       envoy_admin_v3_EndpointsConfigDump_DynamicEndpointConfig_set_error_state(
3745           dynamic_endpoint, CreateUpdateFailureStateUpb(context, meta));
3746     }
3747   }
3748 }
3749 
3750 }  // namespace
3751 
AssembleClientConfig(const ResourceTypeMetadataMap & resource_type_metadata_map)3752 std::string XdsApi::AssembleClientConfig(
3753     const ResourceTypeMetadataMap& resource_type_metadata_map) {
3754   upb::Arena arena;
3755   // Create the ClientConfig for resource metadata from XdsClient
3756   auto* client_config = envoy_service_status_v3_ClientConfig_new(arena.ptr());
3757   // Fill-in the node information
3758   auto* node = envoy_service_status_v3_ClientConfig_mutable_node(client_config,
3759                                                                  arena.ptr());
3760   const EncodingContext context = {client_, tracer_, symtab_.ptr(), arena.ptr(),
3761                                    true};
3762   PopulateNode(context, node_, build_version_, user_agent_name_, node);
3763   // Dump each xDS-type config into PerXdsConfig
3764   for (auto& p : resource_type_metadata_map) {
3765     absl::string_view type_url = p.first;
3766     const ResourceTypeMetadata& resource_type_metadata = p.second;
3767     if (type_url == kLdsTypeUrl) {
3768       auto* per_xds_config =
3769           envoy_service_status_v3_ClientConfig_add_xds_config(client_config,
3770                                                               context.arena);
3771       DumpLdsConfig(context, resource_type_metadata, per_xds_config);
3772     } else if (type_url == kRdsTypeUrl) {
3773       auto* per_xds_config =
3774           envoy_service_status_v3_ClientConfig_add_xds_config(client_config,
3775                                                               context.arena);
3776       DumpRdsConfig(context, resource_type_metadata, per_xds_config);
3777     } else if (type_url == kCdsTypeUrl) {
3778       auto* per_xds_config =
3779           envoy_service_status_v3_ClientConfig_add_xds_config(client_config,
3780                                                               context.arena);
3781       DumpCdsConfig(context, resource_type_metadata, per_xds_config);
3782     } else if (type_url == kEdsTypeUrl) {
3783       auto* per_xds_config =
3784           envoy_service_status_v3_ClientConfig_add_xds_config(client_config,
3785                                                               context.arena);
3786       DumpEdsConfig(context, resource_type_metadata, per_xds_config);
3787     } else {
3788       gpr_log(GPR_ERROR, "invalid type_url %s", std::string(type_url).c_str());
3789       return "";
3790     }
3791   }
3792   // Serialize the upb message to bytes
3793   size_t output_length;
3794   char* output = envoy_service_status_v3_ClientConfig_serialize(
3795       client_config, arena.ptr(), &output_length);
3796   return std::string(output, output_length);
3797 }
3798 
3799 }  // namespace grpc_core
3800