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
commit_max(bw_array_t * b)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
advance_obs(bw_array_t * b)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
add_obs(bw_array_t * b,time_t when,uint64_t n)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 *
bw_array_new(void)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
bw_array_free_(bw_array_t * b)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
bwhist_init(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
bwhist_note_bytes_written(uint64_t num_bytes,time_t when,bool ipv6)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
bwhist_note_bytes_read(uint64_t num_bytes,time_t when,bool ipv6)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
bwhist_note_dir_bytes_written(uint64_t num_bytes,time_t when)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
bwhist_note_dir_bytes_read(uint64_t num_bytes,time_t when)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
find_largest_max(bw_array_t * b,int min_observation_time)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
bwhist_fill_bandwidth_history(char * buf,size_t len,const bw_array_t * b)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
bwhist_get_one_bandwidth_line(buf_t * buf,const char * desc,const bw_array_t * b)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 *
bwhist_get_bandwidth_lines(void)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
bwhist_update_bwhist_state_section(or_state_t * state,const bw_array_t * b,smartlist_t ** s_values,smartlist_t ** s_maxima,time_t * s_begins,int * s_interval)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
bwhist_update_state(or_state_t * state)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
bwhist_load_bwhist_state_section(bw_array_t * b,const smartlist_t * s_values,const smartlist_t * s_maxima,const time_t s_begins,const int s_interval)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
bwhist_load_state(or_state_t * state,char ** err)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
bwhist_free_all(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 }
558