Merge branch 'bug9321_rerebase'

Conflicts:
	src/or/dirvote.h
	src/test/include.am
	src/test/test_entrynodes.c
This commit is contained in:
Nick Mathewson 2015-02-18 09:17:02 -05:00
commit 96211bcf71
20 changed files with 1146 additions and 33 deletions

7
changes/bug9321 Normal file
View File

@ -0,0 +1,7 @@
o Major features:
- Introduce the Guardfraction feature which improves the load
balancing of path selection towards guard nodes. Specifically,
it aims to reduce the traffic gap that guard nodes experience
when they first get the Guard flag. This is a required step if
we want to increase the guard lifetime to 9 months or greater.
Resolves ticket 9321.

View File

@ -1114,6 +1114,17 @@ 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)
[[UseGuardFraction]] **UseGuardFraction** **0**|**1**|**auto**::
This torrc option specifies whether clients should use the
guardfraction information found in the consensus during path
selection. If it's set to 'auto', clients will do what the
UseGuardFraction consensus parameter tells them to do.
[[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

View File

@ -417,6 +417,7 @@ static config_var_t option_vars_[] = {
V(UseBridges, BOOL, "0"),
V(UseEntryGuards, BOOL, "1"),
V(UseEntryGuardsAsDirGuards, BOOL, "1"),
V(UseGuardFraction, AUTOBOOL, "auto"),
V(UseMicrodescriptors, AUTOBOOL, "auto"),
V(UseNTorHandshake, AUTOBOOL, "1"),
V(User, STRING, NULL),
@ -434,6 +435,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"),
@ -2790,6 +2792,10 @@ 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) {
dirserv_read_guardfraction_file(options->GuardfractionFile, NULL);
}
}
if (options->AuthoritativeDir && !options->DirPort_set)

View File

@ -1915,6 +1915,13 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version,
smartlist_add_asprintf(chunks,
" Measured=%d", vrs->measured_bw_kb);
}
/* Write down guardfraction information if we have it. */
if (format == NS_V3_VOTE && vrs && vrs->status.has_guardfraction) {
smartlist_add_asprintf(chunks,
" GuardFraction=%d",
vrs->status.guardfraction_percentage);
}
smartlist_add(chunks, tor_strdup("\n"));
if (desc) {
@ -2144,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_info(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
@ -2456,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,

View File

@ -125,10 +125,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

View File

@ -16,6 +16,7 @@
#include "router.h"
#include "routerlist.h"
#include "routerparse.h"
#include "entrynodes.h" /* needed for guardfraction methods */
/**
* \file dirvote.c
@ -1023,6 +1024,86 @@ networkstatus_compute_bw_weights_v10(smartlist_t *chunks, int64_t G,
return 1;
}
/** Update total bandwidth weights (G/M/E/D/T) with the bandwidth of
* the router in <b>rs</b>. */
static void
update_total_bandwidth_weights(const routerstatus_t *rs,
int is_exit, int is_guard,
int64_t *G, int64_t *M, int64_t *E, int64_t *D,
int64_t *T)
{
int default_bandwidth = rs->bandwidth_kb;
int guardfraction_bandwidth = 0;
if (!rs->has_bandwidth) {
log_info(LD_BUG, "Missing consensus bandwidth for router %s",
rs->nickname);
return;
}
/* If this routerstatus represents a guard that we have
* guardfraction information on, use it to calculate its actual
* bandwidth. From proposal236:
*
* Similarly, when calculating the bandwidth-weights line as in
* section 3.8.3 of dir-spec.txt, directory authorities should treat N
* as if fraction F of its bandwidth has the guard flag and (1-F) does
* not. So when computing the totals G,M,E,D, each relay N with guard
* visibility fraction F and bandwidth B should be added as follows:
*
* G' = G + F*B, if N does not have the exit flag
* M' = M + (1-F)*B, if N does not have the exit flag
*
* or
*
* D' = D + F*B, if N has the exit flag
* E' = E + (1-F)*B, if N has the exit flag
*
* In this block of code, we prepare the bandwidth values by setting
* the default_bandwidth to F*B and guardfraction_bandwidth to (1-F)*B. */
if (rs->has_guardfraction) {
guardfraction_bandwidth_t guardfraction_bw;
tor_assert(is_guard);
guard_get_guardfraction_bandwidth(&guardfraction_bw,
rs->bandwidth_kb,
rs->guardfraction_percentage);
default_bandwidth = guardfraction_bw.guard_bw;
guardfraction_bandwidth = guardfraction_bw.non_guard_bw;
}
/* Now calculate the total bandwidth weights with or without
guardfraction. Depending on the flags of the relay, add its
bandwidth to the appropriate weight pool. If it's a guard and
guardfraction is enabled, add its bandwidth to both pools as
indicated by the previous comment. */
*T += default_bandwidth;
if (is_exit && is_guard) {
*D += default_bandwidth;
if (rs->has_guardfraction) {
*E += guardfraction_bandwidth;
}
} else if (is_exit) {
*E += default_bandwidth;
} else if (is_guard) {
*G += default_bandwidth;
if (rs->has_guardfraction) {
*M += guardfraction_bandwidth;
}
} else {
*M += default_bandwidth;
}
}
/** Given a list of vote networkstatus_t in <b>votes</b>, our public
* authority <b>identity_key</b>, our private authority <b>signing_key</b>,
* and the number of <b>total_authorities</b> that we believe exist in our
@ -1291,8 +1372,11 @@ networkstatus_compute_consensus(smartlist_t *votes,
sizeof(uint32_t));
uint32_t *measured_bws_kb = tor_calloc(smartlist_len(votes),
sizeof(uint32_t));
uint32_t *measured_guardfraction = tor_calloc(smartlist_len(votes),
sizeof(uint32_t));
int num_bandwidths;
int num_mbws;
int num_guardfraction_inputs;
int *n_voter_flags; /* n_voter_flags[j] is the number of flags that
* votes[j] knows about. */
@ -1401,7 +1485,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
/* We need to know how many votes measure bandwidth. */
n_authorities_measuring_bandwidth = 0;
SMARTLIST_FOREACH(votes, networkstatus_t *, v,
SMARTLIST_FOREACH(votes, const networkstatus_t *, v,
if (v->has_measured_bws) {
++n_authorities_measuring_bandwidth;
}
@ -1443,6 +1527,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
smartlist_clear(versions);
num_bandwidths = 0;
num_mbws = 0;
num_guardfraction_inputs = 0;
/* Okay, go through all the entries for this digest. */
SMARTLIST_FOREACH_BEGIN(votes, networkstatus_t *, v) {
@ -1476,6 +1561,12 @@ networkstatus_compute_consensus(smartlist_t *votes,
chosen_name = rs->status.nickname;
}
/* Count guardfraction votes and note down the values. */
if (rs->status.has_guardfraction) {
measured_guardfraction[num_guardfraction_inputs++] =
rs->status.guardfraction_percentage;
}
/* count bandwidths */
if (rs->has_measured_bw)
measured_bws_kb[num_mbws++] = rs->measured_bw_kb;
@ -1565,6 +1656,17 @@ networkstatus_compute_consensus(smartlist_t *votes,
chosen_version = NULL;
}
/* If it's a guard and we have enough guardfraction votes,
calculate its consensus guardfraction value. */
if (is_guard && num_guardfraction_inputs > 2 &&
consensus_method >= MIN_METHOD_FOR_GUARDFRACTION) {
rs_out.has_guardfraction = 1;
rs_out.guardfraction_percentage = median_uint32(measured_guardfraction,
num_guardfraction_inputs);
/* final value should be an integer percentage! */
tor_assert(rs_out.guardfraction_percentage <= 100);
}
/* Pick a bandwidth */
if (num_mbws > 2) {
rs_out.has_bandwidth = 1;
@ -1586,21 +1688,11 @@ networkstatus_compute_consensus(smartlist_t *votes,
/* Fix bug 2203: Do not count BadExit nodes as Exits for bw weights */
is_exit = is_exit && !is_bad_exit;
/* Update total bandwidth weights with the bandwidths of this router. */
{
if (rs_out.has_bandwidth) {
T += rs_out.bandwidth_kb;
if (is_exit && is_guard)
D += rs_out.bandwidth_kb;
else if (is_exit)
E += rs_out.bandwidth_kb;
else if (is_guard)
G += rs_out.bandwidth_kb;
else
M += rs_out.bandwidth_kb;
} else {
log_warn(LD_BUG, "Missing consensus bandwidth for router %s",
rs_out.nickname);
}
update_total_bandwidth_weights(&rs_out,
is_exit, is_guard,
&G, &M, &E, &D, &T);
}
/* Ok, we already picked a descriptor digest we want to list
@ -1719,11 +1811,21 @@ networkstatus_compute_consensus(smartlist_t *votes,
smartlist_add(chunks, tor_strdup("\n"));
/* Now the weight line. */
if (rs_out.has_bandwidth) {
char *guardfraction_str = NULL;
int unmeasured = rs_out.bw_is_unmeasured &&
consensus_method >= MIN_METHOD_TO_CLIP_UNMEASURED_BW;
smartlist_add_asprintf(chunks, "w Bandwidth=%d%s\n",
/* If we have guardfraction info, include it in the 'w' line. */
if (rs_out.has_guardfraction) {
tor_asprintf(&guardfraction_str,
" GuardFraction=%u", rs_out.guardfraction_percentage);
}
smartlist_add_asprintf(chunks, "w Bandwidth=%d%s%s\n",
rs_out.bandwidth_kb,
unmeasured?" Unmeasured=1":"");
unmeasured?" Unmeasured=1":"",
guardfraction_str ? guardfraction_str : "");
tor_free(guardfraction_str);
}
/* Now the exitpolicy summary line. */

View File

@ -55,7 +55,7 @@
#define MIN_SUPPORTED_CONSENSUS_METHOD 13
/** The highest consensus method that we currently support. */
#define MAX_SUPPORTED_CONSENSUS_METHOD 19
#define MAX_SUPPORTED_CONSENSUS_METHOD 20
/** Lowest consensus method where microdesc consensuses omit any entry
* with no microdesc. */
@ -82,8 +82,13 @@
/** Lowest consensus method where we include "package" lines*/
#define MIN_METHOD_FOR_PACKAGE_LINES 19
/** Lowest consensus method where authorities may include
* GuardFraction information in microdescriptors. */
#define MIN_METHOD_FOR_GUARDFRACTION 20
/** Default bandwidth to clip unmeasured bandwidths to using method >=
* MIN_METHOD_TO_CLIP_UNMEASURED_BW */
* MIN_METHOD_TO_CLIP_UNMEASURED_BW. (This is not a consensus method; do not
* get confused with the above macros.) */
#define DEFAULT_MAX_UNMEASURED_BW_KB 20
void dirvote_free_all(void);

View File

@ -1697,6 +1697,63 @@ getinfo_helper_entry_guards(control_connection_t *conn,
return 0;
}
/** Return 0 if we should apply guardfraction information found in the
* consensus. A specific consensus can be specified with the
* <b>ns</b> argument, if NULL the most recent one will be picked.*/
int
should_apply_guardfraction(const networkstatus_t *ns)
{
/* We need to check the corresponding torrc option and the consensus
* parameter if we need to. */
const or_options_t *options = get_options();
/* If UseGuardFraction is 'auto' then check the same-named consensus
* parameter. If the consensus parameter is not present, default to
* "off". */
if (options->UseGuardFraction == -1) {
return networkstatus_get_param(ns, "UseGuardFraction",
0, /* default to "off" */
0, 1);
}
return options->UseGuardFraction;
}
/* Given the original bandwidth of a guard and its guardfraction,
* calculate how much bandwidth the guard should have as a guard and
* as a non-guard.
*
* Quoting from proposal236:
*
* Let Wpf denote the weight from the 'bandwidth-weights' line a
* client would apply to N for position p if it had the guard
* flag, Wpn the weight if it did not have the guard flag, and B the
* measured bandwidth of N in the consensus. Then instead of choosing
* N for position p proportionally to Wpf*B or Wpn*B, clients should
* choose N proportionally to F*Wpf*B + (1-F)*Wpn*B.
*
* This function fills the <b>guardfraction_bw</b> structure. It sets
* <b>guard_bw</b> to F*B and <b>non_guard_bw</b> to (1-F)*B.
*/
void
guard_get_guardfraction_bandwidth(guardfraction_bandwidth_t *guardfraction_bw,
int orig_bandwidth,
uint32_t guardfraction_percentage)
{
double guardfraction_fraction;
/* Turn the percentage into a fraction. */
tor_assert(guardfraction_percentage <= 100);
guardfraction_fraction = guardfraction_percentage / 100.0;
long guard_bw = tor_lround(guardfraction_fraction * orig_bandwidth);
tor_assert(guard_bw <= INT_MAX);
guardfraction_bw->guard_bw = (int) guard_bw;
guardfraction_bw->non_guard_bw = orig_bandwidth - guard_bw;
}
/** A list of configured bridges. Whenever we actually get a descriptor
* for one, we add it as an entry guard. Note that the order of bridges
* in this list does not necessarily correspond to the order of bridges

View File

@ -160,5 +160,21 @@ int validate_pluggable_transports_config(void);
double pathbias_get_close_success_count(entry_guard_t *guard);
double pathbias_get_use_success_count(entry_guard_t *guard);
/** Contains the bandwidth of a relay as a guard and as a non-guard
* after the guardfraction has been considered. */
typedef struct guardfraction_bandwidth_t {
/* Bandwidth as a guard after guardfraction has been considered. */
int guard_bw;
/* Bandwidth as a non-guard after guardfraction has been considered. */
int non_guard_bw;
} guardfraction_bandwidth_t;
int should_apply_guardfraction(const networkstatus_t *ns);
void
guard_get_guardfraction_bandwidth(guardfraction_bandwidth_t *guardfraction_bw,
int orig_bandwidth,
uint32_t guardfraction_percentage);
#endif

View File

@ -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. */
@ -3816,6 +3822,12 @@ typedef struct {
int NumEntryGuards; /**< How many entry guards do we try to establish? */
int UseEntryGuardsAsDirGuards; /** Boolean: Do we try to get directory info
* from a smallish number of fixed nodes? */
/** If 1, we use any guardfraction information we see in the
* consensus. If 0, we don't. If -1, let the consensus parameter
* decide. */
int UseGuardFraction;
int NumDirectoryGuards; /**< How many dir guards do we try to establish?
* If 0, use value from NumEntryGuards. */
int RephistTrackTime; /**< How many seconds do we keep rephist info? */
@ -3951,6 +3963,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;

View File

@ -2003,6 +2003,7 @@ compute_weighted_bandwidths(const smartlist_t *sl,
double Wg = -1, Wm = -1, We = -1, Wd = -1;
double Wgb = -1, Wmb = -1, Web = -1, Wdb = -1;
uint64_t weighted_bw = 0;
guardfraction_bandwidth_t guardfraction_bw;
u64_dbl_t *bandwidths;
/* Can't choose exit and guard at same time */
@ -2092,6 +2093,8 @@ compute_weighted_bandwidths(const smartlist_t *sl,
SMARTLIST_FOREACH_BEGIN(sl, const node_t *, node) {
int is_exit = 0, is_guard = 0, is_dir = 0, this_bw = 0;
double weight = 1;
double weight_without_guard_flag = 0; /* Used for guardfraction */
double final_weight = 0;
is_exit = node->is_exit && ! node->is_bad_exit;
is_guard = node->is_possible_guard;
is_dir = node_is_dir(node);
@ -2119,8 +2122,10 @@ compute_weighted_bandwidths(const smartlist_t *sl,
if (is_guard && is_exit) {
weight = (is_dir ? Wdb*Wd : Wd);
weight_without_guard_flag = (is_dir ? Web*We : We);
} else if (is_guard) {
weight = (is_dir ? Wgb*Wg : Wg);
weight_without_guard_flag = (is_dir ? Wmb*Wm : Wm);
} else if (is_exit) {
weight = (is_dir ? Web*We : We);
} else { // middle
@ -2132,8 +2137,43 @@ compute_weighted_bandwidths(const smartlist_t *sl,
this_bw = 0;
if (weight < 0.0)
weight = 0.0;
if (weight_without_guard_flag < 0.0)
weight_without_guard_flag = 0.0;
bandwidths[node_sl_idx].dbl = weight*this_bw + 0.5;
/* If guardfraction information is available in the consensus, we
* want to calculate this router's bandwidth according to its
* guardfraction. Quoting from proposal236:
*
* Let Wpf denote the weight from the 'bandwidth-weights' line a
* client would apply to N for position p if it had the guard
* flag, Wpn the weight if it did not have the guard flag, and B the
* measured bandwidth of N in the consensus. Then instead of choosing
* N for position p proportionally to Wpf*B or Wpn*B, clients should
* choose N proportionally to F*Wpf*B + (1-F)*Wpn*B.
*/
if (node->rs && node->rs->has_guardfraction && rule != WEIGHT_FOR_GUARD) {
/* XXX The assert should actually check for is_guard. However,
* that crashes dirauths because of #13297. This should be
* equivalent: */
tor_assert(node->rs->is_possible_guard);
guard_get_guardfraction_bandwidth(&guardfraction_bw,
this_bw,
node->rs->guardfraction_percentage);
/* Calculate final_weight = F*Wpf*B + (1-F)*Wpn*B */
final_weight =
guardfraction_bw.guard_bw * weight +
guardfraction_bw.non_guard_bw * weight_without_guard_flag;
log_debug(LD_GENERAL, "%s: Guardfraction weight %f instead of %f (%s)",
node->rs->nickname, final_weight, weight*this_bw,
bandwidth_weight_rule_to_string(rule));
} else { /* no guardfraction information. calculate the weight normally. */
final_weight = weight*this_bw;
}
bandwidths[node_sl_idx].dbl = final_weight + 0.5;
} SMARTLIST_FOREACH_END(node);
log_debug(LD_CIRC, "Generated weighted bandwidths for rule %s based "

View File

@ -9,6 +9,8 @@
* \brief Code to parse and validate router descriptors and directories.
**/
#define ROUTERPARSE_PRIVATE
#include "or.h"
#include "config.h"
#include "circuitstats.h"
@ -23,6 +25,7 @@
#include "networkstatus.h"
#include "rephist.h"
#include "routerparse.h"
#include "entrynodes.h"
#undef log
#include <math.h>
@ -1794,6 +1797,63 @@ find_start_of_next_routerstatus(const char *s)
return eos;
}
/** Parse the GuardFraction string from a consensus or vote.
*
* If <b>vote</b> or <b>vote_rs</b> are set the document getting
* parsed is a vote routerstatus. Otherwise it's a consensus. This is
* the same semantic as in routerstatus_parse_entry_from_string(). */
STATIC int
routerstatus_parse_guardfraction(const char *guardfraction_str,
networkstatus_t *vote,
vote_routerstatus_t *vote_rs,
routerstatus_t *rs)
{
int ok;
const char *end_of_header = NULL;
int is_consensus = !vote_rs;
uint32_t guardfraction;
tor_assert(bool_eq(vote, vote_rs));
/* If this info comes from a consensus, but we should't apply
guardfraction, just exit. */
if (is_consensus && !should_apply_guardfraction(NULL)) {
return 0;
}
end_of_header = strchr(guardfraction_str, '=');
if (!end_of_header) {
return -1;
}
guardfraction = (uint32_t)tor_parse_ulong(end_of_header+1,
10, 0, 100, &ok, NULL);
if (!ok) {
log_warn(LD_DIR, "Invalid GuardFraction %s", escaped(guardfraction_str));
return -1;
}
log_debug(LD_GENERAL, "[*] Parsed %s guardfraction '%s' for '%s'.",
is_consensus ? "consensus" : "vote",
guardfraction_str, rs->nickname);
if (!is_consensus) { /* We are parsing a vote */
vote_rs->status.guardfraction_percentage = guardfraction;
vote_rs->status.has_guardfraction = 1;
} else {
/* We are parsing a consensus. Only apply guardfraction to guards. */
if (rs->is_possible_guard) {
rs->guardfraction_percentage = guardfraction;
rs->has_guardfraction = 1;
} else {
log_warn(LD_BUG, "Got GuardFraction for non-guard %s. "
"This is not supposed to happen. Not applying. ", rs->nickname);
}
}
return 0;
}
/** Given a string at *<b>s</b>, containing a routerstatus object, and an
* empty smartlist at <b>tokens</b>, parse and return the first router status
* object in the string, and advance *<b>s</b> to just after the end of the
@ -1996,6 +2056,11 @@ routerstatus_parse_entry_from_string(memarea_t *area,
vote->has_measured_bws = 1;
} else if (!strcmpstart(tok->args[i], "Unmeasured=1")) {
rs->bw_is_unmeasured = 1;
} else if (!strcmpstart(tok->args[i], "GuardFraction=")) {
if (routerstatus_parse_guardfraction(tok->args[i],
vote, vote_rs, rs) < 0) {
goto err;
}
}
}
}

View File

@ -85,5 +85,12 @@ int rend_parse_introduction_points(rend_service_descriptor_t *parsed,
size_t intro_points_encoded_size);
int rend_parse_client_keys(strmap_t *parsed_clients, const char *str);
#ifdef ROUTERPARSE_PRIVATE
STATIC int routerstatus_parse_guardfraction(const char *guardfraction_str,
networkstatus_t *vote,
vote_routerstatus_t *vote_rs,
routerstatus_t *rs);
#endif
#endif

View File

@ -40,6 +40,7 @@ src_test_test_SOURCES = \
src/test/test_dir.c \
src/test/test_entryconn.c \
src/test/test_entrynodes.c \
src/test/test_guardfraction.c \
src/test/test_extorport.c \
src/test/test_hs.c \
src/test/test_introduce.c \
@ -61,6 +62,7 @@ src_test_test_SOURCES = \
src/test/test_status.c \
src/test/test_threads.c \
src/test/test_util.c \
src/test/test_helpers.c \
src/test/testing_common.c \
src/test/testhelper.c \
src/ext/tinytest.c
@ -121,6 +123,7 @@ noinst_HEADERS+= \
src/test/fakechans.h \
src/test/test.h \
src/test/testhelper.h \
src/test/test_helpers.h \
src/test/test_descriptors.inc \
src/test/example_extrainfo.inc \
src/test/failing_routerdescs.inc \

View File

@ -1135,6 +1135,7 @@ extern struct testcase_t crypto_tests[];
extern struct testcase_t dir_tests[];
extern struct testcase_t entryconn_tests[];
extern struct testcase_t entrynodes_tests[];
extern struct testcase_t guardfraction_tests[];
extern struct testcase_t extorport_tests[];
extern struct testcase_t hs_tests[];
extern struct testcase_t introduce_tests[];
@ -1179,6 +1180,7 @@ struct testgroup_t testgroups[] = {
{ "dir/md/", microdesc_tests },
{ "entryconn/", entryconn_tests },
{ "entrynodes/", entrynodes_tests },
{ "guardfraction/", guardfraction_tests },
{ "extorport/", extorport_tests },
{ "hs/", hs_tests },
{ "introduce/", introduce_tests },

View File

@ -19,6 +19,7 @@
#include "config.h"
#include "testhelper.h"
#include "test_helpers.h"
/* TODO:
* choose_random_entry() test with state set.
@ -271,19 +272,6 @@ state_lines_free(smartlist_t *entry_guard_lines)
smartlist_free(entry_guard_lines);
}
/* Return a statically allocated string representing yesterday's date
* in ISO format. We use it so that state file items are not found to
* be outdated. */
static const char *
get_yesterday_date_str(void)
{
static char buf[ISO_TIME_LEN+1];
time_t yesterday = time(NULL) - 24*60*60;
format_iso_time(buf, yesterday);
return buf;
}
/* Tests entry_guards_parse_state(). It creates a fake Tor state with
a saved entry guard and makes sure that Tor can parse it and
creates the right entry node out of it.

View File

@ -0,0 +1,418 @@
/* Copyright (c) 2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#define DIRSERV_PRIVATE
#define ROUTERPARSE_PRIVATE
#define NETWORKSTATUS_PRIVATE
#include "orconfig.h"
#include "or.h"
#include "config.h"
#include "dirserv.h"
#include "container.h"
#include "entrynodes.h"
#include "util.h"
#include "routerparse.h"
#include "networkstatus.h"
#include "test.h"
#include "test_helpers.h"
/* Generate a vote_routerstatus_t for a router with identity digest
<b>digest_in_hex</b>. */
static vote_routerstatus_t *
gen_vote_routerstatus_for_tests(const char *digest_in_hex, int is_guard)
{
int retval;
vote_routerstatus_t *vrs = NULL;
routerstatus_t *rs;
vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
rs = &vrs->status;
{ /* Useful information for tests */
char digest_tmp[DIGEST_LEN];
/* Guard or not? */
rs->is_possible_guard = is_guard;
/* Fill in the fpr */
tt_int_op(strlen(digest_in_hex), ==, HEX_DIGEST_LEN);
retval = base16_decode(digest_tmp, sizeof(digest_tmp),
digest_in_hex, HEX_DIGEST_LEN);
tt_int_op(retval, ==, 0);
memcpy(rs->identity_digest, digest_tmp, DIGEST_LEN);
}
{ /* Misc info (maybe not used in tests) */
vrs->version = tor_strdup("0.1.2.14");
strlcpy(rs->nickname, "router2", sizeof(rs->nickname));
memset(rs->descriptor_digest, 78, DIGEST_LEN);
rs->addr = 0x99008801;
rs->or_port = 443;
rs->dir_port = 8000;
/* all flags but running cleared */
rs->is_flagged_running = 1;
vrs->has_measured_bw = 1;
rs->has_bandwidth = 1;
}
return vrs;
done:
vote_routerstatus_free(vrs);
return NULL;
}
/** Make sure our parsers reject corrupted guardfraction files. */
static void
test_parse_guardfraction_file_bad(void *arg)
{
int retval;
char *guardfraction_bad = NULL;
const char *yesterday_date_str = get_yesterday_date_str();
(void) arg;
/* Start parsing all those corrupted guardfraction files! */
/* Guardfraction file version is not a number! */
tor_asprintf(&guardfraction_bad,
"guardfraction-file-version nan\n"
"written-at %s\n"
"n-inputs 420 3\n"
"guard-seen D0EDB47BEAD32D26D0A837F7D5357EC3AD3B8777 100 420\n"
"guard-seen 07B5547026DF3E229806E135CFA8552D56AFBABC 5 420\n",
yesterday_date_str);
retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
tt_int_op(retval, ==, -1);
tor_free(guardfraction_bad);
/* This one does not have a date! Parsing should fail. */
tor_asprintf(&guardfraction_bad,
"guardfraction-file-version 1\n"
"written-at not_date\n"
"n-inputs 420 3\n"
"guard-seen D0EDB47BEAD32D26D0A837F7D5357EC3AD3B8777 100 420\n"
"guard-seen 07B5547026DF3E229806E135CFA8552D56AFBABC 5 420\n");
retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
tt_int_op(retval, ==, -1);
tor_free(guardfraction_bad);
/* This one has an incomplete n-inputs line, but parsing should
still continue. */
tor_asprintf(&guardfraction_bad,
"guardfraction-file-version 1\n"
"written-at %s\n"
"n-inputs biggie\n"
"guard-seen D0EDB47BEAD32D26D0A837F7D5357EC3AD3B8777 100 420\n"
"guard-seen 07B5547026DF3E229806E135CFA8552D56AFBABC 5 420\n",
yesterday_date_str);
retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
tt_int_op(retval, ==, 2);
tor_free(guardfraction_bad);
/* This one does not have a fingerprint in the guard line! */
tor_asprintf(&guardfraction_bad,
"guardfraction-file-version 1\n"
"written-at %s\n"
"n-inputs 420 3\n"
"guard-seen not_a_fingerprint 100 420\n",
yesterday_date_str);
retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
tt_int_op(retval, ==, 0);
tor_free(guardfraction_bad);
/* This one does not even have an integer guardfraction value. */
tor_asprintf(&guardfraction_bad,
"guardfraction-file-version 1\n"
"written-at %s\n"
"n-inputs 420 3\n"
"guard-seen D0EDB47BEAD32D26D0A837F7D5357EC3AD3B8777 NaN 420\n"
"guard-seen 07B5547026DF3E229806E135CFA8552D56AFBABC 5 420\n",
yesterday_date_str);
retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
tt_int_op(retval, ==, 1);
tor_free(guardfraction_bad);
/* This one is not a percentage (not in [0, 100]) */
tor_asprintf(&guardfraction_bad,
"guardfraction-file-version 1\n"
"written-at %s\n"
"n-inputs 420 3\n"
"guard-seen D0EDB47BEAD32D26D0A837F7D5357EC3AD3B8777 666 420\n"
"guard-seen 07B5547026DF3E229806E135CFA8552D56AFBABC 5 420\n",
yesterday_date_str);
retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
tt_int_op(retval, ==, 1);
tor_free(guardfraction_bad);
/* This one is not a percentage either (not in [0, 100]) */
tor_asprintf(&guardfraction_bad,
"guardfraction-file-version 1\n"
"written-at %s\n"
"n-inputs 420 3\n"
"guard-seen D0EDB47BEAD32D26D0A837F7D5357EC3AD3B8777 -3 420\n",
yesterday_date_str);
retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
tt_int_op(retval, ==, 0);
done:
tor_free(guardfraction_bad);
}
/* Make sure that our test guardfraction file gets parsed properly, and
* its information are applied properly to our routerstatuses. */
static void
test_parse_guardfraction_file_good(void *arg)
{
int retval;
vote_routerstatus_t *vrs_guard = NULL;
vote_routerstatus_t *vrs_dummy = NULL;
char *guardfraction_good = NULL;
const char *yesterday_date_str = get_yesterday_date_str();
smartlist_t *routerstatuses = smartlist_new();
/* Some test values that we need to validate later */
const char fpr_guard[] = "D0EDB47BEAD32D26D0A837F7D5357EC3AD3B8777";
const char fpr_unlisted[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
const int guardfraction_value = 42;
(void) arg;
{
/* Populate the smartlist with some fake routerstatuses, so that
after parsing the guardfraction file we can check that their
elements got filled properly. */
/* This one is a guard */
vrs_guard = gen_vote_routerstatus_for_tests(fpr_guard, 1);
tt_assert(vrs_guard);
smartlist_add(routerstatuses, vrs_guard);
/* This one is a guard but it's not in the guardfraction file */
vrs_dummy = gen_vote_routerstatus_for_tests(fpr_unlisted, 1);
tt_assert(vrs_dummy);
smartlist_add(routerstatuses, vrs_dummy);
}
tor_asprintf(&guardfraction_good,
"guardfraction-file-version 1\n"
"written-at %s\n"
"n-inputs 420 3\n"
"guard-seen %s %d 420\n",
yesterday_date_str,
fpr_guard, guardfraction_value);
/* Read the guardfraction file */
retval = dirserv_read_guardfraction_file_from_str(guardfraction_good,
routerstatuses);
tt_int_op(retval, ==, 1);
{ /* Test that routerstatus fields got filled properly */
/* The guardfraction fields of the guard should be filled. */
tt_assert(vrs_guard->status.has_guardfraction);
tt_int_op(vrs_guard->status.guardfraction_percentage,
==,
guardfraction_value);
/* The guard that was not in the guardfraction file should not have
been touched either. */
tt_assert(!vrs_dummy->status.has_guardfraction);
}
done:
vote_routerstatus_free(vrs_guard);
vote_routerstatus_free(vrs_dummy);
smartlist_free(routerstatuses);
tor_free(guardfraction_good);
}
/** Make sure that the guardfraction bandwidths get calculated properly. */
static void
test_get_guardfraction_bandwidth(void *arg)
{
guardfraction_bandwidth_t gf_bw;
const int orig_bw = 1000;
(void) arg;
/* A guard with bandwidth 1000 and GuardFraction 0.25, should have
bandwidth 250 as a guard and bandwidth 750 as a non-guard. */
guard_get_guardfraction_bandwidth(&gf_bw,
orig_bw, 25);
tt_int_op(gf_bw.guard_bw, ==, 250);
tt_int_op(gf_bw.non_guard_bw, ==, 750);
/* Also check the 'guard_bw + non_guard_bw == original_bw'
* invariant. */
tt_int_op(gf_bw.non_guard_bw + gf_bw.guard_bw, ==, orig_bw);
done:
;
}
/** Parse the GuardFraction element of the consensus, and make sure it
* gets parsed correctly. */
static void
test_parse_guardfraction_consensus(void *arg)
{
int retval;
or_options_t *options = get_options_mutable();
const char *guardfraction_str_good = "GuardFraction=66";
routerstatus_t rs_good;
routerstatus_t rs_no_guard;
const char *guardfraction_str_bad1 = "GuardFraction="; /* no value */
routerstatus_t rs_bad1;
const char *guardfraction_str_bad2 = "GuardFraction=166"; /* no percentage */
routerstatus_t rs_bad2;
(void) arg;
/* GuardFraction use is currently disabled by default. So we need to
manually enable it. */
options->UseGuardFraction = 1;
{ /* Properly formatted GuardFraction. Check that it gets applied
correctly. */
memset(&rs_good, 0, sizeof(routerstatus_t));
rs_good.is_possible_guard = 1;
retval = routerstatus_parse_guardfraction(guardfraction_str_good,
NULL, NULL,
&rs_good);
tt_int_op(retval, ==, 0);
tt_assert(rs_good.has_guardfraction);
tt_int_op(rs_good.guardfraction_percentage, ==, 66);
}
{ /* Properly formatted GuardFraction but router is not a
guard. GuardFraction should not get applied. */
memset(&rs_no_guard, 0, sizeof(routerstatus_t));
tt_assert(!rs_no_guard.is_possible_guard);
retval = routerstatus_parse_guardfraction(guardfraction_str_good,
NULL, NULL,
&rs_no_guard);
tt_int_op(retval, ==, 0);
tt_assert(!rs_no_guard.has_guardfraction);
}
{ /* Bad GuardFraction. Function should fail and not apply. */
memset(&rs_bad1, 0, sizeof(routerstatus_t));
rs_bad1.is_possible_guard = 1;
retval = routerstatus_parse_guardfraction(guardfraction_str_bad1,
NULL, NULL,
&rs_bad1);
tt_int_op(retval, ==, -1);
tt_assert(!rs_bad1.has_guardfraction);
}
{ /* Bad GuardFraction. Function should fail and not apply. */
memset(&rs_bad2, 0, sizeof(routerstatus_t));
rs_bad2.is_possible_guard = 1;
retval = routerstatus_parse_guardfraction(guardfraction_str_bad2,
NULL, NULL,
&rs_bad2);
tt_int_op(retval, ==, -1);
tt_assert(!rs_bad2.has_guardfraction);
}
done:
;
}
/* Make sure that we use GuardFraction information when we should,
according to the torrc option and consensus parameter. */
static void
test_should_apply_guardfraction(void *arg)
{
networkstatus_t vote_enabled, vote_disabled, vote_missing;
or_options_t *options = get_options_mutable();
(void) arg;
{ /* Fill the votes for later */
/* This one suggests enabled GuardFraction. */
memset(&vote_enabled, 0, sizeof(vote_enabled));
vote_enabled.net_params = smartlist_new();
smartlist_split_string(vote_enabled.net_params,
"UseGuardFraction=1", NULL, 0, 0);
/* This one suggests disabled GuardFraction. */
memset(&vote_disabled, 0, sizeof(vote_disabled));
vote_disabled.net_params = smartlist_new();
smartlist_split_string(vote_disabled.net_params,
"UseGuardFraction=0", NULL, 0, 0);
/* This one doesn't have GuardFraction at all. */
memset(&vote_missing, 0, sizeof(vote_missing));
vote_missing.net_params = smartlist_new();
smartlist_split_string(vote_missing.net_params,
"leon=trout", NULL, 0, 0);
}
/* If torrc option is set to yes, we should always use
* guardfraction.*/
options->UseGuardFraction = 1;
tt_int_op(should_apply_guardfraction(&vote_disabled), ==, 1);
/* If torrc option is set to no, we should never use
* guardfraction.*/
options->UseGuardFraction = 0;
tt_int_op(should_apply_guardfraction(&vote_enabled), ==, 0);
/* Now let's test torrc option set to auto. */
options->UseGuardFraction = -1;
/* If torrc option is set to auto, and consensus parameter is set to
* yes, we should use guardfraction. */
tt_int_op(should_apply_guardfraction(&vote_enabled), ==, 1);
/* If torrc option is set to auto, and consensus parameter is set to
* no, we should use guardfraction. */
tt_int_op(should_apply_guardfraction(&vote_disabled), ==, 0);
/* If torrc option is set to auto, and consensus parameter is not
* set, we should fallback to "no". */
tt_int_op(should_apply_guardfraction(&vote_missing), ==, 0);
done:
SMARTLIST_FOREACH(vote_enabled.net_params, char *, cp, tor_free(cp));
SMARTLIST_FOREACH(vote_disabled.net_params, char *, cp, tor_free(cp));
SMARTLIST_FOREACH(vote_missing.net_params, char *, cp, tor_free(cp));
smartlist_free(vote_enabled.net_params);
smartlist_free(vote_disabled.net_params);
smartlist_free(vote_missing.net_params);
}
struct testcase_t guardfraction_tests[] = {
{ "parse_guardfraction_file_bad", test_parse_guardfraction_file_bad,
TT_FORK, NULL, NULL },
{ "parse_guardfraction_file_good", test_parse_guardfraction_file_good,
TT_FORK, NULL, NULL },
{ "parse_guardfraction_consensus", test_parse_guardfraction_consensus,
TT_FORK, NULL, NULL },
{ "get_guardfraction_bandwidth", test_get_guardfraction_bandwidth,
TT_FORK, NULL, NULL },
{ "should_apply_guardfraction", test_should_apply_guardfraction,
TT_FORK, NULL, NULL },
END_OF_TESTCASES
};

26
src/test/test_helpers.c Normal file
View File

@ -0,0 +1,26 @@
/* Copyright (c) 2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file test_helpers.c
* \brief Some helper functions to avoid code duplication in unit tests.
*/
#include "orconfig.h"
#include "or.h"
#include "test_helpers.h"
/* Return a statically allocated string representing yesterday's date
* in ISO format. We use it so that state file items are not found to
* be outdated. */
const char *
get_yesterday_date_str(void)
{
static char buf[ISO_TIME_LEN+1];
time_t yesterday = time(NULL) - 24*60*60;
format_iso_time(buf, yesterday);
return buf;
}

10
src/test/test_helpers.h Normal file
View File

@ -0,0 +1,10 @@
/* Copyright (c) 2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TOR_TEST_HELPERS_H
#define TOR_TEST_HELPERS_H
const char *get_yesterday_date_str(void);
#endif

View File

@ -6,5 +6,7 @@
void helper_setup_fake_routerlist(void);
extern const char TEST_DESCRIPTORS[];
#endif