commit 2e0fad542cccddf9ad8b8dbaeba8b1e825c09ff4
Merge: 49278cd e46de82
Author: Karsten Loesing <karsten.loesing(a)gmx.net>
Date: Mon Oct 28 11:30:49 2013 +0100
Merge branch 'morestats4' into morestats5
Conflicts:
doc/tor.1.txt
src/or/config.c
src/or/connection.h
src/or/control.c
src/or/control.h
src/or/or.h
src/or/relay.c
src/or/relay.h
src/test/test.c
doc/tor.1.txt | 19 +++
src/or/circuitmux.c | 2 +-
src/or/command.c | 27 ++++
src/or/command.h | 2 +
src/or/config.c | 24 ++++
src/or/connection.c | 180 ++++++++++++++++++++++-
src/or/connection.h | 9 ++
src/or/control.c | 274 ++++++++++++++++++++++++++++++++++-
src/or/control.h | 35 +++++
src/or/main.c | 5 +
src/or/or.h | 70 +++++++++
src/or/relay.c | 121 ++++++++++++++--
src/or/relay.h | 3 +-
src/test/Makefile.nmake | 6 +-
src/test/include.am | 1 +
src/test/test.c | 2 +
src/test/test_cell_queue.c | 6 +-
src/test/test_controller_events.c | 287 +++++++++++++++++++++++++++++++++++++
18 files changed, 1047 insertions(+), 26 deletions(-)
diff --cc doc/tor.1.txt
index ac33c59,3996334..615da2d
--- a/doc/tor.1.txt
+++ b/doc/tor.1.txt
@@@ -2050,19 -2005,11 +2050,22 @@@ The following options are used for runn
TestingV3AuthInitialDistDelay 20 seconds
TestingAuthDirTimeToLearnReachability 0 minutes
TestingEstimatedDescriptorPropagationTime 0 minutes
+ TestingServerDownloadSchedule 0, 0, 0, 5, 10, 15, 20, 30, 60
+ TestingClientDownloadSchedule 0, 0, 5, 10, 15, 20, 30, 60
+ TestingServerConsensusDownloadSchedule 0, 0, 5, 10, 15, 20, 30, 60
+ TestingClientConsensusDownloadSchedule 0, 0, 5, 10, 15, 20, 30, 60
+ TestingBridgeDownloadSchedule 60, 30, 30, 60
+ TestingClientMaxIntervalWithoutRequest 5 seconds
+ TestingDirConnectionMaxStall 30 seconds
+ TestingConsensusMaxDownloadTries 80
+ TestingDescriptorMaxDownloadTries 80
+ TestingMicrodescMaxDownloadTries 80
+ TestingCertMaxDownloadTries 80
+ TestingEnableConnBwEvent 1
+ TestingEnableCellStatsEvent 1
+ TestingEnableTbEmptyEvent 1
-**TestingV3AuthInitialVotingInterval** __N__ **minutes**|**hours**::
+[[TestingV3AuthInitialVotingInterval]] **TestingV3AuthInitialVotingInterval** __N__ **minutes**|**hours**::
Like V3AuthVotingInterval, but for initial voting interval before the first
consensus has been created. Changing this requires that
**TestingTorNetwork** is set. (Default: 30 minutes)
@@@ -2095,66 -2038,22 +2098,82 @@@
Minimum value for the Fast flag. Overrides the ordinary minimum taken
from the consensus when TestingTorNetwork is set. (Default: 0.)
-**TestingEnableConnBwEvent** **0**|**1**::
+[[TestingServerDownloadSchedule]] **TestingServerDownloadSchedule** __N__,__N__,__...__::
+ Schedule for when servers should download things in general. Changing this
+ requires that **TestingTorNetwork** is set. (Default: 0, 0, 0, 60, 60, 120,
+ 300, 900, 2147483647)
+
+[[TestingClientDownloadSchedule]] **TestingClientDownloadSchedule** __N__,__N__,__...__::
+ Schedule for when clients should download things in general. Changing this
+ requires that **TestingTorNetwork** is set. (Default: 0, 0, 60, 300, 600,
+ 2147483647)
+
+[[TestingServerConsensusDownloadSchedule]] **TestingServerConsensusDownloadSchedule** __N__,__N__,__...__::
+ Schedule for when servers should download consensuses. Changing this
+ requires that **TestingTorNetwork** is set. (Default: 0, 0, 60, 300, 600,
+ 1800, 1800, 1800, 1800, 1800, 3600, 7200)
+
+[[TestingClientConsensusDownloadSchedule]] **TestingClientConsensusDownloadSchedule** __N__,__N__,__...__::
+ Schedule for when clients should download consensuses. Changing this
+ requires that **TestingTorNetwork** is set. (Default: 0, 0, 60, 300, 600,
+ 1800, 3600, 3600, 3600, 10800, 21600, 43200)
+
+[[TestingBridgeDownloadSchedule]] **TestingBridgeDownloadSchedule** __N__,__N__,__...__::
+ Schedule for when clients should download bridge descriptors. Changing this
+ requires that **TestingTorNetwork** is set. (Default: 3600, 900, 900, 3600)
+
+[[TestingClientMaxIntervalWithoutRequest]] **TestingClientMaxIntervalWithoutRequest** __N__ **seconds**|**minutes**::
+ When directory clients have only a few descriptors to request, they batch
+ them until they have more, or until this amount of time has passed.
+ Changing this requires that **TestingTorNetwork** is set. (Default: 10
+ minutes)
+
+[[TestingDirConnectionMaxStall]] **TestingDirConnectionMaxStall** __N__ **seconds**|**minutes**::
+ Let a directory connection stall this long before expiring it.
+ Changing this requires that **TestingTorNetwork** is set. (Default:
+ 5 minutes)
+
+[[TestingConsensusMaxDownloadTries]] **TestingConsensusMaxDownloadTries** __NUM__::
+ Try this often to download a consensus before giving up. Changing
+ this requires that **TestingTorNetwork** is set. (Default: 8)
+
+[[TestingDescriptorMaxDownloadTries]] **TestingDescriptorMaxDownloadTries** __NUM__::
+ Try this often to download a router descriptor before giving up.
+ Changing this requires that **TestingTorNetwork** is set. (Default: 8)
+
+[[TestingMicrodescMaxDownloadTries]] **TestingMicrodescMaxDownloadTries** __NUM__::
+ Try this often to download a microdesc descriptor before giving up.
+ Changing this requires that **TestingTorNetwork** is set. (Default: 8)
+
+[[TestingCertMaxDownloadTries]] **TestingCertMaxDownloadTries** __NUM__::
+ Try this often to download a v3 authority certificate before giving up.
+ Changing this requires that **TestingTorNetwork** is set. (Default: 8)
+
+**TestingDirAuthVoteGuard** __node__,__node__,__...__::
+ A list of identity fingerprints, nicknames, country codes and
+ address patterns of nodes to vote Guard for regardless of their
+ uptime and bandwidth. See the **ExcludeNodes** option for more
+ information on how to specify nodes.
+ +
+ In order for this option to have any effect, **TestingTorNetwork**
+ has to be set.
+
++[[TestingEnableConnBwEvent]] **TestingEnableConnBwEvent** **0**|**1**::
+ If this option is set, then Tor controllers may register for CONN_BW
+ events. Changing this requires that **TestingTorNetwork** is set.
+ (Default: 0)
+
-**TestingEnableCellStatsEvent** **0**|**1**::
++[[TestingEnableCellStatsEvent]] **TestingEnableCellStatsEvent** **0**|**1**::
+ If this option is set, then Tor controllers may register for CELL_STATS
+ events. Changing this requires that **TestingTorNetwork** is set.
+ (Default: 0)
+
-**TestingEnableTbEmptyEvent** **0**|**1**::
++[[TestingEnableTbEmptyEvent]] **TestingEnableTbEmptyEvent** **0**|**1**::
+ If this option is set, then Tor controllers may register for TB_EMPTY
+ events. Changing this requires that **TestingTorNetwork** is set.
+ (Default: 0)
+
+
SIGNALS
-------
diff --cc src/or/circuitmux.c
index 47b4230,545cfd0..f2af943
--- a/src/or/circuitmux.c
+++ b/src/or/circuitmux.c
@@@ -1859,40 -1743,3 +1859,40 @@@ circuitmux_assert_okay_pass_three(circu
}
}
+/*DOCDOC */
+void
+circuitmux_append_destroy_cell(channel_t *chan,
+ circuitmux_t *cmux,
+ circid_t circ_id,
+ uint8_t reason)
+{
+ cell_t cell;
+ memset(&cell, 0, sizeof(cell_t));
+ cell.circ_id = circ_id;
+ cell.command = CELL_DESTROY;
+ cell.payload[0] = (uint8_t) reason;
+
- cell_queue_append_packed_copy(&cmux->destroy_cell_queue, &cell,
++ cell_queue_append_packed_copy(NULL, &cmux->destroy_cell_queue, 0, &cell,
+ chan->wide_circ_ids, 0);
+
+ /* Destroy entering the queue, update counters */
+ ++(cmux->destroy_ctr);
+ ++global_destroy_ctr;
+ log_debug(LD_CIRC,
+ "Cmux at %p queued a destroy for circ %u, cmux counter is now "
+ I64_FORMAT", global counter is now "I64_FORMAT,
+ cmux, circ_id,
+ I64_PRINTF_ARG(cmux->destroy_ctr),
+ I64_PRINTF_ARG(global_destroy_ctr));
+
+ /* XXXX Duplicate code from append_cell_to_circuit_queue */
+ if (!channel_has_queued_writes(chan)) {
+ /* There is no data at all waiting to be sent on the outbuf. Add a
+ * cell, so that we can notice when it gets flushed, flushed_some can
+ * get called, and we can start putting more data onto the buffer then.
+ */
+ log_debug(LD_GENERAL, "Primed a buffer.");
+ channel_flush_from_first_active_circuit(chan, 1);
+ }
+}
+
diff --cc src/or/config.c
index 95cede0,4ca0338..e0e5bb6
--- a/src/or/config.c
+++ b/src/or/config.c
@@@ -488,21 -464,9 +491,24 @@@ static const config_var_t testing_tor_n
V(TestingAuthDirTimeToLearnReachability, INTERVAL, "0 minutes"),
V(TestingEstimatedDescriptorPropagationTime, INTERVAL, "0 minutes"),
V(MinUptimeHidServDirectoryV2, INTERVAL, "0 minutes"),
+ V(TestingServerDownloadSchedule, CSV_INTERVAL, "0, 0, 0, 5, 10, 15, "
+ "20, 30, 60"),
+ V(TestingClientDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, 15, 20, "
+ "30, 60"),
+ V(TestingServerConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, "
+ "15, 20, 30, 60"),
+ V(TestingClientConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, "
+ "15, 20, 30, 60"),
+ V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "60, 30, 30, 60"),
+ V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "5 seconds"),
+ V(TestingDirConnectionMaxStall, INTERVAL, "30 seconds"),
+ V(TestingConsensusMaxDownloadTries, UINT, "80"),
+ V(TestingDescriptorMaxDownloadTries, UINT, "80"),
+ V(TestingMicrodescMaxDownloadTries, UINT, "80"),
+ V(TestingCertMaxDownloadTries, UINT, "80"),
+ V(TestingEnableConnBwEvent, BOOL, "1"),
+ V(TestingEnableCellStatsEvent, BOOL, "1"),
+ V(TestingEnableTbEmptyEvent, BOOL, "1"),
VAR("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_, "1"),
{ NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
@@@ -3431,42 -3242,24 +3437,60 @@@ options_validate(or_options_t *old_opti
COMPLAIN("TestingEstimatedDescriptorPropagationTime is insanely high.");
}
+ if (options->TestingClientMaxIntervalWithoutRequest < 1) {
+ REJECT("TestingClientMaxIntervalWithoutRequest is way too low.");
+ } else if (options->TestingClientMaxIntervalWithoutRequest > 3600) {
+ COMPLAIN("TestingClientMaxIntervalWithoutRequest is insanely high.");
+ }
+
+ if (options->TestingDirConnectionMaxStall < 5) {
+ REJECT("TestingDirConnectionMaxStall is way too low.");
+ } else if (options->TestingDirConnectionMaxStall > 3600) {
+ COMPLAIN("TestingDirConnectionMaxStall is insanely high.");
+ }
+
+ if (options->TestingConsensusMaxDownloadTries < 2) {
+ REJECT("TestingConsensusMaxDownloadTries must be greater than 1.");
+ } else if (options->TestingConsensusMaxDownloadTries > 800) {
+ COMPLAIN("TestingConsensusMaxDownloadTries is insanely high.");
+ }
+
+ if (options->TestingDescriptorMaxDownloadTries < 2) {
+ REJECT("TestingDescriptorMaxDownloadTries must be greater than 1.");
+ } else if (options->TestingDescriptorMaxDownloadTries > 800) {
+ COMPLAIN("TestingDescriptorMaxDownloadTries is insanely high.");
+ }
+
+ if (options->TestingMicrodescMaxDownloadTries < 2) {
+ REJECT("TestingMicrodescMaxDownloadTries must be greater than 1.");
+ } else if (options->TestingMicrodescMaxDownloadTries > 800) {
+ COMPLAIN("TestingMicrodescMaxDownloadTries is insanely high.");
+ }
+
+ if (options->TestingCertMaxDownloadTries < 2) {
+ REJECT("TestingCertMaxDownloadTries must be greater than 1.");
+ } else if (options->TestingCertMaxDownloadTries > 800) {
+ COMPLAIN("TestingCertMaxDownloadTries is insanely high.");
+ }
+
+ if (options->TestingEnableConnBwEvent &&
+ !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) {
+ REJECT("TestingEnableConnBwEvent may only be changed in testing "
+ "Tor networks!");
+ }
+
+ if (options->TestingEnableCellStatsEvent &&
+ !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) {
+ REJECT("TestingEnableCellStatsEvent may only be changed in testing "
+ "Tor networks!");
+ }
+
+ if (options->TestingEnableTbEmptyEvent &&
+ !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) {
+ REJECT("TestingEnableTbEmptyEvent may only be changed in testing "
+ "Tor networks!");
+ }
+
if (options->TestingTorNetwork) {
log_warn(LD_CONFIG, "TestingTorNetwork is set. This will make your node "
"almost unusable in the public Tor network, and is "
diff --cc src/or/connection.h
index 0454ac2,53d3da0..4073d9f
--- a/src/or/connection.h
+++ b/src/or/connection.h
@@@ -215,7 -215,14 +215,16 @@@ void connection_enable_rate_limiting(co
#endif
#ifdef CONNECTION_PRIVATE
+STATIC void connection_free_(connection_t *conn);
++
+ /* Used only by connection.c and test*.c */
+ uint32_t bucket_millis_empty(int tokens_before, uint32_t last_empty_time,
+ int tokens_after, int milliseconds_elapsed,
+ const struct timeval *tvnow);
+ void connection_buckets_note_empty_ts(uint32_t *timestamp_var,
+ int tokens_before,
+ size_t tokens_removed,
+ const struct timeval *tvnow);
#endif
#endif
diff --cc src/or/control.c
index 65c543a,9e026b3..49212de
--- a/src/or/control.c
+++ b/src/or/control.c
@@@ -190,6 -237,20 +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;
- for (circ = circuit_get_global_list_(); circ; circ = circ->next) {
++ 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
@@@ -916,7 -981,10 +935,11 @@@ static const struct control_event_t con
{ 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 },
};
@@@ -3902,6 -3940,235 +3933,235 @@@ control_event_stream_bandwidth_used(voi
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;
+
- for (circ = circuit_get_global_list_(); circ; circ = circ->next) {
++ 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));;
- for (circ = circuit_get_global_list_(); circ; circ = circ->next) {
++ 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
diff --cc src/or/control.h
index 099782a,1921d97..c8db643
--- a/src/or/control.h
+++ b/src/or/control.h
@@@ -85,72 -92,38 +92,100 @@@ void enable_control_logging(void)
void monitor_owning_controller_process(const char *process_spec);
void control_event_bootstrap(bootstrap_status_t status, int progress);
-void control_event_bootstrap_problem(const char *warn, int reason);
+MOCK_DECL(void, control_event_bootstrap_problem,(const char *warn,
+ int reason));
void control_event_clients_seen(const char *controller_str);
+void control_event_transport_launched(const char *mode,
+ const char *transport_name,
+ tor_addr_t *addr, uint16_t port);
+
+void control_free_all(void);
#ifdef CONTROL_PRIVATE
-/* Used only by control.c and test*.c */
-size_t write_escaped_data(const char *data, size_t len, char **out);
-size_t read_escaped_data(const char *data, size_t len, char **out);
+/* Recognized asynchronous event types. It's okay to expand this list
+ * because it is used both as a list of v0 event types, and as indices
+ * into the bitfield to determine which controllers want which events.
+ */
+#define EVENT_MIN_ 0x0001
+#define EVENT_CIRCUIT_STATUS 0x0001
+#define EVENT_STREAM_STATUS 0x0002
+#define EVENT_OR_CONN_STATUS 0x0003
+#define EVENT_BANDWIDTH_USED 0x0004
+#define EVENT_CIRCUIT_STATUS_MINOR 0x0005
+#define EVENT_NEW_DESC 0x0006
+#define EVENT_DEBUG_MSG 0x0007
+#define EVENT_INFO_MSG 0x0008
+#define EVENT_NOTICE_MSG 0x0009
+#define EVENT_WARN_MSG 0x000A
+#define EVENT_ERR_MSG 0x000B
+#define EVENT_ADDRMAP 0x000C
+/* Exposed above */
+// #define EVENT_AUTHDIR_NEWDESCS 0x000D
+#define EVENT_DESCCHANGED 0x000E
+/* Exposed above */
+// #define EVENT_NS 0x000F
+#define EVENT_STATUS_CLIENT 0x0010
+#define EVENT_STATUS_SERVER 0x0011
+#define EVENT_STATUS_GENERAL 0x0012
+#define EVENT_GUARD 0x0013
+#define EVENT_STREAM_BANDWIDTH_USED 0x0014
+#define EVENT_CLIENTS_SEEN 0x0015
+#define EVENT_NEWCONSENSUS 0x0016
+#define EVENT_BUILDTIMEOUT_SET 0x0017
+#define EVENT_SIGNAL 0x0018
+#define EVENT_CONF_CHANGED 0x0019
++#define EVENT_CONN_BW 0x001A
++#define EVENT_CELL_STATS 0x001B
++#define EVENT_TB_EMPTY 0x001C
++#define EVENT_CIRC_BANDWIDTH_USED 0x001D
+#define EVENT_TRANSPORT_LAUNCHED 0x0020
+#define EVENT_MAX_ 0x0020
+/* If EVENT_MAX_ ever hits 0x0040, we need to make the mask into a
+ * different structure. */
+
+/* Used only by control.c and test.c */
+STATIC size_t write_escaped_data(const char *data, size_t len, char **out);
+STATIC size_t read_escaped_data(const char *data, size_t len, char **out);
+/** Flag for event_format_t. Indicates that we should use the one standard
+ format. (Other formats previous existed, and are now deprecated)
+ */
+#define ALL_FORMATS 1
+/** Bit field of flags to select how to format a controller event. Recognized
+ * flag is ALL_FORMATS. */
+typedef int event_format_t;
+
+#ifdef TOR_UNIT_TESTS
+MOCK_DECL(STATIC void,
+send_control_event_string,(uint16_t event, event_format_t which,
+ const char *msg));
+
+void control_testing_set_global_event_mask(uint64_t mask);
+#endif
+
+ /** Helper structure: temporarily stores cell statistics for a circuit. */
+ typedef struct cell_stats_t {
+ /** Number of cells added in app-ward direction by command. */
+ uint64_t added_cells_appward[CELL_COMMAND_MAX_ + 1];
+ /** Number of cells added in exit-ward direction by command. */
+ uint64_t added_cells_exitward[CELL_COMMAND_MAX_ + 1];
+ /** Number of cells removed in app-ward direction by command. */
+ uint64_t removed_cells_appward[CELL_COMMAND_MAX_ + 1];
+ /** Number of cells removed in exit-ward direction by command. */
+ uint64_t removed_cells_exitward[CELL_COMMAND_MAX_ + 1];
+ /** Total waiting time of cells in app-ward direction by command. */
+ uint64_t total_time_appward[CELL_COMMAND_MAX_ + 1];
+ /** Total waiting time of cells in exit-ward direction by command. */
+ uint64_t total_time_exitward[CELL_COMMAND_MAX_ + 1];
+ } cell_stats_t;
+ void sum_up_cell_stats_by_command(circuit_t *circ,
+ cell_stats_t *cell_stats);
+ 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);
+ void format_cell_stats(char **event_string, circuit_t *circ,
+ cell_stats_t *cell_stats);
#endif
#endif
diff --cc src/or/or.h
index 2f0f9eb,6dd3ce4..92c0692
--- a/src/or/or.h
+++ b/src/or/or.h
@@@ -1139,10 -1106,12 +1155,12 @@@ typedef struct insertion_command_queue_
/** A queue of cells on a circuit, waiting to be added to the
* or_connection_t's outbuf. */
typedef struct cell_queue_t {
- packed_cell_t *head; /**< The first cell, or NULL if the queue is empty. */
- packed_cell_t *tail; /**< The last cell, or NULL if the queue is empty. */
+ /** Linked list of packed_cell_t*/
+ TOR_SIMPLEQ_HEAD(cell_simpleq, packed_cell_t) head;
int n; /**< The number of cells in the queue. */
insertion_time_queue_t *insertion_times; /**< Insertion times of cells. */
+ /** Commands of inserted cells. */
+ insertion_command_queue_t *insertion_commands;
} cell_queue_t;
/** Beginning of a RELAY cell payload. */
@@@ -4065,10 -3987,15 +4126,19 @@@ typedef struct
/** Minimum value for the Fast flag threshold on testing networks. */
uint64_t TestingMinFastFlagThreshold;
+ /** Relays in a testing network which should be voted Guard
+ * regardless of uptime and bandwidth. */
+ routerset_t *TestingDirAuthVoteGuard;
+
+ /** Enable CONN_BW events. Only altered on testing networks. */
+ int TestingEnableConnBwEvent;
+
+ /** Enable CELL_STATS events. Only altered on testing networks. */
+ int TestingEnableCellStatsEvent;
+
+ /** Enable TB_EMPTY events. Only altered on testing networks. */
+ int TestingEnableTbEmptyEvent;
+
/** If true, and we have GeoIP data, and we're a bridge, keep a per-country
* count of how many client addresses have contacted us so that we can help
* the bridge authority guess which countries have blocked access to us. */
diff --cc src/or/relay.c
index d12850d,66c24f3..7947ca3
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@@ -2144,14 -2153,68 +2152,68 @@@ cell_queue_append(cell_queue_t *queue,
++queue->n;
}
- /** Append a newly allocated copy of <b>cell</b> to the end of <b>queue</b> */
+ /** Append command of type <b>command</b> in direction to <b>queue</b> for
+ * CELL_STATS event. */
+ static void
+ cell_command_queue_append(cell_queue_t *queue, uint8_t command)
+ {
+ insertion_command_queue_t *ic_queue = queue->insertion_commands;
+ if (!ic_pool)
+ ic_pool = mp_pool_new(sizeof(insertion_command_elem_t), 1024);
+ if (!ic_queue) {
+ ic_queue = tor_malloc_zero(sizeof(insertion_command_queue_t));
+ queue->insertion_commands = ic_queue;
+ }
+ if (ic_queue->last && ic_queue->last->command == command) {
+ ic_queue->last->counter++;
+ } else {
+ insertion_command_elem_t *elem = mp_pool_get(ic_pool);
+ elem->next = NULL;
+ elem->command = command;
+ elem->counter = 1;
+ if (ic_queue->last) {
+ ic_queue->last->next = elem;
+ ic_queue->last = elem;
+ } else {
+ ic_queue->first = ic_queue->last = elem;
+ }
+ }
+ }
+
+ /** Retrieve oldest command from <b>queue</b> and write it to
+ * <b>command</b> for CELL_STATS event. Return 0 for success, -1
+ * otherwise. */
+ static int
+ cell_command_queue_pop(uint8_t *command, cell_queue_t *queue)
+ {
+ int res = -1;
+ insertion_command_queue_t *ic_queue = queue->insertion_commands;
+ if (ic_queue && ic_queue->first) {
+ insertion_command_elem_t *ic_elem = ic_queue->first;
+ ic_elem->counter--;
+ if (ic_elem->counter < 1) {
+ ic_queue->first = ic_elem->next;
+ if (ic_elem == ic_queue->last)
+ ic_queue->last = NULL;
+ mp_pool_release(ic_elem);
+ }
+ *command = ic_elem->command;
+ res = 0;
+ }
+ return res;
+ }
+
+ /** Append a newly allocated copy of <b>cell</b> to the end of the
+ * <b>exitward</b> (or app-ward) <b>queue</b> of <b>circ</b>. */
void
- cell_queue_append_packed_copy(cell_queue_t *queue, const cell_t *cell,
+ cell_queue_append_packed_copy(circuit_t *circ, cell_queue_t *queue,
+ int exitward, const cell_t *cell,
- int wide_circ_ids)
+ int wide_circ_ids, int use_stats)
{
packed_cell_t *copy = packed_cell_copy(cell, wide_circ_ids);
/* Remember the time when this cell was put in the queue. */
- if (get_options()->CellStatistics && use_stats) {
- if (get_options()->CellStatistics ||
- get_options()->TestingEnableCellStatsEvent) {
++ if ((get_options()->CellStatistics ||
++ get_options()->TestingEnableCellStatsEvent) && use_stats) {
struct timeval now;
uint32_t added;
insertion_time_queue_t *it_queue = queue->insertion_times;
@@@ -2517,13 -2564,9 +2613,14 @@@ append_cell_to_circuit_queue(circuit_t
cell_t *cell, cell_direction_t direction,
streamid_t fromstream)
{
+ or_circuit_t *orcirc = NULL;
cell_queue_t *queue;
int streams_blocked;
+#if 0
+ uint32_t tgt_max_middle_cells, p_len, n_len, tmp, hard_max_middle_cells;
+#endif
+
+ int exitward;
if (circ->marked_for_close)
return;
@@@ -2536,95 -2580,8 +2634,96 @@@
streams_blocked = circ->streams_blocked_on_p_chan;
}
+ /*
+ * Disabling this for now because of a possible guard discovery attack
+ */
+#if 0
+ /* Are we a middle circuit about to exceed ORCIRC_MAX_MIDDLE_CELLS? */
+ if ((circ->n_chan != NULL) && CIRCUIT_IS_ORCIRC(circ)) {
+ orcirc = TO_OR_CIRCUIT(circ);
+ if (orcirc->p_chan) {
+ /* We are a middle circuit if we have both n_chan and p_chan */
+ /* We'll need to know the current preferred maximum */
+ tgt_max_middle_cells = get_max_middle_cells();
+ if (tgt_max_middle_cells > 0) {
+ /* Do we need to initialize middle_max_cells? */
+ if (orcirc->max_middle_cells == 0) {
+ orcirc->max_middle_cells = tgt_max_middle_cells;
+ } else {
+ if (tgt_max_middle_cells > orcirc->max_middle_cells) {
+ /* If we want to increase the cap, we can do so right away */
+ orcirc->max_middle_cells = tgt_max_middle_cells;
+ } else if (tgt_max_middle_cells < orcirc->max_middle_cells) {
+ /*
+ * If we're shrinking the cap, we can't shrink past either queue;
+ * compare tgt_max_middle_cells rather than tgt_max_middle_cells *
+ * ORCIRC_MAX_MIDDLE_KILL_THRESH so the queues don't shrink enough
+ * to generate spurious warnings, either.
+ */
+ n_len = circ->n_chan_cells.n;
+ p_len = orcirc->p_chan_cells.n;
+ tmp = tgt_max_middle_cells;
+ if (tmp < n_len) tmp = n_len;
+ if (tmp < p_len) tmp = p_len;
+ orcirc->max_middle_cells = tmp;
+ }
+ /* else no change */
+ }
+ } else {
+ /* tgt_max_middle_cells == 0 indicates we should disable the cap */
+ orcirc->max_middle_cells = 0;
+ }
+
+ /* Now we know orcirc->max_middle_cells is set correctly */
+ if (orcirc->max_middle_cells > 0) {
+ hard_max_middle_cells =
+ (uint32_t)(((double)orcirc->max_middle_cells) *
+ ORCIRC_MAX_MIDDLE_KILL_THRESH);
+
+ if ((unsigned)queue->n + 1 >= hard_max_middle_cells) {
+ /* Queueing this cell would put queue over the kill theshold */
+ log_warn(LD_CIRC,
+ "Got a cell exceeding the hard cap of %u in the "
+ "%s direction on middle circ ID %u on chan ID "
+ U64_FORMAT "; killing the circuit.",
+ hard_max_middle_cells,
+ (direction == CELL_DIRECTION_OUT) ? "n" : "p",
+ (direction == CELL_DIRECTION_OUT) ?
+ circ->n_circ_id : orcirc->p_circ_id,
+ U64_PRINTF_ARG(
+ (direction == CELL_DIRECTION_OUT) ?
+ circ->n_chan->global_identifier :
+ orcirc->p_chan->global_identifier));
+ circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT);
+ return;
+ } else if ((unsigned)queue->n + 1 == orcirc->max_middle_cells) {
+ /* Only use ==, not >= for this test so we don't spam the log */
+ log_warn(LD_CIRC,
+ "While trying to queue a cell, reached the soft cap of %u "
+ "in the %s direction on middle circ ID %u "
+ "on chan ID " U64_FORMAT ".",
+ orcirc->max_middle_cells,
+ (direction == CELL_DIRECTION_OUT) ? "n" : "p",
+ (direction == CELL_DIRECTION_OUT) ?
+ circ->n_circ_id : orcirc->p_circ_id,
+ U64_PRINTF_ARG(
+ (direction == CELL_DIRECTION_OUT) ?
+ circ->n_chan->global_identifier :
+ orcirc->p_chan->global_identifier));
+ }
+ }
+ }
+ }
+#endif
+
- cell_queue_append_packed_copy(queue, cell, chan->wide_circ_ids, 1);
+ cell_queue_append_packed_copy(circ, queue, exitward, cell,
- chan->wide_circ_ids);
++ chan->wide_circ_ids, 1);
+
+ if (PREDICT_UNLIKELY(cell_queues_check_size())) {
+ /* We ran the OOM handler */
+ if (circ->marked_for_close)
+ return;
+ }
/* If we have too many cells on the circuit, we should stop reading from
* the edge streams for a while. */
diff --cc src/or/relay.h
index e1b5e38,b545857..20eecfb
--- a/src/or/relay.h
+++ b/src/or/relay.h
@@@ -51,11 -50,11 +51,12 @@@ size_t packed_cell_mem_cost(void)
/* For channeltls.c */
void packed_cell_free(packed_cell_t *cell);
+void cell_queue_init(cell_queue_t *queue);
void cell_queue_clear(cell_queue_t *queue);
void cell_queue_append(cell_queue_t *queue, packed_cell_t *cell);
- void cell_queue_append_packed_copy(cell_queue_t *queue, const cell_t *cell,
+ void cell_queue_append_packed_copy(circuit_t *circ, cell_queue_t *queue,
+ int exitward, const cell_t *cell,
- int wide_circ_ids);
+ int wide_circ_ids, int use_stats);
void append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan,
cell_t *cell, cell_direction_t direction,
diff --cc src/test/include.am
index 41e1153,84a7643..5510293
--- a/src/test/include.am
+++ b/src/test/include.am
@@@ -18,22 -14,16 +18,23 @@@ src_test_AM_CPPFLAGS = -DSHARE_DATADIR=
src_test_test_SOURCES = \
src/test/test.c \
src/test/test_addr.c \
+ src/test/test_buffers.c \
src/test/test_cell_formats.c \
+ src/test/test_circuitlist.c \
+ src/test/test_circuitmux.c \
src/test/test_containers.c \
+ src/test/test_controller_events.c \
src/test/test_crypto.c \
+ src/test/test_cell_queue.c \
src/test/test_data.c \
src/test/test_dir.c \
+ src/test/test_extorport.c \
src/test/test_introduce.c \
src/test/test_microdesc.c \
+ src/test/test_options.c \
src/test/test_pt.c \
src/test/test_replay.c \
+ src/test/test_socks.c \
src/test/test_util.c \
src/test/test_config.c \
src/ext/tinytest.c
diff --cc src/test/test.c
index 6b45acf,eec591a..562de48
--- a/src/test/test.c
+++ b/src/test/test.c
@@@ -1614,12 -2133,7 +1614,13 @@@ extern struct testcase_t config_tests[]
extern struct testcase_t introduce_tests[];
extern struct testcase_t replaycache_tests[];
extern struct testcase_t cell_format_tests[];
+extern struct testcase_t circuitlist_tests[];
+extern struct testcase_t circuitmux_tests[];
+extern struct testcase_t cell_queue_tests[];
+extern struct testcase_t options_tests[];
+extern struct testcase_t socks_tests[];
+extern struct testcase_t extorport_tests[];
+ extern struct testcase_t controller_event_tests[];
static struct testgroup_t testgroups[] = {
{ "", test_array },
@@@ -1637,10 -2149,7 +1638,11 @@@
{ "config/", config_tests },
{ "replaycache/", replaycache_tests },
{ "introduce/", introduce_tests },
+ { "circuitlist/", circuitlist_tests },
+ { "circuitmux/", circuitmux_tests },
+ { "options/", options_tests },
+ { "extorport/", extorport_tests },
+ { "control/", controller_event_tests },
END_OF_GROUPS
};
diff --cc src/test/test_cell_queue.c
index cf2d11a,0000000..1eac073
mode 100644,000000..100644
--- a/src/test/test_cell_queue.c
+++ b/src/test/test_cell_queue.c
@@@ -1,146 -1,0 +1,148 @@@
+/* Copyright (c) 2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define CIRCUITLIST_PRIVATE
+#define RELAY_PRIVATE
+#include "or.h"
+#include "circuitlist.h"
+#include "relay.h"
+#include "test.h"
+
+static void
+test_cq_manip(void *arg)
+{
+ packed_cell_t *pc1=NULL, *pc2=NULL, *pc3=NULL, *pc4=NULL, *pc_tmp=NULL;
+ cell_queue_t cq;
+ cell_t cell;
+ (void) arg;
+
+ init_cell_pool();
+ cell_queue_init(&cq);
+ tt_int_op(cq.n, ==, 0);
+
+ pc1 = packed_cell_new();
+ pc2 = packed_cell_new();
+ pc3 = packed_cell_new();
+ pc4 = packed_cell_new();
+ tt_assert(pc1 && pc2 && pc3 && pc4);
+
+ tt_ptr_op(NULL, ==, cell_queue_pop(&cq));
+
+ /* Add and remove a singleton. */
+ cell_queue_append(&cq, pc1);
+ tt_int_op(cq.n, ==, 1);
+ tt_ptr_op(pc1, ==, cell_queue_pop(&cq));
+ tt_int_op(cq.n, ==, 0);
+
+ /* Add and remove four items */
+ cell_queue_append(&cq, pc4);
+ cell_queue_append(&cq, pc3);
+ cell_queue_append(&cq, pc2);
+ cell_queue_append(&cq, pc1);
+ tt_int_op(cq.n, ==, 4);
+ tt_ptr_op(pc4, ==, cell_queue_pop(&cq));
+ tt_ptr_op(pc3, ==, cell_queue_pop(&cq));
+ tt_ptr_op(pc2, ==, cell_queue_pop(&cq));
+ tt_ptr_op(pc1, ==, cell_queue_pop(&cq));
+ tt_int_op(cq.n, ==, 0);
+ tt_ptr_op(NULL, ==, cell_queue_pop(&cq));
+
+ /* Try a packed copy (wide, then narrow, which is a bit of a cheat, since a
+ * real cell queue has only one type.) */
+ memset(&cell, 0, sizeof(cell));
+ cell.circ_id = 0x12345678;
+ cell.command = 10;
+ strlcpy((char*)cell.payload, "Lorax ipsum gruvvulus thneed amet, snergelly "
+ "once-ler lerkim, sed do barbaloot tempor gluppitus ut labore et "
+ "truffula magna aliqua.",
+ sizeof(cell.payload));
- cell_queue_append_packed_copy(&cq, &cell, 1 /*wide*/, 0 /*stats*/);
++ cell_queue_append_packed_copy(NULL /*circ*/, &cq, 0 /*exitward*/, &cell,
++ 1 /*wide*/, 0 /*stats*/);
+ cell.circ_id = 0x2013;
- cell_queue_append_packed_copy(&cq, &cell, 0 /*wide*/, 0 /*stats*/);
++ cell_queue_append_packed_copy(NULL /*circ*/, &cq, 0 /*exitward*/, &cell,
++ 0 /*wide*/, 0 /*stats*/);
+ tt_int_op(cq.n, ==, 2);
+
+ pc_tmp = cell_queue_pop(&cq);
+ tt_int_op(cq.n, ==, 1);
+ tt_ptr_op(pc_tmp, !=, NULL);
+ test_mem_op(pc_tmp->body, ==, "\x12\x34\x56\x78\x0a", 5);
+ test_mem_op(pc_tmp->body+5, ==, cell.payload, sizeof(cell.payload));
+ packed_cell_free(pc_tmp);
+
+ pc_tmp = cell_queue_pop(&cq);
+ tt_int_op(cq.n, ==, 0);
+ tt_ptr_op(pc_tmp, !=, NULL);
+ test_mem_op(pc_tmp->body, ==, "\x20\x13\x0a", 3);
+ test_mem_op(pc_tmp->body+3, ==, cell.payload, sizeof(cell.payload));
+ packed_cell_free(pc_tmp);
+ pc_tmp = NULL;
+
+ tt_ptr_op(NULL, ==, cell_queue_pop(&cq));
+
+ /* Now make sure cell_queue_clear works. */
+ cell_queue_append(&cq, pc2);
+ cell_queue_append(&cq, pc1);
+ tt_int_op(cq.n, ==, 2);
+ cell_queue_clear(&cq);
+ pc2 = pc1 = NULL; /* prevent double-free */
+ tt_int_op(cq.n, ==, 0);
+
+ done:
+ packed_cell_free(pc1);
+ packed_cell_free(pc2);
+ packed_cell_free(pc3);
+ packed_cell_free(pc4);
+ packed_cell_free(pc_tmp);
+
+ cell_queue_clear(&cq);
+ free_cell_pool();
+}
+
+static void
+test_circuit_n_cells(void *arg)
+{
+ packed_cell_t *pc1=NULL, *pc2=NULL, *pc3=NULL, *pc4=NULL, *pc5=NULL;
+ origin_circuit_t *origin_c=NULL;
+ or_circuit_t *or_c=NULL;
+
+ (void)arg;
+
+ init_cell_pool();
+
+ pc1 = packed_cell_new();
+ pc2 = packed_cell_new();
+ pc3 = packed_cell_new();
+ pc4 = packed_cell_new();
+ pc5 = packed_cell_new();
+ tt_assert(pc1 && pc2 && pc3 && pc4 && pc5);
+
+ or_c = or_circuit_new(0, NULL);
+ origin_c = origin_circuit_new();
+ origin_c->base_.purpose = CIRCUIT_PURPOSE_C_GENERAL;
+
+ tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(or_c)), ==, 0);
+ cell_queue_append(&or_c->p_chan_cells, pc1);
+ tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(or_c)), ==, 1);
+ cell_queue_append(&or_c->base_.n_chan_cells, pc2);
+ cell_queue_append(&or_c->base_.n_chan_cells, pc3);
+ tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(or_c)), ==, 3);
+
+ tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(origin_c)), ==, 0);
+ cell_queue_append(&origin_c->base_.n_chan_cells, pc4);
+ cell_queue_append(&origin_c->base_.n_chan_cells, pc5);
+ tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(origin_c)), ==, 2);
+
+ done:
+ circuit_free(TO_CIRCUIT(or_c));
+ circuit_free(TO_CIRCUIT(origin_c));
+
+ free_cell_pool();
+}
+
+struct testcase_t cell_queue_tests[] = {
+ { "basic", test_cq_manip, TT_FORK, NULL, NULL, },
+ { "circ_n_cells", test_circuit_n_cells, TT_FORK, NULL, NULL },
+ END_OF_TESTCASES
+};
+