From c0b6cb132baaf9cf259bfb09e14c1128d7abc9d6 Mon Sep 17 00:00:00 2001 From: Karsten Loesing Date: Sun, 5 Jul 2009 20:48:16 +0200 Subject: [PATCH] If configured, write entry-node statistics to disk periodically. --- ChangeLog | 4 ++++ configure.in | 7 +++++++ src/or/config.c | 20 ++++++++++++++++++++ src/or/geoip.c | 42 ++++++++++++++++++++++++++++++++++++++++++ src/or/or.h | 9 ++++++++- src/or/router.c | 3 +++ 6 files changed, 84 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 0d5af889da..a5f35fbeef 100644 --- a/ChangeLog +++ b/ChangeLog @@ -21,6 +21,10 @@ Changes in version 0.2.2.1-alpha - 2009-??-?? transferred bytes per port to disk every 24 hours. To enable this, run configure with the --enable-exit-stats option, and set "ExitPortStatistics 1" in your torrc. + - Entry nodes can write statistics on the rough number and origins of + connecting clients to disk every 24 hours. To enable this, run + configure with the --enable-entry-stats option, and set + "EntryStatistics 1" in your torrc. o Minor bugfixes - Hidden service clients didn't use a cached service descriptor that diff --git a/configure.in b/configure.in index 844211588d..0f83207991 100644 --- a/configure.in +++ b/configure.in @@ -99,6 +99,13 @@ if test "$enable_geoip_stats" = "yes"; then AC_DEFINE(ENABLE_GEOIP_STATS, 1, [Defined if we try to collect per-country statistics]) fi +AC_ARG_ENABLE(entry-stats, + AS_HELP_STRING(--enable-entry-stats, enable code for entry guards to collect per-country statistics)) + +if test "$enable_entry_stats" = "yes"; then + AC_DEFINE(ENABLE_ENTRY_STATS, 1, [Defined if we try to collect per-country statistics]) +fi + AC_ARG_ENABLE(gcc-warnings, AS_HELP_STRING(--enable-gcc-warnings, enable verbose warnings)) diff --git a/src/or/config.c b/src/or/config.c index f1fea13e58..c9ef21bb8c 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -198,6 +198,7 @@ static config_var_t _option_vars[] = { V(DownloadExtraInfo, BOOL, "0"), V(EnforceDistinctSubnets, BOOL, "1"), V(EntryNodes, ROUTERSET, NULL), + V(EntryStatistics, BOOL, "0"), V(TestingEstimatedDescriptorPropagationTime, INTERVAL, "10 minutes"), V(ExcludeNodes, ROUTERSET, NULL), V(ExcludeExitNodes, ROUTERSET, NULL), @@ -1394,6 +1395,25 @@ options_act(or_options_t *old_options) if (options->ExitPortStatistics) log_warn(LD_CONFIG, "ExitPortStatistics enabled, but Tor was built " "without port statistics support."); +#endif +#ifdef ENABLE_ENTRY_STATS + if (options->EntryStatistics) { + if (should_record_bridge_info(options)) { + /* Don't allow measuring statistics on entry guards when configured + * as bridge. */ + log_warn(LD_CONFIG, "Bridges cannot be configured to measure " + "additional GeoIP statistics as entry guards."); + return -1; + } else + log_notice(LD_CONFIG, "Configured to measure entry node " + "statistics. Look for the entry-stats file that will " + "first be written to the data directory in 24 hours " + "from now."); + } +#else + if (options->EntryStatistics) + log_warn(LD_CONFIG, "EntryStatistics enabled, but Tor was built " + "without entry node statistics support."); #endif /* Check if we need to parse and add the EntryNodes config option. */ if (options->EntryNodes && diff --git a/src/or/geoip.c b/src/or/geoip.c index 41c8f21cdb..13a6a28500 100644 --- a/src/or/geoip.c +++ b/src/or/geoip.c @@ -13,6 +13,7 @@ static void clear_geoip_db(void); static void dump_geoip_stats(void); +static void dump_entry_stats(void); /** An entry from the GeoIP file: maps an IP range to a country. */ typedef struct geoip_entry_t { @@ -308,8 +309,13 @@ geoip_note_client_seen(geoip_client_action_t action, or_options_t *options = get_options(); clientmap_entry_t lookup, *ent; if (action == GEOIP_CLIENT_CONNECT) { +#ifdef ENABLE_ENTRY_STATS + if (!options->EntryStatistics) + return; +#else if (!(options->BridgeRelay && options->BridgeRecordUsageByCountry)) return; +#endif /* Did we recently switch from bridge to relay or back? */ if (client_history_starts > now) return; @@ -337,6 +343,8 @@ geoip_note_client_seen(geoip_client_action_t action, geoip_remove_old_clients(current_request_period_starts); /* Before rotating, write the current stats to disk. */ dump_geoip_stats(); + if (get_options()->EntryStatistics) + dump_entry_stats(); /* Now rotate request period */ SMARTLIST_FOREACH(geoip_countries, geoip_country_t *, c, { memmove(&c->n_v2_ns_requests[0], &c->n_v2_ns_requests[1], @@ -657,6 +665,40 @@ dump_geoip_stats(void) #endif } +/** Store all our geoip statistics as entry guards into + * $DATADIR/entry-stats. */ +static void +dump_entry_stats(void) +{ +#ifdef ENABLE_ENTRY_STATS + time_t now = time(NULL); + char *filename = get_datadir_fname("entry-stats"); + char *data = NULL; + char since[ISO_TIME_LEN+1], written[ISO_TIME_LEN+1]; + open_file_t *open_file = NULL; + FILE *out; + + data = geoip_get_client_history(now, GEOIP_CLIENT_CONNECT); + format_iso_time(since, geoip_get_history_start()); + format_iso_time(written, now); + out = start_writing_to_stdio_file(filename, OPEN_FLAGS_APPEND, + 0600, &open_file); + if (!out) + goto done; + if (fprintf(out, "written %s\nstarted-at %s\nips %s\n", + written, since, data ? data : "") < 0) + goto done; + + finish_writing_to_file(open_file); + open_file = NULL; + done: + if (open_file) + abort_writing_to_file(open_file); + tor_free(filename); + tor_free(data); +#endif +} + /** Helper used to implement GETINFO ip-to-country/... controller command. */ int getinfo_helper_geoip(control_connection_t *control_conn, diff --git a/src/or/or.h b/src/or/or.h index 935ea523df..65eac0e9e9 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2478,6 +2478,9 @@ typedef struct { /** If true, the user wants us to collect statistics on port usage. */ int ExitPortStatistics; + /** If true, the user wants us to collect statistics as entry node. */ + int EntryStatistics; + /** If true, do not believe anybody who tells us that a domain resolves * to an internal address, or that an internal address has a PTR mapping. * Helps avoid some cross-site attacks. */ @@ -3595,6 +3598,10 @@ int dnsserv_launch_request(const char *name, int is_reverse); * we are willing to talk about it? */ #define DIR_RECORD_USAGE_MIN_OBSERVATION_TIME (24*60*60) +/** Time interval: Flush geoip data to disk this often when measuring on an + * entry guard. */ +#define ENTRY_RECORD_USAGE_RETAIN_IPS (24*60*60) + #ifdef GEOIP_PRIVATE int geoip_parse_entry(const char *line); #endif @@ -3610,7 +3617,7 @@ country_t geoip_get_country(const char *countrycode); * the others, we're not. */ typedef enum { - /** We've noticed a connection as a bridge relay. */ + /** We've noticed a connection as a bridge relay or entry guard. */ GEOIP_CLIENT_CONNECT = 0, /** We've served a networkstatus consensus as a directory server. */ GEOIP_CLIENT_NETWORKSTATUS = 1, diff --git a/src/or/router.c b/src/or/router.c index cb73e378c0..bdea4fa764 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -1918,6 +1918,9 @@ extrainfo_get_client_geoip_summary(time_t now) int geoip_purge_interval = 48*60*60; #ifdef ENABLE_GEOIP_STATS geoip_purge_interval = DIR_RECORD_USAGE_RETAIN_IPS; +#endif +#ifdef ENABLE_ENTRY_STATS + geoip_purge_interval = ENTRY_RECORD_USAGE_RETAIN_IPS; #endif if (now > last_purged_at+geoip_purge_interval) { /* (Note that this also discards items in the client history with