commit a2a8adeee5a3176c3173a85c903c72c5c03ac729 Merge: 0a6beb9 aa178aa Author: Nick Mathewson nickm@torproject.org Date: Fri Feb 25 11:24:35 2011 -0500
Merge remote branch 'origin/maint-0.2.2'
changes/1863_bwhist | 16 +++ src/or/config.c | 4 + src/or/or.h | 8 ++- src/or/rephist.c | 295 +++++++++++++++++++++++++++----------------------- 4 files changed, 186 insertions(+), 137 deletions(-)
diff --combined src/or/config.c index aa4d974,4d2c872..769831b --- a/src/or/config.c +++ b/src/or/config.c @@@ -44,8 -44,6 +44,8 @@@ typedef enum config_type_t CONFIG_TYPE_FILENAME, /**< A filename: some prefixes get expanded. */ CONFIG_TYPE_UINT, /**< A non-negative integer less than MAX_INT */ CONFIG_TYPE_INTERVAL, /**< A number of seconds, with optional units*/ + CONFIG_TYPE_MSEC_INTERVAL,/**< A number of milliseconds, with optional + * units */ CONFIG_TYPE_MEMUNIT, /**< A number of bytes, with optional units*/ CONFIG_TYPE_DOUBLE, /**< A floating-point value */ CONFIG_TYPE_BOOL, /**< A boolean value, expressed as 0 or 1. */ @@@ -201,7 -199,6 +201,7 @@@ static config_var_t _option_vars[] = V(ClientOnly, BOOL, "0"), V(ConsensusParams, STRING, NULL), V(ConnLimit, UINT, "1000"), + V(ConnDirectionStatistics, BOOL, "0"), V(ConstrainedSockets, BOOL, "0"), V(ConstrainedSockSize, MEMUNIT, "8192"), V(ContactInfo, STRING, NULL), @@@ -225,10 -222,9 +225,10 @@@ OBSOLETE("DirRecordUsageGranularity"), OBSOLETE("DirRecordUsageRetainIPs"), OBSOLETE("DirRecordUsageSaveInterval"), - V(DirReqStatistics, BOOL, "0"), + V(DirReqStatistics, BOOL, "1"), VAR("DirServer", LINELIST, DirServers, NULL), V(DisableAllSwap, BOOL, "0"), + V(DisableIOCP, BOOL, "1"), V(DNSPort, UINT, "0"), V(DNSListenAddress, LINELIST, NULL), V(DownloadExtraInfo, BOOL, "0"), @@@ -243,7 -239,7 +243,7 @@@ V(ExitPolicy, LINELIST, NULL), V(ExitPolicyRejectPrivate, BOOL, "1"), V(ExitPortStatistics, BOOL, "0"), - V(ExtraInfoStatistics, BOOL, "0"), + V(ExtraInfoStatistics, BOOL, "1"),
#if defined (WINCE) V(FallbackNetworkstatusFile, FILENAME, "fallback-consensus"), @@@ -267,7 -263,6 +267,7 @@@ #endif OBSOLETE("Group"), V(HardwareAccel, BOOL, "0"), + V(HeartbeatPeriod, INTERVAL, "6 hours"), V(AccelName, STRING, NULL), V(AccelDir, FILENAME, NULL), V(HashedControlPassword, LINELIST, NULL), @@@ -297,7 -292,6 +297,7 @@@ OBSOLETE("LinkPadding"), OBSOLETE("LogLevel"), OBSOLETE("LogFile"), + V(LogTimeGranularity, MSEC_INTERVAL, "1 second"), V(LongLivedPorts, CSV, "21,22,706,1863,5050,5190,5222,5223,6667,6697,8300"), VAR("MapAddress", LINELIST, AddressMap, NULL), @@@ -314,7 -308,7 +314,7 @@@ V(WarnUnsafeSocks, BOOL, "1"), OBSOLETE("NoPublish"), VAR("NodeFamily", LINELIST, NodeFamilies, NULL), - V(NumCPUs, UINT, "1"), + V(NumCPUs, UINT, "0"), V(NumEntryGuards, UINT, "3"), V(ORListenAddress, LINELIST, NULL), V(ORPort, UINT, "0"), @@@ -324,8 -318,6 +324,8 @@@ V(PerConnBWRate, MEMUNIT, "0"), V(PidFile, STRING, NULL), V(TestingTorNetwork, BOOL, "0"), + V(PortForwarding, BOOL, "0"), + V(PortForwardingHelper, FILENAME, "tor-fw-helper"), V(PreferTunneledDirConns, BOOL, "1"), V(ProtocolWarnings, BOOL, "0"), V(PublishServerDescriptor, CSV, "1"), @@@ -394,7 -386,6 +394,7 @@@ VAR("VersioningAuthoritativeDirectory",BOOL,VersioningAuthoritativeDir, "0"), V(VirtualAddrNetwork, STRING, "127.192.0.0/10"), V(WarnPlaintextPorts, CSV, "23,109,110,143"), + V(_UseFilteringSSLBufferevents, BOOL, "0"), VAR("__ReloadTorrcOnSIGHUP", BOOL, ReloadTorrcOnSIGHUP, "1"), VAR("__AllDirActionsPrivate", BOOL, AllDirActionsPrivate, "0"), VAR("__DisablePredictedCircuits",BOOL,DisablePredictedCircuits, "0"), @@@ -455,15 -446,19 +455,19 @@@ static config_var_t _state_vars[] = V(BWHistoryReadEnds, ISOTIME, NULL), V(BWHistoryReadInterval, UINT, "900"), V(BWHistoryReadValues, CSV, ""), + V(BWHistoryReadMaxima, CSV, ""), V(BWHistoryWriteEnds, ISOTIME, NULL), V(BWHistoryWriteInterval, UINT, "900"), V(BWHistoryWriteValues, CSV, ""), + V(BWHistoryWriteMaxima, CSV, ""), V(BWHistoryDirReadEnds, ISOTIME, NULL), V(BWHistoryDirReadInterval, UINT, "900"), V(BWHistoryDirReadValues, CSV, ""), + V(BWHistoryDirReadMaxima, CSV, ""), V(BWHistoryDirWriteEnds, ISOTIME, NULL), V(BWHistoryDirWriteInterval, UINT, "900"), V(BWHistoryDirWriteValues, CSV, ""), + V(BWHistoryDirWriteMaxima, CSV, ""),
V(TorVersion, STRING, NULL),
@@@ -567,9 -562,8 +571,9 @@@ static int is_listening_on_low_port(uin const config_line_t *listen_options);
static uint64_t config_parse_memunit(const char *s, int *ok); +static int config_parse_msec_interval(const char *s, int *ok); static int config_parse_interval(const char *s, int *ok); -static void init_libevent(void); +static void init_libevent(const or_options_t *options); static int opt_streq(const char *s1, const char *s2);
/** Magic value for or_options_t. */ @@@ -703,11 -697,6 +707,11 @@@ or_options_free(or_options_t *options return;
routerset_free(options->_ExcludeExitNodesUnion); + if (options->NodeFamilySets) { + SMARTLIST_FOREACH(options->NodeFamilySets, routerset_t *, + rs, routerset_free(rs)); + smartlist_free(options->NodeFamilySets); + } config_free(&options_format, options); }
@@@ -975,7 -964,7 +979,7 @@@ options_act_reversible(or_options_t *ol /* Set up libevent. (We need to do this before we can register the * listeners as listeners.) */ if (running_tor && !libevent_initialized) { - init_libevent(); + init_libevent(options); libevent_initialized = 1; }
@@@ -1251,17 -1240,6 +1255,17 @@@ options_act(or_options_t *old_options if (accounting_is_enabled(options)) configure_accounting(time(NULL));
+#ifdef USE_BUFFEREVENTS + /* If we're using the bufferevents implementation and our rate limits + * changed, we need to tell the rate-limiting system about it. */ + if (!old_options || + old_options->BandwidthRate != options->BandwidthRate || + old_options->BandwidthBurst != options->BandwidthBurst || + old_options->RelayBandwidthRate != options->RelayBandwidthRate || + old_options->RelayBandwidthBurst != options->RelayBandwidthBurst) + connection_bucket_init(); +#endif + /* parse RefuseUnknownExits tristate */ if (!strcmp(options->RefuseUnknownExits, "0")) options->RefuseUnknownExits_ = 0; @@@ -1373,54 -1351,44 +1377,54 @@@ tor_free(actual_fname); }
- if (options->DirReqStatistics && !geoip_is_loaded()) { - /* Check if GeoIP database could be loaded. */ - log_warn(LD_CONFIG, "Configured to measure directory request " - "statistics, but no GeoIP database found!"); - return -1; - } - - if (options->EntryStatistics) { - if (should_record_bridge_info(options)) { - /* Don't allow measuring statistics on entry guards when configured - * as bridge. */ - log_warn(LD_CONFIG, "Bridges cannot be configured to measure " - "additional GeoIP statistics as entry guards."); - return -1; - } else if (!geoip_is_loaded()) { - /* Check if GeoIP database could be loaded. */ - log_warn(LD_CONFIG, "Configured to measure entry node statistics, " - "but no GeoIP database found!"); - return -1; - } - } - if (options->CellStatistics || options->DirReqStatistics || - options->EntryStatistics || options->ExitPortStatistics) { + options->EntryStatistics || options->ExitPortStatistics || + options->ConnDirectionStatistics) { time_t now = time(NULL); + int print_notice = 0; if ((!old_options || !old_options->CellStatistics) && - options->CellStatistics) + options->CellStatistics) { rep_hist_buffer_stats_init(now); + print_notice = 1; + } if ((!old_options || !old_options->DirReqStatistics) && - options->DirReqStatistics) - geoip_dirreq_stats_init(now); + options->DirReqStatistics) { + if (geoip_is_loaded()) { + geoip_dirreq_stats_init(now); + print_notice = 1; + } else { + options->DirReqStatistics = 0; + /* Don't warn Tor clients, they don't use statistics */ + if (options->ORPort) + log_notice(LD_CONFIG, "Configured to measure directory request " + "statistics, but no GeoIP database found. " + "Please specify a GeoIP database using the " + "GeoIPFile option."); + } + } if ((!old_options || !old_options->EntryStatistics) && - options->EntryStatistics) - geoip_entry_stats_init(now); + options->EntryStatistics && !should_record_bridge_info(options)) { + if (geoip_is_loaded()) { + geoip_entry_stats_init(now); + print_notice = 1; + } else { + options->EntryStatistics = 0; + log_notice(LD_CONFIG, "Configured to measure entry node " + "statistics, but no GeoIP database found. " + "Please specify a GeoIP database using the " + "GeoIPFile option."); + } + } if ((!old_options || !old_options->ExitPortStatistics) && - options->ExitPortStatistics) + options->ExitPortStatistics) { rep_hist_exit_stats_init(now); - if (!old_options) + print_notice = 1; + } + if ((!old_options || !old_options->ConnDirectionStatistics) && + options->ConnDirectionStatistics) { + rep_hist_conn_stats_init(now); + } + if (print_notice) log_notice(LD_CONFIG, "Configured to measure statistics. Look for " "the *-stats files that will first be written to the " "data directory in 24 hours from now."); @@@ -1438,9 -1406,6 +1442,9 @@@ if (old_options && old_options->ExitPortStatistics && !options->ExitPortStatistics) rep_hist_exit_stats_term(); + if (old_options && old_options->ConnDirectionStatistics && + !options->ConnDirectionStatistics) + rep_hist_conn_stats_term();
/* Check if we need to parse and add the EntryNodes config option. */ if (options->EntryNodes && @@@ -1746,18 -1711,6 +1750,18 @@@ config_assign_value(config_format_t *fm break; }
+ case CONFIG_TYPE_MSEC_INTERVAL: { + i = config_parse_msec_interval(c->value, &ok); + if (!ok) { + tor_asprintf(msg, + "Msec interval '%s %s' is malformed or out of bounds.", + c->key, c->value); + return -1; + } + *(int *)lvalue = i; + break; + } + case CONFIG_TYPE_MEMUNIT: { uint64_t u64 = config_parse_memunit(c->value, &ok); if (!ok) { @@@ -2045,7 -1998,6 +2049,7 @@@ get_assigned_option(config_format_t *fm escape_val = 0; /* Can't need escape. */ break; case CONFIG_TYPE_INTERVAL: + case CONFIG_TYPE_MSEC_INTERVAL: case CONFIG_TYPE_UINT: /* This means every or_options_t uint or bool element * needs to be an int. Not, say, a uint16_t or char. */ @@@ -2273,7 -2225,6 +2277,7 @@@ option_clear(config_format_t *fmt, or_o *(time_t*)lvalue = 0; break; case CONFIG_TYPE_INTERVAL: + case CONFIG_TYPE_MSEC_INTERVAL: case CONFIG_TYPE_UINT: case CONFIG_TYPE_BOOL: *(int*)lvalue = 0; @@@ -2380,7 -2331,7 +2384,7 @@@ resolve_my_address(int warn_severity, o int explicit_ip=1; int explicit_hostname=1; int from_interface=0; - char tmpbuf[INET_NTOA_BUF_LEN]; + char *addr_string = NULL; const char *address = options->Address; int notice_severity = warn_severity <= LOG_NOTICE ? LOG_NOTICE : warn_severity; @@@ -2422,43 -2373,48 +2426,43 @@@ return -1; } from_interface = 1; - in.s_addr = htonl(interface_ip); - tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf)); + addr = interface_ip; log_fn(notice_severity, LD_CONFIG, "Learned IP address '%s' for " - "local interface. Using that.", tmpbuf); + "local interface. Using that.", fmt_addr32(addr)); strlcpy(hostname, "<guessed from interfaces>", sizeof(hostname)); } else { /* resolved hostname into addr */ - in.s_addr = htonl(addr); - if (!explicit_hostname && - is_internal_IP(ntohl(in.s_addr), 0)) { + is_internal_IP(addr, 0)) { uint32_t interface_ip;
- tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf)); log_fn(notice_severity, LD_CONFIG, "Guessed local hostname '%s' " - "resolves to a private IP address (%s). Trying something " - "else.", hostname, tmpbuf); + "resolves to a private IP address (%s). Trying something " + "else.", hostname, fmt_addr32(addr));
if (get_interface_address(warn_severity, &interface_ip)) { log_fn(warn_severity, LD_CONFIG, "Could not get local interface IP address. Too bad."); } else if (is_internal_IP(interface_ip, 0)) { - struct in_addr in2; - in2.s_addr = htonl(interface_ip); - tor_inet_ntoa(&in2,tmpbuf,sizeof(tmpbuf)); log_fn(notice_severity, LD_CONFIG, "Interface IP address '%s' is a private address too. " - "Ignoring.", tmpbuf); + "Ignoring.", fmt_addr32(interface_ip)); } else { from_interface = 1; - in.s_addr = htonl(interface_ip); - tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf)); + addr = interface_ip; log_fn(notice_severity, LD_CONFIG, "Learned IP address '%s' for local interface." - " Using that.", tmpbuf); + " Using that.", fmt_addr32(addr)); strlcpy(hostname, "<guessed from interfaces>", sizeof(hostname)); } } } + } else { + addr = ntohl(in.s_addr); /* set addr so that addr_string is not + * illformed */ }
- tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf)); - if (is_internal_IP(ntohl(in.s_addr), 0)) { + addr_string = tor_dup_ip(addr); + if (is_internal_IP(addr, 0)) { /* make sure we're ok with publishing an internal IP */ if (!options->DirServers && !options->AlternateDirAuthority) { /* if they are using the default dirservers, disallow internal IPs @@@ -2466,8 -2422,7 +2470,8 @@@ log_fn(warn_severity, LD_CONFIG, "Address '%s' resolves to private IP address '%s'. " "Tor servers that use the default DirServers must have public " - "IP addresses.", hostname, tmpbuf); + "IP addresses.", hostname, addr_string); + tor_free(addr_string); return -1; } if (!explicit_ip) { @@@ -2475,20 -2430,19 +2479,20 @@@ * they're using an internal address. */ log_fn(warn_severity, LD_CONFIG, "Address '%s' resolves to private " "IP address '%s'. Please set the Address config option to be " - "the IP address you want to use.", hostname, tmpbuf); + "the IP address you want to use.", hostname, addr_string); + tor_free(addr_string); return -1; } }
- log_debug(LD_CONFIG, "Resolved Address to '%s'.", tmpbuf); - *addr_out = ntohl(in.s_addr); + log_debug(LD_CONFIG, "Resolved Address to '%s'.", fmt_addr32(addr)); + *addr_out = addr; if (last_resolved_addr && last_resolved_addr != *addr_out) { /* Leave this as a notice, regardless of the requested severity, * at least until dynamic IP address support becomes bulletproof. */ log_notice(LD_NET, "Your IP address seems to have changed to %s. Updating.", - tmpbuf); + addr_string); ip_address_changed(0); } if (last_resolved_addr != *addr_out) { @@@ -2507,12 -2461,11 +2511,12 @@@ } control_event_server_status(LOG_NOTICE, "EXTERNAL_ADDRESS ADDRESS=%s METHOD=%s %s%s", - tmpbuf, method, h?"HOSTNAME=":"", h); + addr_string, method, h?"HOSTNAME=":"", h); } last_resolved_addr = *addr_out; if (hostname_out) *hostname_out = tor_strdup(hostname); + tor_free(addr_string); return 0; }
@@@ -2919,10 -2872,6 +2923,10 @@@ compute_publishserverdescriptor(or_opti * will generate too many circuits and potentially overload the network. */ #define MIN_CIRCUIT_STREAM_TIMEOUT 10
+/** Lowest allowable value for HeartbeatPeriod; if this is too low, we might + * expose more information than we're comfortable with. */ +#define MIN_HEARTBEAT_PERIOD (30*60) + /** Return 0 if every setting in <b>options</b> is reasonable, and a * permissible transition from <b>old_options</b>. Else return -1. * Should have no side effects, except for normalizing the contents of @@@ -3123,24 -3072,17 +3127,24 @@@ options_validate(or_options_t *old_opti routerset_union(options->_ExcludeExitNodesUnion,options->ExcludeNodes); }
+ if (options->NodeFamilies) { + options->NodeFamilySets = smartlist_create(); + for (cl = options->NodeFamilies; cl; cl = cl->next) { + routerset_t *rs = routerset_new(); + if (routerset_parse(rs, cl->value, cl->key) == 0) { + smartlist_add(options->NodeFamilySets, rs); + } else { + routerset_free(rs); + } + } + } + if (options->ExcludeNodes && options->StrictNodes) { COMPLAIN("You have asked to exclude certain relays from all positions " "in your circuits. Expect hidden services and other Tor " "features to be broken in unpredictable ways."); }
- if (options->EntryNodes && !routerset_is_list(options->EntryNodes)) { - /* XXXX fix this; see entry_guards_prepend_from_config(). */ - REJECT("IPs or countries are not yet supported in EntryNodes."); - } - if (options->AuthoritativeDir) { if (!options->ContactInfo && !options->TestingTorNetwork) REJECT("Authoritative directory servers must set ContactInfo"); @@@ -3385,13 -3327,6 +3389,13 @@@ options->CircuitStreamTimeout = MIN_CIRCUIT_STREAM_TIMEOUT; }
+ if (options->HeartbeatPeriod && + options->HeartbeatPeriod < MIN_HEARTBEAT_PERIOD) { + log_warn(LD_CONFIG, "HeartbeatPeriod option is too short; " + "raising to %d seconds.", MIN_HEARTBEAT_PERIOD); + options->HeartbeatPeriod = MIN_HEARTBEAT_PERIOD; + } + if (options->KeepalivePeriod < 1) REJECT("KeepalivePeriod option must be positive.");
@@@ -3610,12 -3545,8 +3614,12 @@@ if (check_nickname_list(options->MyFamily, "MyFamily", msg)) return -1; for (cl = options->NodeFamilies; cl; cl = cl->next) { - if (check_nickname_list(cl->value, "NodeFamily", msg)) + routerset_t *rs = routerset_new(); + if (routerset_parse(rs, cl->value, cl->key)) { + routerset_free(rs); return -1; + } + routerset_free(rs); }
if (validate_addr_policies(options, msg) < 0) @@@ -4109,8 -4040,6 +4113,8 @@@ load_torrc_from_disk(int argc, char **a "Unable to open configuration file "%s".", fname); goto err; } + } else { + log(LOG_NOTICE, LD_CONFIG, "Read configuration file "%s".", fname); }
return cf; @@@ -4399,35 -4328,6 +4403,35 @@@ options_init_logs(or_options_t *options options->RunAsDaemon; #endif
+ if (options->LogTimeGranularity <= 0) { + log_warn(LD_CONFIG, "Log time granularity '%d' has to be positive.", + options->LogTimeGranularity); + return -1; + } else if (1000 % options->LogTimeGranularity != 0 && + options->LogTimeGranularity % 1000 != 0) { + int granularity = options->LogTimeGranularity; + if (granularity < 40) { + do granularity++; + while (1000 % granularity != 0); + } else if (granularity < 1000) { + granularity = 1000 / granularity; + while (1000 % granularity != 0) + granularity--; + granularity = 1000 / granularity; + } else { + granularity = 1000 * ((granularity / 1000) + 1); + } + log_warn(LD_CONFIG, "Log time granularity '%d' has to be either a " + "divisor or a multiple of 1 second. Changing to " + "'%d'.", + options->LogTimeGranularity, granularity); + if (!validate_only) + set_log_time_granularity(granularity); + } else { + if (!validate_only) + set_log_time_granularity(options->LogTimeGranularity); + } + ok = 1; elts = smartlist_create();
@@@ -4919,26 -4819,6 +4923,26 @@@ static struct unit_table_t time_units[ { NULL, 0 }, };
+/** Table to map the names of time units to the number of milliseconds + * they contain. */ +static struct unit_table_t time_msec_units[] = { + { "", 1 }, + { "msec", 1 }, + { "millisecond", 1 }, + { "milliseconds", 1 }, + { "second", 1000 }, + { "seconds", 1000 }, + { "minute", 60*1000 }, + { "minutes", 60*1000 }, + { "hour", 60*60*1000 }, + { "hours", 60*60*1000 }, + { "day", 24*60*60*1000 }, + { "days", 24*60*60*1000 }, + { "week", 7*24*60*60*1000 }, + { "weeks", 7*24*60*60*1000 }, + { NULL, 0 }, +}; + /** Parse a string <b>val</b> containing a number, zero or more * spaces, and an optional unit string. If the unit appears in the * table <b>u</b>, then multiply the number by the unit multiplier. @@@ -5002,25 -4882,6 +5006,25 @@@ config_parse_memunit(const char *s, in return u; }
+/** Parse a string in the format "number unit", where unit is a unit of + * time in milliseconds. On success, set *<b>ok</b> to true and return + * the number of milliseconds in the provided interval. Otherwise, set + * *<b>ok</b> to 0 and return -1. */ +static int +config_parse_msec_interval(const char *s, int *ok) +{ + uint64_t r; + r = config_parse_units(s, time_msec_units, ok); + if (!ok) + return -1; + if (r > INT_MAX) { + log_warn(LD_CONFIG, "Msec interval '%s' is too long", s); + *ok = 0; + return -1; + } + return (int)r; +} + /** Parse a string in the format "number unit", where unit is a unit of time. * On success, set *<b>ok</b> to true and return the number of seconds in * the provided interval. Otherwise, set *<b>ok</b> to 0 and return -1. @@@ -5040,29 -4901,13 +5044,29 @@@ config_parse_interval(const char *s, in return (int)r; }
+/** Return the number of cpus configured in <b>options</b>. If we are + * told to auto-detect the number of cpus, return the auto-detected number. */ +int +get_num_cpus(const or_options_t *options) +{ + if (options->NumCPUs == 0) { + int n = compute_num_cpus(); + return (n >= 1) ? n : 1; + } else { + return options->NumCPUs; + } +} + /** * Initialize the libevent library. */ static void -init_libevent(void) +init_libevent(const or_options_t *options) { const char *badness=NULL; + tor_libevent_cfg cfg; + + tor_assert(options);
configure_libevent_logging(); /* If the kernel complains that some method (say, epoll) doesn't @@@ -5072,11 -4917,7 +5076,11 @@@
tor_check_libevent_header_compatibility();
- tor_libevent_initialize(); + memset(&cfg, 0, sizeof(cfg)); + cfg.disable_iocp = options->DisableIOCP; + cfg.num_cpus = get_num_cpus(options); + + tor_libevent_initialize(&cfg);
suppress_libevent_log_msg(NULL);
@@@ -5417,7 -5258,6 +5421,7 @@@ getinfo_helper_config(control_connectio case CONFIG_TYPE_FILENAME: type = "Filename"; break; case CONFIG_TYPE_UINT: type = "Integer"; break; case CONFIG_TYPE_INTERVAL: type = "TimeInterval"; break; + case CONFIG_TYPE_MSEC_INTERVAL: type = "TimeMsecInterval"; break; case CONFIG_TYPE_MEMUNIT: type = "DataSize"; break; case CONFIG_TYPE_DOUBLE: type = "Float"; break; case CONFIG_TYPE_BOOL: type = "Boolean"; break; diff --combined src/or/or.h index 1617d94,74ff743..e033a3e --- a/src/or/or.h +++ b/src/or/or.h @@@ -83,13 -83,6 +83,13 @@@ #define snprintf _snprintf #endif
+#ifdef USE_BUFFEREVENTS +#include <event2/bufferevent.h> +#include <event2/buffer.h> +#include <event2/util.h> +#endif + +#include "crypto.h" #include "tortls.h" #include "../common/torlog.h" #include "container.h" @@@ -391,9 -384,7 +391,9 @@@ typedef enum /** A connection to a hidden service directory server: download a v2 rendezvous * descriptor. */ #define DIR_PURPOSE_FETCH_RENDDESC_V2 18 -#define _DIR_PURPOSE_MAX 18 +/** A connection to a directory server: download a microdescriptor. */ +#define DIR_PURPOSE_FETCH_MICRODESC 19 +#define _DIR_PURPOSE_MAX 19
/** True iff <b>p</b> is a purpose corresponding to uploading data to a * directory server. */ @@@ -814,9 -805,6 +814,9 @@@ typedef enum * Tor 0.1.2.x is obsolete, we can remove this. */ #define DEFAULT_CLIENT_NICKNAME "client"
+/** Name chosen by routers that don't configure nicknames */ +#define UNNAMED_ROUTER_NICKNAME "Unnamed" + /** Number of bytes in a SOCKS4 header. */ #define SOCKS4_NETWORK_LEN 8
@@@ -862,8 -850,8 +862,8 @@@ typedef struct cell_t typedef struct var_cell_t { uint8_t command; circid_t circ_id; - uint16_t payload_len; - uint8_t payload[1]; + uint16_t payload_len; /**< The actual length of <b>payload</b>. */ + uint8_t payload[FLEXIBLE_ARRAY_MEMBER]; } var_cell_t;
/** A cell as packed for writing to the network. */ @@@ -980,7 -968,6 +980,7 @@@ typedef struct connection_t /** Our socket; -1 if this connection is closed, or has no socket. */ evutil_socket_t s; int conn_array_index; /**< Index into the global connection array. */ + struct event *read_event; /**< Libevent event structure. */ struct event *write_event; /**< Libevent event structure. */ buf_t *inbuf; /**< Buffer holding data read over this connection. */ @@@ -991,11 -978,6 +991,11 @@@ * read? */ time_t timestamp_lastwritten; /**< When was the last time libevent said we * could write? */ + +#ifdef USE_BUFFEREVENTS + struct bufferevent *bufev; /**< A Libevent buffered IO structure. */ +#endif + time_t timestamp_created; /**< When was this connection_t created? */
/* XXXX_IP6 make this IPv6-capable */ @@@ -1095,16 -1077,10 +1095,16 @@@ typedef struct or_connection_t /* bandwidth* and *_bucket only used by ORs in OPEN state: */ int bandwidthrate; /**< Bytes/s added to the bucket. (OPEN ORs only.) */ int bandwidthburst; /**< Max bucket size for this conn. (OPEN ORs only.) */ +#ifndef USE_BUFFEREVENTS int read_bucket; /**< When this hits 0, stop receiving. Every second we * add 'bandwidthrate' to this, capping it at * bandwidthburst. (OPEN ORs only) */ int write_bucket; /**< When this hits 0, stop writing. Like read_bucket. */ +#else + /** DOCDOC */ + /* XXXX we could share this among all connections. */ + struct ev_token_bucket_cfg *bucket_cfg; +#endif int n_circuits; /**< How many circuits use this connection as p_conn or * n_conn ? */
@@@ -1212,13 -1188,8 +1212,13 @@@ typedef struct edge_connection_t typedef struct dir_connection_t { connection_t _base;
- char *requested_resource; /**< Which 'resource' did we ask the directory - * for? */ + /** Which 'resource' did we ask the directory for? This is typically the part + * of the URL string that defines, relative to the directory conn purpose, + * what thing we want. For example, in router descriptor downloads by + * descriptor digest, it contains "d/", then one ore more +-separated + * fingerprints. + **/ + char *requested_resource; unsigned int dirconn_direct:1; /**< Is this dirconn direct, or via Tor? */
/* Used only for server sides of some dir connections, to implement @@@ -1309,51 -1280,6 +1309,51 @@@ static INLINE control_connection_t *TO_ return DOWNCAST(control_connection_t, c); }
+/* Conditional macros to help write code that works whether bufferevents are + disabled or not. + + We can't just write: + if (conn->bufev) { + do bufferevent stuff; + } else { + do other stuff; + } + because the bufferevent stuff won't even compile unless we have a fairly + new version of Libevent. Instead, we say: + IF_HAS_BUFFEREVENT(conn, { do_bufferevent_stuff } ); + or: + IF_HAS_BUFFEREVENT(conn, { + do bufferevent stuff; + }) ELSE_IF_NO_BUFFEREVENT { + do non-bufferevent stuff; + } + If we're compiling with bufferevent support, then the macros expand more or + less to: + if (conn->bufev) { + do_bufferevent_stuff; + } else { + do non-bufferevent stuff; + } + and if we aren't using bufferevents, they expand more or less to: + { do non-bufferevent stuff; } +*/ +#ifdef USE_BUFFEREVENTS +#define HAS_BUFFEREVENT(c) (((c)->bufev) != NULL) +#define IF_HAS_BUFFEREVENT(c, stmt) \ + if ((c)->bufev) do { \ + stmt ; \ + } while (0) +#define ELSE_IF_NO_BUFFEREVENT ; else +#define IF_HAS_NO_BUFFEREVENT(c) \ + if (NULL == (c)->bufev) +#else +#define HAS_BUFFEREVENT(c) (0) +#define IF_HAS_BUFFEREVENT(c, stmt) (void)0 +#define ELSE_IF_NO_BUFFEREVENT ; +#define IF_HAS_NO_BUFFEREVENT(c) \ + if (1) +#endif + /** What action type does an address policy indicate: accept or reject? */ typedef enum { ADDR_POLICY_ACCEPT=1, @@@ -1517,49 -1443,59 +1517,49 @@@ typedef struct char *contact_info; /**< Declared contact info for this router. */ unsigned int is_hibernating:1; /**< Whether the router claims to be * hibernating */ - unsigned int has_old_dnsworkers:1; /**< Whether the router is using - * dnsworker code. */ - unsigned int caches_extra_info:1; /**< Whether the router caches and serves - * extrainfo documents. */ - unsigned int allow_single_hop_exits:1; /**< Whether the router allows - * single hop exits. */ - - /* local info */ - unsigned int is_running:1; /**< As far as we know, is this OR currently - * running? */ - unsigned int is_valid:1; /**< Has a trusted dirserver validated this OR? - * (For Authdir: Have we validated this OR?) - */ - unsigned int is_named:1; /**< Do we believe the nickname that this OR gives - * us? */ - unsigned int is_fast:1; /** Do we think this is a fast OR? */ - unsigned int is_stable:1; /** Do we think this is a stable OR? */ - unsigned int is_possible_guard:1; /**< Do we think this is an OK guard? */ - unsigned int is_exit:1; /**< Do we think this is an OK exit? */ - unsigned int is_bad_exit:1; /**< Do we think this exit is censored, borked, - * or otherwise nasty? */ - unsigned int is_bad_directory:1; /**< Do we think this directory is junky, - * underpowered, or otherwise useless? */ + unsigned int caches_extra_info:1; /**< Whether the router says it caches and + * serves extrainfo documents. */ + unsigned int allow_single_hop_exits:1; /**< Whether the router says + * it allows single hop exits. */ + unsigned int wants_to_be_hs_dir:1; /**< True iff this router claims to be * a hidden service directory. */ - unsigned int is_hs_dir:1; /**< True iff this router is a hidden service - * directory according to the authorities. */ unsigned int policy_is_reject_star:1; /**< True iff the exit policy for this * router rejects everything. */ /** True if, after we have added this router, we should re-launch * tests for it. */ unsigned int needs_retest_if_added:1;
-/** Tor can use this router for general positions in circuits. */ +/** Tor can use this router for general positions in circuits; we got it + * from a directory server as usual, or we're an authority and a server + * uploaded it. */ #define ROUTER_PURPOSE_GENERAL 0 -/** Tor should avoid using this router for circuit-building. */ +/** Tor should avoid using this router for circuit-building: we got it + * from a crontroller. If the controller wants to use it, it'll have to + * ask for it by identity. */ #define ROUTER_PURPOSE_CONTROLLER 1 -/** Tor should use this router only for bridge positions in circuits. */ +/** Tor should use this router only for bridge positions in circuits: we got + * it via a directory request from the bridge itself, or a bridge + * authority. x*/ #define ROUTER_PURPOSE_BRIDGE 2 /** Tor should not use this router; it was marked in cached-descriptors with * a purpose we didn't recognize. */ #define ROUTER_PURPOSE_UNKNOWN 255
- uint8_t purpose; /** What positions in a circuit is this router good for? */ + /* In what way did we find out about this router? One of ROUTER_PURPOSE_*. + * Routers of different purposes are kept segregated and used for different + * things; see notes on ROUTER_PURPOSE_* macros above. + */ + uint8_t purpose;
/* The below items are used only by authdirservers for * reachability testing. */ + /** When was the last time we could reach this OR? */ time_t last_reachable; /** When did we start testing reachability for this OR? */ time_t testing_since; - /** According to the geoip db what country is this router in? */ - country_t country; + } routerinfo_t;
/** Information needed to keep and cache a signed extra-info document. */ @@@ -1585,9 -1521,8 +1585,9 @@@ typedef struct routerstatus_t * has. */ char identity_digest[DIGEST_LEN]; /**< Digest of the router's identity * key. */ - char descriptor_digest[DIGEST_LEN]; /**< Digest of the router's most recent - * descriptor. */ + /** Digest of the router's most recent descriptor or microdescriptor. + * If it's a descriptor, we only use the first DIGEST_LEN bytes. */ + char descriptor_digest[DIGEST256_LEN]; uint32_t addr; /**< IPv4 address for this router. */ uint16_t or_port; /**< OR port for this router. */ uint16_t dir_port; /**< Directory port for this router. */ @@@ -1595,11 -1530,7 +1595,11 @@@ unsigned int is_exit:1; /**< True iff this router is a good exit. */ unsigned int is_stable:1; /**< True iff this router stays up a long time. */ unsigned int is_fast:1; /**< True iff this router has good bandwidth. */ - unsigned int is_running:1; /**< True iff this router is up. */ + /** True iff this router is called 'running' in the consensus. We give it + * this funny name so that we don't accidentally use this bit as a view of + * whether we think the router is *currently* running. If that's what you + * want to know, look at is_running in node_t. */ + unsigned int is_flagged_running:1; unsigned int is_named:1; /**< True iff "nickname" belongs to this router. */ unsigned int is_unnamed:1; /**< True iff "nickname" belongs to another * router. */ @@@ -1651,31 -1582,15 +1651,31 @@@ * from this authority.) Applies in v2 networkstatus document only. */ unsigned int need_to_mirror:1; - unsigned int name_lookup_warned:1; /**< Have we warned the user for referring - * to this (unnamed) router by nickname? - */ time_t last_dir_503_at; /**< When did this router last tell us that it * was too busy to serve directory info? */ download_status_t dl_status;
} routerstatus_t;
+/** A single entry in a parsed policy summary, describing a range of ports. */ +typedef struct short_policy_entry_t { + uint16_t min_port, max_port; +} short_policy_entry_t; + +/** A short_poliy_t is the parsed version of a policy summary. */ +typedef struct short_policy_t { + /** True if the members of 'entries' are port ranges to accept; false if + * they are port ranges to reject */ + unsigned int is_accept : 1; + /** The actual number of values in 'entries'. */ + unsigned int n_entries : 31; + /** An array of 0 or more short_policy_entry_t values, each describing a + * range of ports that this policy accepts or rejects (depending on the + * value of is_accept). + */ + short_policy_entry_t entries[FLEXIBLE_ARRAY_MEMBER]; +} short_policy_t; + /** A microdescriptor is the smallest amount of information needed to build a * circuit through a router. They are generated by the directory authorities, * using information from the uploaded routerinfo documents. They are not @@@ -1717,83 -1632,15 +1717,83 @@@ typedef struct microdesc_t crypto_pk_env_t *onion_pkey; /** As routerinfo_t.family */ smartlist_t *family; - /** Encoded exit policy summary */ - char *exitsummary; /**< exit policy summary - - * XXX this probably should not stay a string. */ + /** Exit policy summary */ + short_policy_t *exit_policy; } microdesc_t;
+/** A node_t represents a Tor router. + * + * Specifically, a node_t is a Tor router as we are using it: a router that + * we are considering for circuits, connections, and so on. A node_t is a + * thin wrapper around the routerstatus, routerinfo, and microdesc for a + * single wrapper, and provides a consistent interface for all of them. + * + * Also, a node_t has mutable state. While a routerinfo, a routerstatus, + * and a microdesc have[*] only the information read from a router + * descriptor, a consensus entry, and a microdescriptor (respectively)... + * a node_t has flags based on *our own current opinion* of the node. + * + * [*] Actually, there is some leftover information in each that is mutable. + * We should try to excise that. + */ +typedef struct node_t { + /* Indexing information */ + + /** Used to look up the node_t by its identity digest. */ + HT_ENTRY(node_t) ht_ent; + /** Position of the node within the list of nodes */ + int nodelist_idx; + + /** The identity digest of this node_t. No more than one node_t per + * identity may exist at a time. */ + char identity[DIGEST_LEN]; + + microdesc_t *md; + routerinfo_t *ri; + routerstatus_t *rs; + + /* local info: copied from routerstatus, then possibly frobbed based + * on experience. Authorities set this stuff directly. */ + + unsigned int is_running:1; /**< As far as we know, is this OR currently + * running? */ + unsigned int is_valid:1; /**< Has a trusted dirserver validated this OR? + * (For Authdir: Have we validated this OR?) + */ + unsigned int is_fast:1; /** Do we think this is a fast OR? */ + unsigned int is_stable:1; /** Do we think this is a stable OR? */ + unsigned int is_possible_guard:1; /**< Do we think this is an OK guard? */ + unsigned int is_exit:1; /**< Do we think this is an OK exit? */ + unsigned int is_bad_exit:1; /**< Do we think this exit is censored, borked, + * or otherwise nasty? */ + unsigned int is_bad_directory:1; /**< Do we think this directory is junky, + * underpowered, or otherwise useless? */ + unsigned int is_hs_dir:1; /**< True iff this router is a hidden service + * directory according to the authorities. */ + + /* Local info: warning state. */ + + unsigned int name_lookup_warned:1; /**< Have we warned the user for referring + * to this (unnamed) router by nickname? + */ + + /** Local info: we treat this node as if it rejects everything */ + unsigned int rejects_all:1; + + /* Local info: derived. */ + + /** According to the geoip db what country is this router in? */ + country_t country; +} node_t; + /** How many times will we try to download a router's descriptor before giving * up? */ #define MAX_ROUTERDESC_DOWNLOAD_FAILURES 8
+/** How many times will we try to download a microdescriptor before giving + * up? */ +#define MAX_MICRODESC_DOWNLOAD_FAILURES 8 + /** Contents of a v2 (non-consensus, non-vote) network status object. */ typedef struct networkstatus_v2_t { /** When did we receive the network-status document? */ @@@ -2269,12 -2116,10 +2269,12 @@@ typedef struct circuit_t * length ONIONSKIN_CHALLENGE_LEN. */ char *n_conn_onionskin;
- time_t timestamp_created; /**< When was this circuit created? */ + /** When was this circuit created? We keep this timestamp with a higher + * resolution than most so that the circuit-build-time tracking code can + * get millisecond resolution. */ + struct timeval timestamp_created; time_t timestamp_dirty; /**< When the circuit was first used, or 0 if the * circuit is clean. */ - struct timeval highres_created; /**< When exactly was the circuit created? */
uint16_t marked_for_close; /**< Should we close this circuit at the end of * the main loop? (If true, holds the line number @@@ -2499,7 -2344,6 +2499,7 @@@ typedef struct
config_line_t *Logs; /**< New-style list of configuration lines * for logs */ + int LogTimeGranularity; /**< Log resolution in milliseconds. */
int LogMessageDomains; /**< Boolean: Should we log the domain(s) in which * each log message occurs? */ @@@ -2722,9 -2566,6 +2722,9 @@@ * authorizations for hidden services */ char *ContactInfo; /**< Contact info to be published in the directory. */
+ int HeartbeatPeriod; /**< Log heartbeat messages after this many seconds + * have passed. */ + char *HTTPProxy; /**< hostname[:port] to use as http proxy, if any. */ tor_addr_t HTTPProxyAddr; /**< Parsed IPv4 addr for http proxy, if any. */ uint16_t HTTPProxyPort; /**< Parsed port for http proxy, if any. */ @@@ -2762,8 -2603,7 +2762,8 @@@
char *MyFamily; /**< Declared family for this OR. */ config_line_t *NodeFamilies; /**< List of config lines for - * node families */ + * node families */ + smartlist_t *NodeFamilySets; /**< List of parsed NodeFamilies values. */ config_line_t *AuthDirBadDir; /**< Address policy for descriptors to * mark as bad dir mirrors. */ config_line_t *AuthDirBadExit; /**< Address policy for descriptors to @@@ -2868,10 -2708,6 +2868,10 @@@ * possible. */ int PreferTunneledDirConns; /**< If true, avoid dirservers that don't * support BEGIN_DIR, when possible. */ + int PortForwarding; /**< If true, use NAT-PMP or UPnP to automatically + * forward the DirPort and ORPort on the NAT device */ + char *PortForwardingHelper; /** < Filename or full path of the port + forwarding helper executable */ int AllowNonRFC953Hostnames; /**< If true, we allow connections to hostnames * with weird characters. */ /** If true, we try resolving hostnames with weird characters. */ @@@ -2909,9 -2745,6 +2909,9 @@@ /** If true, the user wants us to collect statistics on port usage. */ int ExitPortStatistics;
+ /** If true, the user wants us to collect connection statistics. */ + int ConnDirectionStatistics; + /** If true, the user wants us to collect cell statistics. */ int CellStatistics;
@@@ -3008,12 -2841,6 +3008,12 @@@ */ double CircuitPriorityHalflife;
+ /** If true, do not enable IOCP on windows with bufferevents, even if + * we think we could. */ + int DisableIOCP; + /** For testing only: will go away in 0.2.3.x. */ + int _UseFilteringSSLBufferevents; + } or_options_t;
/** Persistent state for an onion router, as saved to disk. */ @@@ -3045,19 -2872,25 +3045,25 @@@ typedef struct * bandwidth usage. The "Interval" fields hold the granularity, in seconds, * of the entries of Values. The "Values" lists hold decimal string * representations of the number of bytes read or written in each - * interval. */ + * interval. The "Maxima" list holds decimal strings describing the highest + * rate achieved during the interval. + */ time_t BWHistoryReadEnds; int BWHistoryReadInterval; smartlist_t *BWHistoryReadValues; + smartlist_t *BWHistoryReadMaxima; time_t BWHistoryWriteEnds; int BWHistoryWriteInterval; smartlist_t *BWHistoryWriteValues; + smartlist_t *BWHistoryWriteMaxima; time_t BWHistoryDirReadEnds; int BWHistoryDirReadInterval; smartlist_t *BWHistoryDirReadValues; + smartlist_t *BWHistoryDirReadMaxima; time_t BWHistoryDirWriteEnds; int BWHistoryDirWriteInterval; smartlist_t *BWHistoryDirWriteValues; + smartlist_t *BWHistoryDirWriteMaxima;
/** Build time histogram */ config_line_t * BuildtimeHistogram; @@@ -3122,6 -2955,8 +3128,6 @@@ struct socks_request_t * every connection. */ };
-/* all the function prototypes go here */ - /********************************* circuitbuild.c **********************/
/** How many hops does a general-purpose circuit have by default? */ @@@ -3513,7 -3348,7 +3519,7 @@@ typedef enum ADDR_POLICY_PROBABLY_ACCEPTED=1, /** Part of the address was unknown, but as far as we can tell, it was * rejected. */ - ADDR_POLICY_PROBABLY_REJECTED=2 + ADDR_POLICY_PROBABLY_REJECTED=2, } addr_policy_result_t;
/********************************* rephist.c ***************************/ @@@ -3644,8 -3479,6 +3650,8 @@@ typedef struct trusted_dir_server_t * fetches to _any_ single directory server.] */ #define PDS_NO_EXISTING_SERVERDESC_FETCH (1<<3) +#define PDS_NO_EXISTING_MICRODESC_FETCH (1<<4) + #define _PDS_PREFER_TUNNELED_DIR_CONNS (1<<16)
/** Possible ways to weight routers when choosing one randomly. See @@@ -3663,8 -3496,7 +3669,8 @@@ typedef enum CRN_NEED_GUARD = 1<<2, CRN_ALLOW_INVALID = 1<<3, /* XXXX not used, apparently. */ - CRN_WEIGHT_AS_EXIT = 1<<5 + CRN_WEIGHT_AS_EXIT = 1<<5, + CRN_NEED_DESC = 1<<6 } router_crn_flags_t;
/** Return value for router_add_to_routerlist() and dirserv_add_descriptor() */ diff --combined src/or/rephist.c index 3146d70,3f4a704..f34c5b6 --- a/src/or/rephist.c +++ b/src/or/rephist.c @@@ -7,14 -7,13 +7,14 @@@ * \brief Basic history and "reputation" functionality to remember * which servers have worked in the past, how much bandwidth we've * been using, which ports we tend to want, and so on; further, - * exit port statistics and cell statistics. + * exit port statistics, cell statistics, and connection statistics. **/
#include "or.h" #include "circuitlist.h" #include "circuituse.h" #include "config.h" +#include "nodelist.h" #include "rephist.h" #include "router.h" #include "routerlist.h" @@@ -580,7 -579,7 +580,7 @@@ rep_hist_dump_stats(time_t now, int sev size_t len; int ret; unsigned long upt, downt; - routerinfo_t *r; + const node_t *node;
rep_history_clean(now - get_options()->RephistTrackTime);
@@@ -594,8 -593,8 +594,8 @@@ digestmap_iter_get(orhist_it, &digest1, &or_history_p); or_history = (or_history_t*) or_history_p;
- if ((r = router_get_by_digest(digest1))) - name1 = r->nickname; + if ((node = node_get_by_id(digest1)) && node_get_nickname(node)) + name1 = node_get_nickname(node); else name1 = "(unknown)"; base16_encode(hexdigest1, sizeof(hexdigest1), digest1, DIGEST_LEN); @@@ -625,8 -624,8 +625,8 @@@ lhist_it = digestmap_iter_next(or_history->link_history_map, lhist_it)) { digestmap_iter_get(lhist_it, &digest2, &link_history_p); - if ((r = router_get_by_digest(digest2))) - name2 = r->nickname; + if ((node = node_get_by_id(digest2)) && node_get_nickname(node)) + name2 = node_get_nickname(node); else name2 = "(unknown)";
@@@ -757,7 -756,7 +757,7 @@@ rep_hist_record_mtbf_data(time_t now, i base16_encode(dbuf, sizeof(dbuf), digest, DIGEST_LEN);
if (missing_means_down && hist->start_of_run && - !router_get_by_digest(digest)) { + !router_get_by_id_digest(digest)) { /* We think this relay is running, but it's not listed in our * routerlist. Somehow it fell out without telling us it went * down. Complain and also correct it. */ @@@ -872,32 -871,28 +872,32 @@@ rep_hist_get_router_stability_doc(time_ }
DIGESTMAP_FOREACH(history_map, id, or_history_t *, hist) { - routerinfo_t *ri; + const node_t *node; char dbuf[BASE64_DIGEST_LEN+1]; char header_buf[512]; char *info; digest_to_base64(dbuf, id); - ri = router_get_by_digest(id); - if (ri) { - char *ip = tor_dup_ip(ri->addr); + node = node_get_by_id(id); + if (node) { + char ip[INET_NTOA_BUF_LEN+1]; char tbuf[ISO_TIME_LEN+1]; - format_iso_time(tbuf, ri->cache_info.published_on); + time_t published = node_get_published_on(node); + node_get_address_string(node,ip,sizeof(ip)); + if (published > 0) + format_iso_time(tbuf, published); + else + strlcpy(tbuf, "???", sizeof(tbuf)); tor_snprintf(header_buf, sizeof(header_buf), "router %s %s %s\n" "published %s\n" "relevant-flags %s%s%s\n" "declared-uptime %ld\n", - dbuf, ri->nickname, ip, + dbuf, node_get_nickname(node), ip, tbuf, - ri->is_running ? "Running " : "", - ri->is_valid ? "Valid " : "", - ri->is_hibernating ? "Hibernating " : "", - ri->uptime); - tor_free(ip); + node->is_running ? "Running " : "", + node->is_valid ? "Valid " : "", + node->ri && node->ri->is_hibernating ? "Hibernating " : "", + node_get_declared_uptime(node)); } else { tor_snprintf(header_buf, sizeof(header_buf), "router %s {no descriptor}\n", dbuf); @@@ -1170,6 -1165,8 +1170,8 @@@ rep_hist_load_mtbf_data(time_t now * totals? */ #define NUM_SECS_ROLLING_MEASURE 10 /** How large are the intervals for which we track and report bandwidth use? */ + /* XXXX Watch out! Before Tor 0.2.2.21-alpha, using any other value here would + * generate an unparseable state file. */ #define NUM_SECS_BW_SUM_INTERVAL (15*60) /** How far in the past do we remember and publish bandwidth use? */ #define NUM_SECS_BW_SUM_IS_VALID (24*60*60) @@@ -1264,8 -1261,12 +1266,12 @@@ add_obs(bw_array_t *b, time_t when, uin /* If we're currently adding observations for an earlier second than * 'when', advance b->cur_obs_time and b->cur_obs_idx by an * appropriate number of seconds, and do all the other housekeeping */ - while (when>b->cur_obs_time) + while (when>b->cur_obs_time) { + /* Doing this one second at a time is potentially inefficient, if we start + with a state file that is very old. Fortunately, it doesn't seem to + show up in profiles, so we can just ignore it for now. */ advance_obs(b); + }
b->obs[b->cur_obs_idx] += n; b->total_in_period += n; @@@ -1296,10 -1297,15 +1302,15 @@@ static bw_array_t *dir_read_array = NUL directory protocol. */ static bw_array_t *dir_write_array = NULL;
- /** Set up [dir-]read_array and [dir-]write_array. */ + /** Set up [dir-]read_array and [dir-]write_array, freeing them if they + * already exist. */ static void bw_arrays_init(void) { + tor_free(read_array); + tor_free(write_array); + tor_free(dir_read_array); + tor_free(dir_write_array); read_array = bw_array_new(); write_array = bw_array_new(); dir_read_array = bw_array_new(); @@@ -1397,7 -1403,7 +1408,7 @@@ rep_hist_bandwidth_assess(void * It returns the number of bytes written. */ static size_t - rep_hist_fill_bandwidth_history(char *buf, size_t len, bw_array_t *b) + rep_hist_fill_bandwidth_history(char *buf, size_t len, const bw_array_t *b) { char *cp = buf; int i, n; @@@ -1489,163 -1495,175 +1500,175 @@@ rep_hist_get_bandwidth_lines(void return buf; }
- /** Update <b>state</b> with the newest bandwidth history. */ - void - rep_hist_update_state(or_state_t *state) + /** Write a single bw_array_t into the Values, Ends, Interval, and Maximum + * entries of an or_state_t. */ + static void + rep_hist_update_bwhist_state_section(or_state_t *state, + const bw_array_t *b, + smartlist_t **s_values, + smartlist_t **s_maxima, + time_t *s_begins, + int *s_interval) { - int len, r; - char *buf, *cp; - smartlist_t **s_values = NULL; - time_t *s_begins = NULL; - int *s_interval = NULL; - bw_array_t *b = NULL; - - len = 20*NUM_TOTALS+1; - buf = tor_malloc_zero(len); + char *cp; + int i,j;
- for (r=0;r<4;++r) { - switch (r) { - case 0: - b = write_array; - s_begins = &state->BWHistoryWriteEnds; - s_interval = &state->BWHistoryWriteInterval; - s_values = &state->BWHistoryWriteValues; - break; - case 1: - b = read_array; - s_begins = &state->BWHistoryReadEnds; - s_interval = &state->BWHistoryReadInterval; - s_values = &state->BWHistoryReadValues; - break; - case 2: - b = dir_write_array; - s_begins = &state->BWHistoryDirWriteEnds; - s_interval = &state->BWHistoryDirWriteInterval; - s_values = &state->BWHistoryDirWriteValues; - break; - case 3: - b = dir_read_array; - s_begins = &state->BWHistoryDirReadEnds; - s_interval = &state->BWHistoryDirReadInterval; - s_values = &state->BWHistoryDirReadValues; - break; - } - if (*s_values) { - SMARTLIST_FOREACH(*s_values, char *, val, tor_free(val)); - smartlist_free(*s_values); - } - if (! server_mode(get_options())) { - /* Clients don't need to store bandwidth history persistently; - * force these values to the defaults. */ - /* FFFF we should pull the default out of config.c's state table, - * so we don't have two defaults. */ - if (*s_begins != 0 || *s_interval != 900) { - time_t now = time(NULL); - time_t save_at = get_options()->AvoidDiskWrites ? now+3600 : now+600; - or_state_mark_dirty(state, save_at); - } - *s_begins = 0; - *s_interval = 900; - *s_values = smartlist_create(); - continue; + if (*s_values) { + SMARTLIST_FOREACH(*s_values, char *, val, tor_free(val)); + smartlist_free(*s_values); + } + if (*s_maxima) { + SMARTLIST_FOREACH(*s_maxima, char *, val, tor_free(val)); + smartlist_free(*s_maxima); + } + if (! server_mode(get_options())) { + /* Clients don't need to store bandwidth history persistently; + * force these values to the defaults. */ + /* FFFF we should pull the default out of config.c's state table, + * so we don't have two defaults. */ + if (*s_begins != 0 || *s_interval != 900) { + time_t now = time(NULL); + time_t save_at = get_options()->AvoidDiskWrites ? now+3600 : now+600; + or_state_mark_dirty(state, save_at); } - *s_begins = b->next_period; - *s_interval = NUM_SECS_BW_SUM_INTERVAL; - cp = buf; - cp += rep_hist_fill_bandwidth_history(cp, len, b); - tor_snprintf(cp, len-(cp-buf), cp == buf ? U64_FORMAT : ","U64_FORMAT, - U64_PRINTF_ARG(b->total_in_period)); + *s_begins = 0; + *s_interval = 900; *s_values = smartlist_create(); - if (server_mode(get_options())) - smartlist_split_string(*s_values, buf, ",", SPLIT_SKIP_SPACE, 0); + *s_maxima = smartlist_create(); + return; + } + *s_begins = b->next_period; + *s_interval = NUM_SECS_BW_SUM_INTERVAL; + + *s_values = smartlist_create(); + *s_maxima = smartlist_create(); + /* Set i to first position in circular array */ + i = (b->num_maxes_set <= b->next_max_idx) ? 0 : b->next_max_idx; + for (j=0; j < b->num_maxes_set; ++j,++i) { + uint64_t maxval; + if (i >= NUM_TOTALS) + i = 0; + tor_asprintf(&cp, U64_FORMAT, U64_PRINTF_ARG(b->totals[i] & ~0x3ff)); + smartlist_add(*s_values, cp); + maxval = b->maxima[i] / NUM_SECS_ROLLING_MEASURE; + tor_asprintf(&cp, U64_FORMAT, U64_PRINTF_ARG(maxval & ~0x3ff)); + smartlist_add(*s_maxima, cp); } - tor_free(buf); + tor_asprintf(&cp, U64_FORMAT, U64_PRINTF_ARG(b->total_in_period & ~0x3ff)); + smartlist_add(*s_values, cp); + tor_asprintf(&cp, U64_FORMAT, U64_PRINTF_ARG(b->max_total & ~0x3ff)); + smartlist_add(*s_maxima, cp); + } + + /** Update <b>state</b> with the newest bandwidth history. */ + void + rep_hist_update_state(or_state_t *state) + { + #define UPDATE(arrname,st) \ + rep_hist_update_bwhist_state_section(state,\ + (arrname),\ + &state->BWHistory ## st ## Values, \ + &state->BWHistory ## st ## Maxima, \ + &state->BWHistory ## st ## Ends, \ + &state->BWHistory ## st ## Interval) + + UPDATE(write_array, Write); + UPDATE(read_array, Read); + UPDATE(dir_write_array, DirWrite); + UPDATE(dir_read_array, DirRead); + if (server_mode(get_options())) { - or_state_mark_dirty(get_or_state(), time(NULL)+(2*3600)); + or_state_mark_dirty(state, time(NULL)+(2*3600)); } + #undef UPDATE }
- /** Set bandwidth history from our saved state. */ - int - rep_hist_load_state(or_state_t *state, char **err) + /** Load a single bw_array_t from its Values, Ends, Maxima, and Interval + * entries in an or_state_t. */ + static int + rep_hist_load_bwhist_state_section(bw_array_t *b, + const smartlist_t *s_values, + const smartlist_t *s_maxima, + const time_t s_begins, + const int s_interval) { - time_t s_begins = 0, start; time_t now = time(NULL); - uint64_t v; - int r,i,ok; - int all_ok = 1; - int s_interval = 0; - smartlist_t *s_values = NULL; - bw_array_t *b = NULL; - - /* Assert they already have been malloced */ - tor_assert(read_array && write_array); + int retval = 0; + time_t start;
- for (r=0;r<4;++r) { - switch (r) { - case 0: - b = write_array; - s_begins = state->BWHistoryWriteEnds; - s_interval = state->BWHistoryWriteInterval; - s_values = state->BWHistoryWriteValues; - break; - case 1: - b = read_array; - s_begins = state->BWHistoryReadEnds; - s_interval = state->BWHistoryReadInterval; - s_values = state->BWHistoryReadValues; - break; - case 2: - b = dir_write_array; - s_begins = state->BWHistoryDirWriteEnds; - s_interval = state->BWHistoryDirWriteInterval; - s_values = state->BWHistoryDirWriteValues; - break; - case 3: - b = dir_read_array; - s_begins = state->BWHistoryDirReadEnds; - s_interval = state->BWHistoryDirReadInterval; - s_values = state->BWHistoryDirReadValues; - break; - } - if (s_values && s_begins >= now - NUM_SECS_BW_SUM_INTERVAL*NUM_TOTALS) { - start = s_begins - s_interval*(smartlist_len(s_values)); - if (start > now) - continue; - b->cur_obs_time = start; - b->next_period = start + NUM_SECS_BW_SUM_INTERVAL; - SMARTLIST_FOREACH(s_values, char *, cp, { + uint64_t v, mv; + int i,ok,ok_m; + int have_maxima = (smartlist_len(s_values) == smartlist_len(s_maxima)); + + if (s_values && s_begins >= now - NUM_SECS_BW_SUM_INTERVAL*NUM_TOTALS) { + start = s_begins - s_interval*(smartlist_len(s_values)); + if (start > now) + return 0; + b->cur_obs_time = start; + b->next_period = start + NUM_SECS_BW_SUM_INTERVAL; + SMARTLIST_FOREACH_BEGIN(s_values, const char *, cp) { v = tor_parse_uint64(cp, 10, 0, UINT64_MAX, &ok, NULL); - if (!ok) { - all_ok=0; + if (have_maxima) { + const char *maxstr = smartlist_get(s_maxima, cp_sl_idx); + mv = tor_parse_uint64(maxstr, 10, 0, UINT64_MAX, &ok_m, NULL); + mv *= NUM_SECS_ROLLING_MEASURE; + } else { + /* No maxima known; guess average rate to be conservative. */ + mv = v / s_interval; + } + if (!ok || !ok_m) { + retval = -1; log_notice(LD_HIST, "Could not parse '%s' into a number.'", cp); } + if (start < now) { add_obs(b, start, v); - start += NUM_SECS_BW_SUM_INTERVAL; + b->max_total = mv; + /* This will result in some fairly choppy history if s_interval + * is notthe same as NUM_SECS_BW_SUM_INTERVAL. XXXX */ + start += s_interval; } - }); - } + } SMARTLIST_FOREACH_END(cp); + }
- /* Clean up maxima and observed */ - /* Do we really want to zero this for the purpose of max capacity? */ - for (i=0; i<NUM_SECS_ROLLING_MEASURE; ++i) { - b->obs[i] = 0; - } - b->total_obs = 0; - for (i=0; i<NUM_TOTALS; ++i) { - b->maxima[i] = 0; - } - b->max_total = 0; + /* Clean up maxima and observed */ + for (i=0; i<NUM_SECS_ROLLING_MEASURE; ++i) { + b->obs[i] = 0; } + b->total_obs = 0;
+ return retval; + } + + /** Set bandwidth history from our saved state. */ + int + rep_hist_load_state(or_state_t *state, char **err) + { + int all_ok = 1; + + /* Assert they already have been malloced */ + tor_assert(read_array && write_array); + tor_assert(dir_read_array && dir_write_array); + + #define LOAD(arrname,st) \ + if (rep_hist_load_bwhist_state_section( \ + (arrname), \ + state->BWHistory ## st ## Values, \ + state->BWHistory ## st ## Maxima, \ + state->BWHistory ## st ## Ends, \ + state->BWHistory ## st ## Interval)<0) \ + all_ok = 0 + + LOAD(write_array, Write); + LOAD(read_array, Read); + LOAD(dir_write_array, DirWrite); + LOAD(dir_read_array, DirRead); + + #undef LOAD if (!all_ok) { *err = tor_strdup("Parsing of bandwidth history values failed"); /* and create fresh arrays */ - tor_free(read_array); - tor_free(write_array); - read_array = bw_array_new(); - write_array = bw_array_new(); + bw_arrays_init(); return -1; } return 0; @@@ -1653,13 -1671,10 +1676,13 @@@
/*********************************************************************/
+typedef struct predicted_port_t { + uint16_t port; + time_t time; +} predicted_port_t; + /** A list of port numbers that have been used recently. */ static smartlist_t *predicted_ports_list=NULL; -/** The corresponding most recently used time for each port. */ -static smartlist_t *predicted_ports_times=NULL;
/** We just got an application request for a connection with * port <b>port</b>. Remember it for the future, so we can keep @@@ -1668,11 -1683,14 +1691,11 @@@ static void add_predicted_port(time_t now, uint16_t port) { - /* XXXX we could just use uintptr_t here, I think. */ - uint16_t *tmp_port = tor_malloc(sizeof(uint16_t)); - time_t *tmp_time = tor_malloc(sizeof(time_t)); - *tmp_port = port; - *tmp_time = now; - rephist_total_alloc += sizeof(uint16_t) + sizeof(time_t); - smartlist_add(predicted_ports_list, tmp_port); - smartlist_add(predicted_ports_times, tmp_time); + predicted_port_t *pp = tor_malloc(sizeof(predicted_port_t)); + pp->port = port; + pp->time = now; + rephist_total_alloc += sizeof(*pp); + smartlist_add(predicted_ports_list, pp); }
/** Initialize whatever memory and structs are needed for predicting @@@ -1683,6 -1701,7 +1706,6 @@@ static voi predicted_ports_init(void) { predicted_ports_list = smartlist_create(); - predicted_ports_times = smartlist_create(); add_predicted_port(time(NULL), 80); /* add one to kickstart us */ }
@@@ -1692,11 -1711,12 +1715,11 @@@ static void predicted_ports_free(void) { - rephist_total_alloc -= smartlist_len(predicted_ports_list)*sizeof(uint16_t); - SMARTLIST_FOREACH(predicted_ports_list, char *, cp, tor_free(cp)); + rephist_total_alloc -= + smartlist_len(predicted_ports_list)*sizeof(predicted_port_t); + SMARTLIST_FOREACH(predicted_ports_list, predicted_port_t *, + pp, tor_free(pp)); smartlist_free(predicted_ports_list); - rephist_total_alloc -= smartlist_len(predicted_ports_times)*sizeof(time_t); - SMARTLIST_FOREACH(predicted_ports_times, char *, cp, tor_free(cp)); - smartlist_free(predicted_ports_times); }
/** Remember that <b>port</b> has been asked for as of time <b>now</b>. @@@ -1706,17 -1726,24 +1729,17 @@@ void rep_hist_note_used_port(time_t now, uint16_t port) { - int i; - uint16_t *tmp_port; - time_t *tmp_time; - tor_assert(predicted_ports_list); - tor_assert(predicted_ports_times);
if (!port) /* record nothing */ return;
- for (i = 0; i < smartlist_len(predicted_ports_list); ++i) { - tmp_port = smartlist_get(predicted_ports_list, i); - tmp_time = smartlist_get(predicted_ports_times, i); - if (*tmp_port == port) { - *tmp_time = now; + SMARTLIST_FOREACH_BEGIN(predicted_ports_list, predicted_port_t *, pp) { + if (pp->port == port) { + pp->time = now; return; } - } + } SMARTLIST_FOREACH_END(pp); /* it's not there yet; we need to add it */ add_predicted_port(now, port); } @@@ -1725,28 -1752,36 +1748,28 @@@ * we'll want to make connections to the same port in the future. */ #define PREDICTED_CIRCS_RELEVANCE_TIME (60*60)
-/** Return a pointer to the list of port numbers that +/** Return a newly allocated pointer to a list of uint16_t * for ports that * are likely to be asked for in the near future. - * - * The caller promises not to mess with it. */ smartlist_t * rep_hist_get_predicted_ports(time_t now) { - int i; - uint16_t *tmp_port; - time_t *tmp_time; - + smartlist_t *out = smartlist_create(); tor_assert(predicted_ports_list); - tor_assert(predicted_ports_times);
/* clean out obsolete entries */ - for (i = 0; i < smartlist_len(predicted_ports_list); ++i) { - tmp_time = smartlist_get(predicted_ports_times, i); - if (*tmp_time + PREDICTED_CIRCS_RELEVANCE_TIME < now) { - tmp_port = smartlist_get(predicted_ports_list, i); - log_debug(LD_CIRC, "Expiring predicted port %d", *tmp_port); - smartlist_del(predicted_ports_list, i); - smartlist_del(predicted_ports_times, i); - rephist_total_alloc -= sizeof(uint16_t)+sizeof(time_t); - tor_free(tmp_port); - tor_free(tmp_time); - i--; + SMARTLIST_FOREACH_BEGIN(predicted_ports_list, predicted_port_t *, pp) { + if (pp->time + PREDICTED_CIRCS_RELEVANCE_TIME < now) { + log_debug(LD_CIRC, "Expiring predicted port %d", pp->port); + + rephist_total_alloc -= sizeof(predicted_port_t); + tor_free(pp); + SMARTLIST_DEL_CURRENT(predicted_ports_list, pp); + } else { + smartlist_add(out, tor_memdup(&pp->port, sizeof(uint16_t))); } - } - return predicted_ports_list; + } SMARTLIST_FOREACH_END(pp); + return out; }
/** The user asked us to do a resolve. Rather than keeping track of @@@ -1982,9 -2017,7 +2005,9 @@@ rep_hist_exit_stats_term(void tor_free(exit_streams); }
-/** Helper for qsort: compare two ints. */ +/** Helper for qsort: compare two ints. Does not handle overflow properly, + * but works fine for sorting an array of port numbers, which is what we use + * it for. */ static int _compare_int(const void *x, const void *y) { @@@ -2232,6 -2265,7 +2255,6 @@@ typedef struct circ_buffer_stats_t uint32_t processed_cells; double mean_num_cells_in_queue; double mean_time_cells_in_queue; - uint32_t local_circ_id; } circ_buffer_stats_t;
/** Holds stats. */ @@@ -2254,9 -2288,9 +2277,9 @@@ rep_hist_buffer_stats_add_circ(circuit_ return; if (!circuits_for_buffer_stats) circuits_for_buffer_stats = smartlist_create(); - start_of_interval = circ->timestamp_created > - start_of_buffer_stats_interval ? - circ->timestamp_created : + start_of_interval = (circ->timestamp_created.tv_sec > + start_of_buffer_stats_interval) ? + circ->timestamp_created.tv_sec : start_of_buffer_stats_interval; interval_length = (int) (end_of_interval - start_of_interval); stat = tor_malloc_zero(sizeof(circ_buffer_stats_t)); @@@ -2422,227 -2456,6 +2445,227 @@@ rep_hist_buffer_stats_write(time_t now return start_of_buffer_stats_interval + WRITE_STATS_INTERVAL; }
+/*** Connection statistics ***/ + +/** Start of the current connection stats interval or 0 if we're not + * collecting connection statistics. */ +static time_t start_of_conn_stats_interval; + +/** Initialize connection stats. */ +void +rep_hist_conn_stats_init(time_t now) +{ + start_of_conn_stats_interval = now; +} + +/* Count connections that we read and wrote less than these many bytes + * from/to as below threshold. */ +#define BIDI_THRESHOLD 20480 + +/* Count connections that we read or wrote at least this factor as many + * bytes from/to than we wrote or read to/from as mostly reading or + * writing. */ +#define BIDI_FACTOR 10 + +/* Interval length in seconds for considering read and written bytes for + * connection stats. */ +#define BIDI_INTERVAL 10 + +/* Start of next BIDI_INTERVAL second interval. */ +static time_t bidi_next_interval = 0; + +/* Number of connections that we read and wrote less than BIDI_THRESHOLD + * bytes from/to in BIDI_INTERVAL seconds. */ +static uint32_t below_threshold = 0; + +/* Number of connections that we read at least BIDI_FACTOR times more + * bytes from than we wrote to in BIDI_INTERVAL seconds. */ +static uint32_t mostly_read = 0; + +/* Number of connections that we wrote at least BIDI_FACTOR times more + * bytes to than we read from in BIDI_INTERVAL seconds. */ +static uint32_t mostly_written = 0; + +/* Number of connections that we read and wrote at least BIDI_THRESHOLD + * bytes from/to, but not BIDI_FACTOR times more in either direction in + * BIDI_INTERVAL seconds. */ +static uint32_t both_read_and_written = 0; + +/* Entry in a map from connection ID to the number of read and written + * bytes on this connection in a BIDI_INTERVAL second interval. */ +typedef struct bidi_map_entry_t { + HT_ENTRY(bidi_map_entry_t) node; + uint64_t conn_id; /**< Connection ID */ + size_t read; /**< Number of read bytes */ + size_t written; /**< Number of written bytes */ +} bidi_map_entry_t; + +/** Map of OR connections together with the number of read and written + * bytes in the current BIDI_INTERVAL second interval. */ +static HT_HEAD(bidimap, bidi_map_entry_t) bidi_map = + HT_INITIALIZER(); + +static int +bidi_map_ent_eq(const bidi_map_entry_t *a, const bidi_map_entry_t *b) +{ + return a->conn_id == b->conn_id; +} + +static unsigned +bidi_map_ent_hash(const bidi_map_entry_t *entry) +{ + return (unsigned) entry->conn_id; +} + +HT_PROTOTYPE(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash, + bidi_map_ent_eq); +HT_GENERATE(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash, + bidi_map_ent_eq, 0.6, malloc, realloc, free); + +static void +bidi_map_free(void) +{ + bidi_map_entry_t **ptr, **next, *ent; + for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) { + ent = *ptr; + next = HT_NEXT_RMV(bidimap, &bidi_map, ptr); + tor_free(ent); + } + HT_CLEAR(bidimap, &bidi_map); +} + +/** Reset counters for conn statistics. */ +void +rep_hist_reset_conn_stats(time_t now) +{ + start_of_conn_stats_interval = now; + below_threshold = 0; + mostly_read = 0; + mostly_written = 0; + both_read_and_written = 0; + bidi_map_free(); +} + +/** Stop collecting connection stats in a way that we can re-start doing + * so in rep_hist_conn_stats_init(). */ +void +rep_hist_conn_stats_term(void) +{ + rep_hist_reset_conn_stats(0); +} + +/** We read <b>num_read</b> bytes and wrote <b>num_written</b> from/to OR + * connection <b>conn_id</b> in second <b>when</b>. If this is the first + * observation in a new interval, sum up the last observations. Add bytes + * for this connection. */ +void +rep_hist_note_or_conn_bytes(uint64_t conn_id, size_t num_read, + size_t num_written, time_t when) +{ + if (!start_of_conn_stats_interval) + return; + /* Initialize */ + if (bidi_next_interval == 0) + bidi_next_interval = when + BIDI_INTERVAL; + /* Sum up last period's statistics */ + if (when >= bidi_next_interval) { + bidi_map_entry_t **ptr, **next, *ent; + for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) { + ent = *ptr; + if (ent->read + ent->written < BIDI_THRESHOLD) + below_threshold++; + else if (ent->read >= ent->written * BIDI_FACTOR) + mostly_read++; + else if (ent->written >= ent->read * BIDI_FACTOR) + mostly_written++; + else + both_read_and_written++; + next = HT_NEXT_RMV(bidimap, &bidi_map, ptr); + tor_free(ent); + } + while (when >= bidi_next_interval) + bidi_next_interval += BIDI_INTERVAL; + log_info(LD_GENERAL, "%d below threshold, %d mostly read, " + "%d mostly written, %d both read and written.", + below_threshold, mostly_read, mostly_written, + both_read_and_written); + } + /* Add this connection's bytes. */ + if (num_read > 0 || num_written > 0) { + bidi_map_entry_t *entry, lookup; + lookup.conn_id = conn_id; + entry = HT_FIND(bidimap, &bidi_map, &lookup); + if (entry) { + entry->written += num_written; + entry->read += num_read; + } else { + entry = tor_malloc_zero(sizeof(bidi_map_entry_t)); + entry->conn_id = conn_id; + entry->written = num_written; + entry->read = num_read; + HT_INSERT(bidimap, &bidi_map, entry); + } + } +} + +/** Return a newly allocated string containing the connection statistics + * until <b>now</b>, or NULL if we're not collecting conn stats. */ +char * +rep_hist_format_conn_stats(time_t now) +{ + char *result, written[ISO_TIME_LEN+1]; + + if (!start_of_conn_stats_interval) + return NULL; /* Not initialized. */ + + format_iso_time(written, now); + tor_asprintf(&result, "conn-bi-direct %s (%d s) %d,%d,%d,%d\n", + written, + (unsigned) (now - start_of_conn_stats_interval), + below_threshold, + mostly_read, + mostly_written, + both_read_and_written); + return result; +} + +/** If 24 hours have passed since the beginning of the current conn stats + * period, write conn stats to $DATADIR/stats/conn-stats (possibly + * overwriting an existing file) and reset counters. Return when we would + * next want to write conn stats or 0 if we never want to write. */ +time_t +rep_hist_conn_stats_write(time_t now) +{ + char *statsdir = NULL, *filename = NULL, *str = NULL; + + if (!start_of_conn_stats_interval) + return 0; /* Not initialized. */ + if (start_of_conn_stats_interval + WRITE_STATS_INTERVAL > now) + goto done; /* Not ready to write */ + + /* Generate history string. */ + str = rep_hist_format_conn_stats(now); + + /* Reset counters. */ + rep_hist_reset_conn_stats(now); + + /* Try to write to disk. */ + statsdir = get_datadir_fname("stats"); + if (check_private_dir(statsdir, CPD_CREATE) < 0) { + log_warn(LD_HIST, "Unable to create stats/ directory!"); + goto done; + } + filename = get_datadir_fname2("stats", "conn-stats"); + if (write_str_to_file(filename, str, 0) < 0) + log_warn(LD_HIST, "Unable to write conn stats to disk!"); + + done: + tor_free(str); + tor_free(filename); + tor_free(statsdir); + return start_of_conn_stats_interval + WRITE_STATS_INTERVAL; +} + /** Free all storage held by the OR/link history caches, by the * bandwidth history arrays, by the port history, or by statistics . */ void @@@ -2657,6 -2470,5 +2680,6 @@@ rep_hist_free_all(void tor_free(exit_streams); built_last_stability_doc_at = 0; predicted_ports_free(); + bidi_map_free(); }