1
#include "source/extensions/geoip_providers/maxmind/geoip_provider.h"
2

            
3
#include "source/common/common/assert.h"
4
#include "source/common/protobuf/protobuf.h"
5
#include "source/common/runtime/runtime_features.h"
6

            
7
namespace Envoy {
8
namespace Extensions {
9
namespace GeoipProviders {
10
namespace Maxmind {
11

            
12
namespace {
13
static constexpr const char* MMDB_CITY_LOOKUP_ARGS[] = {"city", "names", "en"};
14
static constexpr const char* MMDB_REGION_LOOKUP_ARGS[] = {"subdivisions", "0", "iso_code"};
15
static constexpr const char* MMDB_COUNTRY_LOOKUP_ARGS[] = {"country", "iso_code"};
16
static constexpr const char* MMDB_ASN_LOOKUP_ARGS[] = {"autonomous_system_number"};
17
static constexpr const char* MMDB_ASN_ORG_LOOKUP_ARGS[] = {"autonomous_system_organization"};
18
static constexpr const char* MMDB_ISP_LOOKUP_ARGS[] = {"isp", "autonomous_system_number"};
19
static constexpr const char* MMDB_ISP_ORG_LOOKUP_ARGS[] = {"organization"};
20
static constexpr const char* MMDB_ANON_LOOKUP_ARGS[] = {"is_anonymous", "is_anonymous_vpn",
21
                                                        "is_hosting_provider", "is_tor_exit_node",
22
                                                        "is_public_proxy"};
23

            
24
static constexpr absl::string_view CITY_DB_TYPE = "city_db";
25
static constexpr absl::string_view ISP_DB_TYPE = "isp_db";
26
static constexpr absl::string_view ANON_DB_TYPE = "anon_db";
27
static constexpr absl::string_view ASN_DB_TYPE = "asn_db";
28
static constexpr absl::string_view COUNTRY_DB_TYPE = "country_db";
29

            
30
// Helper to get optional string from config field, returns nullopt if empty.
31
1442
absl::optional<std::string> getOptionalString(const std::string& value) {
32
1442
  return !value.empty() ? absl::make_optional(value) : absl::nullopt;
33
1442
}
34
} // namespace
35

            
36
GeoipProviderConfig::GeoipProviderConfig(
37
    const envoy::extensions::geoip_providers::maxmind::v3::MaxMindConfig& config,
38
    const std::string& stat_prefix, Stats::Scope& scope)
39
85
    : city_db_path_(getOptionalString(config.city_db_path())),
40
85
      isp_db_path_(getOptionalString(config.isp_db_path())),
41
85
      anon_db_path_(getOptionalString(config.anon_db_path())),
42
85
      asn_db_path_(getOptionalString(config.asn_db_path())),
43
85
      country_db_path_(getOptionalString(config.country_db_path())),
44
85
      stats_scope_(scope.createScope(absl::StrCat(stat_prefix, "maxmind."))),
45
85
      stat_name_set_(stats_scope_->symbolTable().makeSet("Maxmind")) {
46
85
  const auto& common_config = config.common_provider_config();
47

            
48
85
  if (common_config.has_geo_field_keys()) {
49
    // Use geo_field_keys (preferred).
50
76
    const auto& keys = common_config.geo_field_keys();
51
76
    country_header_ = getOptionalString(keys.country());
52
76
    city_header_ = getOptionalString(keys.city());
53
76
    region_header_ = getOptionalString(keys.region());
54
76
    asn_header_ = getOptionalString(keys.asn());
55
76
    asn_org_header_ = getOptionalString(keys.asn_org());
56
76
    anon_header_ = getOptionalString(keys.anon());
57
76
    anon_vpn_header_ = getOptionalString(keys.anon_vpn());
58
76
    anon_hosting_header_ = getOptionalString(keys.anon_hosting());
59
76
    anon_tor_header_ = getOptionalString(keys.anon_tor());
60
76
    anon_proxy_header_ = getOptionalString(keys.anon_proxy());
61
76
    isp_header_ = getOptionalString(keys.isp());
62
76
    apple_private_relay_header_ = getOptionalString(keys.apple_private_relay());
63
76
  } else if (common_config.has_geo_headers_to_add()) {
64
    // Fall back to deprecated geo_headers_to_add for backward compatibility.
65
9
    const auto& headers = common_config.geo_headers_to_add();
66
9
    country_header_ = getOptionalString(headers.country());
67
9
    city_header_ = getOptionalString(headers.city());
68
9
    region_header_ = getOptionalString(headers.region());
69
9
    asn_header_ = getOptionalString(headers.asn());
70
9
    asn_org_header_ = getOptionalString(headers.asn_org());
71
    // TODO(barroca): When the is_anon field is fully deprecated, remove this fallback.
72
9
    anon_header_ = !headers.anon().empty() ? absl::make_optional(headers.anon())
73
9
                                           : getOptionalString(headers.is_anon());
74
9
    anon_vpn_header_ = getOptionalString(headers.anon_vpn());
75
9
    anon_hosting_header_ = getOptionalString(headers.anon_hosting());
76
9
    anon_tor_header_ = getOptionalString(headers.anon_tor());
77
9
    anon_proxy_header_ = getOptionalString(headers.anon_proxy());
78
9
    isp_header_ = getOptionalString(headers.isp());
79
9
    apple_private_relay_header_ = getOptionalString(headers.apple_private_relay());
80
9
  }
81

            
82
85
  if (!city_db_path_ && !anon_db_path_ && !asn_db_path_ && !isp_db_path_ && !country_db_path_) {
83
2
    throw EnvoyException("At least one geolocation database path needs to be configured: "
84
2
                         "city_db_path, isp_db_path, asn_db_path, anon_db_path or country_db_path");
85
2
  }
86
83
  if (city_db_path_) {
87
42
    registerGeoDbStats(CITY_DB_TYPE);
88
42
  }
89
83
  if (isp_db_path_) {
90
21
    registerGeoDbStats(ISP_DB_TYPE);
91
21
  }
92
83
  if (anon_db_path_) {
93
22
    registerGeoDbStats(ANON_DB_TYPE);
94
22
  }
95
83
  if (asn_db_path_) {
96
32
    registerGeoDbStats(ASN_DB_TYPE);
97
32
  }
98
83
  if (country_db_path_) {
99
12
    registerGeoDbStats(COUNTRY_DB_TYPE);
100
12
  }
101
83
};
102

            
103
129
void GeoipProviderConfig::registerGeoDbStats(const absl::string_view& db_type) {
104
129
  stat_name_set_->rememberBuiltin(absl::StrCat(db_type, ".total"));
105
129
  stat_name_set_->rememberBuiltin(absl::StrCat(db_type, ".hit"));
106
129
  stat_name_set_->rememberBuiltin(absl::StrCat(db_type, ".lookup_error"));
107
129
  stat_name_set_->rememberBuiltin(absl::StrCat(db_type, ".db_reload_error"));
108
129
  stat_name_set_->rememberBuiltin(absl::StrCat(db_type, ".db_reload_success"));
109
129
  stat_name_set_->rememberBuiltin(absl::StrCat(db_type, ".db_build_epoch"));
110
129
}
111

            
112
1001
bool GeoipProviderConfig::isLookupEnabledForHeader(const absl::optional<std::string>& header) {
113
1001
  return (header && !header.value().empty());
114
1001
}
115

            
116
232
void GeoipProviderConfig::incCounter(Stats::StatName name) {
117
232
  stats_scope_->counterFromStatName(name).inc();
118
232
}
119

            
120
141
void GeoipProviderConfig::setGuage(Stats::StatName name, const uint64_t value) {
121
141
  stats_scope_->gaugeFromStatName(name, Stats::Gauge::ImportMode::Accumulate).set(value);
122
141
}
123

            
124
GeoipProvider::GeoipProvider(Event::Dispatcher& dispatcher, Api::Api& api,
125
                             Singleton::InstanceSharedPtr owner,
126
                             GeoipProviderConfigSharedPtr config)
127
83
    : config_(config), owner_(owner) {
128
83
  city_db_ =
129
83
      config_->cityDbPath() ? initMaxmindDb(config_->cityDbPath().value(), CITY_DB_TYPE) : nullptr;
130
83
  isp_db_ =
131
83
      config_->ispDbPath() ? initMaxmindDb(config_->ispDbPath().value(), ISP_DB_TYPE) : nullptr;
132
83
  anon_db_ =
133
83
      config_->anonDbPath() ? initMaxmindDb(config_->anonDbPath().value(), ANON_DB_TYPE) : nullptr;
134
83
  asn_db_ =
135
83
      config_->asnDbPath() ? initMaxmindDb(config_->asnDbPath().value(), ASN_DB_TYPE) : nullptr;
136
83
  country_db_ = config_->countryDbPath()
137
83
                    ? initMaxmindDb(config_->countryDbPath().value(), COUNTRY_DB_TYPE)
138
83
                    : nullptr;
139
83
  mmdb_reload_dispatcher_ = api.allocateDispatcher("mmdb_reload_routine");
140
83
  mmdb_watcher_ = dispatcher.createFilesystemWatcher();
141
83
  mmdb_reload_thread_ = api.threadFactory().createThread(
142
83
      [this]() -> void {
143
83
        ENVOY_LOG_MISC(debug, "Started mmdb_reload_routine");
144
83
        if (config_->cityDbPath()) {
145
42
          THROW_IF_NOT_OK(mmdb_watcher_->addWatch(
146
42
              config_->cityDbPath().value(), Filesystem::Watcher::Events::MovedTo,
147
42
              [this](uint32_t) {
148
42
                return onMaxmindDbUpdate(config_->cityDbPath().value(), CITY_DB_TYPE);
149
42
              }));
150
42
        }
151
83
        if (config_->ispDbPath()) {
152
21
          THROW_IF_NOT_OK(mmdb_watcher_->addWatch(
153
21
              config_->ispDbPath().value(), Filesystem::Watcher::Events::MovedTo, [this](uint32_t) {
154
21
                return onMaxmindDbUpdate(config_->ispDbPath().value(), ISP_DB_TYPE);
155
21
              }));
156
21
        }
157
83
        if (config_->anonDbPath()) {
158
22
          THROW_IF_NOT_OK(mmdb_watcher_->addWatch(
159
22
              config_->anonDbPath().value(), Filesystem::Watcher::Events::MovedTo,
160
22
              [this](uint32_t) {
161
22
                return onMaxmindDbUpdate(config_->anonDbPath().value(), ANON_DB_TYPE);
162
22
              }));
163
22
        }
164
83
        if (config_->asnDbPath()) {
165
32
          THROW_IF_NOT_OK(mmdb_watcher_->addWatch(
166
32
              config_->asnDbPath().value(), Filesystem::Watcher::Events::MovedTo, [this](uint32_t) {
167
32
                return onMaxmindDbUpdate(config_->asnDbPath().value(), ASN_DB_TYPE);
168
32
              }));
169
32
        }
170
83
        if (config_->countryDbPath()) {
171
12
          THROW_IF_NOT_OK(mmdb_watcher_->addWatch(
172
12
              config_->countryDbPath().value(), Filesystem::Watcher::Events::MovedTo,
173
12
              [this](uint32_t) {
174
12
                return onMaxmindDbUpdate(config_->countryDbPath().value(), COUNTRY_DB_TYPE);
175
12
              }));
176
12
        }
177
83
        mmdb_reload_dispatcher_->run(Event::Dispatcher::RunType::RunUntilExit);
178
83
      },
179
83
      Thread::Options{std::string("mmdb_reload_routine")});
180
83
};
181

            
182
83
GeoipProvider::~GeoipProvider() {
183
83
  ENVOY_LOG(debug, "Shutting down Maxmind geolocation provider");
184
83
  if (mmdb_reload_dispatcher_) {
185
83
    mmdb_reload_dispatcher_->exit();
186
83
  }
187
83
  if (mmdb_reload_thread_) {
188
83
    mmdb_reload_thread_->join();
189
83
    mmdb_reload_thread_.reset();
190
83
  }
191
83
}
192

            
193
void GeoipProvider::lookup(Geolocation::LookupRequest&& request,
194
88
                           Geolocation::LookupGeoHeadersCallback&& cb) const {
195
88
  auto& remote_address = request.remoteAddress();
196
88
  auto lookup_result = absl::flat_hash_map<std::string, std::string>{};
197
88
  lookupInCountryDb(remote_address, lookup_result);
198
88
  lookupInCityDb(remote_address, lookup_result);
199
88
  lookupInAsnDb(remote_address, lookup_result);
200
88
  lookupInAnonDb(remote_address, lookup_result);
201
88
  lookupInIspDb(remote_address, lookup_result);
202
88
  cb(std::move(lookup_result));
203
88
}
204

            
205
void GeoipProvider::lookupInCityDb(
206
    const Network::Address::InstanceConstSharedPtr& remote_address,
207
88
    absl::flat_hash_map<std::string, std::string>& lookup_result) const {
208
  // Country lookup falls back to City DB only if Country DB is not configured.
209
88
  const bool should_lookup_country_from_city_db =
210
88
      !config_->isCountryDbPathSet() && config_->isLookupEnabledForHeader(config_->countryHeader());
211
88
  if (config_->isLookupEnabledForHeader(config_->cityHeader()) ||
212
88
      config_->isLookupEnabledForHeader(config_->regionHeader()) ||
213
88
      should_lookup_country_from_city_db) {
214
40
    int mmdb_error;
215
40
    auto city_db_ptr = getCityDb();
216
    // Used for testing.
217
40
    synchronizer_.syncPoint(std::string(CITY_DB_TYPE).append("_lookup_pre_complete"));
218
40
    if (!city_db_ptr) {
219
1
      IS_ENVOY_BUG("Maxmind city database must be initialised for performing lookups");
220
1
      return;
221
1
    }
222
39
    auto city_db = city_db_ptr.get();
223
39
    MMDB_lookup_result_s mmdb_lookup_result = MMDB_lookup_sockaddr(
224
39
        city_db->mmdb(), reinterpret_cast<const sockaddr*>(remote_address->sockAddr()),
225
39
        &mmdb_error);
226
39
    const uint32_t n_prev_hits = lookup_result.size();
227
39
    if (!mmdb_error && mmdb_lookup_result.found_entry) {
228
27
      MMDB_entry_data_list_s* entry_data_list;
229
27
      int status = MMDB_get_entry_data_list(&mmdb_lookup_result.entry, &entry_data_list);
230
27
      if (status == MMDB_SUCCESS) {
231
27
        if (config_->isLookupEnabledForHeader(config_->cityHeader())) {
232
27
          populateGeoLookupResult(mmdb_lookup_result, lookup_result, config_->cityHeader().value(),
233
27
                                  MMDB_CITY_LOOKUP_ARGS[0], MMDB_CITY_LOOKUP_ARGS[1],
234
27
                                  MMDB_CITY_LOOKUP_ARGS[2]);
235
27
        }
236
27
        if (config_->isLookupEnabledForHeader(config_->regionHeader())) {
237
22
          populateGeoLookupResult(mmdb_lookup_result, lookup_result,
238
22
                                  config_->regionHeader().value(), MMDB_REGION_LOOKUP_ARGS[0],
239
22
                                  MMDB_REGION_LOOKUP_ARGS[1], MMDB_REGION_LOOKUP_ARGS[2]);
240
22
        }
241
        // Country lookup from City DB only when Country DB is not configured.
242
27
        if (should_lookup_country_from_city_db) {
243
24
          populateGeoLookupResult(mmdb_lookup_result, lookup_result,
244
24
                                  config_->countryHeader().value(), MMDB_COUNTRY_LOOKUP_ARGS[0],
245
24
                                  MMDB_COUNTRY_LOOKUP_ARGS[1]);
246
24
        }
247
27
        if (lookup_result.size() > n_prev_hits) {
248
27
          config_->incHit(CITY_DB_TYPE);
249
27
        }
250
27
        MMDB_free_entry_data_list(entry_data_list);
251
27
      }
252

            
253
29
    } else {
254
12
      config_->incLookupError(CITY_DB_TYPE);
255
12
    }
256
39
    config_->incTotal(CITY_DB_TYPE);
257
39
  }
258
88
}
259

            
260
void GeoipProvider::lookupInAsnDb(
261
    const Network::Address::InstanceConstSharedPtr& remote_address,
262
88
    absl::flat_hash_map<std::string, std::string>& lookup_result) const {
263
88
  if (config_->isLookupEnabledForHeader(config_->asnHeader()) ||
264
88
      config_->isLookupEnabledForHeader(config_->asnOrgHeader())) {
265
44
    int mmdb_error;
266
44
    auto asn_db_ptr = getAsnDb();
267
    // Used for testing.
268
44
    synchronizer_.syncPoint(std::string(ASN_DB_TYPE).append("_lookup_pre_complete"));
269
44
    if (!asn_db_ptr) {
270
10
      if (config_->isIspDbPathSet()) {
271
        // ASN information can be looked up from ISP database as well, so we don't need to
272
        // throw an error if is not set.
273
8
        return;
274
8
      }
275
2
      IS_ENVOY_BUG("Maxmind asn database must be initialised for performing lookups");
276
2
      return;
277
10
    }
278
34
    MMDB_lookup_result_s mmdb_lookup_result = MMDB_lookup_sockaddr(
279
34
        asn_db_ptr->mmdb(), reinterpret_cast<const sockaddr*>(remote_address->sockAddr()),
280
34
        &mmdb_error);
281
34
    const uint32_t n_prev_hits = lookup_result.size();
282
34
    if (!mmdb_error && mmdb_lookup_result.found_entry) {
283
20
      MMDB_entry_data_list_s* entry_data_list;
284
20
      int status = MMDB_get_entry_data_list(&mmdb_lookup_result.entry, &entry_data_list);
285
20
      if (status == MMDB_SUCCESS) {
286
20
        if (config_->isLookupEnabledForHeader(config_->asnHeader())) {
287
20
          populateGeoLookupResult(mmdb_lookup_result, lookup_result, config_->asnHeader().value(),
288
20
                                  MMDB_ASN_LOOKUP_ARGS[0]);
289
20
        }
290
20
        if (config_->isLookupEnabledForHeader(config_->asnOrgHeader())) {
291
4
          populateGeoLookupResult(mmdb_lookup_result, lookup_result,
292
4
                                  config_->asnOrgHeader().value(), MMDB_ASN_ORG_LOOKUP_ARGS[0]);
293
4
        }
294

            
295
20
        MMDB_free_entry_data_list(entry_data_list);
296
20
        if (lookup_result.size() > n_prev_hits) {
297
20
          config_->incHit(ASN_DB_TYPE);
298
20
        }
299
20
      } else {
300
        config_->incLookupError(ASN_DB_TYPE);
301
      }
302
20
    }
303
34
    config_->incTotal(ASN_DB_TYPE);
304
34
  }
305
88
}
306

            
307
void GeoipProvider::lookupInAnonDb(
308
    const Network::Address::InstanceConstSharedPtr& remote_address,
309
88
    absl::flat_hash_map<std::string, std::string>& lookup_result) const {
310
88
  if (config_->isLookupEnabledForHeader(config_->anonHeader()) || config_->anonVpnHeader()) {
311
19
    int mmdb_error;
312
19
    auto anon_db_ptr = getAnonDb();
313
    // Used for testing.
314
19
    synchronizer_.syncPoint(std::string(ANON_DB_TYPE).append("_lookup_pre_complete"));
315
19
    if (!anon_db_ptr) {
316
1
      IS_ENVOY_BUG("Maxmind anon database must be initialised for performing lookups");
317
1
      return;
318
1
    }
319
18
    auto anon_db = anon_db_ptr.get();
320
18
    MMDB_lookup_result_s mmdb_lookup_result = MMDB_lookup_sockaddr(
321
18
        anon_db->mmdb(), reinterpret_cast<const sockaddr*>(remote_address->sockAddr()),
322
18
        &mmdb_error);
323
18
    const uint32_t n_prev_hits = lookup_result.size();
324
18
    if (!mmdb_error && mmdb_lookup_result.found_entry) {
325
16
      MMDB_entry_data_list_s* entry_data_list;
326
16
      int status = MMDB_get_entry_data_list(&mmdb_lookup_result.entry, &entry_data_list);
327
16
      if (status == MMDB_SUCCESS) {
328
16
        if (config_->isLookupEnabledForHeader(config_->anonHeader())) {
329
16
          populateGeoLookupResult(mmdb_lookup_result, lookup_result, config_->anonHeader().value(),
330
16
                                  MMDB_ANON_LOOKUP_ARGS[0]);
331
16
        }
332
16
        if (config_->isLookupEnabledForHeader(config_->anonVpnHeader())) {
333
7
          populateGeoLookupResult(mmdb_lookup_result, lookup_result,
334
7
                                  config_->anonVpnHeader().value(), MMDB_ANON_LOOKUP_ARGS[1]);
335
7
        }
336
16
        if (config_->isLookupEnabledForHeader(config_->anonHostingHeader())) {
337
2
          populateGeoLookupResult(mmdb_lookup_result, lookup_result,
338
2
                                  config_->anonHostingHeader().value(), MMDB_ANON_LOOKUP_ARGS[2]);
339
2
        }
340
16
        if (config_->isLookupEnabledForHeader(config_->anonTorHeader())) {
341
2
          populateGeoLookupResult(mmdb_lookup_result, lookup_result,
342
2
                                  config_->anonTorHeader().value(), MMDB_ANON_LOOKUP_ARGS[3]);
343
2
        }
344
16
        if (config_->isLookupEnabledForHeader(config_->anonProxyHeader())) {
345
2
          populateGeoLookupResult(mmdb_lookup_result, lookup_result,
346
2
                                  config_->anonProxyHeader().value(), MMDB_ANON_LOOKUP_ARGS[4]);
347
2
        }
348
16
        if (lookup_result.size() > n_prev_hits) {
349
12
          config_->incHit(ANON_DB_TYPE);
350
12
        }
351
16
        MMDB_free_entry_data_list(entry_data_list);
352
16
      } else {
353
        config_->incLookupError(ANON_DB_TYPE);
354
      }
355
16
    }
356
18
    config_->incTotal(ANON_DB_TYPE);
357
18
  }
358
88
}
359

            
360
void GeoipProvider::lookupInIspDb(
361
    const Network::Address::InstanceConstSharedPtr& remote_address,
362
88
    absl::flat_hash_map<std::string, std::string>& lookup_result) const {
363
88
  if (config_->isLookupEnabledForHeader(config_->ispHeader()) ||
364
88
      config_->isLookupEnabledForHeader(config_->applePrivateRelayHeader()) ||
365
88
      (!config_->isAsnDbPathSet() &&
366
73
       (config_->isLookupEnabledForHeader(config_->asnHeader()) ||
367
48
        config_->isLookupEnabledForHeader(config_->asnOrgHeader())))) {
368
20
    int mmdb_error;
369
20
    auto isp_db_ptr = getIspDb();
370
    // Used for testing.
371
20
    synchronizer_.syncPoint(std::string(ISP_DB_TYPE).append("_lookup_pre_complete"));
372
20
    if (!isp_db_ptr) {
373
3
      IS_ENVOY_BUG("Maxmind isp database must be initialised for performing lookups");
374
3
      return;
375
3
    }
376
17
    auto isp_db = isp_db_ptr.get();
377
17
    MMDB_lookup_result_s mmdb_lookup_result = MMDB_lookup_sockaddr(
378
17
        isp_db->mmdb(), reinterpret_cast<const sockaddr*>(remote_address->sockAddr()), &mmdb_error);
379
17
    const uint32_t n_prev_hits = lookup_result.size();
380
17
    if (!mmdb_error && mmdb_lookup_result.found_entry) {
381
17
      MMDB_entry_data_list_s* entry_data_list;
382
17
      int status = MMDB_get_entry_data_list(&mmdb_lookup_result.entry, &entry_data_list);
383
17
      if (status == MMDB_SUCCESS) {
384
17
        if (config_->isLookupEnabledForHeader(config_->ispHeader())) {
385
13
          populateGeoLookupResult(mmdb_lookup_result, lookup_result, config_->ispHeader().value(),
386
13
                                  MMDB_ISP_LOOKUP_ARGS[0]);
387
13
        }
388
17
        if (config_->isLookupEnabledForHeader(config_->applePrivateRelayHeader())) {
389
5
          populateGeoLookupResult(mmdb_lookup_result, lookup_result,
390
5
                                  config_->applePrivateRelayHeader().value(),
391
5
                                  MMDB_ISP_LOOKUP_ARGS[0]);
392
5
          if (lookup_result.find(config_->applePrivateRelayHeader().value()) !=
393
5
                  lookup_result.end() &&
394
5
              lookup_result[config_->applePrivateRelayHeader().value()] == "iCloud Private Relay") {
395
            lookup_result[config_->applePrivateRelayHeader().value()] = "true";
396
5
          } else {
397
5
            lookup_result[config_->applePrivateRelayHeader().value()] = "false";
398
5
          }
399
5
        }
400
17
        if (!config_->isAsnDbPathSet() && config_->isLookupEnabledForHeader(config_->asnHeader())) {
401
7
          populateGeoLookupResult(mmdb_lookup_result, lookup_result, config_->asnHeader().value(),
402
7
                                  MMDB_ISP_LOOKUP_ARGS[1]);
403
7
        }
404
17
        if (!config_->isAsnDbPathSet() &&
405
17
            config_->isLookupEnabledForHeader(config_->asnOrgHeader())) {
406
3
          populateGeoLookupResult(mmdb_lookup_result, lookup_result,
407
3
                                  config_->asnOrgHeader().value(), MMDB_ISP_ORG_LOOKUP_ARGS[0]);
408
3
        }
409
17
        if (lookup_result.size() > n_prev_hits) {
410
17
          config_->incHit(ISP_DB_TYPE);
411
17
        }
412
17
        MMDB_free_entry_data_list(entry_data_list);
413
17
      } else {
414
        config_->incLookupError(ISP_DB_TYPE);
415
      }
416
17
    }
417
17
    config_->incTotal(ISP_DB_TYPE);
418
17
  }
419
88
}
420

            
421
void GeoipProvider::lookupInCountryDb(
422
    const Network::Address::InstanceConstSharedPtr& remote_address,
423
88
    absl::flat_hash_map<std::string, std::string>& lookup_result) const {
424
88
  if (config_->isLookupEnabledForHeader(config_->countryHeader())) {
425
    // Country DB takes precedence if configured, otherwise fall back to City DB.
426
48
    if (!config_->isCountryDbPathSet()) {
427
      // Country lookup will be handled by lookupInCityDb.
428
35
      return;
429
35
    }
430
13
    int mmdb_error;
431
13
    auto country_db_ptr = getCountryDb();
432
    // Used for testing.
433
13
    synchronizer_.syncPoint(std::string(COUNTRY_DB_TYPE).append("_lookup_pre_complete"));
434
13
    if (!country_db_ptr) {
435
2
      if (config_->isCityDbPathSet()) {
436
        // Country information can be looked up from City database as well, so we don't need to
437
        // throw an error if it is not set.
438
1
        return;
439
1
      }
440
1
      IS_ENVOY_BUG("Maxmind country database must be initialised for performing lookups");
441
1
      return;
442
2
    }
443
11
    auto country_db = country_db_ptr.get();
444
11
    MMDB_lookup_result_s mmdb_lookup_result = MMDB_lookup_sockaddr(
445
11
        country_db->mmdb(), reinterpret_cast<const sockaddr*>(remote_address->sockAddr()),
446
11
        &mmdb_error);
447
11
    const uint32_t n_prev_hits = lookup_result.size();
448
11
    if (!mmdb_error && mmdb_lookup_result.found_entry) {
449
11
      MMDB_entry_data_list_s* entry_data_list;
450
11
      int status = MMDB_get_entry_data_list(&mmdb_lookup_result.entry, &entry_data_list);
451
11
      if (status == MMDB_SUCCESS) {
452
10
        populateGeoLookupResult(mmdb_lookup_result, lookup_result, config_->countryHeader().value(),
453
10
                                MMDB_COUNTRY_LOOKUP_ARGS[0], MMDB_COUNTRY_LOOKUP_ARGS[1]);
454
10
        if (lookup_result.size() > n_prev_hits) {
455
10
          config_->incHit(COUNTRY_DB_TYPE);
456
10
        }
457
10
        MMDB_free_entry_data_list(entry_data_list);
458
10
      } else {
459
1
        config_->incLookupError(COUNTRY_DB_TYPE);
460
1
      }
461
11
    }
462
11
    config_->incTotal(COUNTRY_DB_TYPE);
463
11
  }
464
88
}
465

            
466
MaxmindDbSharedPtr GeoipProvider::initMaxmindDb(const std::string& db_path,
467
143
                                                const absl::string_view& db_type, bool reload) {
468
143
  MMDB_s maxmind_db;
469
143
  int result_code = MMDB_open(db_path.c_str(), MMDB_MODE_MMAP, &maxmind_db);
470

            
471
143
  if (reload && MMDB_SUCCESS != result_code) {
472
2
    ENVOY_LOG(error, "Failed to reload Maxmind database {} from file {}. Error {}", db_type,
473
2
              db_path, std::string(MMDB_strerror(result_code)));
474
2
    return nullptr;
475
141
  } else if (MMDB_SUCCESS != result_code) {
476
    // Crash if this is a failure during initial load.
477
    RELEASE_ASSERT(MMDB_SUCCESS == result_code,
478
                   fmt::format("Unable to open Maxmind database file {}. Error {}", db_path,
479
                               std::string(MMDB_strerror(result_code))));
480
    return nullptr;
481
  }
482

            
483
141
  config_->setDbBuildEpoch(db_type, maxmind_db.metadata.build_epoch);
484

            
485
141
  ENVOY_LOG(info, "Succeeded to reload Maxmind database {} from file {}.", db_type, db_path);
486
141
  return std::make_shared<MaxmindDb>(std::move(maxmind_db));
487
143
}
488

            
489
absl::Status GeoipProvider::mmdbReload(const MaxmindDbSharedPtr reloaded_db,
490
14
                                       const absl::string_view& db_type) {
491
14
  if (reloaded_db) {
492
12
    if (db_type == CITY_DB_TYPE) {
493
4
      updateCityDb(reloaded_db);
494
4
      config_->incDbReloadSuccess(db_type);
495
8
    } else if (db_type == ISP_DB_TYPE) {
496
2
      updateIspDb(reloaded_db);
497
2
      config_->incDbReloadSuccess(db_type);
498
6
    } else if (db_type == ANON_DB_TYPE) {
499
2
      updateAnonDb(reloaded_db);
500
2
      config_->incDbReloadSuccess(db_type);
501
4
    } else if (db_type == ASN_DB_TYPE) {
502
2
      updateAsnDb(reloaded_db);
503
2
      config_->incDbReloadSuccess(db_type);
504
2
    } else if (db_type == COUNTRY_DB_TYPE) {
505
2
      updateCountryDb(reloaded_db);
506
2
      config_->incDbReloadSuccess(db_type);
507
2
    } else {
508
      ENVOY_LOG(error, "Unsupported maxmind db type {}", db_type);
509
      return absl::InvalidArgumentError(fmt::format("Unsupported maxmind db type {}", db_type));
510
    }
511
12
  } else {
512
2
    config_->incDbReloadError(db_type);
513
2
  }
514
14
  return absl::OkStatus();
515
14
}
516

            
517
40
MaxmindDbSharedPtr GeoipProvider::getCityDb() const ABSL_LOCKS_EXCLUDED(mmdb_mutex_) {
518
40
  absl::ReaderMutexLock lock(mmdb_mutex_);
519
40
  return city_db_;
520
40
}
521

            
522
4
void GeoipProvider::updateCityDb(MaxmindDbSharedPtr city_db) ABSL_LOCKS_EXCLUDED(mmdb_mutex_) {
523
4
  absl::MutexLock lock(mmdb_mutex_);
524
4
  city_db_ = city_db;
525
4
}
526

            
527
20
MaxmindDbSharedPtr GeoipProvider::getIspDb() const ABSL_LOCKS_EXCLUDED(mmdb_mutex_) {
528
20
  absl::ReaderMutexLock lock(mmdb_mutex_);
529
20
  return isp_db_;
530
20
}
531

            
532
2
void GeoipProvider::updateIspDb(MaxmindDbSharedPtr isp_db) ABSL_LOCKS_EXCLUDED(mmdb_mutex_) {
533
2
  absl::MutexLock lock(mmdb_mutex_);
534
2
  isp_db_ = isp_db;
535
2
}
536

            
537
44
MaxmindDbSharedPtr GeoipProvider::getAsnDb() const ABSL_LOCKS_EXCLUDED(mmdb_mutex_) {
538
44
  absl::ReaderMutexLock lock(mmdb_mutex_);
539
44
  return asn_db_;
540
44
}
541

            
542
2
void GeoipProvider::updateAsnDb(MaxmindDbSharedPtr asn_db) ABSL_LOCKS_EXCLUDED(mmdb_mutex_) {
543
2
  absl::MutexLock lock(mmdb_mutex_);
544
2
  asn_db_ = asn_db;
545
2
}
546

            
547
19
MaxmindDbSharedPtr GeoipProvider::getAnonDb() const ABSL_LOCKS_EXCLUDED(mmdb_mutex_) {
548
19
  absl::ReaderMutexLock lock(mmdb_mutex_);
549
19
  return anon_db_;
550
19
}
551

            
552
2
void GeoipProvider::updateAnonDb(MaxmindDbSharedPtr anon_db) ABSL_LOCKS_EXCLUDED(mmdb_mutex_) {
553
2
  absl::MutexLock lock(mmdb_mutex_);
554
2
  anon_db_ = anon_db;
555
2
}
556

            
557
13
MaxmindDbSharedPtr GeoipProvider::getCountryDb() const ABSL_LOCKS_EXCLUDED(mmdb_mutex_) {
558
13
  absl::ReaderMutexLock lock(mmdb_mutex_);
559
13
  return country_db_;
560
13
}
561

            
562
void GeoipProvider::updateCountryDb(MaxmindDbSharedPtr country_db)
563
2
    ABSL_LOCKS_EXCLUDED(mmdb_mutex_) {
564
2
  absl::MutexLock lock(mmdb_mutex_);
565
2
  country_db_ = country_db;
566
2
}
567

            
568
absl::Status GeoipProvider::onMaxmindDbUpdate(const std::string& db_path,
569
14
                                              const absl::string_view& db_type) {
570
14
  MaxmindDbSharedPtr reloaded_db = initMaxmindDb(db_path, db_type, true /* reload */);
571
14
  return mmdbReload(reloaded_db, db_type);
572
14
}
573

            
574
template <class... Params>
575
void GeoipProvider::populateGeoLookupResult(
576
    MMDB_lookup_result_s& mmdb_lookup_result,
577
    absl::flat_hash_map<std::string, std::string>& lookup_result, const std::string& result_key,
578
164
    Params... lookup_params) const {
579
164
  MMDB_entry_data_s entry_data;
580
164
  if ((MMDB_get_value(&mmdb_lookup_result.entry, &entry_data, lookup_params..., NULL)) ==
581
164
      MMDB_SUCCESS) {
582
153
    std::string result_value;
583
153
    if (entry_data.has_data && entry_data.type == MMDB_DATA_TYPE_UTF8_STRING) {
584
108
      result_value = std::string(entry_data.utf8_string, entry_data.data_size);
585
108
    } else if (entry_data.has_data && entry_data.type == MMDB_DATA_TYPE_UINT32 &&
586
45
               entry_data.uint32 > 0) {
587
27
      result_value = std::to_string(entry_data.uint32);
588
28
    } else if (entry_data.has_data && entry_data.type == MMDB_DATA_TYPE_BOOLEAN) {
589
18
      result_value = entry_data.boolean ? "true" : "false";
590
18
    }
591
153
    if (!result_value.empty()) {
592
153
      lookup_result.insert(std::make_pair(result_key, result_value));
593
153
    }
594
153
  }
595
164
}
596

            
597
} // namespace Maxmind
598
} // namespace GeoipProviders
599
} // namespace Extensions
600
} // namespace Envoy