Commit ef67077f authored by Karsten Loesing's avatar Karsten Loesing
Browse files

Tweak TB_EMPTY event based on comments by nickm.

- Avoid control_event_refill_global function with 13 arguments and
  increase code reuse factor by moving more code from control.c to
  connection.c.
- Avoid an unsafe uint32_t -> int cast.
- Add TestingEnableTbEmptyEvent option.
- Prepare functions for testing.
- Rename a few functions and improve documentation.
parent 26b49f52
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -2007,6 +2007,7 @@ The following options are used for running a testing Tor network.
       TestingEstimatedDescriptorPropagationTime 0 minutes
       TestingEnableConnBwEvent 1
       TestingEnableCellStatsEvent 1
       TestingEnableTbEmptyEvent 1

**TestingV3AuthInitialVotingInterval** __N__ **minutes**|**hours**::
    Like V3AuthVotingInterval, but for initial voting interval before the first
@@ -2047,6 +2048,11 @@ The following options are used for running a testing Tor network.
    events.  Changing this requires that **TestingTorNetwork** is set.
    (Default: 0)

**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
-------
+8 −0
Original line number Diff line number Diff line
@@ -220,6 +220,7 @@ static config_var_t option_vars_[] = {
  V(DownloadExtraInfo,           BOOL,     "0"),
  V(TestingEnableConnBwEvent,    BOOL,     "0"),
  V(TestingEnableCellStatsEvent, BOOL,     "0"),
  V(TestingEnableTbEmptyEvent,   BOOL,     "0"),
  V(EnforceDistinctSubnets,      BOOL,     "1"),
  V(EntryNodes,                  ROUTERSET,   NULL),
  V(EntryStatistics,             BOOL,     "0"),
@@ -465,6 +466,7 @@ static const config_var_t testing_tor_network_defaults[] = {
  V(MinUptimeHidServDirectoryV2, INTERVAL, "0 minutes"),
  V(TestingEnableConnBwEvent,    BOOL,     "1"),
  V(TestingEnableCellStatsEvent, BOOL,     "1"),
  V(TestingEnableTbEmptyEvent,   BOOL,     "1"),
  VAR("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_, "1"),

  { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
@@ -3252,6 +3254,12 @@ options_validate(or_options_t *old_options, or_options_t *options,
           "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 "
+103 −35
Original line number Diff line number Diff line
@@ -2541,25 +2541,33 @@ record_num_bytes_transferred(connection_t *conn,
#endif

#ifndef USE_BUFFEREVENTS
/** Last emptied global or relay buckets in msec since midnight; only used
 * in TestingTorNetwork mode. */
/** Last time at which the global or relay buckets were emptied in msec
 * since midnight. */
static uint32_t global_relayed_read_emptied = 0,
                global_relayed_write_emptied = 0,
                global_read_emptied = 0,
                global_write_emptied = 0;

/** Check if a bucket has just run out of tokens, and if so, note the
 * timestamp for TB_EMPTY events; only used in TestingTorNetwork mode. */
static void
connection_buckets_empty_ts(uint32_t *timestamp_var, int tokens_before,
                            size_t tokens_removed)
/** Helper: convert given <b>tvnow</b> time value to milliseconds since
 * midnight. */
static uint32_t
msec_since_midnight(struct timeval tvnow)
{
  if (tokens_before > 0 && tokens_before - (int)tokens_removed <= 0) {
    struct timeval tvnow;
    tor_gettimeofday_cached(&tvnow);
    *timestamp_var = (uint32_t)(((tvnow.tv_sec % 86400L) * 1000L) +
  return (uint32_t)(((tvnow.tv_sec % 86400L) * 1000L) +
         ((uint32_t)tvnow.tv_usec / (uint32_t)1000L));
}

/** Check if a bucket which had <b>tokens_before</b> tokens and which got
 * <b>tokens_removed</b> tokens removed at timestamp <b>tvnow</b> has run
 * out of tokens, and if so, note the milliseconds since midnight in
 * <b>timestamp_var</b> for the next TB_EMPTY event. */
static void
connection_buckets_note_empty_ts(uint32_t *timestamp_var,
                                 int tokens_before, size_t tokens_removed,
                                 struct timeval tvnow)
{
  if (tokens_before > 0 && (uint32_t)tokens_before <= tokens_removed)
    *timestamp_var = msec_since_midnight(tvnow);
}

/** We just read <b>num_read</b> and wrote <b>num_written</b> bytes
@@ -2586,23 +2594,25 @@ connection_buckets_decrement(connection_t *conn, time_t now,

  /* If one or more of our token buckets ran dry just now, note the
   * timestamp for TB_EMPTY events. */
  if (get_options()->TestingTorNetwork) {
  if (get_options()->TestingEnableTbEmptyEvent) {
    struct timeval tvnow;
    tor_gettimeofday_cached(&tvnow);
    if (connection_counts_as_relayed_traffic(conn, now)) {
      connection_buckets_empty_ts(&global_relayed_read_emptied,
                                  global_relayed_read_bucket, num_read);
      connection_buckets_empty_ts(&global_relayed_write_emptied,
                                  global_relayed_write_bucket, num_written);
    }
    connection_buckets_empty_ts(&global_read_emptied, global_read_bucket,
                                num_read);
    connection_buckets_empty_ts(&global_write_emptied, global_write_bucket,
                                num_written);
      connection_buckets_note_empty_ts(&global_relayed_read_emptied,
                         global_relayed_read_bucket, num_read, tvnow);
      connection_buckets_note_empty_ts(&global_relayed_write_emptied,
                         global_relayed_write_bucket, num_written, tvnow);
    }
    connection_buckets_note_empty_ts(&global_read_emptied,
                       global_read_bucket, num_read, tvnow);
    connection_buckets_note_empty_ts(&global_write_emptied,
                       global_write_bucket, num_written, tvnow);
    if (connection_speaks_cells(conn) && conn->state == OR_CONN_STATE_OPEN) {
      or_connection_t *or_conn = TO_OR_CONN(conn);
      connection_buckets_empty_ts(&or_conn->read_emptied_time,
                                  or_conn->read_bucket, num_read);
      connection_buckets_empty_ts(&or_conn->write_emptied_time,
                                  or_conn->write_bucket, num_written);
      connection_buckets_note_empty_ts(&or_conn->read_emptied_time,
                         or_conn->read_bucket, num_read, tvnow);
      connection_buckets_note_empty_ts(&or_conn->write_emptied_time,
                         or_conn->write_bucket, num_written, tvnow);
    }
  }

@@ -2712,6 +2722,28 @@ connection_bucket_refill_helper(int *bucket, int rate, int burst,
  }
}

/** Helper: return the time in milliseconds since <b>last_empty_time</b>
 * when a bucket ran empty that previously had <b>tokens_before</b> tokens
 * now has <b>tokens_after</b> tokens after refilling at timestamp
 * <b>tvnow</b>, capped at <b>milliseconds_elapsed</b> milliseconds since
 * last refilling that bucket.  Return 0 if the bucket has not been empty
 * since the last refill or has not been refilled. */
static uint32_t
bucket_millis_empty(int tokens_before, uint32_t last_empty_time,
                    int tokens_after, int milliseconds_elapsed,
                    struct timeval tvnow)
{
  uint32_t result = 0, refilled;
  if (tokens_before <= 0 && tokens_after > tokens_before) {
    refilled = msec_since_midnight(tvnow);
    result = (uint32_t)((refilled + 86400L * 1000L - last_empty_time) %
             (86400L * 1000L));
    if (result > (uint32_t)milliseconds_elapsed)
      result = (uint32_t)milliseconds_elapsed;
  }
  return result;
}

/** Time has passed; increment buckets appropriately. */
void
connection_bucket_refill(int milliseconds_elapsed, time_t now)
@@ -2724,6 +2756,7 @@ connection_bucket_refill(int milliseconds_elapsed, time_t now)
  int prev_global_write = global_write_bucket;
  int prev_relay_read = global_relayed_read_bucket;
  int prev_relay_write = global_relayed_write_bucket;
  struct timeval tvnow; /*< Only used if TB_EMPTY events are enabled. */

  bandwidthrate = (int)options->BandwidthRate;
  bandwidthburst = (int)options->BandwidthBurst;
@@ -2759,14 +2792,31 @@ connection_bucket_refill(int milliseconds_elapsed, time_t now)
                                  milliseconds_elapsed,
                                  "global_relayed_write_bucket");

  control_event_refill_global(global_read_bucket, prev_global_read,
                              global_read_emptied, global_write_bucket,
                              prev_global_write, global_write_emptied,
                              global_relayed_read_bucket, prev_relay_read,
  /* If buckets were empty before and have now been refilled, tell any
   * interested controllers. */
  if (get_options()->TestingEnableTbEmptyEvent) {
    uint32_t global_read_empty_time, global_write_empty_time,
             relay_read_empty_time, relay_write_empty_time;
    tor_gettimeofday_cached(&tvnow);
    global_read_empty_time = bucket_millis_empty(prev_global_read,
                             global_read_emptied, global_read_bucket,
                             milliseconds_elapsed, tvnow);
    global_write_empty_time = bucket_millis_empty(prev_global_write,
                              global_write_emptied, global_write_bucket,
                              milliseconds_elapsed, tvnow);
    control_event_tb_empty("GLOBAL", global_read_empty_time,
                           global_write_empty_time, milliseconds_elapsed);
    relay_read_empty_time = bucket_millis_empty(prev_relay_read,
                            global_relayed_read_emptied,
                              global_relayed_write_bucket, prev_relay_write,
                            global_relayed_read_bucket,
                            milliseconds_elapsed, tvnow);
    relay_write_empty_time = bucket_millis_empty(prev_relay_write,
                             global_relayed_write_emptied,
                              milliseconds_elapsed);
                             global_relayed_write_bucket,
                             milliseconds_elapsed, tvnow);
    control_event_tb_empty("RELAY", relay_read_empty_time,
                           relay_write_empty_time, milliseconds_elapsed);
  }

  /* refill the per-connection buckets */
  SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
@@ -2793,8 +2843,26 @@ connection_bucket_refill(int milliseconds_elapsed, time_t now)
                                        "or_conn->write_bucket");
      }

      control_event_refill_conn(or_conn, prev_conn_read, prev_conn_write,
                                (uint32_t)milliseconds_elapsed);
      /* If buckets were empty before and have now been refilled, tell any
       * interested controllers. */
      if (get_options()->TestingEnableTbEmptyEvent) {
        char *bucket;
        uint32_t conn_read_empty_time, conn_write_empty_time;
        tor_asprintf(&bucket, "ORCONN ID="U64_FORMAT,
                     U64_PRINTF_ARG(or_conn->base_.global_identifier));
        conn_read_empty_time = bucket_millis_empty(prev_conn_read,
                               or_conn->read_emptied_time,
                               or_conn->read_bucket,
                               milliseconds_elapsed, tvnow);
        conn_write_empty_time = bucket_millis_empty(prev_conn_write,
                                or_conn->write_emptied_time,
                                or_conn->write_bucket,
                                milliseconds_elapsed, tvnow);
        control_event_tb_empty(bucket, conn_read_empty_time,
                               conn_write_empty_time,
                               milliseconds_elapsed);
        tor_free(bucket);
      }
    }

    if (conn->read_blocked_on_bw == 1 /* marked to turn reading back on now */
+13 −89
Original line number Diff line number Diff line
@@ -4155,99 +4155,23 @@ control_event_circuit_cell_stats(void)
  return 0;
}

/** Helper: return the time in millis that a given bucket was empty,
 * capped at the time in millis since last refilling that bucket.  Return
 * 0 if the bucket was not empty during the last refill period. */
static uint32_t
bucket_millis_empty(int prev_tokens, uint32_t last_empty_time,
                    uint32_t milliseconds_elapsed)
{
  uint32_t result = 0, refilled;
  if (prev_tokens <= 0) {
    struct timeval tvnow;
    tor_gettimeofday_cached(&tvnow);
    refilled = (uint32_t)((tvnow.tv_sec % 86400L) * 1000L +
               (uint32_t)tvnow.tv_usec / (uint32_t)1000L);
    result = (uint32_t)((refilled + 86400L * 1000L - last_empty_time) %
             (86400L * 1000L));
    if (result > milliseconds_elapsed)
      result = milliseconds_elapsed;
  }
  return result;
}

/** Token buckets have been refilled: tell any interested control
 * connections how global and relay token buckets have changed. */
/** 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_refill_global(int global_read, int prev_global_read,
                            uint32_t global_read_emptied_time,
                            int global_write, int prev_global_write,
                            uint32_t global_write_emptied_time,
                            int relay_read, int prev_relay_read,
                            uint32_t relay_read_emptied_time,
                            int relay_write, int prev_relay_write,
                            uint32_t relay_write_emptied_time,
                            uint32_t milliseconds_elapsed)
{
  uint32_t global_read_empty_time, global_write_empty_time,
      relay_read_empty_time, relay_write_empty_time;
  if (!get_options()->TestingTorNetwork ||
      !EVENT_IS_INTERESTING(EVENT_TB_EMPTY))
    return 0;
  if (prev_global_read == global_read &&
      prev_global_write == global_write &&
      prev_relay_read == relay_read &&
      prev_relay_write == relay_write)
    return 0;
  if (prev_global_read <= 0 && prev_global_write <= 0) {
    global_read_empty_time = bucket_millis_empty(prev_global_read,
        global_read_emptied_time, milliseconds_elapsed);
    global_write_empty_time = bucket_millis_empty(prev_global_write,
        global_write_emptied_time, milliseconds_elapsed);
    send_control_event(EVENT_TB_EMPTY, ALL_FORMATS,
                       "650 TB_EMPTY GLOBAL READ=%d WRITTEN=%d "
                       "LAST=%d\r\n",
                       global_read_empty_time, global_write_empty_time,
                       milliseconds_elapsed);
  }
  if (prev_relay_read <= 0 && prev_relay_write <= 0) {
    relay_read_empty_time = bucket_millis_empty(prev_relay_read,
        relay_read_emptied_time, milliseconds_elapsed);
    relay_write_empty_time = bucket_millis_empty(prev_relay_write,
        relay_write_emptied_time, milliseconds_elapsed);
    send_control_event(EVENT_TB_EMPTY, ALL_FORMATS,
                       "650 TB_EMPTY RELAY READ=%d WRITTEN=%d "
                       "LAST=%d\r\n",
                       relay_read_empty_time, relay_write_empty_time,
                       milliseconds_elapsed);
  }
  return 0;
}

/** Token buckets of a connection have been refilled: tell any interested
 * control connections how per-connection token buckets have changed. */
int
control_event_refill_conn(or_connection_t *or_conn,
                          int prev_read, int prev_write,
                          uint32_t milliseconds_elapsed)
control_event_tb_empty(const char *bucket, uint32_t read_empty_time,
                       uint32_t write_empty_time,
                       int milliseconds_elapsed)
{
  uint32_t read_bucket_empty_time, write_bucket_empty_time;
  if (!get_options()->TestingTorNetwork ||
      !EVENT_IS_INTERESTING(EVENT_TB_EMPTY))
    return 0;
  if (prev_read == or_conn->read_bucket &&
      prev_write == or_conn->write_bucket)
    return 0;
  if (prev_read <= 0 || prev_write <= 0) {
    read_bucket_empty_time = bucket_millis_empty(prev_read,
        or_conn->read_emptied_time, milliseconds_elapsed);
    write_bucket_empty_time = bucket_millis_empty(prev_write,
        or_conn->write_emptied_time, 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 ORCONN ID="U64_FORMAT" READ=%d "
                       "WRITTEN=%d LAST=%d\r\n",
                       U64_PRINTF_ARG(or_conn->base_.global_identifier),
                       read_bucket_empty_time, write_bucket_empty_time,
                       "650 TB_EMPTY %s READ=%d WRITTEN=%d "
                       "LAST=%d\r\n",
                       bucket, read_empty_time, write_empty_time,
                       milliseconds_elapsed);
  }
  return 0;
+3 −12
Original line number Diff line number Diff line
@@ -54,18 +54,9 @@ int control_event_circ_bandwidth_used(void);
int control_event_conn_bandwidth(connection_t *conn);
int control_event_conn_bandwidth_used(void);
int control_event_circuit_cell_stats(void);
int control_event_refill_global(int global_read, int prev_global_read,
                                uint32_t global_read_emptied,
                                int global_write, int prev_global_write,
                                uint32_t global_write_emptied,
                                int relay_read, int prev_relay_read,
                                uint32_t relay_read_emptied,
                                int relay_write, int prev_relay_write,
                                uint32_t relay_write_emptied,
                                uint32_t milliseconds_elapsed);
int control_event_refill_conn(or_connection_t *or_conn,
                              int prev_read, int prev_write,
                              uint32_t milliseconds_elapsed);
int control_event_tb_empty(const char *bucket, uint32_t read_empty_time,
                           uint32_t write_empty_time,
                           int milliseconds_elapsed);
void control_event_logmsg(int severity, uint32_t domain, const char *msg);
int control_event_descriptors_changed(smartlist_t *routers);
int control_event_address_mapped(const char *from, const char *to,
Loading