#include "core/or/or.h"
#include "app/config/config.h"
#include "core/or/channelpadding.h"
#include "core/or/circuituse.h"
#include "feature/relay/routermode.h"
#include "feature/relay/selftest.h"
#include "feature/stats/predict_ports.h"
#include "lib/container/bitarray.h"
#include "lib/time/tvdiff.h"
static size_t predicted_ports_total_alloc = 0;
static void predicted_ports_alloc(void);
typedef struct predicted_port_t {
uint16_t port;
time_t time;
} predicted_port_t;
static smartlist_t *predicted_ports_list=NULL;
static time_t prediction_timeout=0;
static time_t last_prediction_add_time=0;
int
predicted_ports_prediction_time_remaining(time_t now)
{
time_t seconds_waited;
time_t seconds_left;
seconds_waited = time_diff(last_prediction_add_time, now);
if (seconds_waited == TIME_MAX) {
last_prediction_add_time = now;
seconds_waited = 0;
}
if (seconds_waited > prediction_timeout)
return 0;
seconds_left = time_diff(seconds_waited, prediction_timeout);
if (BUG(seconds_left == TIME_MAX))
return INT_MAX;
return (int)(seconds_left);
}
static void
add_predicted_port(time_t now, uint16_t port)
{
predicted_port_t *pp = tor_malloc(sizeof(predicted_port_t));
if (!any_predicted_circuits(now)) {
prediction_timeout =
(time_t)channelpadding_get_circuits_available_timeout();
}
last_prediction_add_time = now;
log_info(LD_CIRC,
"New port prediction added. Will continue predictive circ building "
"for %d more seconds.",
predicted_ports_prediction_time_remaining(now));
pp->port = port;
pp->time = now;
predicted_ports_total_alloc += sizeof(*pp);
smartlist_add(predicted_ports_list, pp);
}
void
rep_hist_note_used_port(time_t now, uint16_t port)
{
tor_assert(predicted_ports_list);
if (!port)
return;
SMARTLIST_FOREACH_BEGIN(predicted_ports_list, predicted_port_t *, pp) {
if (pp->port == port) {
pp->time = now;
last_prediction_add_time = now;
log_info(LD_CIRC,
"New port prediction added. Will continue predictive circ "
"building for %d more seconds.",
predicted_ports_prediction_time_remaining(now));
return;
}
} SMARTLIST_FOREACH_END(pp);
add_predicted_port(now, port);
}
smartlist_t *
rep_hist_get_predicted_ports(time_t now)
{
int predicted_circs_relevance_time;
smartlist_t *out = smartlist_new();
tor_assert(predicted_ports_list);
predicted_circs_relevance_time = (int)prediction_timeout;
SMARTLIST_FOREACH_BEGIN(predicted_ports_list, predicted_port_t *, pp) {
if (pp->time + predicted_circs_relevance_time < now) {
log_debug(LD_CIRC, "Expiring predicted port %d", pp->port);
predicted_ports_total_alloc -= sizeof(predicted_port_t);
tor_free(pp);
SMARTLIST_DEL_CURRENT(predicted_ports_list, pp);
} else {
smartlist_add(out, tor_memdup(&pp->port, sizeof(uint16_t)));
}
} SMARTLIST_FOREACH_END(pp);
return out;
}
void
rep_hist_remove_predicted_ports(const smartlist_t *rmv_ports)
{
bitarray_t *remove_ports = bitarray_init_zero(UINT16_MAX);
SMARTLIST_FOREACH(rmv_ports, const uint16_t *, p,
bitarray_set(remove_ports, *p));
SMARTLIST_FOREACH_BEGIN(predicted_ports_list, predicted_port_t *, pp) {
if (bitarray_is_set(remove_ports, pp->port)) {
tor_free(pp);
predicted_ports_total_alloc -= sizeof(*pp);
SMARTLIST_DEL_CURRENT(predicted_ports_list, pp);
}
} SMARTLIST_FOREACH_END(pp);
bitarray_free(remove_ports);
}
void
rep_hist_note_used_resolve(time_t now)
{
rep_hist_note_used_port(now, 80);
}
static time_t predicted_internal_time = 0;
static time_t predicted_internal_uptime_time = 0;
static time_t predicted_internal_capacity_time = 0;
void
rep_hist_note_used_internal(time_t now, int need_uptime, int need_capacity)
{
if (!any_predicted_circuits(now)) {
prediction_timeout = channelpadding_get_circuits_available_timeout();
}
last_prediction_add_time = now;
log_info(LD_CIRC,
"New port prediction added. Will continue predictive circ building "
"for %d more seconds.",
predicted_ports_prediction_time_remaining(now));
predicted_internal_time = now;
if (need_uptime)
predicted_internal_uptime_time = now;
if (need_capacity)
predicted_internal_capacity_time = now;
}
int
rep_hist_get_predicted_internal(time_t now, int *need_uptime,
int *need_capacity)
{
int predicted_circs_relevance_time;
predicted_circs_relevance_time = (int)prediction_timeout;
if (!predicted_internal_time) {
predicted_internal_time = now;
predicted_internal_uptime_time = now;
predicted_internal_capacity_time = now;
}
if (predicted_internal_time + predicted_circs_relevance_time < now)
return 0;
if (predicted_internal_uptime_time + predicted_circs_relevance_time >= now)
*need_uptime = 1;
*need_capacity = 1;
return 1;
}
int
any_predicted_circuits(time_t now)
{
int predicted_circs_relevance_time;
predicted_circs_relevance_time = (int)prediction_timeout;
return smartlist_len(predicted_ports_list) ||
predicted_internal_time + predicted_circs_relevance_time >= now;
}
int
rep_hist_circbuilding_dormant(time_t now)
{
const or_options_t *options = get_options();
if (any_predicted_circuits(now))
return 0;
if (server_mode(options) &&
(!check_whether_orport_reachable(options) ||
!circuit_enough_testing_circs()))
return 0;
if (!check_whether_dirport_reachable(options))
return 0;
return 1;
}
static void
predicted_ports_alloc(void)
{
predicted_ports_list = smartlist_new();
}
void
predicted_ports_init(void)
{
predicted_ports_alloc();
add_predicted_port(time(NULL), 443); }
void
predicted_ports_free_all(void)
{
if (!predicted_ports_list)
return;
predicted_ports_total_alloc -=
smartlist_len(predicted_ports_list)*sizeof(predicted_port_t);
SMARTLIST_FOREACH(predicted_ports_list, predicted_port_t *,
pp, tor_free(pp));
smartlist_free(predicted_ports_list);
}