Commit 5ee48d47 authored by George Kadianakis's avatar George Kadianakis Committed by Nick Mathewson
Browse files

Parse Guardfraction file and apply results to routerstatuses.

Parse the file just before voting and apply its information to the
provided vote_routerstatus_t. This follows the same logic as when
dirauths parse bwauth files.
parent a3de2dfd
......@@ -1117,6 +1117,11 @@ The following options are useful only for clients (that is, if
download any non-default directory material. It doesn't currently
do anything when we lack a live consensus. (Default: 1)
[[GuardfractionFile]] **GuardfractionFile** __FILENAME__::
V3 authoritative directories only. Configures the location of the
guardfraction file which contains information about how long relays
have been guards. (Default: unset)
[[NumEntryGuards]] **NumEntryGuards** __NUM__::
If UseEntryGuards is set to 1, we will try to pick a total of NUM routers
as long-term entries for our circuits. If NUM is 0, we try to learn
......
......@@ -428,6 +428,7 @@ static config_var_t option_vars_[] = {
V(V3AuthNIntervalsValid, UINT, "3"),
V(V3AuthUseLegacyKey, BOOL, "0"),
V(V3BandwidthsFile, FILENAME, NULL),
V(GuardfractionFile, FILENAME, NULL),
VAR("VersioningAuthoritativeDirectory",BOOL,VersioningAuthoritativeDir, "0"),
V(VirtualAddrNetworkIPv4, STRING, "127.192.0.0/10"),
V(VirtualAddrNetworkIPv6, STRING, "[FE80::]/10"),
......@@ -2787,6 +2788,17 @@ options_validate(or_options_t *old_options, or_options_t *options,
if (options->V3BandwidthsFile && !old_options) {
dirserv_read_measured_bandwidths(options->V3BandwidthsFile, NULL);
}
/* same for guardfraction file */
if (options->GuardfractionFile && !old_options) {
file_status_t fs = file_status(options->GuardfractionFile);
if (fs == FN_EMPTY) {
REJECT("GuardfractionFile set but it's an empty file? Failing");
} else if (fs != FN_FILE) {
REJECT("GuardfractionFile set but not a file? Failing");
}
dirserv_read_guardfraction_file(options->GuardfractionFile, NULL);
}
}
if (options->AuthoritativeDir && !options->DirPort_set)
......
......@@ -2151,6 +2151,319 @@ clear_status_flags_on_sybil(routerstatus_t *rs)
* forget to add it to this clause. */
}
/** The guardfraction of the guard with identity fingerprint <b>guard_id</b>
* is <b>guardfraction_percentage</b>. See if we have a vote routerstatus for
* this guard in <b>vote_routerstatuses</b>, and if we do, register the
* information to it.
*
* Return 1 if we applied the information and 0 if we couldn't find a
* matching guard.
*
* Requires that <b>vote_routerstatuses</b> be sorted.
*/
static int
guardfraction_line_apply(const char *guard_id,
uint32_t guardfraction_percentage,
smartlist_t *vote_routerstatuses)
{
vote_routerstatus_t *vrs = NULL;
tor_assert(vote_routerstatuses);
vrs = smartlist_bsearch(vote_routerstatuses, guard_id,
compare_digest_to_vote_routerstatus_entry);
if (!vrs) {
return 0;
}
vrs->status.has_guardfraction = 1;
vrs->status.guardfraction_percentage = guardfraction_percentage;
return 1;
}
/* Given a guard line from a guardfraction file, parse it and register
* its information to <b>vote_routerstatuses</b>.
*
* Return:
* * 1 if the line was proper and its information got registered.
* * 0 if the line was proper but no currently active guard was found
* to register the guardfraction information to.
* * -1 if the line could not be parsed and set <b>err_msg</b> to a
newly allocated string containing the error message.
*/
static int
guardfraction_file_parse_guard_line(const char *guard_line,
smartlist_t *vote_routerstatuses,
char **err_msg)
{
char guard_id[DIGEST_LEN];
uint32_t guardfraction;
char *inputs_tmp = NULL;
int num_ok = 1;
smartlist_t *sl = smartlist_new();
int retval = -1;
tor_assert(err_msg);
/* guard_line should contain something like this:
<hex digest> <guardfraction> <appearances> */
smartlist_split_string(sl, guard_line, " ",
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 3);
if (smartlist_len(sl) < 3) {
tor_asprintf(err_msg, "bad line '%s'", guard_line);
goto done;
}
inputs_tmp = smartlist_get(sl, 0);
if (strlen(inputs_tmp) != HEX_DIGEST_LEN ||
base16_decode(guard_id, DIGEST_LEN, inputs_tmp, HEX_DIGEST_LEN)) {
tor_asprintf(err_msg, "bad digest '%s'", inputs_tmp);
goto done;
}
inputs_tmp = smartlist_get(sl, 1);
/* Guardfraction is an integer in [0, 100]. */
guardfraction =
(uint32_t) tor_parse_long(inputs_tmp, 10, 0, 100, &num_ok, NULL);
if (!num_ok) {
tor_asprintf(err_msg, "wrong percentage '%s'", inputs_tmp);
goto done;
}
/* If routerstatuses were provided, apply this info to actual routers. */
if (vote_routerstatuses) {
retval = guardfraction_line_apply(guard_id, guardfraction,
vote_routerstatuses);
} else {
retval = 0; /* If we got this far, line was correctly formatted. */
}
done:
SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
smartlist_free(sl);
return retval;
}
/** Given an inputs line from a guardfraction file, parse it and
* register its information to <b>total_consensuses</b> and
* <b>total_days</b>.
*
* Return 0 if it parsed well. Return -1 if there was an error, and
* set <b>err_msg</b> to a newly allocated string containing the
* error message.
*/
static int
guardfraction_file_parse_inputs_line(const char *inputs_line,
int *total_consensuses,
int *total_days,
char **err_msg)
{
int retval = -1;
char *inputs_tmp = NULL;
int num_ok = 1;
smartlist_t *sl = smartlist_new();
tor_assert(err_msg);
/* Second line is inputs information:
* n-inputs <total_consensuses> <total_days>. */
smartlist_split_string(sl, inputs_line, " ",
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 3);
if (smartlist_len(sl) < 2) {
tor_asprintf(err_msg, "incomplete line '%s'", inputs_line);
goto done;
}
inputs_tmp = smartlist_get(sl, 0);
*total_consensuses =
(int) tor_parse_long(inputs_tmp, 10, 0, INT_MAX, &num_ok, NULL);
if (!num_ok) {
tor_asprintf(err_msg, "unparseable consensus '%s'", inputs_tmp);
goto done;
}
inputs_tmp = smartlist_get(sl, 1);
*total_days =
(int) tor_parse_long(inputs_tmp, 10, 0, INT_MAX, &num_ok, NULL);
if (!num_ok) {
tor_asprintf(err_msg, "unparseable days '%s'", inputs_tmp);
goto done;
}
retval = 0;
done:
SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
smartlist_free(sl);
return retval;
}
/* Maximum age of a guardfraction file that we are willing to accept. */
#define MAX_GUARDFRACTION_FILE_AGE (7*24*60*60) /* approx a week */
/** Static strings of guardfraction files. */
#define GUARDFRACTION_DATE_STR "written-at"
#define GUARDFRACTION_INPUTS "n-inputs"
#define GUARDFRACTION_GUARD "guard-seen"
#define GUARDFRACTION_VERSION "guardfraction-file-version"
/** Given a guardfraction file in a string, parse it and register the
* guardfraction information to the provided vote routerstatuses.
*
* This is the rough format of the guardfraction file:
*
* guardfraction-file-version 1
* written-at <date and time>
* n-inputs <number of consesuses parsed> <number of days considered>
*
* guard-seen <fpr 1> <guardfraction percentage> <consensus appearances>
* guard-seen <fpr 2> <guardfraction percentage> <consensus appearances>
* guard-seen <fpr 3> <guardfraction percentage> <consensus appearances>
* guard-seen <fpr 4> <guardfraction percentage> <consensus appearances>
* guard-seen <fpr 5> <guardfraction percentage> <consensus appearances>
* ...
*
* Return -1 if the parsing failed and 0 if it went smoothly. Parsing
* should tolerate errors in all lines but the written-at header.
*/
STATIC int
dirserv_read_guardfraction_file_from_str(const char *guardfraction_file_str,
smartlist_t *vote_routerstatuses)
{
config_line_t *front=NULL, *line;
int ret_tmp;
int retval = -1;
int current_line_n = 0; /* line counter for better log messages */
/* Guardfraction info to be parsed */
int total_consensuses = 0;
int total_days = 0;
/* Stats */
int guards_read_n = 0;
int guards_applied_n = 0;
/* Parse file and split it in lines */
ret_tmp = config_get_lines(guardfraction_file_str, &front, 0);
if (ret_tmp < 0) {
log_warn(LD_CONFIG, "Error reading from guardfraction file");
goto done;
}
/* Sort routerstatuses (needed later when applying guardfraction info) */
if (vote_routerstatuses)
smartlist_sort(vote_routerstatuses, compare_vote_routerstatus_entries);
for (line = front; line; line=line->next) {
current_line_n++;
if (!strcmp(line->key, GUARDFRACTION_VERSION)) {
int num_ok = 1;
unsigned int version;
version =
(unsigned int) tor_parse_long(line->value,
10, 0, INT_MAX, &num_ok, NULL);
if (!num_ok || version != 1) {
log_warn(LD_GENERAL, "Got unknown guardfraction version %d.", version);
goto done;
}
} else if (!strcmp(line->key, GUARDFRACTION_DATE_STR)) {
time_t file_written_at;
time_t now = time(NULL);
/* First line is 'written-at <date>' */
if (parse_iso_time(line->value, &file_written_at) < 0) {
log_warn(LD_CONFIG, "Guardfraction:%d: Bad date '%s'. Ignoring",
current_line_n, line->value);
goto done; /* don't tolerate failure here. */
}
if (file_written_at < now - MAX_GUARDFRACTION_FILE_AGE) {
log_warn(LD_CONFIG, "Guardfraction:%d: was written very long ago '%s'",
current_line_n, line->value);
goto done; /* don't tolerate failure here. */
}
} else if (!strcmp(line->key, GUARDFRACTION_INPUTS)) {
char *err_msg = NULL;
if (guardfraction_file_parse_inputs_line(line->value,
&total_consensuses,
&total_days,
&err_msg) < 0) {
log_warn(LD_CONFIG, "Guardfraction:%d: %s",
current_line_n, err_msg);
tor_free(err_msg);
continue;
}
} else if (!strcmp(line->key, GUARDFRACTION_GUARD)) {
char *err_msg = NULL;
ret_tmp = guardfraction_file_parse_guard_line(line->value,
vote_routerstatuses,
&err_msg);
if (ret_tmp < 0) { /* failed while parsing the guard line */
log_warn(LD_CONFIG, "Guardfraction:%d: %s",
current_line_n, err_msg);
tor_free(err_msg);
continue;
}
/* Successfully parsed guard line. Check if it was applied properly. */
guards_read_n++;
if (ret_tmp > 0) {
guards_applied_n++;
}
} else {
log_warn(LD_CONFIG, "Unknown guardfraction line %d (%s %s)",
current_line_n, line->key, line->value);
}
}
retval = 0;
log_warn(LD_CONFIG,
"Successfully parsed guardfraction file with %d consensuses over "
"%d days. Parsed %d nodes and applied %d of them%s.",
total_consensuses, total_days, guards_read_n, guards_applied_n,
vote_routerstatuses ? "" : " (no routerstatus provided)" );
done:
config_free_lines(front);
if (retval < 0) {
return retval;
} else {
return guards_read_n;
}
}
/** Read a guardfraction file at <b>fname</b> and load all its
* information to <b>vote_routerstatuses</b>. */
int
dirserv_read_guardfraction_file(const char *fname,
smartlist_t *vote_routerstatuses)
{
char *guardfraction_file_str;
/* Read file to a string */
guardfraction_file_str = read_file_to_str(fname, RFTS_IGNORE_MISSING, NULL);
if (!guardfraction_file_str) {
log_warn(LD_FS, "Cannot open guardfraction file '%s'. Failing.", fname);
return -1;
}
return dirserv_read_guardfraction_file_from_str(guardfraction_file_str,
vote_routerstatuses);
}
/**
* Helper function to parse out a line in the measured bandwidth file
* into a measured_bw_line_t output structure. Returns -1 on failure
......@@ -2463,6 +2776,12 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key,
smartlist_free(routers);
digestmap_free(omit_as_sybil, NULL);
/* Apply guardfraction information to routerstatuses. */
if (options->GuardfractionFile) {
dirserv_read_guardfraction_file(options->GuardfractionFile,
routerstatuses);
}
/* This pass through applies the measured bw lines to the routerstatuses */
if (options->V3BandwidthsFile) {
dirserv_read_measured_bandwidths(options->V3BandwidthsFile,
......
......@@ -123,10 +123,17 @@ STATIC int dirserv_query_measured_bw_cache_kb(const char *node_id,
long *bw_out,
time_t *as_of_out);
STATIC int dirserv_has_measured_bw(const char *node_id);
STATIC int
dirserv_read_guardfraction_file_from_str(const char *guardfraction_file_str,
smartlist_t *vote_routerstatuses);
#endif
int dirserv_read_measured_bandwidths(const char *from_file,
smartlist_t *routerstatuses);
int dirserv_read_guardfraction_file(const char *fname,
smartlist_t *vote_routerstatuses);
#endif
......@@ -2145,6 +2145,12 @@ typedef struct routerstatus_t {
uint32_t bandwidth_kb; /**< Bandwidth (capacity) of the router as reported in
* the vote/consensus, in kilobytes/sec. */
/** The consensus has guardfraction information for this router. */
unsigned int has_guardfraction:1;
/** The guardfraction value of this router. */
uint32_t guardfraction_percentage;
char *exitsummary; /**< exit policy summary -
* XXX weasel: this probably should not stay a string. */
......@@ -3947,6 +3953,9 @@ typedef struct {
/** Location of bandwidth measurement file */
char *V3BandwidthsFile;
/** Location of guardfraction file */
char *GuardfractionFile;
/** Authority only: key=value pairs that we add to our networkstatus
* consensus vote on the 'params' line. */
char *ConsensusParams;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment