1
#pragma once
2

            
3
#include "envoy/common/platform.h"
4
#include "envoy/extensions/geoip_providers/maxmind/v3/maxmind.pb.h"
5
#include "envoy/geoip/geoip_provider_driver.h"
6

            
7
#include "source/common/common/logger.h"
8
#include "source/common/common/thread_synchronizer.h"
9

            
10
#include "maxminddb.h"
11

            
12
namespace Envoy {
13
namespace Extensions {
14
namespace GeoipProviders {
15
namespace Maxmind {
16

            
17
class GeoipProviderConfig {
18
public:
19
  GeoipProviderConfig(const envoy::extensions::geoip_providers::maxmind::v3::MaxMindConfig& config,
20
                      const std::string& stat_prefix, Stats::Scope& scope);
21

            
22
258
  const absl::optional<std::string>& cityDbPath() const { return city_db_path_; }
23
212
  const absl::optional<std::string>& ispDbPath() const { return isp_db_path_; }
24
214
  const absl::optional<std::string>& anonDbPath() const { return anon_db_path_; }
25
232
  const absl::optional<std::string>& asnDbPath() const { return asn_db_path_; }
26
195
  const absl::optional<std::string>& countryDbPath() const { return country_db_path_; }
27

            
28
  bool isLookupEnabledForHeader(const absl::optional<std::string>& header);
29
107
  bool isAsnDbPathSet() const { return asn_db_path_.has_value(); }
30
10
  bool isIspDbPathSet() const { return isp_db_path_.has_value(); }
31
136
  bool isCountryDbPathSet() const { return country_db_path_.has_value(); }
32
4
  bool isCityDbPathSet() const { return city_db_path_.has_value(); }
33

            
34
203
  const absl::optional<std::string>& countryHeader() const { return country_header_; }
35
146
  const absl::optional<std::string>& cityHeader() const { return city_header_; }
36
101
  const absl::optional<std::string>& regionHeader() const { return region_header_; }
37
194
  const absl::optional<std::string>& asnHeader() const { return asn_header_; }
38
125
  const absl::optional<std::string>& asnOrgHeader() const { return asn_org_header_; }
39

            
40
120
  const absl::optional<std::string>& anonHeader() const { return anon_header_; }
41
94
  const absl::optional<std::string>& anonVpnHeader() const { return anon_vpn_header_; }
42
20
  const absl::optional<std::string>& anonHostingHeader() const { return anon_hosting_header_; }
43
20
  const absl::optional<std::string>& anonTorHeader() const { return anon_tor_header_; }
44
20
  const absl::optional<std::string>& anonProxyHeader() const { return anon_proxy_header_; }
45

            
46
120
  const absl::optional<std::string>& ispHeader() const { return isp_header_; }
47
111
  const absl::optional<std::string>& applePrivateRelayHeader() const {
48
111
    return apple_private_relay_header_;
49
111
  }
50

            
51
13
  void incLookupError(absl::string_view maxmind_db_type) {
52
13
    incCounter(
53
13
        stat_name_set_->getBuiltin(absl::StrCat(maxmind_db_type, ".lookup_error"), unknown_hit_));
54
13
  }
55

            
56
119
  void incTotal(absl::string_view maxmind_db_type) {
57
119
    incCounter(stat_name_set_->getBuiltin(absl::StrCat(maxmind_db_type, ".total"), unknown_hit_));
58
119
  }
59

            
60
86
  void incHit(absl::string_view maxmind_db_type) {
61
86
    incCounter(stat_name_set_->getBuiltin(absl::StrCat(maxmind_db_type, ".hit"), unknown_hit_));
62
86
  }
63

            
64
12
  void incDbReloadSuccess(absl::string_view maxmind_db_type) {
65
12
    incCounter(stat_name_set_->getBuiltin(absl::StrCat(maxmind_db_type, ".db_reload_success"),
66
12
                                          unknown_hit_));
67
12
  }
68

            
69
2
  void incDbReloadError(absl::string_view maxmind_db_type) {
70
2
    incCounter(stat_name_set_->getBuiltin(absl::StrCat(maxmind_db_type, ".db_reload_error"),
71
2
                                          unknown_hit_));
72
2
  }
73

            
74
141
  void setDbBuildEpoch(absl::string_view maxmind_db_type, const uint64_t value) {
75
141
    setGuage(
76
141
        stat_name_set_->getBuiltin(absl::StrCat(maxmind_db_type, ".db_build_epoch"), unknown_hit_),
77
141
        value);
78
141
  }
79

            
80
  void registerGeoDbStats(const absl::string_view& db_type);
81

            
82
50
  Stats::Scope& getStatsScopeForTest() const { return *stats_scope_; }
83

            
84
private:
85
  absl::optional<std::string> city_db_path_;
86
  absl::optional<std::string> isp_db_path_;
87
  absl::optional<std::string> anon_db_path_;
88
  absl::optional<std::string> asn_db_path_;
89
  absl::optional<std::string> country_db_path_;
90

            
91
  absl::optional<std::string> country_header_;
92
  absl::optional<std::string> city_header_;
93
  absl::optional<std::string> region_header_;
94
  absl::optional<std::string> asn_header_;
95
  absl::optional<std::string> asn_org_header_;
96

            
97
  absl::optional<std::string> anon_header_;
98
  absl::optional<std::string> anon_vpn_header_;
99
  absl::optional<std::string> anon_hosting_header_;
100
  absl::optional<std::string> anon_tor_header_;
101
  absl::optional<std::string> anon_proxy_header_;
102

            
103
  absl::optional<std::string> isp_header_;
104
  absl::optional<std::string> apple_private_relay_header_;
105

            
106
  Stats::ScopeSharedPtr stats_scope_;
107
  Stats::StatNameSetPtr stat_name_set_;
108
  const Stats::StatName unknown_hit_;
109
  void incCounter(Stats::StatName name);
110
  void setGuage(Stats::StatName name, const uint64_t value);
111
};
112

            
113
using GeoipProviderConfigSharedPtr = std::shared_ptr<GeoipProviderConfig>;
114

            
115
// Wrapper class for MMDB_s type that ensures a proper cleanup of the MMDB_s
116
// instance resources prior to its destruction.
117
class MaxmindDb {
118
public:
119
141
  MaxmindDb(MMDB_s&& db) : db_(db) {}
120
141
  ~MaxmindDb() { MMDB_close(&db_); }
121
119
  const MMDB_s* mmdb() const { return &db_; }
122

            
123
private:
124
  MMDB_s db_;
125
};
126

            
127
using MaxmindDbSharedPtr = std::shared_ptr<MaxmindDb>;
128
class GeoipProvider : public Envoy::Geolocation::Driver,
129
                      public Logger::Loggable<Logger::Id::geolocation> {
130

            
131
public:
132
  GeoipProvider(Event::Dispatcher& dispatcher, Api::Api& api, Singleton::InstanceSharedPtr owner,
133
                GeoipProviderConfigSharedPtr config);
134

            
135
  ~GeoipProvider() override;
136

            
137
  // Envoy::Geolocation::Driver
138
  void lookup(Geolocation::LookupRequest&&, Geolocation::LookupGeoHeadersCallback&&) const override;
139

            
140
private:
141
  // Allow the unit test to have access to private members.
142
  friend class GeoipProviderPeer;
143
  GeoipProviderConfigSharedPtr config_;
144
  mutable absl::Mutex mmdb_mutex_;
145
  MaxmindDbSharedPtr city_db_ ABSL_GUARDED_BY(mmdb_mutex_);
146
  MaxmindDbSharedPtr isp_db_ ABSL_GUARDED_BY(mmdb_mutex_);
147
  MaxmindDbSharedPtr anon_db_ ABSL_GUARDED_BY(mmdb_mutex_);
148
  MaxmindDbSharedPtr asn_db_ ABSL_GUARDED_BY(mmdb_mutex_);
149
  MaxmindDbSharedPtr country_db_ ABSL_GUARDED_BY(mmdb_mutex_);
150
  Thread::ThreadPtr mmdb_reload_thread_;
151
  Event::DispatcherPtr mmdb_reload_dispatcher_;
152
  Filesystem::WatcherPtr mmdb_watcher_;
153
  MaxmindDbSharedPtr initMaxmindDb(const std::string& db_path, const absl::string_view& db_type,
154
                                   bool reload = false);
155
  void lookupInCityDb(const Network::Address::InstanceConstSharedPtr& remote_address,
156
                      absl::flat_hash_map<std::string, std::string>& lookup_result) const;
157
  void lookupInAsnDb(const Network::Address::InstanceConstSharedPtr& remote_address,
158
                     absl::flat_hash_map<std::string, std::string>& lookup_result) const;
159
  void lookupInAnonDb(const Network::Address::InstanceConstSharedPtr& remote_address,
160
                      absl::flat_hash_map<std::string, std::string>& lookup_result) const;
161
  void lookupInIspDb(const Network::Address::InstanceConstSharedPtr& remote_address,
162
                     absl::flat_hash_map<std::string, std::string>& lookup_result) const;
163
  void lookupInCountryDb(const Network::Address::InstanceConstSharedPtr& remote_address,
164
                         absl::flat_hash_map<std::string, std::string>& lookup_result) const;
165
  absl::Status onMaxmindDbUpdate(const std::string& db_path, const absl::string_view& db_type);
166
  absl::Status mmdbReload(const MaxmindDbSharedPtr reloaded_db, const absl::string_view& db_type)
167
      ABSL_LOCKS_EXCLUDED(mmdb_mutex_);
168
  template <typename... Params>
169
  void populateGeoLookupResult(MMDB_lookup_result_s& mmdb_lookup_result,
170
                               absl::flat_hash_map<std::string, std::string>& lookup_result,
171
                               const std::string& result_key, Params... lookup_params) const;
172
  MaxmindDbSharedPtr getCityDb() const ABSL_LOCKS_EXCLUDED(mmdb_mutex_);
173
  MaxmindDbSharedPtr getIspDb() const ABSL_LOCKS_EXCLUDED(mmdb_mutex_);
174
  MaxmindDbSharedPtr getAnonDb() const ABSL_LOCKS_EXCLUDED(mmdb_mutex_);
175
  MaxmindDbSharedPtr getAsnDb() const ABSL_LOCKS_EXCLUDED(mmdb_mutex_);
176
  MaxmindDbSharedPtr getCountryDb() const ABSL_LOCKS_EXCLUDED(mmdb_mutex_);
177
  void updateCityDb(MaxmindDbSharedPtr city_db) ABSL_LOCKS_EXCLUDED(mmdb_mutex_);
178
  void updateIspDb(MaxmindDbSharedPtr isp_db) ABSL_LOCKS_EXCLUDED(mmdb_mutex_);
179
  void updateAnonDb(MaxmindDbSharedPtr anon_db) ABSL_LOCKS_EXCLUDED(mmdb_mutex_);
180
  void updateAsnDb(MaxmindDbSharedPtr asn_db) ABSL_LOCKS_EXCLUDED(mmdb_mutex_);
181
  void updateCountryDb(MaxmindDbSharedPtr country_db) ABSL_LOCKS_EXCLUDED(mmdb_mutex_);
182
  // A shared_ptr to keep the provider singleton alive as long as any of its providers are in use.
183
  const Singleton::InstanceSharedPtr owner_;
184
  // Used for testing only.
185
  mutable Thread::ThreadSynchronizer synchronizer_;
186
};
187

            
188
using GeoipProviderSharedPtr = std::shared_ptr<GeoipProvider>;
189

            
190
} // namespace Maxmind
191
} // namespace GeoipProviders
192
} // namespace Extensions
193
} // namespace Envoy