Merge branch 'maint-0.4.3'

This commit is contained in:
Nick Mathewson 2020-02-20 08:50:18 -05:00
commit 09fb7987c5
15 changed files with 371 additions and 49 deletions

5
changes/ticket33029 Normal file
View File

@ -0,0 +1,5 @@
o Major bugfixes (directory authority):
- Directory authorities will now send a 503 (not enough bandwidth) code to
clients when under bandwidth pressure. Known relays and other authorities
will always be answered regardless of the bandwidth situation. Fixes bug
33029; bugfix on 0.1.2.5-alpha.

View File

@ -2965,6 +2965,13 @@ on the public Tor network.
//Out of order because it logically belongs with the other CCs options. //Out of order because it logically belongs with the other CCs options.
[[AuthDirInvalidCCs]] **AuthDirInvalidCCs** __CC__,... + [[AuthDirInvalidCCs]] **AuthDirInvalidCCs** __CC__,... +
[[AuthDirRejectRequestsUnderLoad]] **AuthDirRejectRequestsUnderLoad** **0**|**1**::
If set, the directory authority will start rejecting directory requests
from non relay connections by sending a 503 error code if it is under
bandwidth pressure (reaching the configured limit if any). Relays will
always tried to be answered even if this is on. (Default: 1)
[[AuthDirRejectCCs]] **AuthDirRejectCCs** __CC__,...:: [[AuthDirRejectCCs]] **AuthDirRejectCCs** __CC__,...::
Authoritative directories only. These options contain a comma-separated Authoritative directories only. These options contain a comma-separated
list of country codes such that any server in one of those country codes list of country codes such that any server in one of those country codes
@ -3064,7 +3071,8 @@ on the public Tor network.
V3 authoritative directories only. Configures the server's preferred delay V3 authoritative directories only. Configures the server's preferred delay
between publishing its vote and assuming it has all the votes from all the between publishing its vote and assuming it has all the votes from all the
other authorities. Note that the actual time used is not the server's other authorities. Note that the actual time used is not the server's
preferred time, but the consensus of all preferences. (Default: 5 minutes) preferred time, but the consensus of all preferences. (Default: 5
minutes)
[[V3AuthVotingInterval]] **V3AuthVotingInterval** __N__ **minutes**|**hours**:: [[V3AuthVotingInterval]] **V3AuthVotingInterval** __N__ **minutes**|**hours**::
V3 authoritative directories only. Configures the server's preferred voting V3 authoritative directories only. Configures the server's preferred voting

View File

@ -71,7 +71,7 @@ problem function-size /src/core/mainloop/connection.c:connection_handle_read_imp
problem function-size /src/core/mainloop/connection.c:connection_buf_read_from_socket() 180 problem function-size /src/core/mainloop/connection.c:connection_buf_read_from_socket() 180
problem function-size /src/core/mainloop/connection.c:connection_handle_write_impl() 241 problem function-size /src/core/mainloop/connection.c:connection_handle_write_impl() 241
problem function-size /src/core/mainloop/connection.c:assert_connection_ok() 143 problem function-size /src/core/mainloop/connection.c:assert_connection_ok() 143
problem dependency-violation /src/core/mainloop/connection.c 44 problem dependency-violation /src/core/mainloop/connection.c 47
problem dependency-violation /src/core/mainloop/cpuworker.c 12 problem dependency-violation /src/core/mainloop/cpuworker.c 12
problem include-count /src/core/mainloop/mainloop.c 64 problem include-count /src/core/mainloop/mainloop.c 64
problem function-size /src/core/mainloop/mainloop.c:conn_close_if_marked() 108 problem function-size /src/core/mainloop/mainloop.c:conn_close_if_marked() 108

View File

