|
@@ -73,6 +73,38 @@ static smartlist_t *geoip_ipv4_entries = NULL, *geoip_ipv6_entries = NULL;
|
|
static char geoip_digest[DIGEST_LEN];
|
|
static char geoip_digest[DIGEST_LEN];
|
|
static char geoip6_digest[DIGEST_LEN];
|
|
static char geoip6_digest[DIGEST_LEN];
|
|
|
|
|
|
|
|
+
|
|
|
|
+ * handler. */
|
|
|
|
+static size_t geoip_client_history_cache_size;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ * This prevents an overflow and set it to its maximum in that case. */
|
|
|
|
+static inline void
|
|
|
|
+geoip_increment_client_history_cache_size(size_t bytes)
|
|
|
|
+{
|
|
|
|
+
|
|
|
|
+ IF_BUG_ONCE(geoip_client_history_cache_size > (SIZE_MAX - bytes)) {
|
|
|
|
+ geoip_client_history_cache_size = SIZE_MAX;
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ geoip_client_history_cache_size += bytes;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ * This prevents an underflow and set it to 0 in that case. */
|
|
|
|
+static inline void
|
|
|
|
+geoip_decrement_client_history_cache_size(size_t bytes)
|
|
|
|
+{
|
|
|
|
+
|
|
|
|
+ * incrementing the counter or we have different sizes when allocating and
|
|
|
|
+ * freeing. It shouldn't happened so log it. */
|
|
|
|
+ IF_BUG_ONCE(geoip_client_history_cache_size < bytes) {
|
|
|
|
+ geoip_client_history_cache_size = 0;
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ geoip_client_history_cache_size -= bytes;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
|
|
* country list if it is a valid 2-letter country code, otherwise
|
|
* country list if it is a valid 2-letter country code, otherwise
|
|
* return -1. */
|
|
* return -1. */
|
|
@@ -509,6 +541,15 @@ HT_PROTOTYPE(clientmap, clientmap_entry_t, node, clientmap_entry_hash,
|
|
HT_GENERATE2(clientmap, clientmap_entry_t, node, clientmap_entry_hash,
|
|
HT_GENERATE2(clientmap, clientmap_entry_t, node, clientmap_entry_hash,
|
|
clientmap_entries_eq, 0.6, tor_reallocarray_, tor_free_)
|
|
clientmap_entries_eq, 0.6, tor_reallocarray_, tor_free_)
|
|
|
|
|
|
|
|
+
|
|
|
|
+static inline size_t
|
|
|
|
+clientmap_entry_size(const clientmap_entry_t *ent)
|
|
|
|
+{
|
|
|
|
+ tor_assert(ent);
|
|
|
|
+ return (sizeof(clientmap_entry_t) +
|
|
|
|
+ (ent->transport_name ? strlen(ent->transport_name) : 0));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
|
|
static void
|
|
static void
|
|
clientmap_entry_free(clientmap_entry_t *ent)
|
|
clientmap_entry_free(clientmap_entry_t *ent)
|
|
@@ -519,11 +560,37 @@ clientmap_entry_free(clientmap_entry_t *ent)
|
|
|
|
|
|
* any actions can be taken about it. */
|
|
* any actions can be taken about it. */
|
|
dos_geoip_entry_about_to_free(ent);
|
|
dos_geoip_entry_about_to_free(ent);
|
|
|
|
+ geoip_decrement_client_history_cache_size(clientmap_entry_size(ent));
|
|
|
|
|
|
tor_free(ent->transport_name);
|
|
tor_free(ent->transport_name);
|
|
tor_free(ent);
|
|
tor_free(ent);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+
|
|
|
|
+ * that are mandatory. The transport_name can be optional. This can't fail. */
|
|
|
|
+static clientmap_entry_t *
|
|
|
|
+clientmap_entry_new(geoip_client_action_t action, const tor_addr_t *addr,
|
|
|
|
+ const char *transport_name)
|
|
|
|
+{
|
|
|
|
+ clientmap_entry_t *entry;
|
|
|
|
+
|
|
|
|
+ tor_assert(action == GEOIP_CLIENT_CONNECT ||
|
|
|
|
+ action == GEOIP_CLIENT_NETWORKSTATUS);
|
|
|
|
+ tor_assert(addr);
|
|
|
|
+
|
|
|
|
+ entry = tor_malloc_zero(sizeof(clientmap_entry_t));
|
|
|
|
+ entry->action = action;
|
|
|
|
+ tor_addr_copy(&entry->addr, addr);
|
|
|
|
+ if (transport_name) {
|
|
|
|
+ entry->transport_name = tor_strdup(transport_name);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ geoip_increment_client_history_cache_size(clientmap_entry_size(entry));
|
|
|
|
+
|
|
|
|
+ return entry;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
|
|
static void
|
|
static void
|
|
client_history_clear(void)
|
|
client_history_clear(void)
|
|
@@ -575,11 +642,7 @@ geoip_note_client_seen(geoip_client_action_t action,
|
|
|
|
|
|
ent = geoip_lookup_client(addr, transport_name, action);
|
|
ent = geoip_lookup_client(addr, transport_name, action);
|
|
if (! ent) {
|
|
if (! ent) {
|
|
- ent = tor_malloc_zero(sizeof(clientmap_entry_t));
|
|
+ ent = clientmap_entry_new(action, addr, transport_name);
|
|
- tor_addr_copy(&ent->addr, addr);
|
|
|
|
- if (transport_name)
|
|
|
|
- ent->transport_name = tor_strdup(transport_name);
|
|
|
|
- ent->action = (int)action;
|
|
|
|
HT_INSERT(clientmap, &client_history, ent);
|
|
HT_INSERT(clientmap, &client_history, ent);
|
|
}
|
|
}
|
|
if (now / 60 <= (int)MAX_LAST_SEEN_IN_MINUTES && now >= 0)
|
|
if (now / 60 <= (int)MAX_LAST_SEEN_IN_MINUTES && now >= 0)
|
|
@@ -640,6 +703,81 @@ geoip_lookup_client(const tor_addr_t *addr, const char *transport_name,
|
|
return HT_FIND(clientmap, &client_history, &lookup);
|
|
return HT_FIND(clientmap, &client_history, &lookup);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+
|
|
|
|
+ * number of bytes freed. If 0 is returned, nothing was freed. */
|
|
|
|
+static size_t
|
|
|
|
+oom_clean_client_entries(time_t cutoff)
|
|
|
|
+{
|
|
|
|
+ size_t bytes = 0;
|
|
|
|
+ clientmap_entry_t **ent, **ent_next;
|
|
|
|
+
|
|
|
|
+ for (ent = HT_START(clientmap, &client_history); ent; ent = ent_next) {
|
|
|
|
+ clientmap_entry_t *entry = *ent;
|
|
|
|
+ if (entry->last_seen_in_minutes < (cutoff / 60)) {
|
|
|
|
+ ent_next = HT_NEXT_RMV(clientmap, &client_history, ent);
|
|
|
|
+ bytes += clientmap_entry_size(entry);
|
|
|
|
+ clientmap_entry_free(entry);
|
|
|
|
+ } else {
|
|
|
|
+ ent_next = HT_NEXT(clientmap, &client_history, ent);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return bytes;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+#define GEOIP_CLIENT_CACHE_OOM_MIN_CUTOFF (4 * 60 * 60)
|
|
|
|
+
|
|
|
|
+#define GEOIP_CLIENT_CACHE_OOM_STEP (15 * 50)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ * the amount of bytes removed. This can return a value below or above
|
|
|
|
+ * min_remove_bytes but will stop as oon as the min_remove_bytes has been
|
|
|
|
+ * reached. */
|
|
|
|
+size_t
|
|
|
|
+geoip_client_cache_handle_oom(time_t now, size_t min_remove_bytes)
|
|
|
|
+{
|
|
|
|
+ time_t k;
|
|
|
|
+ size_t bytes_removed = 0;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ tor_assert(min_remove_bytes != 0);
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ * to try to remove as much as we can. */
|
|
|
|
+ k = WRITE_STATS_INTERVAL;
|
|
|
|
+
|
|
|
|
+ do {
|
|
|
|
+ time_t cutoff;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ * remove every single entries which would be pretty bad for the DoS
|
|
|
|
+ * mitigation subsystem if by just filling the geoip cache, it was enough
|
|
|
|
+ * to trigger the OOM and clean every single entries. */
|
|
|
|
+ if (k <= GEOIP_CLIENT_CACHE_OOM_MIN_CUTOFF) {
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cutoff = now - k;
|
|
|
|
+ bytes_removed += oom_clean_client_entries(cutoff);
|
|
|
|
+ k -= GEOIP_CLIENT_CACHE_OOM_STEP;
|
|
|
|
+ } while (bytes_removed < min_remove_bytes);
|
|
|
|
+
|
|
|
|
+ return bytes_removed;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+size_t
|
|
|
|
+geoip_client_cache_total_allocation(void)
|
|
|
|
+{
|
|
|
|
+ size_t bytes = 0;
|
|
|
|
+ clientmap_entry_t **ent;
|
|
|
|
+
|
|
|
|
+ HT_FOREACH(ent, clientmap, &client_history) {
|
|
|
|
+ bytes += clientmap_entry_size(*ent);
|
|
|
|
+ }
|
|
|
|
+ return bytes;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
|
|
* statuses? */
|
|
* statuses? */
|
|
static uint32_t ns_v3_responses[GEOIP_NS_RESPONSE_NUM];
|
|
static uint32_t ns_v3_responses[GEOIP_NS_RESPONSE_NUM];
|