|
@@ -19,6 +19,7 @@
|
|
|
#include "circuitlist.h"
|
|
|
#include "circuitstats.h"
|
|
|
#include "circuituse.h"
|
|
|
+#include "command.h"
|
|
|
#include "config.h"
|
|
|
#include "confparse.h"
|
|
|
#include "connection.h"
|
|
@@ -190,6 +191,20 @@ log_severity_to_event(int severity)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/** Helper: clear bandwidth counters of all origin circuits. */
|
|
|
+static void
|
|
|
+clear_circ_bw_fields(void)
|
|
|
+{
|
|
|
+ circuit_t *circ;
|
|
|
+ origin_circuit_t *ocirc;
|
|
|
+ TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) {
|
|
|
+ if (!CIRCUIT_IS_ORIGIN(circ))
|
|
|
+ continue;
|
|
|
+ ocirc = TO_ORIGIN_CIRCUIT(circ);
|
|
|
+ ocirc->n_written_circ_bw = ocirc->n_read_circ_bw = 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/** Set <b>global_event_mask*</b> to the bitwise OR of each live control
|
|
|
* connection's event_mask field. */
|
|
|
void
|
|
@@ -215,8 +230,8 @@ control_update_global_event_mask(void)
|
|
|
* we want to hear...*/
|
|
|
control_adjust_event_log_severity();
|
|
|
|
|
|
- /* ...then, if we've started logging stream bw, clear the appropriate
|
|
|
- * fields. */
|
|
|
+ /* ...then, if we've started logging stream or circ bw, clear the
|
|
|
+ * appropriate fields. */
|
|
|
if (! (old_mask & EVENT_STREAM_BANDWIDTH_USED) &&
|
|
|
(new_mask & EVENT_STREAM_BANDWIDTH_USED)) {
|
|
|
SMARTLIST_FOREACH(conns, connection_t *, conn,
|
|
@@ -227,6 +242,10 @@ control_update_global_event_mask(void)
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
+ if (! (old_mask & EVENT_CIRC_BANDWIDTH_USED) &&
|
|
|
+ (new_mask & EVENT_CIRC_BANDWIDTH_USED)) {
|
|
|
+ clear_circ_bw_fields();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/** Adjust the log severities that result in control_event_logmsg being called
|
|
@@ -916,6 +935,10 @@ static const struct control_event_t control_event_table[] = {
|
|
|
{ EVENT_BUILDTIMEOUT_SET, "BUILDTIMEOUT_SET" },
|
|
|
{ EVENT_SIGNAL, "SIGNAL" },
|
|
|
{ EVENT_CONF_CHANGED, "CONF_CHANGED"},
|
|
|
+ { EVENT_CONN_BW, "CONN_BW" },
|
|
|
+ { EVENT_CELL_STATS, "CELL_STATS" },
|
|
|
+ { EVENT_TB_EMPTY, "TB_EMPTY" },
|
|
|
+ { EVENT_CIRC_BANDWIDTH_USED, "CIRC_BW" },
|
|
|
{ EVENT_TRANSPORT_LAUNCHED, "TRANSPORT_LAUNCHED" },
|
|
|
{ 0, NULL },
|
|
|
};
|
|
@@ -3834,17 +3857,17 @@ control_event_or_conn_status(or_connection_t *conn, or_conn_status_event_t tp,
|
|
|
}
|
|
|
ncircs += connection_or_get_num_circuits(conn);
|
|
|
if (ncircs && (tp == OR_CONN_EVENT_FAILED || tp == OR_CONN_EVENT_CLOSED)) {
|
|
|
- tor_snprintf(ncircs_buf, sizeof(ncircs_buf), "%sNCIRCS=%d",
|
|
|
- reason ? " " : "", ncircs);
|
|
|
+ tor_snprintf(ncircs_buf, sizeof(ncircs_buf), " NCIRCS=%d", ncircs);
|
|
|
}
|
|
|
|
|
|
orconn_target_get_name(name, sizeof(name), conn);
|
|
|
send_control_event(EVENT_OR_CONN_STATUS, ALL_FORMATS,
|
|
|
- "650 ORCONN %s %s %s%s%s\r\n",
|
|
|
+ "650 ORCONN %s %s%s%s%s ID="U64_FORMAT"\r\n",
|
|
|
name, status,
|
|
|
- reason ? "REASON=" : "",
|
|
|
+ reason ? " REASON=" : "",
|
|
|
orconn_end_reason_to_control_string(reason),
|
|
|
- ncircs_buf);
|
|
|
+ ncircs_buf,
|
|
|
+ U64_PRINTF_ARG(conn->base_.global_identifier));
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -3855,6 +3878,8 @@ control_event_or_conn_status(or_connection_t *conn, or_conn_status_event_t tp,
|
|
|
int
|
|
|
control_event_stream_bandwidth(edge_connection_t *edge_conn)
|
|
|
{
|
|
|
+ circuit_t *circ;
|
|
|
+ origin_circuit_t *ocirc;
|
|
|
if (EVENT_IS_INTERESTING(EVENT_STREAM_BANDWIDTH_USED)) {
|
|
|
if (!edge_conn->n_read && !edge_conn->n_written)
|
|
|
return 0;
|
|
@@ -3865,6 +3890,12 @@ control_event_stream_bandwidth(edge_connection_t *edge_conn)
|
|
|
(unsigned long)edge_conn->n_read,
|
|
|
(unsigned long)edge_conn->n_written);
|
|
|
|
|
|
+ circ = circuit_get_by_edge_conn(edge_conn);
|
|
|
+ if (circ && CIRCUIT_IS_ORIGIN(circ)) {
|
|
|
+ ocirc = TO_ORIGIN_CIRCUIT(circ);
|
|
|
+ ocirc->n_read_circ_bw += edge_conn->n_read;
|
|
|
+ ocirc->n_written_circ_bw += edge_conn->n_written;
|
|
|
+ }
|
|
|
edge_conn->n_written = edge_conn->n_read = 0;
|
|
|
}
|
|
|
|
|
@@ -3902,6 +3933,235 @@ control_event_stream_bandwidth_used(void)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/** A second or more has elapsed: tell any interested control connections
|
|
|
+ * how much bandwidth origin circuits have used. */
|
|
|
+int
|
|
|
+control_event_circ_bandwidth_used(void)
|
|
|
+{
|
|
|
+ circuit_t *circ;
|
|
|
+ origin_circuit_t *ocirc;
|
|
|
+ if (!EVENT_IS_INTERESTING(EVENT_CIRC_BANDWIDTH_USED))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) {
|
|
|
+ if (!CIRCUIT_IS_ORIGIN(circ))
|
|
|
+ continue;
|
|
|
+ ocirc = TO_ORIGIN_CIRCUIT(circ);
|
|
|
+ if (!ocirc->n_read_circ_bw && !ocirc->n_written_circ_bw)
|
|
|
+ continue;
|
|
|
+ send_control_event(EVENT_CIRC_BANDWIDTH_USED, ALL_FORMATS,
|
|
|
+ "650 CIRC_BW ID=%d READ=%lu WRITTEN=%lu\r\n",
|
|
|
+ ocirc->global_identifier,
|
|
|
+ (unsigned long)ocirc->n_read_circ_bw,
|
|
|
+ (unsigned long)ocirc->n_written_circ_bw);
|
|
|
+ ocirc->n_written_circ_bw = ocirc->n_read_circ_bw = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/** Print out CONN_BW event for a single OR/DIR/EXIT <b>conn</b> and reset
|
|
|
+ * bandwidth counters. */
|
|
|
+int
|
|
|
+control_event_conn_bandwidth(connection_t *conn)
|
|
|
+{
|
|
|
+ const char *conn_type_str;
|
|
|
+ if (!get_options()->TestingEnableConnBwEvent ||
|
|
|
+ !EVENT_IS_INTERESTING(EVENT_CONN_BW))
|
|
|
+ return 0;
|
|
|
+ if (!conn->n_read_conn_bw && !conn->n_written_conn_bw)
|
|
|
+ return 0;
|
|
|
+ switch (conn->type) {
|
|
|
+ case CONN_TYPE_OR:
|
|
|
+ conn_type_str = "OR";
|
|
|
+ break;
|
|
|
+ case CONN_TYPE_DIR:
|
|
|
+ conn_type_str = "DIR";
|
|
|
+ break;
|
|
|
+ case CONN_TYPE_EXIT:
|
|
|
+ conn_type_str = "EXIT";
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ send_control_event(EVENT_CONN_BW, ALL_FORMATS,
|
|
|
+ "650 CONN_BW ID="U64_FORMAT" TYPE=%s "
|
|
|
+ "READ=%lu WRITTEN=%lu\r\n",
|
|
|
+ U64_PRINTF_ARG(conn->global_identifier),
|
|
|
+ conn_type_str,
|
|
|
+ (unsigned long)conn->n_read_conn_bw,
|
|
|
+ (unsigned long)conn->n_written_conn_bw);
|
|
|
+ conn->n_written_conn_bw = conn->n_read_conn_bw = 0;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/** A second or more has elapsed: tell any interested control
|
|
|
+ * connections how much bandwidth connections have used. */
|
|
|
+int
|
|
|
+control_event_conn_bandwidth_used(void)
|
|
|
+{
|
|
|
+ if (get_options()->TestingEnableConnBwEvent &&
|
|
|
+ EVENT_IS_INTERESTING(EVENT_CONN_BW)) {
|
|
|
+ SMARTLIST_FOREACH(get_connection_array(), connection_t *, conn,
|
|
|
+ control_event_conn_bandwidth(conn));
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/** Helper: iterate over cell statistics of <b>circ</b> and sum up added
|
|
|
+ * cells, removed cells, and waiting times by cell command and direction.
|
|
|
+ * Store results in <b>cell_stats</b>. Free cell statistics of the
|
|
|
+ * circuit afterwards. */
|
|
|
+void
|
|
|
+sum_up_cell_stats_by_command(circuit_t *circ, cell_stats_t *cell_stats)
|
|
|
+{
|
|
|
+ memset(cell_stats, 0, sizeof(cell_stats_t));
|
|
|
+ SMARTLIST_FOREACH_BEGIN(circ->testing_cell_stats,
|
|
|
+ testing_cell_stats_entry_t *, ent) {
|
|
|
+ tor_assert(ent->command <= CELL_COMMAND_MAX_);
|
|
|
+ if (!ent->removed && !ent->exitward) {
|
|
|
+ cell_stats->added_cells_appward[ent->command] += 1;
|
|
|
+ } else if (!ent->removed && ent->exitward) {
|
|
|
+ cell_stats->added_cells_exitward[ent->command] += 1;
|
|
|
+ } else if (!ent->exitward) {
|
|
|
+ cell_stats->removed_cells_appward[ent->command] += 1;
|
|
|
+ cell_stats->total_time_appward[ent->command] += ent->waiting_time * 10;
|
|
|
+ } else {
|
|
|
+ cell_stats->removed_cells_exitward[ent->command] += 1;
|
|
|
+ cell_stats->total_time_exitward[ent->command] += ent->waiting_time * 10;
|
|
|
+ }
|
|
|
+ tor_free(ent);
|
|
|
+ } SMARTLIST_FOREACH_END(ent);
|
|
|
+ smartlist_free(circ->testing_cell_stats);
|
|
|
+ circ->testing_cell_stats = NULL;
|
|
|
+}
|
|
|
+
|
|
|
+/** Helper: append a cell statistics string to <code>event_parts</code>,
|
|
|
+ * prefixed with <code>key</code>=. Statistics consist of comma-separated
|
|
|
+ * key:value pairs with lower-case command strings as keys and cell
|
|
|
+ * numbers or total waiting times as values. A key:value pair is included
|
|
|
+ * if the entry in <code>include_if_non_zero</code> is not zero, but with
|
|
|
+ * the (possibly zero) entry from <code>number_to_include</code>. Both
|
|
|
+ * arrays are expected to have a length of CELL_COMMAND_MAX_ + 1. If no
|
|
|
+ * entry in <code>include_if_non_zero</code> is positive, no string will
|
|
|
+ * be added to <code>event_parts</code>. */
|
|
|
+void
|
|
|
+append_cell_stats_by_command(smartlist_t *event_parts, const char *key,
|
|
|
+ const uint64_t *include_if_non_zero,
|
|
|
+ const uint64_t *number_to_include)
|
|
|
+{
|
|
|
+ smartlist_t *key_value_strings = smartlist_new();
|
|
|
+ int i;
|
|
|
+ for (i = 0; i <= CELL_COMMAND_MAX_; i++) {
|
|
|
+ if (include_if_non_zero[i] > 0) {
|
|
|
+ smartlist_add_asprintf(key_value_strings, "%s:"U64_FORMAT,
|
|
|
+ cell_command_to_string(i),
|
|
|
+ U64_PRINTF_ARG(number_to_include[i]));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (smartlist_len(key_value_strings) > 0) {
|
|
|
+ char *joined = smartlist_join_strings(key_value_strings, ",", 0, NULL);
|
|
|
+ smartlist_add_asprintf(event_parts, "%s=%s", key, joined);
|
|
|
+ SMARTLIST_FOREACH(key_value_strings, char *, cp, tor_free(cp));
|
|
|
+ tor_free(joined);
|
|
|
+ }
|
|
|
+ smartlist_free(key_value_strings);
|
|
|
+}
|
|
|
+
|
|
|
+/** Helper: format <b>cell_stats</b> for <b>circ</b> for inclusion in a
|
|
|
+ * CELL_STATS event and write result string to <b>event_string</b>. */
|
|
|
+void
|
|
|
+format_cell_stats(char **event_string, circuit_t *circ,
|
|
|
+ cell_stats_t *cell_stats)
|
|
|
+{
|
|
|
+ smartlist_t *event_parts = smartlist_new();
|
|
|
+ if (CIRCUIT_IS_ORIGIN(circ)) {
|
|
|
+ origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
|
|
|
+ smartlist_add_asprintf(event_parts, "ID=%lu",
|
|
|
+ (unsigned long)ocirc->global_identifier);
|
|
|
+ } else if (TO_OR_CIRCUIT(circ)->p_chan) {
|
|
|
+ or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
|
|
|
+ smartlist_add_asprintf(event_parts, "InboundQueue=%lu",
|
|
|
+ (unsigned long)or_circ->p_circ_id);
|
|
|
+ smartlist_add_asprintf(event_parts, "InboundConn="U64_FORMAT,
|
|
|
+ U64_PRINTF_ARG(or_circ->p_chan->global_identifier));
|
|
|
+ append_cell_stats_by_command(event_parts, "InboundAdded",
|
|
|
+ cell_stats->added_cells_appward,
|
|
|
+ cell_stats->added_cells_appward);
|
|
|
+ append_cell_stats_by_command(event_parts, "InboundRemoved",
|
|
|
+ cell_stats->removed_cells_appward,
|
|
|
+ cell_stats->removed_cells_appward);
|
|
|
+ append_cell_stats_by_command(event_parts, "InboundTime",
|
|
|
+ cell_stats->removed_cells_appward,
|
|
|
+ cell_stats->total_time_appward);
|
|
|
+ }
|
|
|
+ if (circ->n_chan) {
|
|
|
+ smartlist_add_asprintf(event_parts, "OutboundQueue=%lu",
|
|
|
+ (unsigned long)circ->n_circ_id);
|
|
|
+ smartlist_add_asprintf(event_parts, "OutboundConn="U64_FORMAT,
|
|
|
+ U64_PRINTF_ARG(circ->n_chan->global_identifier));
|
|
|
+ append_cell_stats_by_command(event_parts, "OutboundAdded",
|
|
|
+ cell_stats->added_cells_exitward,
|
|
|
+ cell_stats->added_cells_exitward);
|
|
|
+ append_cell_stats_by_command(event_parts, "OutboundRemoved",
|
|
|
+ cell_stats->removed_cells_exitward,
|
|
|
+ cell_stats->removed_cells_exitward);
|
|
|
+ append_cell_stats_by_command(event_parts, "OutboundTime",
|
|
|
+ cell_stats->removed_cells_exitward,
|
|
|
+ cell_stats->total_time_exitward);
|
|
|
+ }
|
|
|
+ *event_string = smartlist_join_strings(event_parts, " ", 0, NULL);
|
|
|
+ SMARTLIST_FOREACH(event_parts, char *, cp, tor_free(cp));
|
|
|
+ smartlist_free(event_parts);
|
|
|
+}
|
|
|
+
|
|
|
+/** A second or more has elapsed: tell any interested control connection
|
|
|
+ * how many cells have been processed for a given circuit. */
|
|
|
+int
|
|
|
+control_event_circuit_cell_stats(void)
|
|
|
+{
|
|
|
+ circuit_t *circ;
|
|
|
+ cell_stats_t *cell_stats;
|
|
|
+ char *event_string;
|
|
|
+ if (!get_options()->TestingEnableCellStatsEvent ||
|
|
|
+ !EVENT_IS_INTERESTING(EVENT_CELL_STATS))
|
|
|
+ return 0;
|
|
|
+ cell_stats = tor_malloc(sizeof(cell_stats_t));;
|
|
|
+ TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) {
|
|
|
+ if (!circ->testing_cell_stats)
|
|
|
+ continue;
|
|
|
+ sum_up_cell_stats_by_command(circ, cell_stats);
|
|
|
+ format_cell_stats(&event_string, circ, cell_stats);
|
|
|
+ send_control_event(EVENT_CELL_STATS, ALL_FORMATS,
|
|
|
+ "650 CELL_STATS %s\r\n", event_string);
|
|
|
+ tor_free(event_string);
|
|
|
+ }
|
|
|
+ tor_free(cell_stats);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/** Tokens in <b>bucket</b> have been refilled: the read bucket was empty
|
|
|
+ * for <b>read_empty_time</b> millis, the write bucket was empty for
|
|
|
+ * <b>write_empty_time</b> millis, and buckets were last refilled
|
|
|
+ * <b>milliseconds_elapsed</b> millis ago. Only emit TB_EMPTY event if
|
|
|
+ * either read or write bucket have been empty before. */
|
|
|
+int
|
|
|
+control_event_tb_empty(const char *bucket, uint32_t read_empty_time,
|
|
|
+ uint32_t write_empty_time,
|
|
|
+ int milliseconds_elapsed)
|
|
|
+{
|
|
|
+ if (get_options()->TestingEnableTbEmptyEvent &&
|
|
|
+ EVENT_IS_INTERESTING(EVENT_TB_EMPTY) &&
|
|
|
+ (read_empty_time > 0 || write_empty_time > 0)) {
|
|
|
+ send_control_event(EVENT_TB_EMPTY, ALL_FORMATS,
|
|
|
+ "650 TB_EMPTY %s READ=%d WRITTEN=%d "
|
|
|
+ "LAST=%d\r\n",
|
|
|
+ bucket, read_empty_time, write_empty_time,
|
|
|
+ milliseconds_elapsed);
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/** A second or more has elapsed: tell any interested control
|
|
|
* connections how much bandwidth we used. */
|
|
|
int
|