hs_pow: stop having a "minimum effort", and let PoW effort start low

I don't think the concept of "minimum effort" is really useful to us,
so this patch removes it entirely and consequentially changes the way
that "total" effort is calculated so that we don't rely on any minimum
and we instead ramp up effort no faster than necessary.

If at least some portion of the attack is conducted by clients that
avoid PoW or provide incorrect solutions, those (potentially very
cheap) attacks will end up keeping the pqueue full. Prior to this patch,
that would cause suggested efforts to be unnecessarily high, because
rounding these very cheap requests up to even a minimum of 1 will
overestimate how much actual attack effort is being spent.

The result is that this patch is a simplification and it also allows a
slower start, where PoW effort jumps up either by a single unit or by an
amount calculated from actual effort in the queue.

Signed-off-by: Micah Elizabeth Scott <beth@torproject.org>
This commit is contained in:
Micah Elizabeth Scott 2023-03-21 12:28:23 -07:00
parent d15bbf32da
commit 6a0809c4e3
6 changed files with 8 additions and 32 deletions

View File

@ -1377,10 +1377,8 @@ hs_circ_handle_introduce2(const hs_service_t *service,
goto done;
}
/* Increase the total effort in valid requests received this period,
* but count 0-effort as min-effort, for estimation purposes. */
service->state.pow_state->total_effort += MAX(data.rdv_data.pow_effort,
service->state.pow_state->min_effort);
/* Track the total effort in valid requests received this period */
service->state.pow_state->total_effort += data.rdv_data.pow_effort;
/* Successfully added rend circuit to priority queue. */
ret = 0;

View File

@ -27,7 +27,6 @@
/* Default values for the HS anti-DoS PoW defenses. */
#define HS_CONFIG_V3_POW_DEFENSES_DEFAULT 0
#define HS_CONFIG_V3_POW_DEFENSES_MIN_EFFORT_DEFAULT 1
/* API */

View File

@ -256,15 +256,6 @@ hs_pow_verify(const hs_pow_service_state_t *pow_state,
tor_assert(pow_state);
tor_assert(pow_solution);
/* Notice, but don't fail, if E = POW_EFFORT is lower than the minimum
* effort. We will take whatever valid cells arrive, put them into the
* pqueue, and get to whichever ones we get to. */
if (pow_solution->effort < pow_state->min_effort) {
log_info(LD_REND, "Effort %d used in solution is less than the minimum "
"effort %d required by the service. That's ok.",
pow_solution->effort, pow_state->min_effort);
}
/* Find a valid seed C that starts with the seed head. Fail if no such seed
* exists. */
if (fast_memeq(pow_state->seed_current, pow_solution->seed_head,

View File

@ -81,9 +81,6 @@ typedef struct hs_pow_service_state_t {
/* The time at which the current seed expires and rotates for a new one. */
time_t expiration_time;
/* The minimum effort required for a valid solution. */
uint32_t min_effort;
/* The suggested effort that clients should use in order for their request to
* be serviced in a timely manner. */
uint32_t suggested_effort;

View File

@ -264,7 +264,6 @@ set_service_default_config(hs_service_config_t *c,
c->intro_dos_burst_per_sec = HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT;
/* PoW default options. */
c->has_dos_defense_enabled = HS_CONFIG_V3_POW_DEFENSES_DEFAULT;
c->pow_min_effort = HS_CONFIG_V3_POW_DEFENSES_MIN_EFFORT_DEFAULT;
}
/** Initialize PoW defenses */
@ -288,8 +287,6 @@ initialize_pow_defenses(hs_service_t *service)
(uint32_t) approx_time());
}
pow_state->min_effort = service->config.pow_min_effort;
/* We recalculate and update the suggested effort every HS_UPDATE_PERIOD
* seconds. */
pow_state->suggested_effort = 0;
@ -2696,12 +2693,14 @@ update_suggested_effort(hs_service_t *service, time_t now)
pow_state->rend_handled);
} else if (pow_state->had_queue) {
/* If we had a queue during this period, and the current top of queue
* is at or above the suggested effort, we should re-estimate the effort.
* Otherwise, it can stay the same (no change to effort). */
* is at or above the suggested effort, we should re-estimate the effort
* and increase it at least a minimal amount. Otherwise, it can stay the
* same (no change to effort). */
if (smartlist_len(pow_state->rend_request_pqueue) > 0 &&
top_of_rend_pqueue_is_worthwhile(pow_state)) {
pow_state->suggested_effort = (uint32_t)(pow_state->total_effort /
pow_state->rend_handled);
pow_state->suggested_effort = MAX(pow_state->suggested_effort + 1,
(uint32_t)(pow_state->total_effort /
pow_state->rend_handled));
}
} else {
/* If we were able to keep the queue drained the entire update period,
@ -2714,13 +2713,6 @@ update_suggested_effort(hs_service_t *service, time_t now)
log_debug(LD_REND, "Recalculated suggested effort: %u",
pow_state->suggested_effort);
/* If the suggested effort has been decreased below the minimum, set it
* to zero: no pow needed again until we queue or trim */
if (pow_state->suggested_effort < pow_state->min_effort) {
// XXX: Verify this disables pow being done at all.
pow_state->suggested_effort = 0;
}
/* Reset the total effort sum and number of rends for this update period. */
pow_state->total_effort = 0;
pow_state->rend_handled = 0;

View File

@ -264,7 +264,6 @@ typedef struct hs_service_config_t {
/** True iff PoW anti-DoS defenses are enabled. */
unsigned int has_pow_defenses_enabled : 1;
uint32_t pow_min_effort;
uint32_t pow_queue_rate;
uint32_t pow_queue_burst;