1 //
2 //
3 // Copyright 2020 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 "absl/strings/str_replace.h"
22
23 #include "src/core/ext/xds/xds_certificate_provider.h"
24 #include "src/core/ext/xds/xds_client.h"
25 #include "src/core/lib/address_utils/sockaddr_utils.h"
26 #include "src/core/lib/channel/channel_args.h"
27 #include "src/core/lib/gprpp/host_port.h"
28 #include "src/core/lib/iomgr/sockaddr.h"
29 #include "src/core/lib/iomgr/socket_utils.h"
30 #include "src/core/lib/security/credentials/xds/xds_credentials.h"
31 #include "src/core/lib/surface/api_trace.h"
32 #include "src/core/lib/surface/server.h"
33 #include "src/core/lib/uri/uri_parser.h"
34
35 namespace grpc_core {
36
37 TraceFlag grpc_xds_server_config_fetcher_trace(false,
38 "xds_server_config_fetcher");
39
40 namespace {
41
42 class FilterChainMatchManager
43 : public grpc_server_config_fetcher::ConnectionManager {
44 public:
FilterChainMatchManager(RefCountedPtr<XdsClient> xds_client,XdsApi::LdsUpdate::FilterChainMap filter_chain_map,absl::optional<XdsApi::LdsUpdate::FilterChainData> default_filter_chain)45 FilterChainMatchManager(
46 RefCountedPtr<XdsClient> xds_client,
47 XdsApi::LdsUpdate::FilterChainMap filter_chain_map,
48 absl::optional<XdsApi::LdsUpdate::FilterChainData> default_filter_chain)
49 : xds_client_(xds_client),
50 filter_chain_map_(std::move(filter_chain_map)),
51 default_filter_chain_(std::move(default_filter_chain)) {}
52
53 absl::StatusOr<grpc_channel_args*> UpdateChannelArgsForConnection(
54 grpc_channel_args* args, grpc_endpoint* tcp) override;
55
filter_chain_map() const56 const XdsApi::LdsUpdate::FilterChainMap& filter_chain_map() const {
57 return filter_chain_map_;
58 }
59
60 const absl::optional<XdsApi::LdsUpdate::FilterChainData>&
default_filter_chain() const61 default_filter_chain() const {
62 return default_filter_chain_;
63 }
64
65 private:
66 struct CertificateProviders {
67 // We need to save our own refs to the root and instance certificate
68 // providers since the xds certificate provider just stores a ref to their
69 // distributors.
70 RefCountedPtr<grpc_tls_certificate_provider> root;
71 RefCountedPtr<grpc_tls_certificate_provider> instance;
72 RefCountedPtr<XdsCertificateProvider> xds;
73 };
74
75 absl::StatusOr<RefCountedPtr<XdsCertificateProvider>>
76 CreateOrGetXdsCertificateProviderFromFilterChainData(
77 const XdsApi::LdsUpdate::FilterChainData* filter_chain);
78
79 const RefCountedPtr<XdsClient> xds_client_;
80 const XdsApi::LdsUpdate::FilterChainMap filter_chain_map_;
81 const absl::optional<XdsApi::LdsUpdate::FilterChainData>
82 default_filter_chain_;
83 Mutex mu_;
84 std::map<const XdsApi::LdsUpdate::FilterChainData*, CertificateProviders>
85 certificate_providers_map_ ABSL_GUARDED_BY(mu_);
86 };
87
IsLoopbackIp(const grpc_resolved_address * address)88 bool IsLoopbackIp(const grpc_resolved_address* address) {
89 const grpc_sockaddr* sock_addr =
90 reinterpret_cast<const grpc_sockaddr*>(&address->addr);
91 if (sock_addr->sa_family == GRPC_AF_INET) {
92 const grpc_sockaddr_in* addr4 =
93 reinterpret_cast<const grpc_sockaddr_in*>(sock_addr);
94 if (addr4->sin_addr.s_addr == grpc_htonl(INADDR_LOOPBACK)) {
95 return true;
96 }
97 } else if (sock_addr->sa_family == GRPC_AF_INET6) {
98 const grpc_sockaddr_in6* addr6 =
99 reinterpret_cast<const grpc_sockaddr_in6*>(sock_addr);
100 if (memcmp(&addr6->sin6_addr, &in6addr_loopback,
101 sizeof(in6addr_loopback)) == 0) {
102 return true;
103 }
104 }
105 return false;
106 }
107
FindFilterChainDataForSourcePort(const XdsApi::LdsUpdate::FilterChainMap::SourcePortsMap & source_ports_map,absl::string_view port_str)108 const XdsApi::LdsUpdate::FilterChainData* FindFilterChainDataForSourcePort(
109 const XdsApi::LdsUpdate::FilterChainMap::SourcePortsMap& source_ports_map,
110 absl::string_view port_str) {
111 int port = 0;
112 if (!absl::SimpleAtoi(port_str, &port)) return nullptr;
113 auto it = source_ports_map.find(port);
114 if (it != source_ports_map.end()) {
115 return it->second.data.get();
116 }
117 // Search for the catch-all port 0 since we didn't get a direct match
118 it = source_ports_map.find(0);
119 if (it != source_ports_map.end()) {
120 return it->second.data.get();
121 }
122 return nullptr;
123 }
124
FindFilterChainDataForSourceIp(const XdsApi::LdsUpdate::FilterChainMap::SourceIpVector & source_ip_vector,const grpc_resolved_address * source_ip,absl::string_view port)125 const XdsApi::LdsUpdate::FilterChainData* FindFilterChainDataForSourceIp(
126 const XdsApi::LdsUpdate::FilterChainMap::SourceIpVector& source_ip_vector,
127 const grpc_resolved_address* source_ip, absl::string_view port) {
128 const XdsApi::LdsUpdate::FilterChainMap::SourceIp* best_match = nullptr;
129 for (const auto& entry : source_ip_vector) {
130 // Special case for catch-all
131 if (!entry.prefix_range.has_value()) {
132 if (best_match == nullptr) {
133 best_match = &entry;
134 }
135 continue;
136 }
137 if (best_match != nullptr && best_match->prefix_range.has_value() &&
138 best_match->prefix_range->prefix_len >=
139 entry.prefix_range->prefix_len) {
140 continue;
141 }
142 if (grpc_sockaddr_match_subnet(source_ip, &entry.prefix_range->address,
143 entry.prefix_range->prefix_len)) {
144 best_match = &entry;
145 }
146 }
147 if (best_match == nullptr) return nullptr;
148 return FindFilterChainDataForSourcePort(best_match->ports_map, port);
149 }
150
FindFilterChainDataForSourceType(const XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceTypesArray & source_types_array,grpc_endpoint * tcp,absl::string_view destination_ip)151 const XdsApi::LdsUpdate::FilterChainData* FindFilterChainDataForSourceType(
152 const XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceTypesArray&
153 source_types_array,
154 grpc_endpoint* tcp, absl::string_view destination_ip) {
155 auto source_uri = URI::Parse(grpc_endpoint_get_peer(tcp));
156 if (!source_uri.ok() ||
157 (source_uri->scheme() != "ipv4" && source_uri->scheme() != "ipv6")) {
158 return nullptr;
159 }
160 std::string host;
161 std::string port;
162 if (!SplitHostPort(source_uri->path(), &host, &port)) {
163 return nullptr;
164 }
165 grpc_resolved_address source_addr;
166 grpc_error_handle error = grpc_string_to_sockaddr(
167 &source_addr, host.c_str(), 0 /* port doesn't matter here */);
168 if (error != GRPC_ERROR_NONE) {
169 gpr_log(GPR_DEBUG, "Could not parse string to socket address: %s",
170 host.c_str());
171 GRPC_ERROR_UNREF(error);
172 return nullptr;
173 }
174 // Use kAny only if kSameIporLoopback and kExternal are empty
175 if (source_types_array[static_cast<int>(
176 XdsApi::LdsUpdate::FilterChainMap::
177 ConnectionSourceType::kSameIpOrLoopback)]
178 .empty() &&
179 source_types_array[static_cast<int>(XdsApi::LdsUpdate::FilterChainMap::
180 ConnectionSourceType::kExternal)]
181 .empty()) {
182 return FindFilterChainDataForSourceIp(
183 source_types_array[static_cast<int>(
184 XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceType::kAny)],
185 &source_addr, port);
186 }
187 if (IsLoopbackIp(&source_addr) || host == destination_ip) {
188 return FindFilterChainDataForSourceIp(
189 source_types_array[static_cast<int>(
190 XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceType::
191 kSameIpOrLoopback)],
192 &source_addr, port);
193 } else {
194 return FindFilterChainDataForSourceIp(
195 source_types_array[static_cast<int>(
196 XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceType::
197 kExternal)],
198 &source_addr, port);
199 }
200 }
201
FindFilterChainDataForDestinationIp(const XdsApi::LdsUpdate::FilterChainMap::DestinationIpVector destination_ip_vector,grpc_endpoint * tcp)202 const XdsApi::LdsUpdate::FilterChainData* FindFilterChainDataForDestinationIp(
203 const XdsApi::LdsUpdate::FilterChainMap::DestinationIpVector
204 destination_ip_vector,
205 grpc_endpoint* tcp) {
206 auto destination_uri = URI::Parse(grpc_endpoint_get_local_address(tcp));
207 if (!destination_uri.ok() || (destination_uri->scheme() != "ipv4" &&
208 destination_uri->scheme() != "ipv6")) {
209 return nullptr;
210 }
211 std::string host;
212 std::string port;
213 if (!SplitHostPort(destination_uri->path(), &host, &port)) {
214 return nullptr;
215 }
216 grpc_resolved_address destination_addr;
217 grpc_error_handle error = grpc_string_to_sockaddr(
218 &destination_addr, host.c_str(), 0 /* port doesn't matter here */);
219 if (error != GRPC_ERROR_NONE) {
220 gpr_log(GPR_DEBUG, "Could not parse string to socket address: %s",
221 host.c_str());
222 GRPC_ERROR_UNREF(error);
223 return nullptr;
224 }
225 const XdsApi::LdsUpdate::FilterChainMap::DestinationIp* best_match = nullptr;
226 for (const auto& entry : destination_ip_vector) {
227 // Special case for catch-all
228 if (!entry.prefix_range.has_value()) {
229 if (best_match == nullptr) {
230 best_match = &entry;
231 }
232 continue;
233 }
234 if (best_match != nullptr && best_match->prefix_range.has_value() &&
235 best_match->prefix_range->prefix_len >=
236 entry.prefix_range->prefix_len) {
237 continue;
238 }
239 if (grpc_sockaddr_match_subnet(&destination_addr,
240 &entry.prefix_range->address,
241 entry.prefix_range->prefix_len)) {
242 best_match = &entry;
243 }
244 }
245 if (best_match == nullptr) return nullptr;
246 return FindFilterChainDataForSourceType(best_match->source_types_array, tcp,
247 host);
248 }
249
250 absl::StatusOr<RefCountedPtr<XdsCertificateProvider>>
CreateOrGetXdsCertificateProviderFromFilterChainData(const XdsApi::LdsUpdate::FilterChainData * filter_chain)251 FilterChainMatchManager::CreateOrGetXdsCertificateProviderFromFilterChainData(
252 const XdsApi::LdsUpdate::FilterChainData* filter_chain) {
253 MutexLock lock(&mu_);
254 auto it = certificate_providers_map_.find(filter_chain);
255 if (it != certificate_providers_map_.end()) {
256 return it->second.xds;
257 }
258 CertificateProviders certificate_providers;
259 // Configure root cert.
260 absl::string_view root_provider_instance_name =
261 filter_chain->downstream_tls_context.common_tls_context
262 .combined_validation_context
263 .validation_context_certificate_provider_instance.instance_name;
264 absl::string_view root_provider_cert_name =
265 filter_chain->downstream_tls_context.common_tls_context
266 .combined_validation_context
267 .validation_context_certificate_provider_instance.certificate_name;
268 if (!root_provider_instance_name.empty()) {
269 certificate_providers.root =
270 xds_client_->certificate_provider_store()
271 .CreateOrGetCertificateProvider(root_provider_instance_name);
272 if (certificate_providers.root == nullptr) {
273 return absl::NotFoundError(
274 absl::StrCat("Certificate provider instance name: \"",
275 root_provider_instance_name, "\" not recognized."));
276 }
277 }
278 // Configure identity cert.
279 absl::string_view identity_provider_instance_name =
280 filter_chain->downstream_tls_context.common_tls_context
281 .tls_certificate_certificate_provider_instance.instance_name;
282 absl::string_view identity_provider_cert_name =
283 filter_chain->downstream_tls_context.common_tls_context
284 .tls_certificate_certificate_provider_instance.certificate_name;
285 if (!identity_provider_instance_name.empty()) {
286 certificate_providers.instance =
287 xds_client_->certificate_provider_store()
288 .CreateOrGetCertificateProvider(identity_provider_instance_name);
289 if (certificate_providers.instance == nullptr) {
290 return absl::NotFoundError(
291 absl::StrCat("Certificate provider instance name: \"",
292 identity_provider_instance_name, "\" not recognized."));
293 }
294 }
295 certificate_providers.xds = MakeRefCounted<XdsCertificateProvider>();
296 certificate_providers.xds->UpdateRootCertNameAndDistributor(
297 "", root_provider_cert_name,
298 certificate_providers.root == nullptr
299 ? nullptr
300 : certificate_providers.root->distributor());
301 certificate_providers.xds->UpdateIdentityCertNameAndDistributor(
302 "", identity_provider_cert_name,
303 certificate_providers.instance == nullptr
304 ? nullptr
305 : certificate_providers.instance->distributor());
306 certificate_providers.xds->UpdateRequireClientCertificate(
307 "", filter_chain->downstream_tls_context.require_client_certificate);
308 auto xds_certificate_provider = certificate_providers.xds;
309 certificate_providers_map_.emplace(filter_chain,
310 std::move(certificate_providers));
311 return xds_certificate_provider;
312 }
313
314 absl::StatusOr<grpc_channel_args*>
UpdateChannelArgsForConnection(grpc_channel_args * args,grpc_endpoint * tcp)315 FilterChainMatchManager::UpdateChannelArgsForConnection(grpc_channel_args* args,
316 grpc_endpoint* tcp) {
317 const auto* filter_chain = FindFilterChainDataForDestinationIp(
318 filter_chain_map_.destination_ip_vector, tcp);
319 if (filter_chain == nullptr && default_filter_chain_.has_value()) {
320 filter_chain = &default_filter_chain_.value();
321 }
322 if (filter_chain == nullptr) {
323 grpc_channel_args_destroy(args);
324 return absl::UnavailableError("No matching filter chain found");
325 }
326 // Nothing to update if credentials are not xDS.
327 grpc_server_credentials* server_creds =
328 grpc_find_server_credentials_in_args(args);
329 if (server_creds == nullptr || server_creds->type() != kCredentialsTypeXds) {
330 return args;
331 }
332 absl::StatusOr<RefCountedPtr<XdsCertificateProvider>> result =
333 CreateOrGetXdsCertificateProviderFromFilterChainData(filter_chain);
334 if (!result.ok()) {
335 grpc_channel_args_destroy(args);
336 return result.status();
337 }
338 RefCountedPtr<XdsCertificateProvider> xds_certificate_provider =
339 std::move(*result);
340 GPR_ASSERT(xds_certificate_provider != nullptr);
341 grpc_arg arg_to_add = xds_certificate_provider->MakeChannelArg();
342 grpc_channel_args* updated_args =
343 grpc_channel_args_copy_and_add(args, &arg_to_add, 1);
344 grpc_channel_args_destroy(args);
345 return updated_args;
346 }
347
348 class XdsServerConfigFetcher : public grpc_server_config_fetcher {
349 public:
XdsServerConfigFetcher(RefCountedPtr<XdsClient> xds_client,grpc_server_xds_status_notifier notifier)350 explicit XdsServerConfigFetcher(RefCountedPtr<XdsClient> xds_client,
351 grpc_server_xds_status_notifier notifier)
352 : xds_client_(std::move(xds_client)), serving_status_notifier_(notifier) {
353 GPR_ASSERT(xds_client_ != nullptr);
354 }
355
StartWatch(std::string listening_address,grpc_channel_args * args,std::unique_ptr<grpc_server_config_fetcher::WatcherInterface> watcher)356 void StartWatch(std::string listening_address, grpc_channel_args* args,
357 std::unique_ptr<grpc_server_config_fetcher::WatcherInterface>
358 watcher) override {
359 grpc_server_config_fetcher::WatcherInterface* watcher_ptr = watcher.get();
360 auto listener_watcher = absl::make_unique<ListenerWatcher>(
361 std::move(watcher), args, xds_client_, serving_status_notifier_,
362 listening_address);
363 auto* listener_watcher_ptr = listener_watcher.get();
364 listening_address = absl::StrReplaceAll(
365 xds_client_->bootstrap().server_listener_resource_name_template(),
366 {{"%s", listening_address}});
367 xds_client_->WatchListenerData(listening_address,
368 std::move(listener_watcher));
369 MutexLock lock(&mu_);
370 auto& watcher_state = watchers_[watcher_ptr];
371 watcher_state.listening_address = listening_address;
372 watcher_state.listener_watcher = listener_watcher_ptr;
373 }
374
CancelWatch(grpc_server_config_fetcher::WatcherInterface * watcher)375 void CancelWatch(
376 grpc_server_config_fetcher::WatcherInterface* watcher) override {
377 MutexLock lock(&mu_);
378 auto it = watchers_.find(watcher);
379 if (it != watchers_.end()) {
380 // Cancel the watch on the listener before erasing
381 xds_client_->CancelListenerDataWatch(it->second.listening_address,
382 it->second.listener_watcher,
383 false /* delay_unsubscription */);
384 watchers_.erase(it);
385 }
386 }
387
388 // Return the interested parties from the xds client so that it can be polled.
interested_parties()389 grpc_pollset_set* interested_parties() override {
390 return xds_client_->interested_parties();
391 }
392
393 private:
394 class ListenerWatcher : public XdsClient::ListenerWatcherInterface {
395 public:
ListenerWatcher(std::unique_ptr<grpc_server_config_fetcher::WatcherInterface> server_config_watcher,grpc_channel_args * args,RefCountedPtr<XdsClient> xds_client,grpc_server_xds_status_notifier serving_status_notifier,std::string listening_address)396 explicit ListenerWatcher(
397 std::unique_ptr<grpc_server_config_fetcher::WatcherInterface>
398 server_config_watcher,
399 grpc_channel_args* args, RefCountedPtr<XdsClient> xds_client,
400 grpc_server_xds_status_notifier serving_status_notifier,
401 std::string listening_address)
402 : server_config_watcher_(std::move(server_config_watcher)),
403 args_(args),
404 xds_client_(std::move(xds_client)),
405 serving_status_notifier_(serving_status_notifier),
406 listening_address_(std::move(listening_address)) {}
407
~ListenerWatcher()408 ~ListenerWatcher() override { grpc_channel_args_destroy(args_); }
409
410 // Deleted due to special handling required for args_. Copy the channel args
411 // if we ever need these.
412 ListenerWatcher(const ListenerWatcher&) = delete;
413 ListenerWatcher& operator=(const ListenerWatcher&) = delete;
414
OnListenerChanged(XdsApi::LdsUpdate listener)415 void OnListenerChanged(XdsApi::LdsUpdate listener) override {
416 if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_server_config_fetcher_trace)) {
417 gpr_log(
418 GPR_INFO,
419 "[ListenerWatcher %p] Received LDS update from xds client %p: %s",
420 this, xds_client_.get(), listener.ToString().c_str());
421 }
422 if (listener.address != listening_address_) {
423 OnFatalError(absl::FailedPreconditionError(
424 "Address in LDS update does not match listening address"));
425 return;
426 }
427 if (filter_chain_match_manager_ == nullptr) {
428 if (serving_status_notifier_.on_serving_status_update != nullptr) {
429 serving_status_notifier_.on_serving_status_update(
430 serving_status_notifier_.user_data, listening_address_.c_str(),
431 GRPC_STATUS_OK, "");
432 } else {
433 gpr_log(GPR_INFO,
434 "xDS Listener resource obtained; will start serving on %s",
435 listening_address_.c_str());
436 }
437 }
438 if (filter_chain_match_manager_ == nullptr ||
439 !(listener.filter_chain_map ==
440 filter_chain_match_manager_->filter_chain_map() &&
441 listener.default_filter_chain ==
442 filter_chain_match_manager_->default_filter_chain())) {
443 filter_chain_match_manager_ = MakeRefCounted<FilterChainMatchManager>(
444 xds_client_, std::move(listener.filter_chain_map),
445 std::move(listener.default_filter_chain));
446 server_config_watcher_->UpdateConnectionManager(
447 filter_chain_match_manager_);
448 }
449 }
450
OnError(grpc_error_handle error)451 void OnError(grpc_error_handle error) override {
452 if (filter_chain_match_manager_ != nullptr) {
453 gpr_log(GPR_ERROR,
454 "ListenerWatcher:%p XdsClient reports error: %s for %s; "
455 "ignoring in favor of existing resource",
456 this, grpc_error_std_string(error).c_str(),
457 listening_address_.c_str());
458 } else {
459 if (serving_status_notifier_.on_serving_status_update != nullptr) {
460 serving_status_notifier_.on_serving_status_update(
461 serving_status_notifier_.user_data, listening_address_.c_str(),
462 GRPC_STATUS_UNAVAILABLE, grpc_error_std_string(error).c_str());
463 } else {
464 gpr_log(
465 GPR_ERROR,
466 "ListenerWatcher:%p error obtaining xDS Listener resource: %s; "
467 "not serving on %s",
468 this, grpc_error_std_string(error).c_str(),
469 listening_address_.c_str());
470 }
471 }
472 GRPC_ERROR_UNREF(error);
473 }
474
OnFatalError(absl::Status status)475 void OnFatalError(absl::Status status) {
476 gpr_log(
477 GPR_ERROR,
478 "ListenerWatcher:%p Encountered fatal error %s; not serving on %s",
479 this, status.ToString().c_str(), listening_address_.c_str());
480 if (filter_chain_match_manager_ != nullptr) {
481 // The server has started listening already, so we need to gracefully
482 // stop serving.
483 server_config_watcher_->StopServing();
484 filter_chain_match_manager_.reset();
485 }
486 if (serving_status_notifier_.on_serving_status_update != nullptr) {
487 serving_status_notifier_.on_serving_status_update(
488 serving_status_notifier_.user_data, listening_address_.c_str(),
489 static_cast<grpc_status_code>(status.raw_code()),
490 std::string(status.message()).c_str());
491 }
492 }
493
OnResourceDoesNotExist()494 void OnResourceDoesNotExist() override {
495 OnFatalError(absl::NotFoundError("Requested listener does not exist"));
496 }
497
498 private:
499 std::unique_ptr<grpc_server_config_fetcher::WatcherInterface>
500 server_config_watcher_;
501 grpc_channel_args* args_;
502 RefCountedPtr<XdsClient> xds_client_;
503 grpc_server_xds_status_notifier serving_status_notifier_;
504 std::string listening_address_;
505 RefCountedPtr<FilterChainMatchManager> filter_chain_match_manager_;
506 };
507
508 struct WatcherState {
509 std::string listening_address;
510 ListenerWatcher* listener_watcher = nullptr;
511 };
512
513 RefCountedPtr<XdsClient> xds_client_;
514 grpc_server_xds_status_notifier serving_status_notifier_;
515 Mutex mu_;
516 std::map<grpc_server_config_fetcher::WatcherInterface*, WatcherState>
517 watchers_ ABSL_GUARDED_BY(mu_);
518 };
519
520 } // namespace
521 } // namespace grpc_core
522
grpc_server_config_fetcher_xds_create(grpc_server_xds_status_notifier notifier,const grpc_channel_args * args)523 grpc_server_config_fetcher* grpc_server_config_fetcher_xds_create(
524 grpc_server_xds_status_notifier notifier, const grpc_channel_args* args) {
525 grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
526 grpc_core::ExecCtx exec_ctx;
527 GRPC_API_TRACE("grpc_server_config_fetcher_xds_create()", 0, ());
528 grpc_error_handle error = GRPC_ERROR_NONE;
529 grpc_core::RefCountedPtr<grpc_core::XdsClient> xds_client =
530 grpc_core::XdsClient::GetOrCreate(args, &error);
531 if (error != GRPC_ERROR_NONE) {
532 gpr_log(GPR_ERROR, "Failed to create xds client: %s",
533 grpc_error_std_string(error).c_str());
534 GRPC_ERROR_UNREF(error);
535 return nullptr;
536 }
537 if (xds_client->bootstrap()
538 .server_listener_resource_name_template()
539 .empty()) {
540 gpr_log(GPR_ERROR,
541 "server_listener_resource_name_template not provided in bootstrap "
542 "file.");
543 return nullptr;
544 }
545 return new grpc_core::XdsServerConfigFetcher(std::move(xds_client), notifier);
546 }
547