From 997f779a7f05540e5f564b4d121706c4a7069fb2 Mon Sep 17 00:00:00 2001 From: Matthew Finkel Date: Sun, 8 Feb 2015 06:51:51 +0000 Subject: Add new DirCache configuration option This will give relay operators the ability of disabling the caching of directory data. In general, this should not be necessary, but on some lower-resource systems it may beneficial. --- doc/tor.1.txt | 6 +++ src/or/config.c | 61 +++++++++++++++++++++++++ src/or/config.h | 2 + src/or/or.h | 4 ++ src/or/router.c | 4 +- src/test/test_options.c | 115 +++++++++++++++++++++++++++++++++++++++++++++--- 6 files changed, 186 insertions(+), 6 deletions(-) diff --git a/doc/tor.1.txt b/doc/tor.1.txt index f173a97aa3..021353dcb0 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -1987,6 +1987,12 @@ if DirPort is non-zero): except that port specifiers are ignored. Any address not matched by some entry in the policy is accepted. +[[DirCache]] **DirCache** **0**|**1**:: + When this option is set, Tor caches all current directory documents and + accepts client requests for them. Setting DirPort is not required for this, + because clients connect via the ORPort by default. Setting either DirPort + or BridgeRelay and setting DirCache to 0 is not supported. (Default: 1) + DIRECTORY AUTHORITY SERVER OPTIONS ---------------------------------- diff --git a/src/or/config.c b/src/or/config.c index 9ec47d2459..f9cab9f670 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -222,6 +222,7 @@ static config_var_t option_vars_[] = { V(DirPortFrontPage, FILENAME, NULL), VAR("DirReqStatistics", BOOL, DirReqStatistics_option, "1"), VAR("DirAuthority", LINELIST, DirAuthorities, NULL), + V(DirCache, BOOL, "1"), V(DirAuthorityFallbackRate, DOUBLE, "1.0"), V(DisableAllSwap, BOOL, "0"), V(DisableDebuggerAttachment, BOOL, "1"), @@ -3457,6 +3458,24 @@ options_validate(or_options_t *old_options, or_options_t *options, REJECT("AccountingRule must be 'sum' or 'max'"); } + if (options->DirPort_set && !options->DirCache) { + REJECT("DirPort configured but DirCache disabled. DirPort requires " + "DirCache."); + } + + if (options->BridgeRelay && !options->DirCache) { + REJECT("We're a bridge but DirCache is disabled. BridgeRelay requires " + "DirCache."); + } + + if (server_mode(options)) { + char *msg = NULL; + if (have_enough_mem_for_dircache(options, 0, &msg)) { + log_warn(LD_CONFIG, "%s", msg); + tor_free(msg); + } + } + if (options->HTTPProxy) { /* parse it now */ if (tor_addr_port_lookup(options->HTTPProxy, &options->HTTPProxyAddr, &options->HTTPProxyPort) < 0) @@ -4065,6 +4084,48 @@ compute_real_max_mem_in_queues(const uint64_t val, int log_guess) } } +/* If we have less than 300 MB suggest disabling dircache */ +#define DIRCACHE_MIN_MB_BANDWIDTH 300 +#define DIRCACHE_MIN_BANDWIDTH (DIRCACHE_MIN_MB_BANDWIDTH*ONE_MEGABYTE) +#define STRINGIFY(val) #val + +/** Create a warning message for emitting if we are a dircache but may not have + * enough system memory, or if we are not a dircache but probably should be. + * Return -1 when a message is returned in *msg*, else return 0. */ +STATIC int +have_enough_mem_for_dircache(const or_options_t *options, size_t total_mem, + char **msg) +{ + *msg = NULL; + if (total_mem == 0) { + if (get_total_system_memory(&total_mem) < 0) + total_mem = options->MaxMemInQueues; + } + if (options->DirCache) { + if (total_mem < DIRCACHE_MIN_BANDWIDTH) { + if (options->BridgeRelay) { + *msg = strdup("Running a Bridge with less than " + STRINGIFY(DIRCACHE_MIN_MB_BANDWIDTH) " MB of memory is " + "not recommended."); + } else { + *msg = strdup("Being a directory cache (default) with less than " + STRINGIFY(DIRCACHE_MIN_MB_BANDWIDTH) " MB of memory is " + "not recommended and may consume most of the available " + "resources, consider disabling this functionality by " + "setting the DirCache option to 0."); + } + } + } else { + if (total_mem >= DIRCACHE_MIN_BANDWIDTH) { + *msg = strdup("DirCache is disabled and we are configured as a " + "relay. This may disqualify us from becoming a guard in the " + "future."); + } + } + return *msg == NULL ? 0 : -1; +} +#undef STRINGIFY + /** Helper: return true iff s1 and s2 are both NULL, or both non-NULL * equal strings. */ static int diff --git a/src/or/config.h b/src/or/config.h index bfdd1694eb..6e08f9d178 100644 --- a/src/or/config.h +++ b/src/or/config.h @@ -158,6 +158,8 @@ STATIC int parse_dir_authority_line(const char *line, dirinfo_type_t required_type, int validate_only); STATIC int parse_dir_fallback_line(const char *line, int validate_only); +STATIC int have_enough_mem_for_dircache(const or_options_t *options, + size_t total_mem, char **msg); #endif #endif diff --git a/src/or/or.h b/src/or/or.h index 3cb1e7d7ef..89c539817f 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -3969,6 +3969,10 @@ typedef struct { /** Should we fetch our dir info at the start of the consensus period? */ int FetchDirInfoExtraEarly; + int DirCache; /**< Cache all directory documents and accept requests via + * tunnelled dir conns from clients. If 1, enabled (default); + * If 0, disabled. */ + char *VirtualAddrNetworkIPv4; /**< Address and mask to hand out for virtual * MAPADDRESS requests for IPv4 addresses */ char *VirtualAddrNetworkIPv6; /**< Address and mask to hand out for virtual diff --git a/src/or/router.c b/src/or/router.c index 1927cfd38b..5e4f855410 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -1176,7 +1176,9 @@ router_should_be_directory_server(const or_options_t *options, int dir_port) int dir_server_mode(const or_options_t *options) { - return (server_mode(options) || options->DirPort_set) && + if (!options->DirCache) + return 0; + return (server_mode(options) || options->DirPort_set) && router_should_be_directory_server(options, 0); } diff --git a/src/test/test_options.c b/src/test/test_options.c index a8ebadb14b..7e47f33de6 100644 --- a/src/test/test_options.c +++ b/src/test/test_options.c @@ -69,22 +69,29 @@ clear_log_messages(void) messages = NULL; } +#define setup_options(opt,dflt) \ + do { \ + opt = options_new(); \ + opt->command = CMD_RUN_TOR; \ + options_init(opt); \ + \ + dflt = config_dup(&options_format, opt); \ + clear_log_messages(); \ + } while (0) + static void test_options_validate_impl(const char *configuration, const char *expect_errmsg, int expect_log_severity, const char *expect_log) { - or_options_t *opt = options_new(); + or_options_t *opt=NULL; or_options_t *dflt; config_line_t *cl=NULL; char *msg=NULL; int r; - opt->command = CMD_RUN_TOR; - options_init(opt); - dflt = config_dup(&options_format, opt); - clear_log_messages(); + setup_options(opt, dflt); r = config_get_lines(configuration, &cl, 1); tt_int_op(r, OP_EQ, 0); @@ -159,12 +166,110 @@ test_options_validate(void *arg) "ServerTransportOptions did not parse", LOG_WARN, "\"slingsnappy\" is not a k=v"); + WANT_ERR("DirPort 8080\nDirCache 0", + "DirPort configured but DirCache disabled."); + WANT_ERR("BridgeRelay 1\nDirCache 0", + "We're a bridge but DirCache is disabled."); + clear_log_messages(); return; } +#define MEGABYTEIFY(mb) (U64_LITERAL(mb) << 20) +static void +test_have_enough_mem_for_dircache(void *arg) +{ + (void)arg; + or_options_t *opt=NULL; + or_options_t *dflt; + config_line_t *cl=NULL; + char *msg=NULL;; + int r; + const char *configuration = "ORPort 8080\nDirCache 1", *expect_errmsg; + + setup_options(opt, dflt); + setup_log_callback(); + (void)dflt; + + r = config_get_lines(configuration, &cl, 1); + tt_int_op(r, OP_EQ, 0); + + r = config_assign(&options_format, opt, cl, 0, 0, &msg); + tt_int_op(r, OP_EQ, 0); + + /* 300 MB RAM available, DirCache enabled */ + r = have_enough_mem_for_dircache(opt, MEGABYTEIFY(300), &msg); + tt_int_op(r, OP_EQ, 0); + tt_assert(!msg); + + /* 200 MB RAM available, DirCache enabled */ + r = have_enough_mem_for_dircache(opt, MEGABYTEIFY(200), &msg); + tt_int_op(r, OP_EQ, -1); + expect_errmsg = "Being a directory cache (default) with less than "; + if (!strstr(msg, expect_errmsg)) { + TT_DIE(("Expected error message <%s> from <%s>, but got <%s>.", + expect_errmsg, configuration, msg)); + } + tor_free(msg); + + configuration = "ORPort 8080\nDirCache 1\nBridgeRelay 1"; + r = config_get_lines(configuration, &cl, 1); + tt_int_op(r, OP_EQ, 0); + + r = config_assign(&options_format, opt, cl, 0, 0, &msg); + tt_int_op(r, OP_EQ, 0); + + /* 300 MB RAM available, DirCache enabled, Bridge */ + r = have_enough_mem_for_dircache(opt, MEGABYTEIFY(300), &msg); + tt_int_op(r, OP_EQ, 0); + tt_assert(!msg); + + /* 200 MB RAM available, DirCache enabled, Bridge */ + r = have_enough_mem_for_dircache(opt, MEGABYTEIFY(200), &msg); + tt_int_op(r, OP_EQ, -1); + expect_errmsg = "Running a Bridge with less than "; + if (!strstr(msg, expect_errmsg)) { + TT_DIE(("Expected error message <%s> from <%s>, but got <%s>.", + expect_errmsg, configuration, msg)); + } + tor_free(msg); + + configuration = "ORPort 8080\nDirCache 0"; + r = config_get_lines(configuration, &cl, 1); + tt_int_op(r, OP_EQ, 0); + + r = config_assign(&options_format, opt, cl, 0, 0, &msg); + tt_int_op(r, OP_EQ, 0); + + /* 200 MB RAM available, DirCache disabled */ + r = have_enough_mem_for_dircache(opt, MEGABYTEIFY(200), &msg); + tt_int_op(r, OP_EQ, 0); + tt_assert(!msg); + + /* 300 MB RAM available, DirCache disabled */ + r = have_enough_mem_for_dircache(opt, MEGABYTEIFY(300), &msg); + tt_int_op(r, OP_EQ, -1); + expect_errmsg = "DirCache is disabled and we are configured as a "; + if (!strstr(msg, expect_errmsg)) { + TT_DIE(("Expected error message <%s> from <%s>, but got <%s>.", + expect_errmsg, configuration, msg)); + } + tor_free(msg); + + clear_log_messages(); + + done: + if (msg) + tor_free(msg); + tor_free(dflt); + tor_free(opt); + tor_free(cl); + return; +} + struct testcase_t options_tests[] = { { "validate", test_options_validate, TT_FORK, NULL, NULL }, + { "mem_dircache", test_have_enough_mem_for_dircache, TT_FORK, NULL, NULL }, END_OF_TESTCASES }; -- cgit v1.2.3-54-g00ecf