@ -91,6 +91,7 @@
#include "feature/control/control.h" #include "feature/control/control.h"
#include "feature/control/control_events.h" #include "feature/control/control_events.h"
#include "feature/dirauth/authmode.h" #include "feature/dirauth/authmode.h"
#include "feature/dirauth/dirauth_config.h"
#include "feature/dircache/dirserv.h" #include "feature/dircache/dirserv.h"
#include "feature/dircommon/directory.h" #include "feature/dircommon/directory.h"
#include "feature/hibernate/hibernate.h" #include "feature/hibernate/hibernate.h"
@ -3137,7 +3138,7 @@ connection_mark_all_noncontrol_connections(void)
* uses pluggable transports, since we should then limit it even if it * uses pluggable transports, since we should then limit it even if it
* comes from an internal IP address. */ * comes from an internal IP address. */
static int static int
connection_is_rate_limited(connection_t *conn) connection_is_rate_limited(const connection_t *conn)
{ {
const or_options_t *options = get_options(); const or_options_t *options = get_options();
if (conn->linked) if (conn->linked)
@ -3272,14 +3273,14 @@ connection_bucket_write_limit(connection_t *conn, time_t now)
global_bucket_val, conn_bucket); global_bucket_val, conn_bucket);
} }
/** Return 1 if the global write buckets are low enough that we /** Return true iff the global write buckets are low enough that we
* shouldn't send <b>attempt</b> bytes of low-priority directory stuff * shouldn't send <b>attempt</b> bytes of low-priority directory stuff
* out to <b>conn</b>. Else return 0. * out to <b>conn</b>.
* Priority was 1 for v1 requests (directories and running-routers),
* and 2 for v2 requests and later (statuses and descriptors).
* *
* There are a lot of parameters we could use here: * If we are a directory authority, always answer dir requests thus true is
* always returned.
*
* Note: There are a lot of parameters we could use here:
* - global_relayed_write_bucket. Low is bad. * - global_relayed_write_bucket. Low is bad.
* - global_write_bucket. Low is bad. * - global_write_bucket. Low is bad.
* - bandwidthrate. Low is bad. * - bandwidthrate. Low is bad.
@ -3291,39 +3292,40 @@ connection_bucket_write_limit(connection_t *conn, time_t now)
* mean is "total directory bytes added to outbufs recently", but * mean is "total directory bytes added to outbufs recently", but
* that's harder to quantify and harder to keep track of. * that's harder to quantify and harder to keep track of.
*/ */
int bool
global_write_bucket_low(connection_t *conn, size_t attempt, int priority) connection_dir_is_global_write_low(const connection_t *conn, size_t attempt)
{ {
size_t smaller_bucket = size_t smaller_bucket =
MIN(token_bucket_rw_get_write(&global_bucket), MIN(token_bucket_rw_get_write(&global_bucket),
token_bucket_rw_get_write(&global_relayed_bucket)); token_bucket_rw_get_write(&global_relayed_bucket));
if (authdir_mode(get_options()) && priority>1)
return 0; /* there's always room to answer v2 if we're an auth dir */ /* Special case for authorities (directory only). */
if (authdir_mode_v3(get_options())) {
/* Are we configured to possibly reject requests under load? */
if (!dirauth_should_reject_requests_under_load()) {
/* Answer request no matter what. */
return false;
}
/* Always answer requests from a known relay which includes the other
* authorities. The following looks up the addresses for relays that we
* have their descriptor _and_ any configured trusted directories. */
if (nodelist_probably_contains_address(&conn->addr)) {
return false;
}
}
if (!connection_is_rate_limited(conn)) if (!connection_is_rate_limited(conn))
return 0; /* local conns don't get limited */ return false; /* local conns don't get limited */
if (smaller_bucket < attempt) if (smaller_bucket < attempt)
return 1; /* not enough space no matter the priority */ return true; /* not enough space. */
{ {
const time_t diff = approx_time() - write_buckets_last_empty_at; const time_t diff = approx_time() - write_buckets_last_empty_at;
if (diff <= 1) if (diff <= 1)
return 1; /* we're already hitting our limits, no more please */ return true; /* we're already hitting our limits, no more please */
} }
return false;
if (priority == 1) { /* old-style v1 query */
/* Could we handle *two* of these requests within the next two seconds? */
const or_options_t *options = get_options();
size_t can_write = (size_t) (smaller_bucket
+ 2*(options->RelayBandwidthRate ? options->RelayBandwidthRate :
options->BandwidthRate));
if (can_write < 2*attempt)
return 1;
} else { /* v2 query */
/* no further constraints yet */
}
return 0;
} }
/** When did we last tell the accounting subsystem about transmitted /** When did we last tell the accounting subsystem about transmitted

View File

@ -219,8 +219,8 @@ void connection_mark_all_noncontrol_listeners(void);
void connection_mark_all_noncontrol_connections(void); void connection_mark_all_noncontrol_connections(void);
ssize_t connection_bucket_write_limit(struct connection_t *conn, time_t now); ssize_t connection_bucket_write_limit(struct connection_t *conn, time_t now);
int global_write_bucket_low(struct connection_t *conn, bool connection_dir_is_global_write_low(const struct connection_t *conn,
size_t attempt, int priority); size_t attempt);
void connection_bucket_init(void); void connection_bucket_init(void);
void connection_bucket_adjust(const struct or_options_t *options); void connection_bucket_adjust(const struct or_options_t *options);
void connection_bucket_refill_all(time_t now, void connection_bucket_refill_all(time_t now,

View File

@ -27,6 +27,7 @@
#include "feature/dirauth/authmode.h" #include "feature/dirauth/authmode.h"
#include "feature/dirauth/bwauth.h" #include "feature/dirauth/bwauth.h"
#include "feature/dirauth/dirauth_periodic.h" #include "feature/dirauth/dirauth_periodic.h"
#include "feature/dirauth/dirauth_sys.h"
#include "feature/dirauth/dirvote.h" #include "feature/dirauth/dirvote.h"
#include "feature/dirauth/guardfraction.h" #include "feature/dirauth/guardfraction.h"
#include "feature/dirauth/dirauth_options_st.h" #include "feature/dirauth/dirauth_options_st.h"
@ -45,6 +46,14 @@
#define YES_IF_CHANGED_INT(opt) \ #define YES_IF_CHANGED_INT(opt) \
if (!CFG_EQ_INT(old_options, new_options, opt)) return 1; if (!CFG_EQ_INT(old_options, new_options, opt)) return 1;
/** Return true iff we are configured to reject request under load for non
* relay connections. */
bool
dirauth_should_reject_requests_under_load(void)
{
return !!dirauth_get_options()->AuthDirRejectRequestsUnderLoad;
}
/** /**
* Legacy validation/normalization function for the dirauth mode options in * Legacy validation/normalization function for the dirauth mode options in
* options. Uses old_options as the previous options. * options. Uses old_options as the previous options.

View File

@ -35,6 +35,8 @@ int options_act_dirauth_mtbf(const struct or_options_t *old_options);
int options_act_dirauth_stats(const struct or_options_t *old_options, int options_act_dirauth_stats(const struct or_options_t *old_options,
bool *print_notice_out); bool *print_notice_out);
bool dirauth_should_reject_requests_under_load(void);
extern const struct config_format_t dirauth_options_fmt; extern const struct config_format_t dirauth_options_fmt;
#else /* !defined(HAVE_MODULE_DIRAUTH) */ #else /* !defined(HAVE_MODULE_DIRAUTH) */
@ -78,6 +80,8 @@ options_validate_dirauth_mode(const struct or_options_t *old_options,
#define options_act_dirauth_stats(old_options, print_notice_out) \ #define options_act_dirauth_stats(old_options, print_notice_out) \
(((void)(old_options)),((void)(print_notice_out)),0) (((void)(old_options)),((void)(print_notice_out)),0)
#define dirauth_should_reject_requests_under_load() (false)
#endif /* defined(HAVE_MODULE_DIRAUTH) */ #endif /* defined(HAVE_MODULE_DIRAUTH) */
#endif /* !defined(TOR_FEATURE_DIRAUTH_DIRAUTH_CONFIG_H) */ #endif /* !defined(TOR_FEATURE_DIRAUTH_DIRAUTH_CONFIG_H) */

View File

@ -95,4 +95,11 @@ CONF_VAR(TestingMinFastFlagThreshold, MEMUNIT, 0, "0")
* versions? */ * versions? */
CONF_VAR(VersioningAuthoritativeDirectory, BOOL, 0, "0") CONF_VAR(VersioningAuthoritativeDirectory, BOOL, 0, "0")
/** Boolean: Under bandwidth pressure, if set to 1, the authority will always
* answer directory requests from relays but will start sending 503 error code
* for the other connections. If set to 0, all connections are considered the
* same and the authority will try to answer them all regardless of bandwidth
* pressure or not. */
CONF_VAR(AuthDirRejectRequestsUnderLoad, BOOL, 0, "1")
END_CONF_STRUCT(dirauth_options_t) END_CONF_STRUCT(dirauth_options_t)

View File

@ -957,7 +957,7 @@ handle_get_current_consensus(dir_connection_t *conn,
goto done; goto done;
} }
if (global_write_bucket_low(TO_CONN(conn), size_guess, 2)) { if (connection_dir_is_global_write_low(TO_CONN(conn), size_guess)) {
log_debug(LD_DIRSERV, log_debug(LD_DIRSERV,
"Client asked for network status lists, but we've been " "Client asked for network status lists, but we've been "
"writing too many bytes lately. Sending 503 Dir busy."); "writing too many bytes lately. Sending 503 Dir busy.");
@ -1066,7 +1066,7 @@ handle_get_status_vote(dir_connection_t *conn, const get_handler_args_t *args)
} }
}); });
if (global_write_bucket_low(TO_CONN(conn), estimated_len, 2)) { if (connection_dir_is_global_write_low(TO_CONN(conn), estimated_len)) {
write_short_http_response(conn, 503, "Directory busy, try again later"); write_short_http_response(conn, 503, "Directory busy, try again later");
goto vote_done; goto vote_done;
} }
@ -1125,7 +1125,7 @@ handle_get_microdesc(dir_connection_t *conn, const get_handler_args_t *args)
write_short_http_response(conn, 404, "Not found"); write_short_http_response(conn, 404, "Not found");
goto done; goto done;
} }
if (global_write_bucket_low(TO_CONN(conn), size_guess, 2)) { if (connection_dir_is_global_write_low(TO_CONN(conn), size_guess)) {
log_info(LD_DIRSERV, log_info(LD_DIRSERV,
"Client asked for server descriptors, but we've been " "Client asked for server descriptors, but we've been "
"writing too many bytes lately. Sending 503 Dir busy."); "writing too many bytes lately. Sending 503 Dir busy.");
@ -1223,7 +1223,7 @@ handle_get_descriptor(dir_connection_t *conn, const get_handler_args_t *args)
msg = "Not found"; msg = "Not found";
write_short_http_response(conn, 404, msg); write_short_http_response(conn, 404, msg);
} else { } else {
if (global_write_bucket_low(TO_CONN(conn), size_guess, 2)) { if (connection_dir_is_global_write_low(TO_CONN(conn), size_guess)) {
log_info(LD_DIRSERV, log_info(LD_DIRSERV,
"Client asked for server descriptors, but we've been " "Client asked for server descriptors, but we've been "
"writing too many bytes lately. Sending 503 Dir busy."); "writing too many bytes lately. Sending 503 Dir busy.");
@ -1319,9 +1319,8 @@ handle_get_keys(dir_connection_t *conn, const get_handler_args_t *args)
SMARTLIST_FOREACH(certs, authority_cert_t *, c, SMARTLIST_FOREACH(certs, authority_cert_t *, c,
len += c->cache_info.signed_descriptor_len); len += c->cache_info.signed_descriptor_len);
if (global_write_bucket_low(TO_CONN(conn), if (connection_dir_is_global_write_low(TO_CONN(conn),
compress_method != NO_METHOD ? len/2 : len, compress_method != NO_METHOD ? len/2 : len)) {
2)) {
write_short_http_response(conn, 503, "Directory busy, try again later"); write_short_http_response(conn, 503, "Directory busy, try again later");
goto keys_done; goto keys_done;
} }

View File

@ -49,6 +49,37 @@ static smartlist_t *trusted_dir_servers = NULL;
* and all fallback directory servers. */ * and all fallback directory servers. */
static smartlist_t *fallback_dir_servers = NULL; static smartlist_t *fallback_dir_servers = NULL;
/** Helper: From a given trusted directory entry, add the v4 or/and v6 address
* to the nodelist address set. */
static void
add_trusted_dir_to_nodelist_addr_set(const dir_server_t *dir)
{
tor_assert(dir);
tor_assert(dir->is_authority);
/* Add IPv4 and then IPv6 if applicable. */
nodelist_add_addr4_to_address_set(dir->addr);
if (!tor_addr_is_null(&dir->ipv6_addr)) {
nodelist_add_addr6_to_address_set(&dir->ipv6_addr);
}
}
/** Go over the trusted directory server list and add their address(es) to the
* nodelist address set. This is called everytime a new consensus is set. */
MOCK_IMPL(void,
dirlist_add_trusted_dir_addresses, (void))
{
if (!trusted_dir_servers) {
return;
}
SMARTLIST_FOREACH_BEGIN(trusted_dir_servers, const dir_server_t *, ent) {
if (ent->is_authority) {
add_trusted_dir_to_nodelist_addr_set(ent);
}
} SMARTLIST_FOREACH_END(ent);
}
/** Return the number of directory authorities whose type matches some bit set /** Return the number of directory authorities whose type matches some bit set
* in <b>type</b> */ * in <b>type</b> */
int int

View File

@ -44,4 +44,6 @@ void dir_server_add(dir_server_t *ent);
void clear_dir_servers(void); void clear_dir_servers(void);
void dirlist_free_all(void); void dirlist_free_all(void);
MOCK_DECL(void, dirlist_add_trusted_dir_addresses, (void));
#endif /* !defined(TOR_DIRLIST_H) */ #endif /* !defined(TOR_DIRLIST_H) */

View File

@ -455,22 +455,43 @@ node_add_to_address_set(const node_t *node)
if (node->rs) { if (node->rs) {
if (node->rs->addr) if (node->rs->addr)
address_set_add_ipv4h(the_nodelist->node_addrs, node->rs->addr); nodelist_add_addr4_to_address_set(node->rs->addr);
if (!tor_addr_is_null(&node->rs->ipv6_addr)) if (!tor_addr_is_null(&node->rs->ipv6_addr))
address_set_add(the_nodelist->node_addrs, &node->rs->ipv6_addr); nodelist_add_addr6_to_address_set(&node->rs->ipv6_addr);
} }
if (node->ri) { if (node->ri) {
if (node->ri->addr) if (node->ri->addr)
address_set_add_ipv4h(the_nodelist->node_addrs, node->ri->addr); nodelist_add_addr4_to_address_set(node->ri->addr);
if (!tor_addr_is_null(&node->ri->ipv6_addr)) if (!tor_addr_is_null(&node->ri->ipv6_addr))
address_set_add(the_nodelist->node_addrs, &node->ri->ipv6_addr); nodelist_add_addr6_to_address_set(&node->ri->ipv6_addr);
} }
if (node->md) { if (node->md) {
if (!tor_addr_is_null(&node->md->ipv6_addr)) if (!tor_addr_is_null(&node->md->ipv6_addr))
address_set_add(the_nodelist->node_addrs, &node->md->ipv6_addr); nodelist_add_addr6_to_address_set(&node->md->ipv6_addr);
} }
} }
/** Add the given v4 address into the nodelist address set. */
void
nodelist_add_addr4_to_address_set(const uint32_t addr)
{
if (!the_nodelist || !the_nodelist->node_addrs || addr == 0) {
return;
}
address_set_add_ipv4h(the_nodelist->node_addrs, addr);
}
/** Add the given v6 address into the nodelist address set. */
void
nodelist_add_addr6_to_address_set(const tor_addr_t *addr)
{
if (BUG(!addr) || tor_addr_is_null(addr) || tor_addr_is_v4(addr) ||
!the_nodelist || !the_nodelist->node_addrs) {
return;
}
address_set_add(the_nodelist->node_addrs, addr);
}
/** Return true if <b>addr</b> is the address of some node in the nodelist. /** Return true if <b>addr</b> is the address of some node in the nodelist.
* If not, probably return false. */ * If not, probably return false. */
int int
@ -612,9 +633,12 @@ nodelist_set_consensus(networkstatus_t *ns)
SMARTLIST_FOREACH(the_nodelist->nodes, node_t *, node, SMARTLIST_FOREACH(the_nodelist->nodes, node_t *, node,
node->rs = NULL); node->rs = NULL);
/* Conservatively estimate that every node will have 2 addresses. */ /* Conservatively estimate that every node will have 2 addresses (v4 and
const int estimated_addresses = smartlist_len(ns->routerstatus_list) * * v6). Then we add the number of configured trusted authorities we have. */
get_estimated_address_per_node(); int estimated_addresses = smartlist_len(ns->routerstatus_list) *
get_estimated_address_per_node();
estimated_addresses += (get_n_authorities(V3_DIRINFO & BRIDGE_DIRINFO) *
get_estimated_address_per_node());
address_set_free(the_nodelist->node_addrs); address_set_free(the_nodelist->node_addrs);
the_nodelist->node_addrs = address_set_new(estimated_addresses); the_nodelist->node_addrs = address_set_new(estimated_addresses);
@ -665,6 +689,9 @@ nodelist_set_consensus(networkstatus_t *ns)
SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) { SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) {
node_add_to_address_set(node); node_add_to_address_set(node);
} SMARTLIST_FOREACH_END(node); } SMARTLIST_FOREACH_END(node);
/* Then, add all trusted configured directories. Some might not be in the
* consensus so make sure we know them. */
dirlist_add_trusted_dir_addresses();
if (! authdir) { if (! authdir) {
SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) { SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) {

View File

@ -35,6 +35,8 @@ node_t *nodelist_add_microdesc(microdesc_t *md);
void nodelist_set_consensus(networkstatus_t *ns); void nodelist_set_consensus(networkstatus_t *ns);
void nodelist_ensure_freshness(networkstatus_t *ns); void nodelist_ensure_freshness(networkstatus_t *ns);
int nodelist_probably_contains_address(const tor_addr_t *addr); int nodelist_probably_contains_address(const tor_addr_t *addr);
void nodelist_add_addr4_to_address_set(const uint32_t addr);
void nodelist_add_addr6_to_address_set(const tor_addr_t *addr);
void nodelist_remove_microdesc(const char *identity_digest, microdesc_t *md); void nodelist_remove_microdesc(const char *identity_digest, microdesc_t *md);
void nodelist_remove_routerinfo(routerinfo_t *ri); void nodelist_remove_routerinfo(routerinfo_t *ri);

View File

@ -4,6 +4,7 @@
#include "core/or/or.h" #include "core/or/or.h"
#include "lib/crypt_ops/crypto_rand.h" #include "lib/crypt_ops/crypto_rand.h"
#include "core/or/address_set.h" #include "core/or/address_set.h"
#include "feature/nodelist/dirlist.h"
#include "feature/nodelist/microdesc.h" #include "feature/nodelist/microdesc.h"
#include "feature/nodelist/networkstatus.h" #include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h" #include "feature/nodelist/nodelist.h"
@ -31,6 +32,12 @@ mock_networkstatus_get_latest_consensus_by_flavor(consensus_flavor_t f)
return dummy_ns; return dummy_ns;
} }
static void
mock_dirlist_add_trusted_dir_addresses(void)
{
return;
}
/* Number of address a single node_t can have. Default to the production /* Number of address a single node_t can have. Default to the production
* value. This is to control the size of the bloom filter. */ * value. This is to control the size of the bloom filter. */
static int addr_per_node = 2; static int addr_per_node = 2;
@ -98,6 +105,8 @@ test_nodelist(void *arg)
mock_networkstatus_get_latest_consensus_by_flavor); mock_networkstatus_get_latest_consensus_by_flavor);
MOCK(get_estimated_address_per_node, MOCK(get_estimated_address_per_node,
mock_get_estimated_address_per_node); mock_get_estimated_address_per_node);
MOCK(dirlist_add_trusted_dir_addresses,
mock_dirlist_add_trusted_dir_addresses);
dummy_ns = tor_malloc_zero(sizeof(*dummy_ns)); dummy_ns = tor_malloc_zero(sizeof(*dummy_ns));
dummy_ns->flavor = FLAV_MICRODESC; dummy_ns->flavor = FLAV_MICRODESC;
@ -113,7 +122,10 @@ test_nodelist(void *arg)
* (the_nodelist->node_addrs) so we will fail the contain test rarely. */ * (the_nodelist->node_addrs) so we will fail the contain test rarely. */
addr_per_node = 1024; addr_per_node = 1024;
/* No node no nothing. The lookups should be empty. */ /* No node no nothing. The lookups should be empty. We've mocked the
* dirlist_add_trusted_dir_addresses in order for _no_ authorities to be
* added to the filter else it makes this test to trigger many false
* positive. */
nodelist_set_consensus(dummy_ns); nodelist_set_consensus(dummy_ns);
/* The address set should be empty. */ /* The address set should be empty. */
@ -167,6 +179,7 @@ test_nodelist(void *arg)
UNMOCK(networkstatus_get_latest_consensus); UNMOCK(networkstatus_get_latest_consensus);
UNMOCK(networkstatus_get_latest_consensus_by_flavor); UNMOCK(networkstatus_get_latest_consensus_by_flavor);
UNMOCK(get_estimated_address_per_node); UNMOCK(get_estimated_address_per_node);
UNMOCK(dirlist_add_trusted_dir_addresses);
} }
struct testcase_t address_set_tests[] = { struct testcase_t address_set_tests[] = {

View File

@ -6,18 +6,70 @@
* \brief tests for bandwidth management / token bucket functions * \brief tests for bandwidth management / token bucket functions
*/ */
#define CONFIG_PRIVATE
#define CONNECTION_PRIVATE
#define DIRAUTH_SYS_PRIVATE
#define TOKEN_BUCKET_PRIVATE #define TOKEN_BUCKET_PRIVATE
#include "core/or/or.h" #include "core/or/or.h"
#include "test/test.h"
#include "app/config/config.h"
#include "core/mainloop/connection.h"
#include "feature/dirauth/dirauth_sys.h"
#include "feature/dircommon/directory.h"
#include "feature/nodelist/microdesc.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
#include "feature/nodelist/routerlist.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/evloop/token_bucket.h" #include "lib/evloop/token_bucket.h"
#include "test/test.h"
#include "test/test_helpers.h"
#include "app/config/or_options_st.h"
#include "core/or/connection_st.h"
#include "feature/dirauth/dirauth_options_st.h"
#include "feature/nodelist/microdesc_st.h"
#include "feature/nodelist/networkstatus_st.h"
#include "feature/nodelist/routerinfo_st.h"
#include "feature/nodelist/routerstatus_st.h"
// an imaginary time, in timestamp units. Chosen so it will roll over. // an imaginary time, in timestamp units. Chosen so it will roll over.
static const uint32_t START_TS = UINT32_MAX-10; static const uint32_t START_TS = UINT32_MAX-10;
static const int32_t KB = 1024; static const int32_t KB = 1024;
static const uint32_t GB = (UINT64_C(1) << 30); static const uint32_t GB = (UINT64_C(1) << 30);
static or_options_t mock_options;
static const or_options_t *
mock_get_options(void)
{
return &mock_options;
}
static networkstatus_t *dummy_ns = NULL;
static networkstatus_t *
mock_networkstatus_get_latest_consensus(void)
{
return dummy_ns;
}
static networkstatus_t *
mock_networkstatus_get_latest_consensus_by_flavor(consensus_flavor_t f)
{
tor_assert(f == FLAV_MICRODESC);
return dummy_ns;
}
/* Number of address a single node_t can have. Default to the production
* value. This is to control the size of the bloom filter. */
static int addr_per_node = 2;
static int
mock_get_estimated_address_per_node(void)
{
return addr_per_node;
}
static void static void
test_bwmgt_token_buf_init(void *arg) test_bwmgt_token_buf_init(void *arg)
{ {
@ -220,8 +272,167 @@ test_bwmgt_token_buf_helpers(void *arg)
; ;
} }
static void
test_bwmgt_dir_conn_global_write_low(void *arg)
{
bool ret;
int addr_family;
connection_t *conn = NULL;
routerstatus_t *rs = NULL; microdesc_t *md = NULL; routerinfo_t *ri = NULL;
tor_addr_t relay_addr;
dirauth_options_t *dirauth_opts = NULL;
(void) arg;
memset(&mock_options, 0, sizeof(or_options_t));
MOCK(networkstatus_get_latest_consensus,
mock_networkstatus_get_latest_consensus);
MOCK(networkstatus_get_latest_consensus_by_flavor,
mock_networkstatus_get_latest_consensus_by_flavor);
MOCK(get_estimated_address_per_node,
mock_get_estimated_address_per_node);
/*
* The following is rather complex but that is what it takes to add a dummy
* consensus with a valid routerlist which will populate our node address
* set that we need to lookup to test the known relay code path.
*
* We MUST do that before we MOCK(get_options) else it is another world of
* complexity.
*/
/* This will be the address of our relay. */
tor_addr_parse(&relay_addr, "1.2.3.4");
/* We'll now add a relay into our routerlist and see if we let it. */
dummy_ns = tor_malloc_zero(sizeof(*dummy_ns));
dummy_ns->flavor = FLAV_MICRODESC;
dummy_ns->routerstatus_list = smartlist_new();
md = tor_malloc_zero(sizeof(*md));
ri = tor_malloc_zero(sizeof(*ri));
rs = tor_malloc_zero(sizeof(*rs));
crypto_rand(rs->identity_digest, sizeof(rs->identity_digest));
crypto_rand(md->digest, sizeof(md->digest));
memcpy(rs->descriptor_digest, md->digest, DIGEST256_LEN);
/* Set IP address. */
rs->addr = tor_addr_to_ipv4h(&relay_addr);
ri->addr = rs->addr;
/* Add the rs to the consensus becoming a node_t. */
smartlist_add(dummy_ns->routerstatus_list, rs);
/* Add all configured authorities (hardcoded) before we set the consensus so
* the address set exists. */
ret = consider_adding_dir_servers(&mock_options, &mock_options);
tt_int_op(ret, OP_EQ, 0);
/* This will make the nodelist bloom filter very large
* (the_nodelist->node_addrs) so we will fail the contain test rarely. */
addr_per_node = 1024;
nodelist_set_consensus(dummy_ns);
dirauth_opts = tor_malloc_zero(sizeof(dirauth_options_t));
dirauth_opts->AuthDirRejectRequestsUnderLoad = 0;
dirauth_set_options(dirauth_opts);
/* Ok, now time to control which options we use. */
MOCK(get_options, mock_get_options);
/* Set ourselves as an authoritative dir. */
mock_options.AuthoritativeDir = 1;
mock_options.V3AuthoritativeDir = 1;
mock_options.UseDefaultFallbackDirs = 0;
/* This will set our global bucket to 1 byte and thus we will hit the
* banwdith limit in our test. */
mock_options.BandwidthRate = 1;
mock_options.BandwidthBurst = 1;
/* Else an IPv4 address screams. */
mock_options.ClientUseIPv4 = 1;
mock_options.ClientUseIPv6 = 1;
/* Initialize the global buckets. */
connection_bucket_init();
/* The address "127.0.0.1" is set with this helper. */
conn = test_conn_get_connection(DIR_CONN_STATE_MIN_, CONN_TYPE_DIR,
DIR_PURPOSE_MIN_);
tt_assert(conn);
/* First try a non authority non relay IP thus a client but we are not
* configured to reject requests under load so we should get a false value
* that our limit is _not_ low. */
addr_family = tor_addr_parse(&conn->addr, "1.1.1.1");
tt_int_op(addr_family, OP_EQ, AF_INET);
ret = connection_dir_is_global_write_low(conn, INT_MAX);
tt_int_op(ret, OP_EQ, 0);
/* Now, we will reject requests under load so try again a non authority non
* relay IP thus a client. We should get a warning that our limit is too
* low. */
dirauth_opts->AuthDirRejectRequestsUnderLoad = 1;
addr_family = tor_addr_parse(&conn->addr, "1.1.1.1");
tt_int_op(addr_family, OP_EQ, AF_INET);
ret = connection_dir_is_global_write_low(conn, INT_MAX);
tt_int_op(ret, OP_EQ, 1);
/* Now, lets try with a connection address from moria1. It should always
* pass even though our limit is too low. */
addr_family = tor_addr_parse(&conn->addr, "128.31.0.39");
tt_int_op(addr_family, OP_EQ, AF_INET);
ret = connection_dir_is_global_write_low(conn, INT_MAX);
tt_int_op(ret, OP_EQ, 0);
/* IPv6 testing of gabelmoo. */
addr_family = tor_addr_parse(&conn->addr, "[2001:638:a000:4140::ffff:189]");
tt_int_op(addr_family, OP_EQ, AF_INET6);
ret = connection_dir_is_global_write_low(conn, INT_MAX);
tt_int_op(ret, OP_EQ, 0);
/* Lets retry with a known relay address. It should pass. Possible due to
* our consensus setting above. */
memcpy(&conn->addr, &relay_addr, sizeof(tor_addr_t));
ret = connection_dir_is_global_write_low(conn, INT_MAX);
tt_int_op(ret, OP_EQ, 0);
/* Lets retry with a random IP that is not an authority nor a relay. */
addr_family = tor_addr_parse(&conn->addr, "1.2.3.4");
tt_int_op(addr_family, OP_EQ, AF_INET);
ret = connection_dir_is_global_write_low(conn, INT_MAX);
tt_int_op(ret, OP_EQ, 0);
/* Finally, just make sure it still denies an IP if we are _not_ a v3
* directory authority. */
mock_options.V3AuthoritativeDir = 0;
addr_family = tor_addr_parse(&conn->addr, "1.2.3.4");
tt_int_op(addr_family, OP_EQ, AF_INET);
ret = connection_dir_is_global_write_low(conn, INT_MAX);
tt_int_op(ret, OP_EQ, 1);
/* Random IPv6 should not be allowed. */
addr_family = tor_addr_parse(&conn->addr, "[CAFE::ACAB]");
tt_int_op(addr_family, OP_EQ, AF_INET6);
ret = connection_dir_is_global_write_low(conn, INT_MAX);
tt_int_op(ret, OP_EQ, 1);
done:
connection_free_minimal(conn);
routerstatus_free(rs); routerinfo_free(ri); microdesc_free(md);
smartlist_clear(dummy_ns->routerstatus_list);
networkstatus_vote_free(dummy_ns);
UNMOCK(get_estimated_address_per_node);
UNMOCK(networkstatus_get_latest_consensus);
UNMOCK(networkstatus_get_latest_consensus_by_flavor);
UNMOCK(get_options);
}
#define BWMGT(name) \ #define BWMGT(name) \
{ #name, test_bwmgt_ ## name , 0, NULL, NULL } { #name, test_bwmgt_ ## name , TT_FORK, NULL, NULL }
struct testcase_t bwmgt_tests[] = { struct testcase_t bwmgt_tests[] = {
BWMGT(token_buf_init), BWMGT(token_buf_init),
@ -229,5 +440,7 @@ struct testcase_t bwmgt_tests[] = {
BWMGT(token_buf_dec), BWMGT(token_buf_dec),
BWMGT(token_buf_refill), BWMGT(token_buf_refill),
BWMGT(token_buf_helpers), BWMGT(token_buf_helpers),
BWMGT(dir_conn_global_write_low),
END_OF_TESTCASES END_OF_TESTCASES
}; };