tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

bwhist.c (18085B)


      1 /* Copyright (c) 2001 Matej Pfajfar.
      2 * Copyright (c) 2001-2004, Roger Dingledine.
      3 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
      4 * Copyright (c) 2007-2021, The Tor Project, Inc. */
      5 /* See LICENSE for licensing information */
      6 
      7 /**
      8 * @file bwhist.c
      9 * @brief Tracking for relay bandwidth history
     10 *
     11 * This module handles bandwidth usage history, used by relays to
     12 * self-report how much bandwidth they've used for different
     13 * purposes over last day or so, in order to generate the
     14 * {dirreq-,}{read,write}-history lines in that they publish.
     15 **/
     16 
     17 #define BWHIST_PRIVATE
     18 #include "orconfig.h"
     19 #include "core/or/or.h"
     20 #include "feature/stats/bwhist.h"
     21 
     22 #include "app/config/config.h"
     23 #include "app/config/statefile.h"
     24 #include "feature/relay/routermode.h"
     25 
     26 #include "feature/stats/bw_array_st.h"
     27 #include "app/config/or_state_st.h"
     28 #include "app/config/or_options_st.h"
     29 
     30 /** Shift the current period of b forward by one. */
     31 STATIC void
     32 commit_max(bw_array_t *b)
     33 {
     34  /* Store total from current period. */
     35  b->totals[b->next_max_idx] = b->total_in_period;
     36  /* Store maximum from current period. */
     37  b->maxima[b->next_max_idx++] = b->max_total;
     38  /* Advance next_period and next_max_idx */
     39  b->next_period += NUM_SECS_BW_SUM_INTERVAL;
     40  if (b->next_max_idx == NUM_TOTALS)
     41    b->next_max_idx = 0;
     42  if (b->num_maxes_set < NUM_TOTALS)
     43    ++b->num_maxes_set;
     44  /* Reset max_total. */
     45  b->max_total = 0;
     46  /* Reset total_in_period. */
     47  b->total_in_period = 0;
     48 }
     49 
     50 /** Shift the current observation time of <b>b</b> forward by one second. */
     51 STATIC void
     52 advance_obs(bw_array_t *b)
     53 {
     54  int nextidx;
     55  uint64_t total;
     56 
     57  /* Calculate the total bandwidth for the last NUM_SECS_ROLLING_MEASURE
     58   * seconds; adjust max_total as needed.*/
     59  total = b->total_obs + b->obs[b->cur_obs_idx];
     60  if (total > b->max_total)
     61    b->max_total = total;
     62 
     63  nextidx = b->cur_obs_idx+1;
     64  if (nextidx == NUM_SECS_ROLLING_MEASURE)
     65    nextidx = 0;
     66 
     67  b->total_obs = total - b->obs[nextidx];
     68  b->obs[nextidx]=0;
     69  b->cur_obs_idx = nextidx;
     70 
     71  if (++b->cur_obs_time >= b->next_period)
     72    commit_max(b);
     73 }
     74 
     75 /** Add <b>n</b> bytes to the number of bytes in <b>b</b> for second
     76 * <b>when</b>. */
     77 STATIC void
     78 add_obs(bw_array_t *b, time_t when, uint64_t n)
     79 {
     80  if (when < b->cur_obs_time)
     81    return; /* Don't record data in the past. */
     82 
     83  /* If we're currently adding observations for an earlier second than
     84   * 'when', advance b->cur_obs_time and b->cur_obs_idx by an
     85   * appropriate number of seconds, and do all the other housekeeping. */
     86  while (when > b->cur_obs_time) {
     87    /* Doing this one second at a time is potentially inefficient, if we start
     88       with a state file that is very old.  Fortunately, it doesn't seem to
     89       show up in profiles, so we can just ignore it for now. */
     90    advance_obs(b);
     91  }
     92 
     93  b->obs[b->cur_obs_idx] += n;
     94  b->total_in_period += n;
     95 }
     96 
     97 /** Allocate, initialize, and return a new bw_array. */
     98 STATIC bw_array_t *
     99 bw_array_new(void)
    100 {
    101  bw_array_t *b;
    102  time_t start;
    103  b = tor_malloc_zero(sizeof(bw_array_t));
    104  start = time(NULL);
    105  b->cur_obs_time = start;
    106  b->next_period = start + NUM_SECS_BW_SUM_INTERVAL;
    107  return b;
    108 }
    109 
    110 /** Free storage held by bandwidth array <b>b</b>. */
    111 STATIC void
    112 bw_array_free_(bw_array_t *b)
    113 {
    114  if (!b) {
    115    return;
    116  }
    117 
    118  tor_free(b);
    119 }
    120 
    121 /** Recent history of bandwidth observations for (all) read operations. */
    122 static bw_array_t *read_array = NULL;
    123 /** Recent history of bandwidth observations for IPv6 read operations. */
    124 static bw_array_t *read_array_ipv6 = NULL;
    125 /** Recent history of bandwidth observations for (all) write operations. */
    126 STATIC bw_array_t *write_array = NULL;
    127 /** Recent history of bandwidth observations for IPv6 write operations. */
    128 static bw_array_t *write_array_ipv6 = NULL;
    129 /** Recent history of bandwidth observations for read operations for the
    130    directory protocol. */
    131 static bw_array_t *dir_read_array = NULL;
    132 /** Recent history of bandwidth observations for write operations for the
    133    directory protocol. */
    134 static bw_array_t *dir_write_array = NULL;
    135 
    136 /** Set up structures for bandwidth history, clearing them if they already
    137 * exist. */
    138 void
    139 bwhist_init(void)
    140 {
    141  bw_array_free(read_array);
    142  bw_array_free(read_array_ipv6);
    143  bw_array_free(write_array);
    144  bw_array_free(write_array_ipv6);
    145  bw_array_free(dir_read_array);
    146  bw_array_free(dir_write_array);
    147 
    148  read_array = bw_array_new();
    149  read_array_ipv6 = bw_array_new();
    150  write_array = bw_array_new();
    151  write_array_ipv6 = bw_array_new();
    152  dir_read_array = bw_array_new();
    153  dir_write_array = bw_array_new();
    154 }
    155 
    156 /** Remember that we read <b>num_bytes</b> bytes in second <b>when</b>.
    157 *
    158 * Add num_bytes to the current running total for <b>when</b>.
    159 *
    160 * <b>when</b> can go back to time, but it's safe to ignore calls
    161 * earlier than the latest <b>when</b> you've heard of.
    162 */
    163 void
    164 bwhist_note_bytes_written(uint64_t num_bytes, time_t when, bool ipv6)
    165 {
    166 /* Maybe a circular array for recent seconds, and step to a new point
    167 * every time a new second shows up. Or simpler is to just to have
    168 * a normal array and push down each item every second; it's short.
    169 */
    170 /* When a new second has rolled over, compute the sum of the bytes we've
    171 * seen over when-1 to when-1-NUM_SECS_ROLLING_MEASURE, and stick it
    172 * somewhere. See bwhist_bandwidth_assess() below.
    173 */
    174  add_obs(write_array, when, num_bytes);
    175  if (ipv6)
    176    add_obs(write_array_ipv6, when, num_bytes);
    177 }
    178 
    179 /** Remember that we wrote <b>num_bytes</b> bytes in second <b>when</b>.
    180 * (like bwhist_note_bytes_written() above)
    181 */
    182 void
    183 bwhist_note_bytes_read(uint64_t num_bytes, time_t when, bool ipv6)
    184 {
    185 /* if we're smart, we can make this func and the one above share code */
    186  add_obs(read_array, when, num_bytes);
    187  if (ipv6)
    188    add_obs(read_array_ipv6, when, num_bytes);
    189 }
    190 
    191 /** Remember that we wrote <b>num_bytes</b> directory bytes in second
    192 * <b>when</b>. (like bwhist_note_bytes_written() above)
    193 */
    194 void
    195 bwhist_note_dir_bytes_written(uint64_t num_bytes, time_t when)
    196 {
    197  add_obs(dir_write_array, when, num_bytes);
    198 }
    199 
    200 /** Remember that we read <b>num_bytes</b> directory bytes in second
    201 * <b>when</b>. (like bwhist_note_bytes_written() above)
    202 */
    203 void
    204 bwhist_note_dir_bytes_read(uint64_t num_bytes, time_t when)
    205 {
    206  add_obs(dir_read_array, when, num_bytes);
    207 }
    208 
    209 /**
    210 * Helper: Return the largest value in b->maxima.  (This is equal to the
    211 * most bandwidth used in any NUM_SECS_ROLLING_MEASURE period for the last
    212 * NUM_SECS_BW_SUM_IS_VALID seconds.)
    213 *
    214 * Also include the current period if we have been observing it for
    215 * at least min_observation_time seconds.
    216 */
    217 STATIC uint64_t
    218 find_largest_max(bw_array_t *b, int min_observation_time)
    219 {
    220  int i;
    221  uint64_t max;
    222  time_t period_start = b->next_period - NUM_SECS_BW_SUM_INTERVAL;
    223  if (b->cur_obs_time > period_start + min_observation_time)
    224    max = b->max_total;
    225  else
    226    max = 0;
    227  for (i=0; i<NUM_TOTALS; ++i) {
    228    if (b->maxima[i]>max)
    229      max = b->maxima[i];
    230  }
    231  return max;
    232 }
    233 
    234 /** Find the largest sums in the past NUM_SECS_BW_SUM_IS_VALID (roughly)
    235 * seconds. Find one sum for reading and one for writing. They don't have
    236 * to be at the same time.
    237 *
    238 * Return the smaller of these sums, divided by NUM_SECS_ROLLING_MEASURE.
    239 */
    240 MOCK_IMPL(int,
    241 bwhist_bandwidth_assess,(void))
    242 {
    243  uint64_t w,r;
    244  int min_obs_time = get_options()->TestingMinTimeToReportBandwidth;
    245  r = find_largest_max(read_array, min_obs_time);
    246  w = find_largest_max(write_array, min_obs_time);
    247  if (r>w)
    248    return (int)(((double)w)/NUM_SECS_ROLLING_MEASURE);
    249  else
    250    return (int)(((double)r)/NUM_SECS_ROLLING_MEASURE);
    251 }
    252 
    253 /** Print the bandwidth history of b (either [dir-]read_array or
    254 * [dir-]write_array) into the buffer pointed to by buf.  The format is
    255 * simply comma separated numbers, from oldest to newest.
    256 *
    257 * It returns the number of bytes written.
    258 */
    259 STATIC size_t
    260 bwhist_fill_bandwidth_history(char *buf, size_t len, const bw_array_t *b)
    261 {
    262  char *cp = buf;
    263  int i, n;
    264  const or_options_t *options = get_options();
    265  uint64_t cutoff;
    266 
    267  if (b->num_maxes_set <= b->next_max_idx) {
    268    /* We haven't been through the circular array yet; time starts at i=0.*/
    269    i = 0;
    270  } else {
    271    /* We've been around the array at least once.  The next i to be
    272       overwritten is the oldest. */
    273    i = b->next_max_idx;
    274  }
    275 
    276  if (options->RelayBandwidthRate) {
    277    /* We don't want to report that we used more bandwidth than the max we're
    278     * willing to relay; otherwise everybody will know how much traffic
    279     * we used ourself. */
    280    cutoff = options->RelayBandwidthRate * NUM_SECS_BW_SUM_INTERVAL;
    281  } else {
    282    cutoff = UINT64_MAX;
    283  }
    284 
    285  for (n=0; n<b->num_maxes_set; ++n,++i) {
    286    uint64_t total;
    287    if (i >= NUM_TOTALS)
    288      i -= NUM_TOTALS;
    289    tor_assert(i < NUM_TOTALS);
    290    /* Round the bandwidth used down to the nearest 1k. */
    291    total = b->totals[i] & ~0x3ff;
    292    if (total > cutoff)
    293      total = cutoff;
    294 
    295    if (n==(b->num_maxes_set-1))
    296      tor_snprintf(cp, len-(cp-buf), "%"PRIu64, (total));
    297    else
    298      tor_snprintf(cp, len-(cp-buf), "%"PRIu64",", (total));
    299    cp += strlen(cp);
    300  }
    301  return cp-buf;
    302 }
    303 
    304 /** Encode a single bandwidth history line into <b>buf</b>. */
    305 static void
    306 bwhist_get_one_bandwidth_line(buf_t *buf, const char *desc,
    307                              const bw_array_t *b)
    308 {
    309  /* [dirreq-](read|write)-history yyyy-mm-dd HH:MM:SS (n s) n,n,n... */
    310  /* The n,n,n part above. Largest representation of a uint64_t is 20 chars
    311   * long, plus the comma. */
    312 #define MAX_HIST_VALUE_LEN (21*NUM_TOTALS)
    313 
    314  char tmp[MAX_HIST_VALUE_LEN];
    315  char end[ISO_TIME_LEN+1];
    316 
    317  size_t slen = bwhist_fill_bandwidth_history(tmp, MAX_HIST_VALUE_LEN, b);
    318  /* If we don't have anything to write, skip to the next entry. */
    319  if (slen == 0)
    320    return;
    321 
    322  format_iso_time(end, b->next_period-NUM_SECS_BW_SUM_INTERVAL);
    323  buf_add_printf(buf, "%s %s (%d s) %s\n",
    324                 desc, end, NUM_SECS_BW_SUM_INTERVAL, tmp);
    325 }
    326 
    327 /** Allocate and return lines for representing this server's bandwidth
    328 * history in its descriptor. We publish these lines in our extra-info
    329 * descriptor.
    330 */
    331 char *
    332 bwhist_get_bandwidth_lines(void)
    333 {
    334  buf_t *buf = buf_new();
    335 
    336  bwhist_get_one_bandwidth_line(buf, "write-history", write_array);
    337  bwhist_get_one_bandwidth_line(buf, "read-history", read_array);
    338  bwhist_get_one_bandwidth_line(buf, "ipv6-write-history", write_array_ipv6);
    339  bwhist_get_one_bandwidth_line(buf, "ipv6-read-history", read_array_ipv6);
    340  bwhist_get_one_bandwidth_line(buf, "dirreq-write-history", dir_write_array);
    341  bwhist_get_one_bandwidth_line(buf, "dirreq-read-history", dir_read_array);
    342 
    343  char *result = buf_extract(buf, NULL);
    344  buf_free(buf);
    345  return result;
    346 }
    347 
    348 /** Write a single bw_array_t into the Values, Ends, Interval, and Maximum
    349 * entries of an or_state_t. Done before writing out a new state file. */
    350 static void
    351 bwhist_update_bwhist_state_section(or_state_t *state,
    352                                     const bw_array_t *b,
    353                                     smartlist_t **s_values,
    354                                     smartlist_t **s_maxima,
    355                                     time_t *s_begins,
    356                                     int *s_interval)
    357 {
    358  int i,j;
    359  uint64_t maxval;
    360 
    361  if (*s_values) {
    362    SMARTLIST_FOREACH(*s_values, char *, val, tor_free(val));
    363    smartlist_free(*s_values);
    364  }
    365  if (*s_maxima) {
    366    SMARTLIST_FOREACH(*s_maxima, char *, val, tor_free(val));
    367    smartlist_free(*s_maxima);
    368  }
    369  if (! server_mode(get_options())) {
    370    /* Clients don't need to store bandwidth history persistently;
    371     * force these values to the defaults. */
    372    /* FFFF we should pull the default out of config.c's state table,
    373     * so we don't have two defaults. */
    374    if (*s_begins != 0 || *s_interval != 900) {
    375      time_t now = time(NULL);
    376      time_t save_at = get_options()->AvoidDiskWrites ? now+3600 : now+600;
    377      or_state_mark_dirty(state, save_at);
    378    }
    379    *s_begins = 0;
    380    *s_interval = 900;
    381    *s_values = smartlist_new();
    382    *s_maxima = smartlist_new();
    383    return;
    384  }
    385  *s_begins = b->next_period;
    386  *s_interval = NUM_SECS_BW_SUM_INTERVAL;
    387 
    388  *s_values = smartlist_new();
    389  *s_maxima = smartlist_new();
    390  /* Set i to first position in circular array */
    391  i = (b->num_maxes_set <= b->next_max_idx) ? 0 : b->next_max_idx;
    392  for (j=0; j < b->num_maxes_set; ++j,++i) {
    393    if (i >= NUM_TOTALS)
    394      i = 0;
    395    smartlist_add_asprintf(*s_values, "%"PRIu64,
    396                           (b->totals[i] & ~0x3ff));
    397    maxval = b->maxima[i] / NUM_SECS_ROLLING_MEASURE;
    398    smartlist_add_asprintf(*s_maxima, "%"PRIu64,
    399                           (maxval & ~0x3ff));
    400  }
    401  smartlist_add_asprintf(*s_values, "%"PRIu64,
    402                         (b->total_in_period & ~0x3ff));
    403  maxval = b->max_total / NUM_SECS_ROLLING_MEASURE;
    404  smartlist_add_asprintf(*s_maxima, "%"PRIu64,
    405                         (maxval & ~0x3ff));
    406 }
    407 
    408 /** Update <b>state</b> with the newest bandwidth history. Done before
    409 * writing out a new state file. */
    410 void
    411 bwhist_update_state(or_state_t *state)
    412 {
    413 #define UPDATE(arrname,st) \
    414  bwhist_update_bwhist_state_section(state,\
    415                                       (arrname),\
    416                                       &state->BWHistory ## st ## Values, \
    417                                       &state->BWHistory ## st ## Maxima, \
    418                                       &state->BWHistory ## st ## Ends, \
    419                                       &state->BWHistory ## st ## Interval)
    420 
    421  UPDATE(write_array, Write);
    422  UPDATE(read_array, Read);
    423  UPDATE(write_array_ipv6, IPv6Write);
    424  UPDATE(read_array_ipv6, IPv6Read);
    425  UPDATE(dir_write_array, DirWrite);
    426  UPDATE(dir_read_array, DirRead);
    427 
    428  if (server_mode(get_options())) {
    429    or_state_mark_dirty(state, time(NULL)+(2*3600));
    430  }
    431 #undef UPDATE
    432 }
    433 
    434 /** Load a single bw_array_t from its Values, Ends, Maxima, and Interval
    435 * entries in an or_state_t. Done while reading the state file. */
    436 static int
    437 bwhist_load_bwhist_state_section(bw_array_t *b,
    438                                   const smartlist_t *s_values,
    439                                   const smartlist_t *s_maxima,
    440                                   const time_t s_begins,
    441                                   const int s_interval)
    442 {
    443  time_t now = time(NULL);
    444  int retval = 0;
    445  time_t start;
    446 
    447  uint64_t v, mv;
    448  int i,ok,ok_m = 0;
    449  int have_maxima = s_maxima && s_values &&
    450    (smartlist_len(s_values) == smartlist_len(s_maxima));
    451 
    452  if (s_values && s_begins >= now - NUM_SECS_BW_SUM_INTERVAL*NUM_TOTALS) {
    453    start = s_begins - s_interval*(smartlist_len(s_values));
    454    if (start > now)
    455      return 0;
    456    b->cur_obs_time = start;
    457    b->next_period = start + NUM_SECS_BW_SUM_INTERVAL;
    458    SMARTLIST_FOREACH_BEGIN(s_values, const char *, cp) {
    459        const char *maxstr = NULL;
    460        v = tor_parse_uint64(cp, 10, 0, UINT64_MAX, &ok, NULL);
    461        if (have_maxima) {
    462          maxstr = smartlist_get(s_maxima, cp_sl_idx);
    463          mv = tor_parse_uint64(maxstr, 10, 0, UINT64_MAX, &ok_m, NULL);
    464          mv *= NUM_SECS_ROLLING_MEASURE;
    465        } else {
    466          /* No maxima known; guess average rate to be conservative. */
    467          mv = (v / s_interval) * NUM_SECS_ROLLING_MEASURE;
    468        }
    469        if (!ok) {
    470          retval = -1;
    471          log_notice(LD_HIST, "Could not parse value '%s' into a number.'",cp);
    472        }
    473        if (maxstr && !ok_m) {
    474          retval = -1;
    475          log_notice(LD_HIST, "Could not parse maximum '%s' into a number.'",
    476                     maxstr);
    477        }
    478 
    479        if (start < now) {
    480          time_t cur_start = start;
    481          time_t actual_interval_len = s_interval;
    482          uint64_t cur_val = 0;
    483          /* Calculate the average per second. This is the best we can do
    484           * because our state file doesn't have per-second resolution. */
    485          if (start + s_interval > now)
    486            actual_interval_len = now - start;
    487          cur_val = v / actual_interval_len;
    488          /* This is potentially inefficient, but since we don't do it very
    489           * often it should be ok. */
    490          while (cur_start < start + actual_interval_len) {
    491            add_obs(b, cur_start, cur_val);
    492            ++cur_start;
    493          }
    494          b->max_total = mv;
    495          /* This will result in some fairly choppy history if s_interval
    496           * is not the same as NUM_SECS_BW_SUM_INTERVAL. XXXX */
    497          start += actual_interval_len;
    498        }
    499    } SMARTLIST_FOREACH_END(cp);
    500  }
    501 
    502  /* Clean up maxima and observed */
    503  for (i=0; i<NUM_SECS_ROLLING_MEASURE; ++i) {
    504    b->obs[i] = 0;
    505  }
    506  b->total_obs = 0;
    507 
    508  return retval;
    509 }
    510 
    511 /** Set bandwidth history from the state file we just loaded. */
    512 int
    513 bwhist_load_state(or_state_t *state, char **err)
    514 {
    515  int all_ok = 1;
    516 
    517  /* Assert they already have been malloced */
    518  tor_assert(read_array && write_array);
    519  tor_assert(read_array_ipv6 && write_array_ipv6);
    520  tor_assert(dir_read_array && dir_write_array);
    521 
    522 #define LOAD(arrname,st)                                                \
    523  if (bwhist_load_bwhist_state_section(                               \
    524                                (arrname),                              \
    525                                state->BWHistory ## st ## Values,       \
    526                                state->BWHistory ## st ## Maxima,       \
    527                                state->BWHistory ## st ## Ends,         \
    528                                state->BWHistory ## st ## Interval)<0)  \
    529    all_ok = 0
    530 
    531  LOAD(write_array, Write);
    532  LOAD(read_array, Read);
    533  LOAD(write_array_ipv6, IPv6Write);
    534  LOAD(read_array_ipv6, IPv6Read);
    535  LOAD(dir_write_array, DirWrite);
    536  LOAD(dir_read_array, DirRead);
    537 
    538 #undef LOAD
    539  if (!all_ok) {
    540    *err = tor_strdup("Parsing of bandwidth history values failed");
    541    /* and create fresh arrays */
    542    bwhist_init();
    543    return -1;
    544  }
    545  return 0;
    546 }
    547 
    548 void
    549 bwhist_free_all(void)
    550 {
    551  bw_array_free(read_array);
    552  bw_array_free(read_array_ipv6);
    553  bw_array_free(write_array);
    554  bw_array_free(write_array_ipv6);
    555  bw_array_free(dir_read_array);
    556  bw_array_free(dir_write_array);
    557 }