|
@@ -95,6 +95,13 @@ static uint64_t n_bytes_read_in_interval = 0;
|
|
|
static uint64_t n_bytes_written_in_interval = 0;
|
|
|
/** How many seconds have we been running this interval? */
|
|
|
static uint32_t n_seconds_active_in_interval = 0;
|
|
|
+/** How many seconds were we active in this interval before we hit our soft
|
|
|
+ * limit? */
|
|
|
+static int n_seconds_to_hit_soft_limit = 0;
|
|
|
+/** When in this interval was the soft limit hit. */
|
|
|
+static time_t soft_limit_hit_at = 0;
|
|
|
+/** How many bytes had we read/written when we hit the soft limit? */
|
|
|
+static uint64_t n_bytes_at_soft_limit = 0;
|
|
|
/** When did this accounting interval start? */
|
|
|
static time_t interval_start_time = 0;
|
|
|
/** When will this accounting interval end? */
|
|
@@ -374,23 +381,42 @@ configure_accounting(time_t now)
|
|
|
static void
|
|
|
update_expected_bandwidth(void)
|
|
|
{
|
|
|
- uint64_t used, expected;
|
|
|
- uint64_t max_configured = (get_options()->BandwidthRate * 60);
|
|
|
-
|
|
|
- if (n_seconds_active_in_interval < 1800) {
|
|
|
+ uint64_t expected;
|
|
|
+ or_options_t *options= get_options();
|
|
|
+ uint64_t max_configured = (options->RelayBandwidthRate > 0 ?
|
|
|
+ options->RelayBandwidthRate :
|
|
|
+ options->BandwidthRate) * 60;
|
|
|
+
|
|
|
+#define MIN_TIME_FOR_MEASUREMENT (1800)
|
|
|
+
|
|
|
+ if (soft_limit_hit_at > interval_start_time && n_bytes_at_soft_limit &&
|
|
|
+ (soft_limit_hit_at - interval_start_time) > MIN_TIME_FOR_MEASUREMENT) {
|
|
|
+ /* If we hit our soft limit last time, only count the bytes up to that
|
|
|
+ * time. This is a better predictor of our actual bandwidth than
|
|
|
+ * considering the entirety of the last interval, since we likely started
|
|
|
+ * using bytes very slowly once we hit our soft limit. */
|
|
|
+ expected = n_bytes_at_soft_limit /
|
|
|
+ (soft_limit_hit_at - interval_start_time);
|
|
|
+ expected /= 60;
|
|
|
+ } else if (n_seconds_active_in_interval >= MIN_TIME_FOR_MEASUREMENT) {
|
|
|
+ /* Otherwise, we either measured enough time in the last interval but
|
|
|
+ * never hit our soft limit, or we're using a state file from a Tor that
|
|
|
+ * doesn't know to store soft-limit info. Just take rate at which
|
|
|
+ * we were reading/writing in the last interval as our expected rate.
|
|
|
+ */
|
|
|
+ uint64_t used = MAX(n_bytes_written_in_interval,
|
|
|
+ n_bytes_read_in_interval);
|
|
|
+ expected = used / (n_seconds_active_in_interval / 60);
|
|
|
+ } else {
|
|
|
/* If we haven't gotten enough data last interval, set 'expected'
|
|
|
* to 0. This will set our wakeup to the start of the interval.
|
|
|
* Next interval, we'll choose our starting time based on how much
|
|
|
* we sent this interval.
|
|
|
*/
|
|
|
expected = 0;
|
|
|
- } else {
|
|
|
- used = n_bytes_written_in_interval < n_bytes_read_in_interval ?
|
|
|
- n_bytes_read_in_interval : n_bytes_written_in_interval;
|
|
|
- expected = used / (n_seconds_active_in_interval / 60);
|
|
|
- if (expected > max_configured)
|
|
|
- expected = max_configured;
|
|
|
}
|
|
|
+ if (expected > max_configured)
|
|
|
+ expected = max_configured;
|
|
|
expected_bandwidth_usage = expected;
|
|
|
}
|
|
|
|
|
@@ -408,6 +434,9 @@ reset_accounting(time_t now)
|
|
|
n_bytes_read_in_interval = 0;
|
|
|
n_bytes_written_in_interval = 0;
|
|
|
n_seconds_active_in_interval = 0;
|
|
|
+ n_bytes_at_soft_limit = 0;
|
|
|
+ soft_limit_hit_at = 0;
|
|
|
+ n_seconds_to_hit_soft_limit = 0;
|
|
|
}
|
|
|
|
|
|
/** Return true iff we should save our bandwidth usage to disk. */
|
|
@@ -568,6 +597,10 @@ accounting_record_bandwidth_usage(time_t now, or_state_t *state)
|
|
|
state->AccountingSecondsActive = n_seconds_active_in_interval;
|
|
|
state->AccountingExpectedUsage = expected_bandwidth_usage;
|
|
|
|
|
|
+ state->AccountingSecondsToReachSoftLimit = n_seconds_to_hit_soft_limit;
|
|
|
+ state->AccountingSoftLimitHitAt = soft_limit_hit_at;
|
|
|
+ state->AccountingBytesAtSoftLimit = n_bytes_at_soft_limit;
|
|
|
+
|
|
|
or_state_mark_dirty(state,
|
|
|
now+(get_options()->AvoidDiskWrites ? 7200 : 60));
|
|
|
|
|
@@ -591,10 +624,6 @@ read_bandwidth_usage(void)
|
|
|
if (!state)
|
|
|
return -1;
|
|
|
|
|
|
- /* Okay; it looks like the state file is more up-to-date than the
|
|
|
- * bw_accounting file, or the bw_accounting file is nonexistent,
|
|
|
- * or the bw_accounting file is corrupt.
|
|
|
- */
|
|
|
log_info(LD_ACCT, "Reading bandwidth accounting data from state file");
|
|
|
n_bytes_read_in_interval = state->AccountingBytesReadInInterval;
|
|
|
n_bytes_written_in_interval = state->AccountingBytesWrittenInInterval;
|
|
@@ -602,6 +631,21 @@ read_bandwidth_usage(void)
|
|
|
interval_start_time = state->AccountingIntervalStart;
|
|
|
expected_bandwidth_usage = state->AccountingExpectedUsage;
|
|
|
|
|
|
+ /* Older versions of Tor (before 0.2.2.17-alpha or so) didn't generate these
|
|
|
+ * fields. If you switch back and forth, you might get an
|
|
|
+ * AccountingSoftLimitHitAt value from long before the most recent
|
|
|
+ * interval_start_time. If that's so, then ignore the softlimit-related
|
|
|
+ * values. */
|
|
|
+ if (state->AccountingSoftLimitHitAt > interval_start_time) {
|
|
|
+ soft_limit_hit_at = state->AccountingSoftLimitHitAt;
|
|
|
+ n_bytes_at_soft_limit = state->AccountingBytesAtSoftLimit;
|
|
|
+ n_seconds_to_hit_soft_limit = state->AccountingSecondsToReachSoftLimit;
|
|
|
+ } else {
|
|
|
+ soft_limit_hit_at = 0;
|
|
|
+ n_bytes_at_soft_limit = 0;
|
|
|
+ n_seconds_to_hit_soft_limit = 0;
|
|
|
+ }
|
|
|
+
|
|
|
{
|
|
|
char tbuf1[ISO_TIME_LEN+1];
|
|
|
char tbuf2[ISO_TIME_LEN+1];
|
|
@@ -641,8 +685,27 @@ hibernate_hard_limit_reached(void)
|
|
|
static int
|
|
|
hibernate_soft_limit_reached(void)
|
|
|
{
|
|
|
- uint64_t soft_limit = DBL_TO_U64(U64_TO_DBL(get_options()->AccountingMax)
|
|
|
- * .95);
|
|
|
+ const uint64_t acct_max = get_options()->AccountingMax;
|
|
|
+#define SOFT_LIM_PCT (.95)
|
|
|
+#define SOFT_LIM_BYTES (500*1024*1024)
|
|
|
+#define SOFT_LIM_MINUTES (3*60)
|
|
|
+ /* The 'soft limit' is a fair bit more complicated now than once it was.
|
|
|
+ * We want to stop accepting connections when ALL of the following are true:
|
|
|
+ * - We expect to use up the remaining bytes in under 3 hours
|
|
|
+ * - We have used up 95% of our bytes.
|
|
|
+ * - We have less than 500MB of bytes left.
|
|
|
+ */
|
|
|
+ uint64_t soft_limit = DBL_TO_U64(U64_TO_DBL(acct_max) * SOFT_LIM_PCT);
|
|
|
+ if (acct_max > SOFT_LIM_BYTES && acct_max - SOFT_LIM_BYTES > soft_limit) {
|
|
|
+ soft_limit = acct_max - SOFT_LIM_BYTES;
|
|
|
+ }
|
|
|
+ if (expected_bandwidth_usage) {
|
|
|
+ const uint64_t expected_usage =
|
|
|
+ expected_bandwidth_usage * SOFT_LIM_MINUTES;
|
|
|
+ if (acct_max > expected_usage && acct_max - expected_usage > soft_limit)
|
|
|
+ soft_limit = acct_max - expected_usage;
|
|
|
+ }
|
|
|
+
|
|
|
if (!soft_limit)
|
|
|
return 0;
|
|
|
return n_bytes_read_in_interval >= soft_limit
|
|
@@ -667,6 +730,14 @@ hibernate_begin(hibernate_state_t new_state, time_t now)
|
|
|
exit(0);
|
|
|
}
|
|
|
|
|
|
+ if (new_state == HIBERNATE_STATE_LOWBANDWIDTH &&
|
|
|
+ hibernate_state == HIBERNATE_STATE_LIVE) {
|
|
|
+ soft_limit_hit_at = now;
|
|
|
+ n_seconds_to_hit_soft_limit = n_seconds_active_in_interval;
|
|
|
+ n_bytes_at_soft_limit = MAX(n_bytes_read_in_interval,
|
|
|
+ n_bytes_written_in_interval);
|
|
|
+ }
|
|
|
+
|
|
|
/* close listeners. leave control listener(s). */
|
|
|
while ((conn = connection_get_by_type(CONN_TYPE_OR_LISTENER)) ||
|
|
|
(conn = connection_get_by_type(CONN_TYPE_AP_LISTENER)) ||
|