| 
					
				 | 
			
			
				@@ -1320,6 +1320,176 @@ rep_hist_note_bytes_read(size_t num_bytes, time_t when) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   add_obs(read_array, when, num_bytes); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#ifdef ENABLE_EXIT_STATS 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* Some constants */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** How long are the intervals for measuring exit stats? */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define EXIT_STATS_INTERVAL_SEC (24 * 60 * 60) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** To what multiple should byte numbers be rounded up? */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define EXIT_STATS_ROUND_UP_BYTES 1024 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** To what multiple should stream counts be rounded up? */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define EXIT_STATS_ROUND_UP_STREAMS 4 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Number of TCP ports */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define EXIT_STATS_NUM_PORTS 65536 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* The following data structures are arrays and no fancy smartlists or maps, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * so that all write operations can be done in constant time. This comes at 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * the price of some memory (1.25 MB) and linear complexity when writing 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * stats. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Number of bytes read in current period by exit port */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static uint64_t exit_bytes_read[EXIT_STATS_NUM_PORTS]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Number of bytes written in current period by exit port */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static uint64_t exit_bytes_written[EXIT_STATS_NUM_PORTS]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Number of streams opened in current period by exit port */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static uint32_t exit_streams[EXIT_STATS_NUM_PORTS]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** When does the current exit stats period end? */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static time_t end_of_current_exit_stats_period = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Write exit stats for the current period to disk and reset counters. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+write_exit_stats(time_t when) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  char t[ISO_TIME_LEN+1]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int r, i, comma; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint64_t *b; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  char *filename = get_datadir_fname("exit-stats"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  open_file_t *open_file = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  FILE *out = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  log_debug(LD_HIST, "Considering writing exit port statistics to disk.."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  while (when > end_of_current_exit_stats_period) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    format_iso_time(t, end_of_current_exit_stats_period); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    log_info(LD_HIST, "Writing exit port statistics to disk for period " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+             "ending at %s.", t); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (!open_file) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      out = start_writing_to_stdio_file(filename, OPEN_FLAGS_APPEND, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                        0600, &open_file); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (!out) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        log_warn(LD_HIST, "Couldn't open '%s'.", filename); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        goto done; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /* written yyyy-mm-dd HH:MM:SS (n s) */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (fprintf(out, "written %s (%d s)\n", t, EXIT_STATS_INTERVAL_SEC) < 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      goto done; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /* kibibytes-(read|written) port=kibibytes,.. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    for (r = 0; r < 2; r++) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      b = r ? exit_bytes_read : exit_bytes_written; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      tor_assert(b); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (fprintf(out, "%s ", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  r == 0 ? "kibibytes-read" : "kibibytes-written")<0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        goto done; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      comma = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      for (i = 1; i < EXIT_STATS_NUM_PORTS; i++) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (b[i] > 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          uint64_t num = b[i]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          num += EXIT_STATS_ROUND_UP_BYTES - 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          num /= EXIT_STATS_ROUND_UP_BYTES; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          num *= EXIT_STATS_ROUND_UP_BYTES; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          num /= 1024; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          if (fprintf(out, "%s%d="U64_FORMAT, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      comma++ ? "," : "", i, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      U64_PRINTF_ARG(num)) < 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            goto done; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (fprintf(out, "\n")<0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        goto done; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      /* Reset counters */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      memset(b, 0, EXIT_STATS_NUM_PORTS*sizeof(uint64_t)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /* streams-opened port=num,.. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (fprintf(out, "streams-opened ")<0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      goto done; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    comma = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    for (i = 1; i < EXIT_STATS_NUM_PORTS; i++) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (exit_streams[i] > 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        uint32_t num = exit_streams[i]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        num += EXIT_STATS_ROUND_UP_STREAMS - 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        num /= EXIT_STATS_ROUND_UP_STREAMS; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        num *= EXIT_STATS_ROUND_UP_STREAMS; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (fprintf(out, "%s%d=%d", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    comma++ ? "," : "", i, num)<0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          goto done; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (fprintf(out, "\n")<0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      goto done; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /* Reset counters */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    memset(exit_streams, 0, sizeof(exit_streams)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    end_of_current_exit_stats_period += EXIT_STATS_INTERVAL_SEC; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (open_file) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    finish_writing_to_file(open_file); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  open_file = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ done: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (open_file) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    abort_writing_to_file(open_file); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_free(filename); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Prepare to add an exit stats observation at second <b>when</b> by 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * checking whether this observation lies in the current observation 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * period; if not, shift the current period forward by one until the 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * reported event fits it and write all results in between to disk. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+add_exit_obs(time_t when) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (when > end_of_current_exit_stats_period) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (end_of_current_exit_stats_period) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      write_exit_stats(when); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    else 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      end_of_current_exit_stats_period = when + EXIT_STATS_INTERVAL_SEC; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Note that we wrote <b>num_bytes</b> to an exit connection to 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>port</b> in second <b>when</b>. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+rep_hist_note_exit_bytes_written(uint16_t port, size_t num_bytes, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                 time_t when) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (!get_options()->ExitPortStatistics) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  add_exit_obs(when); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  exit_bytes_written[port] += num_bytes; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  log_debug(LD_HIST, "Written %lu bytes to exit connection to port %d.", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            (unsigned long)num_bytes, port); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Note that we read <b>num_bytes</b> from an exit connection to 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>port</b> in second <b>when</b>. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+rep_hist_note_exit_bytes_read(uint16_t port, size_t num_bytes, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                              time_t when) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (!get_options()->ExitPortStatistics) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  add_exit_obs(when); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  exit_bytes_read[port] += num_bytes; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  log_debug(LD_HIST, "Read %lu bytes from exit connection to port %d.", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            (unsigned long)num_bytes, port); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Note that we opened an exit stream to <b>port</b> in second 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>when</b>. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+rep_hist_note_exit_stream_opened(uint16_t port, time_t when) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (!get_options()->ExitPortStatistics) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  add_exit_obs(when); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  exit_streams[port]++; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  log_debug(LD_HIST, "Opened exit stream to port %d", port); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#endif 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /** Helper: Return the largest value in b->maxima.  (This is equal to the 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  * most bandwidth used in any NUM_SECS_ROLLING_MEASURE period for the last 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  * NUM_SECS_BW_SUM_IS_VALID seconds.) 
			 |