Ticket #961: fix_961_v3.patch

File fix_961_v3.patch, 14.1 KB (added by chobe, 4 years ago)
  • doc/tor.1.txt

    diff --git a/doc/tor.1.txt b/doc/tor.1.txt
    index a997bc3..89523aa 100644
    a b is non-zero): 
    16351635    to 0 will disable the heartbeat. (Default: 6 hours)
    16361636
    16371637[[AccountingMax]] **AccountingMax** __N__ **bytes**|**KBytes**|**MBytes**|**GBytes**|**KBits**|**MBits**|**GBits**|**TBytes**::
    1638     Never send more than the specified number of bytes in a given accounting
    1639     period, or receive more than that number in the period. For example, with
    1640     AccountingMax set to 1 GByte, a server could send 900 MBytes and
    1641     receive 800 MBytes and continue running. It will only hibernate once
    1642     one of the two reaches 1 GByte. When the number of bytes gets low,
    1643     Tor will stop accepting new connections and circuits.  When the
    1644     number of bytes is exhausted, Tor will hibernate until some
    1645     time in the next accounting period. To prevent all servers from waking at
    1646     the same time, Tor will also wait until a random point in each period
    1647     before waking up. If you have bandwidth cost issues, enabling hibernation
    1648     is preferable to setting a low bandwidth, since it provides users with a
    1649     collection of fast servers that are up some of the time, which is more
    1650     useful than a set of slow servers that are always "available".
     1638    Limits the max number of bytes sent and received within a set time period
     1639    using a given calculation rule (see: AccountingStart, AccountingRule).
     1640    Useful if you need to stay under a specific bandwidth. By default, the
     1641    number used for calculation is the max of either the bytes sent or
     1642    received. For example, with AccountingMax set to 1 GByte, a server
     1643    could send 900 MBytes and receive 800 MBytes and continue running.
     1644    It will only hibernate once one of the two reaches 1 GByte. This can
     1645    be changed to use the sum of the both bytes received and sent by setting
     1646    the AccountingRule option to "sum" (total bandwidth in/out). When the
     1647    number of bytes remaining gets low, Tor will stop accepting new connections
     1648    and circuits. When the number of bytes is exhausted, Tor will hibernate
     1649    until some time in the next accounting period. To prevent all servers
     1650    from waking at the same time, Tor will also wait until a random point
     1651    in each period before waking up. If you have bandwidth cost issues,
     1652    enabling hibernation is preferable to setting a low bandwidth, since
     1653    it provides users with a collection of fast servers that are up some
     1654    of the time, which is more useful than a set of slow servers that are
     1655    always "available".
     1656
     1657[[AccountingRule]] **AccountingRule** **sum**|**max**::
     1658    How we determine when our AccountingMax has been reached (when we
     1659    should hibernate) during a time interval. Set to "max" to calculate
     1660    using the higher of either the sent or received bytes (this is the
     1661    default functionality). Set to "sum" to calculate using the sent
     1662    plus received bytes.
    16511663
    16521664[[AccountingStart]] **AccountingStart** **day**|**week**|**month** [__day__] __HH:MM__::
    16531665    Specify how long accounting periods last. If **month** is given, each
  • src/or/config.c

    diff --git a/src/or/config.c b/src/or/config.c
    index 7800ec1..c1f93f8 100644
    a b static config_abbrev_t option_abbrevs_[] = { 
    129129static config_var_t option_vars_[] = {
    130130  OBSOLETE("AccountingMaxKB"),
    131131  V(AccountingMax,               MEMUNIT,  "0 bytes"),
     132  V(AccountingRule,              STRING,  "max"),
    132133  V(AccountingStart,             STRING,   NULL),
    133134  V(Address,                     STRING,   NULL),
    134135  V(AllowDotExit,                BOOL,     "0"),
    options_validate(or_options_t *old_options, or_options_t *options, 
    31373138               "risky: they will all turn off at the same time, which may "
    31383139               "alert observers that they are being run by the same party.");
    31393140    }
     3141    if (options->AccountingRule &&
     3142      strcmp(options->AccountingRule, "sum") != 0 &&
     3143      strcmp(options->AccountingRule, "max") != 0)
     3144      REJECT("AccountingRule must be 'sum' or 'max'");
    31403145  }
    31413146
    31423147  if (options->HTTPProxy) { /* parse it now */
  • src/or/hibernate.c

    diff --git a/src/or/hibernate.c b/src/or/hibernate.c
    index c433ac1..72953ae 100644
    a b configure_accounting(time_t now) 
    410410  accounting_set_wakeup_time();
    411411}
    412412
     413/** Return the relevant number of bytes sent/received this interval
     414 * based on the set AccountingRule */
     415static uint64_t
     416get_accounting_bytes()
     417{
     418  if (strcmp(get_options()->AccountingRule, "sum") == 0)
     419    return n_bytes_read_in_interval+n_bytes_written_in_interval;
     420  return MAX(n_bytes_read_in_interval, n_bytes_written_in_interval);
     421}
     422
    413423/** Set expected_bandwidth_usage based on how much we sent/received
    414424 * per minute last interval (if we were up for at least 30 minutes),
    415425 * or based on our declared bandwidth otherwise. */
    update_expected_bandwidth(void) 
    421431  uint64_t max_configured = (options->RelayBandwidthRate > 0 ?
    422432                             options->RelayBandwidthRate :
    423433                             options->BandwidthRate) * 60;
     434  /* max_configured is the larger of bytes read and bytes written
     435   * If we are accounting based on sum, worst case is both are
     436   * at max, doubling the expected sum of bandwidth */
     437  if (strcmp(get_options()->AccountingRule, "sum") == 0)
     438    max_configured *= 2;
    424439
    425440#define MIN_TIME_FOR_MEASUREMENT (1800)
    426441
    update_expected_bandwidth(void) 
    439454     * doesn't know to store soft-limit info.  Just take rate at which
    440455     * we were reading/writing in the last interval as our expected rate.
    441456     */
    442     uint64_t used = MAX(n_bytes_written_in_interval,
    443                         n_bytes_read_in_interval);
     457    uint64_t used = get_accounting_bytes();
    444458    expected = used / (n_seconds_active_in_interval / 60);
    445459  } else {
    446460    /* If we haven't gotten enough data last interval, set 'expected'
    hibernate_hard_limit_reached(void) 
    715729  uint64_t hard_limit = get_options()->AccountingMax;
    716730  if (!hard_limit)
    717731    return 0;
    718   return n_bytes_read_in_interval >= hard_limit
    719     || n_bytes_written_in_interval >= hard_limit;
     732  return get_accounting_bytes() >= hard_limit;
    720733}
    721734
    722735/** Return true iff we have sent/received almost all the bytes we are willing
    hibernate_soft_limit_reached(void) 
    747760
    748761  if (!soft_limit)
    749762    return 0;
    750   return n_bytes_read_in_interval >= soft_limit
    751     || n_bytes_written_in_interval >= soft_limit;
     763  return get_accounting_bytes() >= soft_limit;
    752764}
    753765
    754766/** Called when we get a SIGINT, or when bandwidth soft limit is
    hibernate_begin(hibernate_state_t new_state, time_t now) 
    772784      hibernate_state == HIBERNATE_STATE_LIVE) {
    773785    soft_limit_hit_at = now;
    774786    n_seconds_to_hit_soft_limit = n_seconds_active_in_interval;
    775     n_bytes_at_soft_limit = MAX(n_bytes_read_in_interval,
    776                                 n_bytes_written_in_interval);
     787    n_bytes_at_soft_limit = get_accounting_bytes();
    777788  }
    778789
    779790  /* close listeners. leave control listener(s). */
    getinfo_helper_accounting(control_connection_t *conn, 
    10031014                 U64_PRINTF_ARG(n_bytes_written_in_interval));
    10041015  } else if (!strcmp(question, "accounting/bytes-left")) {
    10051016    uint64_t limit = get_options()->AccountingMax;
    1006     uint64_t read_left = 0, write_left = 0;
    1007     if (n_bytes_read_in_interval < limit)
    1008       read_left = limit - n_bytes_read_in_interval;
    1009     if (n_bytes_written_in_interval < limit)
    1010       write_left = limit - n_bytes_written_in_interval;
    1011     tor_asprintf(answer, U64_FORMAT" "U64_FORMAT,
    1012                  U64_PRINTF_ARG(read_left), U64_PRINTF_ARG(write_left));
     1017    if (strcmp(get_options()->AccountingRule, "sum") == 0) {
     1018      uint64_t total_left = 0;
     1019      uint64_t total_bytes = get_accounting_bytes();
     1020      if (total_bytes < limit)
     1021        total_left = limit - total_bytes;
     1022      tor_asprintf(answer, U64_FORMAT" "U64_FORMAT,
     1023                   U64_PRINTF_ARG(total_left), U64_PRINTF_ARG(total_left));
     1024    } else {
     1025      uint64_t read_left = 0, write_left = 0;
     1026      if (n_bytes_read_in_interval < limit)
     1027        read_left = limit - n_bytes_read_in_interval;
     1028      if (n_bytes_written_in_interval < limit)
     1029        write_left = limit - n_bytes_written_in_interval;
     1030      tor_asprintf(answer, U64_FORMAT" "U64_FORMAT,
     1031                   U64_PRINTF_ARG(read_left), U64_PRINTF_ARG(write_left));
     1032    }
    10131033  } else if (!strcmp(question, "accounting/interval-start")) {
    10141034    *answer = tor_malloc(ISO_TIME_LEN+1);
    10151035    format_iso_time(*answer, interval_start_time);
  • src/or/hibernate.h

    diff --git a/src/or/hibernate.h b/src/or/hibernate.h
    index 38ecb75..799b582 100644
    a b void consider_hibernation(time_t now); 
    2828int getinfo_helper_accounting(control_connection_t *conn,
    2929                              const char *question, char **answer,
    3030                              const char **errmsg);
     31uint64_t get_accounting_max_total(void);
    3132
    3233#ifdef HIBERNATE_PRIVATE
    3334/** Possible values of hibernate_state */
  • src/or/or.h

    diff --git a/src/or/or.h b/src/or/or.h
    index 3683607..6d98c99 100644
    a b typedef struct { 
    37903790  uint64_t AccountingMax; /**< How many bytes do we allow per accounting
    37913791                           * interval before hibernation?  0 for "never
    37923792                           * hibernate." */
     3793  char *AccountingRule; /**< How do we determine when our AccountingMax
     3794                         * has been reached?
     3795                         * "max" for when in or out reaches AccountingMax
     3796                         * "sum for when in plus out reaches AccountingMax */
    37933797
    37943798  /** Base64-encoded hash of accepted passwords for the control system. */
    37953799  config_line_t *HashedControlPassword;
  • src/or/router.c

    diff --git a/src/or/router.c b/src/or/router.c
    index 4d1e74e..c8c89f7 100644
    a b decide_to_advertise_dirport(const or_options_t *options, uint16_t dir_port) 
    10911091                         "accounting interval length %d", effective_bw,
    10921092                         U64_PRINTF_ARG(options->AccountingMax),
    10931093                         interval_length);
     1094    uint64_t acc_bytes = options->AccountingMax;
     1095    if (strcmp(options->AccountingRule, "sum") == 0)
     1096      acc_bytes /= 2;
    10941097    if (effective_bw >=
    1095         options->AccountingMax / interval_length) {
     1098        acc_bytes / interval_length) {
    10961099      new_choice = 0;
    10971100      reason = "AccountingMax enabled";
    10981101    }
  • src/or/status.c

    diff --git a/src/or/status.c b/src/or/status.c
    index c4156d0..5d40613 100644
    a b log_accounting(const time_t now, const or_options_t *options) 
    145145  or_state_t *state = get_or_state();
    146146  char *acc_rcvd = bytes_to_usage(state->AccountingBytesReadInInterval);
    147147  char *acc_sent = bytes_to_usage(state->AccountingBytesWrittenInInterval);
    148   char *acc_max = bytes_to_usage(options->AccountingMax);
     148  char *acc_rule = options->AccountingRule;
     149  uint64_t acc_bytes = options->AccountingMax;
     150  if (strcmp(acc_rule, "sum") == 0)
     151    acc_bytes *= 2;
     152  char *acc_max = bytes_to_usage(acc_bytes);
    149153  time_t interval_end = accounting_get_end_time();
    150154  char end_buf[ISO_TIME_LEN + 1];
    151155  char *remaining = NULL;
  • src/test/include.am

    diff --git a/src/test/include.am b/src/test/include.am
    index 77c92f1..03755a4 100644
    a b src_test_test_SOURCES = \ 
    3434        src/test/test_logging.c \
    3535        src/test/test_microdesc.c \
    3636        src/test/test_oom.c \
     37        src/test/test_accounting.c \
    3738        src/test/test_options.c \
    3839        src/test/test_pt.c \
    3940        src/test/test_relaycell.c \
  • src/test/test.c

    diff --git a/src/test/test.c b/src/test/test.c
    index e836160..381eaea 100644
    a b extern struct testcase_t hs_tests[]; 
    13221322extern struct testcase_t nodelist_tests[];
    13231323extern struct testcase_t routerkeys_tests[];
    13241324extern struct testcase_t oom_tests[];
     1325extern struct testcase_t accounting_tests[];
    13251326extern struct testcase_t policy_tests[];
    13261327extern struct testcase_t status_tests[];
    13271328extern struct testcase_t routerset_tests[];
    static struct testgroup_t testgroups[] = { 
    13541355  { "nodelist/", nodelist_tests },
    13551356  { "routerkeys/", routerkeys_tests },
    13561357  { "oom/", oom_tests },
     1358  { "accounting/", accounting_tests },
    13571359  { "policy/" , policy_tests },
    13581360  { "status/" , status_tests },
    13591361  { "routerset/" , routerset_tests },
  • new file src/test/test_accounting.c

    diff --git a/src/test/test_accounting.c b/src/test/test_accounting.c
    new file mode 100644
    index 0000000..02a6caa
    - +  
     1#include "or.h"
     2#include "test.h"
     3#define HIBERNATE_PRIVATE
     4#include "hibernate.h"
     5#include "config.h"
     6#define STATEFILE_PRIVATE
     7#include "statefile.h"
     8
     9#define NS_MODULE accounting
     10
     11#define NS_SUBMODULE limits
     12
     13/*
     14 * Test to make sure accounting triggers hibernation
     15 * correctly with both sum or max rules set
     16 */
     17
     18or_state_t *or_state;
     19NS_DECL(or_state_t *, get_or_state, (void));
     20static or_state_t *
     21NS(get_or_state)(void)
     22{
     23  return or_state;
     24}
     25
     26static void
     27test_accounting_limits(void *arg)
     28{
     29  NS_MOCK(get_or_state);
     30  or_state = or_state_new();
     31  or_options_t *options = get_options_mutable();
     32  options->AccountingMax = 100;
     33  options->AccountingRule = "max";
     34
     35  tor_assert(accounting_is_enabled(options));
     36  time_t fake_time = time(NULL);
     37  configure_accounting(fake_time);
     38
     39  accounting_add_bytes(10, 0, 1);
     40  fake_time += 1;
     41  consider_hibernation(fake_time);
     42  tor_assert(we_are_hibernating() == 0);
     43
     44  accounting_add_bytes(90, 0, 1);
     45  fake_time += 1;
     46  consider_hibernation(fake_time);
     47  tor_assert(we_are_hibernating() == 1);
     48
     49  options->AccountingMax = 200;
     50  options->AccountingRule = "sum";
     51
     52  accounting_add_bytes(0, 10, 1);
     53  fake_time += 1;
     54  consider_hibernation(fake_time);
     55  tor_assert(we_are_hibernating() == 0);
     56
     57  accounting_add_bytes(0, 90, 1);
     58  fake_time += 1;
     59  consider_hibernation(fake_time);
     60  tor_assert(we_are_hibernating() == 1);
     61  goto done;
     62 done:
     63    NS_UNMOCK(get_or_state);
     64    or_state_free(or_state);
     65  ;
     66}
     67
     68#undef NS_SUBMODULE
     69
     70struct testcase_t accounting_tests[] = {
     71  { "bwlimits", test_accounting_limits, TT_FORK, NULL, NULL },
     72  END_OF_TESTCASES
     73};
     74