#include "or.h"
/********* START VARIABLES **********/
+/** Global list of circuit build times */
+// XXX: Make this a smartlist..
+uint16_t circuit_build_times[NCIRCUITS_TO_OBSERVE];
+int build_times_idx = 0;
+int total_build_times = 0;
/** A global list of all circuits at this hop. */
extern circuit_t *global_circuitlist;
static void entry_guards_changed(void);
static time_t start_of_month(time_t when);
+static int circuit_build_times_add_time(time_t time);
+/** circuit_build_times is a circular array, so loop around when
+ * array is full
+ *
+ * time units are milliseconds
+ */
+static
+int
+circuit_build_times_add_time(long time)
+{
+ if(time > UINT16_MAX) {
+ log_notice(LD_CIRC,
+ "Circuit build time of %dms exceeds max. Capping at 65536ms", time);
+ time = UINT16_MAX;
+ }
+ circuit_build_times[build_times_idx] = time;
+ build_times_idx = (build_times_idx + 1) % NCIRCUITS_TO_OBSERVE;
+ if(total_build_times + 1 < NCIRCUITS_TO_OBSERVE)
+ total_build_times++;
+
+ return 0;
+}
+
+/**
+ * Calculate histogram
+ */
+void
+circuit_build_times_create_histogram(uint16_t * histogram)
+{
+ int i, c;
+ // calculate histogram
+ for(i = 0; i < NCIRCUITS_TO_OBSERVE; i++) {
+ if(circuit_build_times[i] == 0) continue; /* 0 <-> uninitialized */
+
+ c = (circuit_build_times[i] / BUILDTIME_BIN_WIDTH);
+ histogram[c]++;
+ }
+}
+
+/**
+ * Find maximum circuit build time
+ */
+uint16_t
+circuit_build_times_max()
+{
+ int i = 0, max_build_time = 0;
+ for( i = 0; i < NCIRCUITS_TO_OBSERVE; i++) {
+ if(circuit_build_times[i] > max_build_time)
+ max_build_time = circuit_build_times[i];
+ }
+ return max_build_time;
+}
+
+uint16_t
+circuit_build_times_min()
+{
+ int i = 0;
+ uint16_t min_build_time = UINT16_MAX;
+ for( i = 0; i < NCIRCUITS_TO_OBSERVE; i++) {
+ if(circuit_build_times[i] && /* 0 <-> uninitialized */
+ circuit_build_times[i] < min_build_time)
+ min_build_time = circuit_build_times[i];
+ }
+ return min_build_time;
+}
+
+/**
+ * output a histogram of current circuit build times
+ */
+void
+circuit_build_times_update_state(or_state_t * state)
+{
+ uint16_t max_build_time = 0, *histogram;
+ int i = 0, nbins = 0;
+ config_line_t **next, *line;
+
+ max_build_time = circuit_build_times_max();
+ nbins = 1 + (max_build_time / BUILDTIME_BIN_WIDTH);
+ histogram = tor_malloc_zero(nbins * sizeof(uint16_t));
+
+ circuit_build_times_create_histogram(histogram);
+ // write to state
+ config_free_lines(state->BuildtimeHistogram);
+ next = &state->BuildtimeHistogram;
+ *next = NULL;
+
+ state->TotalBuildTimes = total_build_times;
+
+ // total build times?
+ for(i = 0; i < nbins; i++) {
+ if(histogram[i] == 0) continue; // compress the histogram by skipping the blanks
+ *next = line = tor_malloc_zero(sizeof(config_line_t));
+ line->key = tor_strdup("CircuitBuildTimeBin");
+ line->value = tor_malloc(20);
+ tor_snprintf(line->value, 20, "%d %d", i*BUILDTIME_BIN_WIDTH,
+ histogram[i]);
+ next = &(line->next);
+ }
+ if(!get_options()->AvoidDiskWrites)
+ or_state_mark_dirty(get_or_state(), 0);
+
+ if(histogram) tor_free(histogram);
+}
+
+int
+find_next_available(int chosen)
+{// find index of next open slot in circuit_build_times
+ int idx = 0;
+ for(idx = (chosen + 1) % NCIRCUITS_TO_OBSERVE; idx < chosen;
+ idx = ((idx + 1 ) % NCIRCUITS_TO_OBSERVE)) {
+ if(circuit_build_times[idx] == 0) {
+ return idx;
+ }
+ }
+ return 0;
+}
+
+/** Load histogram from state */
+int
+circuit_build_times_parse_state(or_state_t *state, char **msg)
+{
+ config_line_t *line;
+ msg = NULL;
+ memset(circuit_build_times, 0, NCIRCUITS_TO_OBSERVE);
+ total_build_times = state->TotalBuildTimes;
+
+ for(line = state->BuildtimeHistogram; line; line = line->next) {
+ smartlist_t * args = smartlist_create();
+ smartlist_split_string(args, line->value, " ",
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ if(smartlist_len(args) < 2) {
+ *msg = tor_strdup("Unable to parse circuit build times: "
+ "Too few arguments to CircuitBuildTIme");
+ break;
+ } else {
+ uint16_t ms, count, i;
+ /* XXX: use tor_strtol */
+ ms = atol(smartlist_get(args,0));
+ count = atol(smartlist_get(args,1));
+ for(i = 0; i < count; i++) {
+ circuit_build_times_add_time(ms);
+ }
+ }
+ }
+ return (msg ? -1 : 0);
+}
+
+
+
+
/** Iterate over values of circ_id, starting from conn-\>next_circ_id,
* and with the high bit specified by conn-\>circ_id_type, until we get
* a circ_id that is not in use by any other circuit on that conn.
log_debug(LD_CIRC,"starting to send subsequent skin.");
hop = onion_next_hop_in_cpath(circ->cpath);
if (!hop) {
+ struct timeval end;
+ tor_gettimeofday(&end);
/* done building the circuit. whew. */
circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_OPEN);
+ circuit_build_times_add_time(tor_mdiff(&circ->_base.timestamp_created,
+ &end));
+ circuit_build_times_recompute();
log_info(LD_CIRC,"circuit built!");
circuit_reset_failure_count(0);
if (circ->build_state->onehop_tunnel)
V(LastRotatedOnionKey, ISOTIME, NULL),
V(LastWritten, ISOTIME, NULL),
+ VAR("TotalBuildTimes", UINT, TotalBuildTimes, NULL),
+ VAR("CircuitBuildTimeBin", LINELIST_S, BuildtimeHistogram, NULL),
+ VAR("BuildtimeHistogram", LINELIST_V, BuildtimeHistogram, NULL),
+
+
{ NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
};
/* Hidden service options: HiddenService: dir,excludenodes, nodes,
* options, port. PublishHidServDescriptor */
+ /* Circuit build time histogram options */
+ { "CircuitBuildTimeBin", "Histogram of recent circuit build times"},
+ { "TotalBuildTimes", "Total number of buildtimes in histogram"},
+
/* Nonpersistent options: __LeaveStreamsUnattached, __AllDirActionsPrivate */
{ NULL, NULL },
};
log_warn(LD_GENERAL,"Unparseable bandwidth history state: %s",err);
tor_free(err);
}
+
+ if(circuit_build_times_parse_state(global_state, &err) < 0) {
+ log_warn(LD_GENERAL,"%s",err);
+ tor_free(err);
+
+ }
+
}
/** Reload the persistent state from disk, generating a new state as needed.
* to avoid redundant writes. */
entry_guards_update_state(global_state);
rep_hist_update_state(global_state);
+ circuit_build_times_update_state(global_state);
if (accounting_is_enabled(get_options()))
accounting_run_housekeeping(now);
DH_KEY_LEN)
#define ONIONSKIN_REPLY_LEN (DH_KEY_LEN+DIGEST_LEN)
+// XXX: Do we want to artifically tweak CircuitIdleTimeout and
+// the number of circuits we build at a time if < MIN here?
+#define MIN_CIRCUITS_TO_OBSERVE 1000
+#define NCIRCUITS_TO_OBSERVE 10000 /* approx 3 weeks worth of circuits */
+#define BUILDTIME_BIN_WIDTH 50
+
+
/** Information used to build a circuit. */
typedef struct {
/** Intended length of the final circuit. */
time_t timestamp_created; /**< When was this circuit created? */
time_t timestamp_dirty; /**< When the circuit was first used, or 0 if the
* circuit is clean. */
+ struct timeval highres_created; /**< When exactly was this circuit created? */
uint16_t marked_for_close; /**< Should we close this circuit at the end of
* the main loop? (If true, holds the line number
int BWHistoryWriteInterval;
smartlist_t *BWHistoryWriteValues;
+ /** Build time histogram */
+ config_line_t * BuildtimeHistogram;
+ uint16_t TotalBuildTimes;
+
+
/** What version of Tor wrote this state file? */
char *TorVersion;
void entry_guards_free_all(void);
+void circuit_build_times_update_state(or_state_t *state);
+int circuit_build_times_parse_state(or_state_t *state, char **msg);
+
+
+
/********************************* circuitlist.c ***********************/
circuit_t * _circuit_get_global_list(void);