commit 475218c108ad84aa302f0daec013faab9ff778f2 Merge: 33d9889a2 e758d659a Author: David Goulet dgoulet@torproject.org Date: Fri Feb 2 14:55:01 2018 -0500
Merge branch 'ticket25122_029_02' into ticket24902_029_05
changes/ticket25122 | 4 ++ src/or/geoip.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/or/geoip.h | 2 + src/or/relay.c | 16 ++++-- src/test/test.c | 18 +++++++ 5 files changed, 180 insertions(+), 8 deletions(-)
diff --cc src/or/geoip.c index 4e4f6e639,76fca43f6..20dad5f15 --- a/src/or/geoip.c +++ b/src/or/geoip.c @@@ -516,9 -574,7 +557,10 @@@ clientmap_entry_free(clientmap_entry_t if (!ent) return;
+ /* This entry is about to be freed so pass it to the DoS subsystem to see if + * any actions can be taken about it. */ + 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); @@@ -573,13 -651,13 +640,9 @@@ geoip_note_client_seen(geoip_client_act safe_str_client(fmt_addr((addr))), transport_name ? transport_name : "<no transport>");
- tor_addr_copy(&lookup.addr, addr); - lookup.action = (int)action; - lookup.transport_name = (char*) transport_name; - ent = HT_FIND(clientmap, &client_history, &lookup); - + ent = geoip_lookup_client(addr, transport_name, action); if (! ent) { - ent = tor_malloc_zero(sizeof(clientmap_entry_t)); - tor_addr_copy(&ent->addr, addr); - if (transport_name) - ent->transport_name = tor_strdup(transport_name); - ent->action = (int)action; + ent = clientmap_entry_new(action, addr, transport_name); HT_INSERT(clientmap, &client_history, ent); } if (now / 60 <= (int)MAX_LAST_SEEN_IN_MINUTES && now >= 0) @@@ -621,25 -699,81 +684,100 @@@ geoip_remove_old_clients(time_t cutoff &cutoff); }
+/* Return a client entry object matching the given address, transport name and + * geoip action from the clientmap. NULL if not found. The transport_name can + * be NULL. */ +clientmap_entry_t * +geoip_lookup_client(const tor_addr_t *addr, const char *transport_name, + geoip_client_action_t action) +{ + clientmap_entry_t lookup; + + tor_assert(addr); + + /* We always look for a client connection with no transport. */ + tor_addr_copy(&lookup.addr, addr); + lookup.action = action; + lookup.transport_name = (char *) transport_name; + + return HT_FIND(clientmap, &client_history, &lookup); +} + + /* Cleanup client entries older than the cutoff. Used for the OOM. Return the + * 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; + } + + /* Below this minimum lifetime, the OOM won't cleanup any entries. */ + #define GEOIP_CLIENT_CACHE_OOM_MIN_CUTOFF (4 * 60 * 60) + /* The OOM moves the cutoff by that much every run. */ + #define GEOIP_CLIENT_CACHE_OOM_STEP (15 * 50) + + /* Cleanup the geoip client history cache called from the OOM handler. Return + * 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; + + /* Our OOM handler called with 0 bytes to remove is a code flow error. */ + tor_assert(min_remove_bytes != 0); + + /* Set k to the initial cutoff of an entry. We then going to move it by step + * to try to remove as much as we can. */ + k = WRITE_STATS_INTERVAL; + + do { + time_t cutoff; + + /* If k has reached the minimum lifetime, we have to stop else we might + * 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; + } + + /* Return the total size in bytes of the client history cache. */ + 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; + } + /** How many responses are we giving to clients requesting v3 network * statuses? */ static uint32_t ns_v3_responses[GEOIP_NS_RESPONSE_NUM]; diff --cc src/or/geoip.h index aa0fca50f,42d0c1cfd..c8ea9f85e --- a/src/or/geoip.h +++ b/src/or/geoip.h @@@ -57,9 -33,8 +57,11 @@@ void geoip_note_client_seen(geoip_clien const tor_addr_t *addr, const char *transport_name, time_t now); void geoip_remove_old_clients(time_t cutoff); +clientmap_entry_t *geoip_lookup_client(const tor_addr_t *addr, + const char *transport_name, + geoip_client_action_t action); + size_t geoip_client_cache_total_allocation(void); + size_t geoip_client_cache_handle_oom(time_t now, size_t min_remove_bytes);
void geoip_note_ns_response(geoip_ns_response_t response); char *geoip_get_transport_history(void);