Avoid calling node_get_all_orports() from node_is_a_configured_bridge()

All node_get_all_orports() does is allocate and return a smartlist
with at most two tor_addr_port_t members that match ORPort's of
node configuration. This is harmful for memory efficiency, as it
allocates the same stuff every time it is called. However,
node_is_a_configured_bridge() does not need to call it, as it
already has all the information to check if there is configured
bridge for a given node.

The new code is arranged in a way that hopefully makes each succeeding
linear search through bridge_list less likely.
This commit is contained in:
rl1987 2018-08-24 18:26:27 +03:00 committed by Nick Mathewson
parent 874eca6a8c
commit 1e77376e1a
2 changed files with 102 additions and 9 deletions

5
changes/bug27224 Normal file
View File

@ -0,0 +1,5 @@
o Minor bugfixes (performance)::
- Rework node_is_a_configured_bridge() to no longer
call node_get_all_orports(), which was performing too
many memory allocations. Fixes bug 27224; bugfix on
0.2.3.9.

View File

@ -31,6 +31,7 @@
#include "feature/nodelist/node_st.h"
#include "feature/nodelist/routerinfo_st.h"
#include "feature/nodelist/routerstatus_st.h"
#include "feature/nodelist/microdesc_st.h"
/** Information about a configured bridge. Currently this just matches the
* ones in the torrc file, but one day we may be able to learn about new
@ -283,17 +284,105 @@ routerinfo_is_a_configured_bridge(const routerinfo_t *ri)
return get_configured_bridge_by_routerinfo(ri) ? 1 : 0;
}
/** Return 1 if <b>node</b> is one of our configured bridges, else 0. */
/**
* Return 1 iff <b>bridge_list</b> contains entry matching
* given; IPv4 address in host byte order (<b>ipv4_addr</b>
* and <b>port</b> (and no identity digest) OR it contains an
* entry whose identity matches <b>digest</b>. Otherwise,
* return 0.
*/
static int
bridge_exists_with_ipv4h_addr_and_port(const uint32_t ipv4_addr,
const uint16_t port,
const char *digest)
{
tor_addr_t node_ipv4;
if (tor_addr_port_is_valid_ipv4h(ipv4_addr, port, 0)) {
tor_addr_from_ipv4h(&node_ipv4, ipv4_addr);
bridge_info_t *bridge =
get_configured_bridge_by_addr_port_digest(&node_ipv4,
port,
digest);
return (bridge != NULL);
}
return 0;
}
/**
* Return 1 iff <b>bridge_list</b> contains entry matching given
* <b>ipv6_addr</b> and <b>port</b> (and no identity digest) OR
* it contains an entry whose identity matches <b>digest</b>.
* Otherwise, return 0.
*/
static int
bridge_exists_with_ipv6_addr_and_port(const tor_addr_t *ipv6_addr,
const uint16_t port,
const char *digest)
{
if (!tor_addr_port_is_valid(ipv6_addr, port, 0))
return 0;
bridge_info_t *bridge =
get_configured_bridge_by_addr_port_digest(ipv6_addr,
port,
digest);
return (bridge != NULL);
}
/** Return 1 if <b>node</b> is one of our configured bridges, else 0.
* More specifically, return 1 iff: a bridge_info_t object exists in
* <b>bridge_list</b> such that: 1) It's identity is equal to node
* identity OR 2) It's identity digest is zero, but it matches
* address and port of any ORPort in the node.
*/
int
node_is_a_configured_bridge(const node_t *node)
{
int retval = 0;
smartlist_t *orports = node_get_all_orports(node);
retval = get_configured_bridge_by_orports_digest(node->identity,
orports) != NULL;
SMARTLIST_FOREACH(orports, tor_addr_port_t *, p, tor_free(p));
smartlist_free(orports);
return retval;
/* First, let's try searching for a bridge with matching identity. */
if (BUG(tor_digest_is_zero(node->identity)))
return 0;
if (find_bridge_by_digest(node->identity) != NULL)
return 1;
/* At this point, we have established that no bridge exists with
* matching identity digest. However, we still pass it into
* bridge_exists_* functions because we want further code to
* check for absence of identity digest in a bridge.
*/
if (node->ri) {
if (bridge_exists_with_ipv4h_addr_and_port(node->ri->addr,
node->ri->or_port,
node->identity))
return 1;
if (bridge_exists_with_ipv6_addr_and_port(&node->ri->ipv6_addr,
node->ri->ipv6_orport,
node->identity))
return 1;
} else if (node->rs) {
if (bridge_exists_with_ipv4h_addr_and_port(node->rs->addr,
node->rs->or_port,
node->identity))
return 1;
if (bridge_exists_with_ipv6_addr_and_port(&node->rs->ipv6_addr,
node->rs->ipv6_orport,
node->identity))
return 1;
} else if (node->md) {
if (bridge_exists_with_ipv6_addr_and_port(&node->md->ipv6_addr,
node->md->ipv6_orport,
node->identity))
return 1;
}
return 0;
}
/** We made a connection to a router at <b>addr</b>:<b>port</b>
@ -934,4 +1023,3 @@ bridges_free_all(void)
smartlist_free(bridge_list);
bridge_list = NULL;
}