Line data Source code
1 : #pragma once
2 :
3 : #include "envoy/common/backoff_strategy.h"
4 : #include "envoy/common/key_value_store.h"
5 : #include "envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.pb.h"
6 : #include "envoy/http/filter.h"
7 : #include "envoy/network/dns.h"
8 : #include "envoy/server/factory_context.h"
9 : #include "envoy/thread_local/thread_local.h"
10 :
11 : #include "source/common/common/cleanup.h"
12 : #include "source/extensions/common/dynamic_forward_proxy/dns_cache.h"
13 : #include "source/extensions/common/dynamic_forward_proxy/dns_cache_resource_manager.h"
14 : #include "source/server/generic_factory_context.h"
15 :
16 : #include "absl/container/flat_hash_map.h"
17 :
18 : namespace Envoy {
19 : namespace Extensions {
20 : namespace Common {
21 : namespace DynamicForwardProxy {
22 :
23 : /**
24 : * All DNS cache stats. @see stats_macros.h
25 : */
26 : #define ALL_DNS_CACHE_STATS(COUNTER, GAUGE) \
27 0 : COUNTER(cache_load) \
28 0 : COUNTER(dns_query_attempt) \
29 0 : COUNTER(dns_query_failure) \
30 0 : COUNTER(dns_query_success) \
31 0 : COUNTER(dns_query_timeout) \
32 0 : COUNTER(host_added) \
33 0 : COUNTER(host_address_changed) \
34 0 : COUNTER(host_overflow) \
35 0 : COUNTER(host_removed) \
36 0 : COUNTER(dns_rq_pending_overflow) \
37 0 : GAUGE(num_hosts, NeverImport)
38 :
39 : /**
40 : * Struct definition for all DNS cache stats. @see stats_macros.h
41 : */
42 : struct DnsCacheStats {
43 : ALL_DNS_CACHE_STATS(GENERATE_COUNTER_STRUCT, GENERATE_GAUGE_STRUCT)
44 : };
45 :
46 : class DnsCacheImplTest;
47 :
48 : class DnsCacheImpl : public DnsCache, Logger::Loggable<Logger::Id::forward_proxy> {
49 : public:
50 : // Create a DnsCacheImpl or return a failed status;
51 : static absl::StatusOr<std::shared_ptr<DnsCacheImpl>> createDnsCacheImpl(
52 : Server::Configuration::GenericFactoryContext& context,
53 : const envoy::extensions::common::dynamic_forward_proxy::v3::DnsCacheConfig& config);
54 :
55 : ~DnsCacheImpl() override;
56 : static DnsCacheStats generateDnsCacheStats(Stats::Scope& scope);
57 : static Network::DnsResolverSharedPtr selectDnsResolver(
58 : const envoy::extensions::common::dynamic_forward_proxy::v3::DnsCacheConfig& config,
59 : Event::Dispatcher& main_thread_dispatcher,
60 : Server::Configuration::CommonFactoryContext& context);
61 :
62 : // DnsCache
63 : LoadDnsCacheEntryResult loadDnsCacheEntry(absl::string_view host, uint16_t default_port,
64 : bool is_proxy_lookup,
65 : LoadDnsCacheEntryCallbacks& callbacks) override;
66 : AddUpdateCallbacksHandlePtr addUpdateCallbacks(UpdateCallbacks& callbacks) override;
67 : void iterateHostMap(IterateHostMapCb cb) override;
68 : absl::optional<const DnsHostInfoSharedPtr> getHost(absl::string_view host_name) override;
69 : Upstream::ResourceAutoIncDecPtr canCreateDnsRequest() override;
70 : void forceRefreshHosts() override;
71 :
72 : private:
73 : DnsCacheImpl(Server::Configuration::GenericFactoryContext& context,
74 : const envoy::extensions::common::dynamic_forward_proxy::v3::DnsCacheConfig& config);
75 : struct LoadDnsCacheEntryHandleImpl
76 : : public LoadDnsCacheEntryHandle,
77 : RaiiMapOfListElement<std::string, LoadDnsCacheEntryHandleImpl*> {
78 : LoadDnsCacheEntryHandleImpl(
79 : absl::flat_hash_map<std::string, std::list<LoadDnsCacheEntryHandleImpl*>>& parent,
80 : absl::string_view host, LoadDnsCacheEntryCallbacks& callbacks)
81 : : RaiiMapOfListElement<std::string, LoadDnsCacheEntryHandleImpl*>(parent, host, this),
82 0 : callbacks_(callbacks) {}
83 :
84 : LoadDnsCacheEntryCallbacks& callbacks_;
85 : };
86 :
87 : class DnsHostInfoImpl;
88 : using DnsHostInfoImplSharedPtr = std::shared_ptr<DnsHostInfoImpl>;
89 :
90 : struct HostMapUpdateInfo {
91 : HostMapUpdateInfo(const std::string& host, DnsHostInfoImplSharedPtr info)
92 0 : : host_(host), info_(std::move(info)) {}
93 : std::string host_;
94 : DnsHostInfoImplSharedPtr info_;
95 : };
96 : using HostMapUpdateInfoSharedPtr = std::shared_ptr<HostMapUpdateInfo>;
97 :
98 : // Per-thread DNS cache info including pending callbacks.
99 : struct ThreadLocalHostInfo : public ThreadLocal::ThreadLocalObject {
100 0 : ThreadLocalHostInfo(DnsCacheImpl& parent) : parent_{parent} {}
101 : ~ThreadLocalHostInfo() override;
102 : void onHostMapUpdate(const HostMapUpdateInfoSharedPtr& resolved_info);
103 : absl::flat_hash_map<std::string, std::list<LoadDnsCacheEntryHandleImpl*>> pending_resolutions_;
104 : DnsCacheImpl& parent_;
105 : };
106 :
107 : class DnsHostInfoImpl : public DnsHostInfo {
108 : public:
109 : DnsHostInfoImpl(TimeSource& time_source, absl::string_view resolved_host, bool is_ip_address)
110 : : time_source_(time_source), resolved_host_(resolved_host), is_ip_address_(is_ip_address),
111 0 : stale_at_time_(time_source.monotonicTime()) {
112 0 : touch();
113 0 : }
114 :
115 : // DnsHostInfo
116 0 : Network::Address::InstanceConstSharedPtr address() const override {
117 0 : absl::ReaderMutexLock lock{&resolve_lock_};
118 0 : return address_;
119 0 : }
120 :
121 0 : std::vector<Network::Address::InstanceConstSharedPtr> addressList() const override {
122 0 : std::vector<Network::Address::InstanceConstSharedPtr> ret;
123 0 : absl::ReaderMutexLock lock{&resolve_lock_};
124 0 : ret = address_list_;
125 0 : return ret;
126 0 : }
127 :
128 0 : const std::string& resolvedHost() const override { return resolved_host_; }
129 0 : bool isIpAddress() const override { return is_ip_address_; }
130 0 : void touch() final { last_used_time_ = time_source_.monotonicTime().time_since_epoch(); }
131 0 : void updateStale(MonotonicTime resolution_time, std::chrono::seconds ttl) {
132 0 : stale_at_time_ = resolution_time + ttl;
133 0 : }
134 0 : bool isStale() {
135 0 : return time_source_.monotonicTime() > static_cast<MonotonicTime>(stale_at_time_);
136 0 : }
137 :
138 : void setAddresses(Network::Address::InstanceConstSharedPtr address,
139 0 : std::vector<Network::Address::InstanceConstSharedPtr>&& list) {
140 0 : absl::WriterMutexLock lock{&resolve_lock_};
141 0 : if (!(Runtime::runtimeFeatureEnabled(
142 0 : "envoy.reloadable_features.dns_cache_set_first_resolve_complete"))) {
143 0 : first_resolve_complete_ = true;
144 0 : }
145 0 : address_ = address;
146 0 : address_list_ = std::move(list);
147 0 : }
148 :
149 0 : std::chrono::steady_clock::duration lastUsedTime() const { return last_used_time_.load(); }
150 :
151 0 : bool firstResolveComplete() const override {
152 0 : absl::ReaderMutexLock lock{&resolve_lock_};
153 0 : return first_resolve_complete_;
154 0 : }
155 :
156 0 : void setFirstResolveComplete() {
157 0 : absl::WriterMutexLock lock{&resolve_lock_};
158 0 : first_resolve_complete_ = true;
159 0 : }
160 :
161 : private:
162 : friend class DnsCacheImplTest;
163 : TimeSource& time_source_;
164 : const std::string resolved_host_;
165 : const bool is_ip_address_;
166 : mutable absl::Mutex resolve_lock_;
167 : Network::Address::InstanceConstSharedPtr address_ ABSL_GUARDED_BY(resolve_lock_);
168 : std::vector<Network::Address::InstanceConstSharedPtr>
169 : address_list_ ABSL_GUARDED_BY(resolve_lock_);
170 :
171 : // Using std::chrono::steady_clock::duration is required for compilation within an atomic vs.
172 : // using MonotonicTime.
173 : std::atomic<std::chrono::steady_clock::duration> last_used_time_;
174 : std::atomic<MonotonicTime> stale_at_time_;
175 : bool first_resolve_complete_ ABSL_GUARDED_BY(resolve_lock_){false};
176 : };
177 :
178 : // Primary host information that accounts for TTL, re-resolution, etc.
179 : struct PrimaryHostInfo {
180 : PrimaryHostInfo(DnsCacheImpl& parent, absl::string_view host_to_resolve, uint16_t port,
181 : bool is_ip_address, const Event::TimerCb& refresh_timer_cb,
182 : const Event::TimerCb& timeout_timer_cb);
183 : ~PrimaryHostInfo();
184 :
185 : DnsCacheImpl& parent_;
186 : const uint16_t port_;
187 : const Event::TimerPtr refresh_timer_;
188 : const Event::TimerPtr timeout_timer_;
189 : const DnsHostInfoImplSharedPtr host_info_;
190 : const BackOffStrategyPtr failure_backoff_strategy_;
191 : Network::ActiveDnsQuery* active_query_{};
192 : };
193 :
194 : // Hold PrimaryHostInfo by shared_ptr to avoid having to hold the map mutex while updating
195 : // individual entries.
196 : using PrimaryHostInfoPtr = std::unique_ptr<PrimaryHostInfo>;
197 :
198 : struct AddUpdateCallbacksHandleImpl : public AddUpdateCallbacksHandle,
199 : RaiiListElement<AddUpdateCallbacksHandleImpl*> {
200 : AddUpdateCallbacksHandleImpl(std::list<AddUpdateCallbacksHandleImpl*>& parent,
201 : UpdateCallbacks& callbacks)
202 0 : : RaiiListElement<AddUpdateCallbacksHandleImpl*>(parent, this), callbacks_(callbacks) {}
203 :
204 : UpdateCallbacks& callbacks_;
205 : };
206 :
207 : void startCacheLoad(const std::string& host, uint16_t default_port, bool is_proxy_lookup);
208 :
209 : void startResolve(const std::string& host, PrimaryHostInfo& host_info)
210 : ABSL_LOCKS_EXCLUDED(primary_hosts_lock_);
211 :
212 : void finishResolve(const std::string& host, Network::DnsResolver::ResolutionStatus status,
213 : std::list<Network::DnsResponse>&& response,
214 : absl::optional<MonotonicTime> resolution_time = {},
215 : bool is_proxy_lookup = false);
216 : void runAddUpdateCallbacks(const std::string& host, const DnsHostInfoSharedPtr& host_info);
217 : void runResolutionCompleteCallbacks(const std::string& host,
218 : const DnsHostInfoSharedPtr& host_info,
219 : Network::DnsResolver::ResolutionStatus status);
220 : void runRemoveCallbacks(const std::string& host);
221 : void notifyThreads(const std::string& host, const DnsHostInfoImplSharedPtr& resolved_info);
222 : void onReResolve(const std::string& host);
223 : void onResolveTimeout(const std::string& host);
224 : PrimaryHostInfo& getPrimaryHost(const std::string& host);
225 :
226 : void addCacheEntry(const std::string& host,
227 : const Network::Address::InstanceConstSharedPtr& address,
228 : const std::vector<Network::Address::InstanceConstSharedPtr>& address_list,
229 : const std::chrono::seconds ttl);
230 : void removeCacheEntry(const std::string& host);
231 : void loadCacheEntries(
232 : const envoy::extensions::common::dynamic_forward_proxy::v3::DnsCacheConfig& config);
233 : PrimaryHostInfo* createHost(const std::string& host, uint16_t default_port);
234 : absl::optional<Network::DnsResponse> parseValue(absl::string_view value,
235 : absl::optional<MonotonicTime>& resolution_time);
236 :
237 : Event::Dispatcher& main_thread_dispatcher_;
238 : const envoy::extensions::common::dynamic_forward_proxy::v3::DnsCacheConfig config_;
239 : Random::RandomGenerator& random_generator_;
240 : const Network::DnsLookupFamily dns_lookup_family_;
241 : const Network::DnsResolverSharedPtr resolver_;
242 : ThreadLocal::TypedSlot<ThreadLocalHostInfo> tls_slot_;
243 : Stats::ScopeSharedPtr scope_;
244 : DnsCacheStats stats_;
245 : std::list<AddUpdateCallbacksHandleImpl*> update_callbacks_;
246 : absl::Mutex primary_hosts_lock_;
247 : absl::flat_hash_map<std::string, PrimaryHostInfoPtr>
248 : primary_hosts_ ABSL_GUARDED_BY(primary_hosts_lock_);
249 : std::unique_ptr<KeyValueStore> key_value_store_;
250 : DnsCacheResourceManagerImpl resource_manager_;
251 : const std::chrono::milliseconds refresh_interval_;
252 : const std::chrono::milliseconds min_refresh_interval_;
253 : const std::chrono::milliseconds timeout_interval_;
254 : Filesystem::Instance& file_system_;
255 : ProtobufMessage::ValidationVisitor& validation_visitor_;
256 : const std::chrono::milliseconds host_ttl_;
257 : const uint32_t max_hosts_;
258 : };
259 :
260 : } // namespace DynamicForwardProxy
261 : } // namespace Common
262 : } // namespace Extensions
263 : } // namespace Envoy
|