diff --git a/src/or/directory.c b/src/or/directory.c
index 0f9cef53df..cf9f5543d7 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -2493,6 +2493,8 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
/* v2 or v3 network status fetch. */
smartlist_t *dir_fps = smartlist_create();
int is_v3 = !strcmpstart(url, "/tor/status-vote");
+ geoip_client_action_t act =
+ is_v3 ? GEOIP_CLIENT_NETWORKSTATUS : GEOIP_CLIENT_NETWORKSTATUS_V2;
const char *request_type = NULL;
const char *key = url + strlen("/tor/status/");
long lifetime = NETWORKSTATUS_CACHE_LIFETIME;
@@ -2517,6 +2519,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
write_http_status_line(conn, 404, "Consensus not signed by sufficient "
"number of requested authorities");
smartlist_free(dir_fps);
+ geoip_note_ns_response(act, GEOIP_REJECT_NOT_ENOUGH_SIGS);
goto done;
}
@@ -2529,6 +2532,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
if (!smartlist_len(dir_fps)) { /* we failed to create/cache cp */
write_http_status_line(conn, 503, "Network status object unavailable");
smartlist_free(dir_fps);
+ geoip_note_ns_response(act, GEOIP_REJECT_UNAVAILABLE);
goto done;
}
@@ -2536,11 +2540,13 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
write_http_status_line(conn, 404, "Not found");
SMARTLIST_FOREACH(dir_fps, char *, cp, tor_free(cp));
smartlist_free(dir_fps);
+ geoip_note_ns_response(act, GEOIP_REJECT_NOT_FOUND);
goto done;
} else if (!smartlist_len(dir_fps)) {
write_http_status_line(conn, 304, "Not modified");
SMARTLIST_FOREACH(dir_fps, char *, cp, tor_free(cp));
smartlist_free(dir_fps);
+ geoip_note_ns_response(act, GEOIP_REJECT_NOT_MODIFIED);
goto done;
}
@@ -2552,16 +2558,16 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
write_http_status_line(conn, 503, "Directory busy, try again later");
SMARTLIST_FOREACH(dir_fps, char *, fp, tor_free(fp));
smartlist_free(dir_fps);
+ geoip_note_ns_response(act, GEOIP_REJECT_BUSY);
goto done;
}
#ifdef ENABLE_GEOIP_STATS
{
- geoip_client_action_t act =
- is_v3 ? GEOIP_CLIENT_NETWORKSTATUS : GEOIP_CLIENT_NETWORKSTATUS_V2;
struct in_addr in;
if (tor_inet_aton((TO_CONN(conn))->address, &in))
geoip_note_client_seen(act, ntohl(in.s_addr), time(NULL));
+ geoip_note_ns_response(act, GEOIP_SUCCESS);
}
#endif
diff --git a/src/or/geoip.c b/src/or/geoip.c
index 6cfc1392bb..0aa5fd873b 100644
--- a/src/or/geoip.c
+++ b/src/or/geoip.c
@@ -492,6 +492,43 @@ geoip_remove_old_clients(time_t cutoff)
client_history_starts = cutoff;
}
+#ifdef ENABLE_GEOIP_STATS
+/** How many responses are we giving to clients requesting v2 network
+ * statuses? */
+static uint32_t ns_v2_responses[GEOIP_NS_RESPONSE_NUM];
+
+/** How many responses are we giving to clients requesting v3 network
+ * statuses? */
+static uint32_t ns_v3_responses[GEOIP_NS_RESPONSE_NUM];
+#endif
+
+/** Note that we've rejected a client's request for a v2 or v3 network
+ * status, encoded in action for reason reason at time
+ * now. */
+void
+geoip_note_ns_response(geoip_client_action_t action,
+ geoip_ns_response_t response)
+{
+#ifdef ENABLE_GEOIP_STATS
+ static int arrays_initialized = 0;
+ if (!arrays_initialized) {
+ memset(ns_v2_responses, 0, sizeof(ns_v2_responses));
+ memset(ns_v3_responses, 0, sizeof(ns_v3_responses));
+ arrays_initialized = 1;
+ }
+ tor_assert(action == GEOIP_CLIENT_NETWORKSTATUS ||
+ action == GEOIP_CLIENT_NETWORKSTATUS_V2);
+ tor_assert(response < GEOIP_NS_RESPONSE_NUM);
+ if (action == GEOIP_CLIENT_NETWORKSTATUS)
+ ns_v3_responses[response]++;
+ else
+ ns_v2_responses[response]++;
+#else
+ (void) action;
+ (void) response;
+#endif
+}
+
/** Do not mention any country from which fewer than this number of IPs have
* connected. This conceivably avoids reporting information that could
* deanonymize users, though analysis is lacking. */
@@ -686,6 +723,7 @@ dump_geoip_stats(void)
open_file_t *open_file = NULL;
double v2_share = 0.0, v3_share = 0.0;
FILE *out;
+ int i;
data_v2 = geoip_get_client_history(now, GEOIP_CLIENT_NETWORKSTATUS_V2);
data_v3 = geoip_get_client_history(now, GEOIP_CLIENT_NETWORKSTATUS);
@@ -711,6 +749,33 @@ dump_geoip_stats(void)
since,
data_v3 ? data_v3 : "", data_v2 ? data_v2 : "") < 0)
goto done;
+#define RESPONSE_GRANULARITY 8
+ for (i = 0; i < GEOIP_NS_RESPONSE_NUM; i++) {
+ ns_v2_responses[i] = round_uint32_to_next_multiple_of(
+ ns_v2_responses[i], RESPONSE_GRANULARITY);
+ ns_v3_responses[i] = round_uint32_to_next_multiple_of(
+ ns_v3_responses[i], RESPONSE_GRANULARITY);
+ }
+#undef RESPONSE_GRANULARITY
+ if (fprintf(out, "n-ns-resp ok=%u,not-enough-sigs=%u,unavailable=%u,"
+ "not-found=%u,not-modified=%u,busy=%u\n",
+ ns_v3_responses[GEOIP_SUCCESS],
+ ns_v3_responses[GEOIP_REJECT_NOT_ENOUGH_SIGS],
+ ns_v3_responses[GEOIP_REJECT_UNAVAILABLE],
+ ns_v3_responses[GEOIP_REJECT_NOT_FOUND],
+ ns_v3_responses[GEOIP_REJECT_NOT_MODIFIED],
+ ns_v3_responses[GEOIP_REJECT_BUSY]) < 0)
+ goto done;
+ if (fprintf(out, "n-v2-ns-resp ok=%u,unavailable=%u,"
+ "not-found=%u,not-modified=%u,busy=%u\n",
+ ns_v2_responses[GEOIP_SUCCESS],
+ ns_v2_responses[GEOIP_REJECT_UNAVAILABLE],
+ ns_v2_responses[GEOIP_REJECT_NOT_FOUND],
+ ns_v2_responses[GEOIP_REJECT_NOT_MODIFIED],
+ ns_v2_responses[GEOIP_REJECT_BUSY]) < 0)
+ goto done;
+ memset(ns_v2_responses, 0, sizeof(ns_v2_responses));
+ memset(ns_v3_responses, 0, sizeof(ns_v3_responses));
if (!geoip_get_mean_shares(now, &v2_share, &v3_share)) {
if (fprintf(out, "v2-ns-share %0.2lf%%\n", v2_share*100) < 0)
goto done;
diff --git a/src/or/or.h b/src/or/or.h
index 46daa2056a..c18ef15faf 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -3645,6 +3645,26 @@ typedef enum {
void geoip_note_client_seen(geoip_client_action_t action,
uint32_t addr, time_t now);
void geoip_remove_old_clients(time_t cutoff);
+/** Indicates either a positive reply or a reason for rejectng a network
+ * status request that will be included in geoip statistics. */
+typedef enum {
+ /** Request is answered successfully. */
+ GEOIP_SUCCESS = 0,
+ /** V3 network status is not signed by a sufficient number of requested
+ * authorities. */
+ GEOIP_REJECT_NOT_ENOUGH_SIGS = 1,
+ /** Requested network status object is unavailable. */
+ GEOIP_REJECT_UNAVAILABLE = 2,
+ /** Requested network status not found. */
+ GEOIP_REJECT_NOT_FOUND = 3,
+ /** Network status has not been modified since If-Modified-Since time. */
+ GEOIP_REJECT_NOT_MODIFIED = 4,
+ /** Directory is busy. */
+ GEOIP_REJECT_BUSY = 5,
+} geoip_ns_response_t;
+#define GEOIP_NS_RESPONSE_NUM 6
+void geoip_note_ns_response(geoip_client_action_t action,
+ geoip_ns_response_t response);
time_t geoip_get_history_start(void);
char *geoip_get_client_history(time_t now, geoip_client_action_t action);
char *geoip_get_request_history(time_t now, geoip_client_action_t action);