node: Move reentry set to use a digestmap_t

Any lookup now will be certain and not probabilistic as the bloomfilter.

Closes #40269

Signed-off-by: David Goulet <dgoulet@torproject.org>
This commit is contained in:
David Goulet 2021-02-01 12:20:39 -05:00
parent 838e07be9d
commit bd4a3f64a1
4 changed files with 57 additions and 23 deletions

View File

@ -4290,7 +4290,7 @@ connection_exit_connect(edge_connection_t *edge_conn)
* case of an attack so this is a small price to pay. */ * case of an attack so this is a small price to pay. */
if (!connection_edge_is_rendezvous_stream(edge_conn) && if (!connection_edge_is_rendezvous_stream(edge_conn) &&
!network_reentry_is_allowed() && !network_reentry_is_allowed() &&
nodelist_reentry_probably_contains(&conn->addr, conn->port)) { nodelist_reentry_contains(&conn->addr, conn->port)) {
log_info(LD_EXIT, "%s tried to connect back to a known relay address. " log_info(LD_EXIT, "%s tried to connect back to a known relay address. "
"Closing.", connection_describe(conn)); "Closing.", connection_describe(conn));
connection_edge_end(edge_conn, END_STREAM_REASON_TORPROTOCOL); connection_edge_end(edge_conn, END_STREAM_REASON_TORPROTOCOL);

View File

@ -137,7 +137,7 @@ typedef struct nodelist_t {
/* Set of addresses + port that belong to nodes we know and that we don't /* Set of addresses + port that belong to nodes we know and that we don't
* allow network re-entry towards them. */ * allow network re-entry towards them. */
addr_port_set_t *reentry_set; digestmap_t *reentry_set;
/* The valid-after time of the last live consensus that initialized the /* The valid-after time of the last live consensus that initialized the
* nodelist. We use this to detect outdated nodelists that need to be * nodelist. We use this to detect outdated nodelists that need to be
@ -485,6 +485,42 @@ node_add_to_address_set(const node_t *node)
} }
} }
/** Build a construction for the reentry set consisting of an address and port
* pair.
*
* If the given address is _not_ AF_INET or AF_INET6, then the item is an
* array of 0s.
*
* Return a pointer to a static buffer containing the item. Next call to this
* function invalidates its previous content. */
static char *
build_addr_port_item(const tor_addr_t *addr, const uint16_t port)
{
/* At most 16 bytes are put in this (IPv6) and then 2 bytes for the port
* which is within the maximum of 20 bytes (DIGEST_LEN). */
static char data[DIGEST_LEN];
memset(data, 0, sizeof(data));
switch (tor_addr_family(addr)) {
case AF_INET:
memcpy(data, &addr->addr.in_addr.s_addr, 4);
break;
case AF_INET6:
memcpy(data, &addr->addr.in6_addr.s6_addr, 16);
break;
case AF_UNSPEC:
/* Leave the 0. */
break;
default:
/* LCOV_EXCL_START */
tor_fragile_assert();
/* LCOV_EXCL_STOP */
}
memcpy(data + 16, &port, sizeof(port));
return data;
}
/** Add the given address into the nodelist address set. */ /** Add the given address into the nodelist address set. */
void void
nodelist_add_addr_to_address_set(const tor_addr_t *addr, nodelist_add_addr_to_address_set(const tor_addr_t *addr,
@ -498,10 +534,12 @@ nodelist_add_addr_to_address_set(const tor_addr_t *addr,
} }
address_set_add(the_nodelist->node_addrs, addr); address_set_add(the_nodelist->node_addrs, addr);
if (or_port != 0) { if (or_port != 0) {
addr_port_set_add(the_nodelist->reentry_set, addr, or_port); digestmap_set(the_nodelist->reentry_set,
build_addr_port_item(addr, or_port), (void*) 1);
} }
if (dir_port != 0) { if (dir_port != 0) {
addr_port_set_add(the_nodelist->reentry_set, addr, dir_port); digestmap_set(the_nodelist->reentry_set,
build_addr_port_item(addr, dir_port), (void*) 1);
} }
} }
@ -522,7 +560,7 @@ nodelist_probably_contains_address(const tor_addr_t *addr)
/** Return true if <b>addr</b> is the address of some node in the nodelist and /** Return true if <b>addr</b> is the address of some node in the nodelist and
* corresponds also to the given port. If not, probably return false. */ * corresponds also to the given port. If not, probably return false. */
bool bool
nodelist_reentry_probably_contains(const tor_addr_t *addr, uint16_t port) nodelist_reentry_contains(const tor_addr_t *addr, uint16_t port)
{ {
if (BUG(!addr) || BUG(!port)) if (BUG(!addr) || BUG(!port))
return false; return false;
@ -530,8 +568,8 @@ nodelist_reentry_probably_contains(const tor_addr_t *addr, uint16_t port)
if (!the_nodelist || !the_nodelist->reentry_set) if (!the_nodelist || !the_nodelist->reentry_set)
return false; return false;
return addr_port_set_probably_contains(the_nodelist->reentry_set, return digestmap_get(the_nodelist->reentry_set,
addr, port); build_addr_port_item(addr, port)) != NULL;
} }
/** Add <b>ri</b> to an appropriate node in the nodelist. If we replace an /** Add <b>ri</b> to an appropriate node in the nodelist. If we replace an
@ -667,15 +705,12 @@ nodelist_set_consensus(const networkstatus_t *ns)
get_estimated_address_per_node(); get_estimated_address_per_node();
estimated_addresses += (get_n_authorities(V3_DIRINFO | BRIDGE_DIRINFO) * estimated_addresses += (get_n_authorities(V3_DIRINFO | BRIDGE_DIRINFO) *
get_estimated_address_per_node()); get_estimated_address_per_node());
/* Clear our sets because we will repopulate them with what this new
* consensus contains. */
address_set_free(the_nodelist->node_addrs); address_set_free(the_nodelist->node_addrs);
addr_port_set_free(the_nodelist->reentry_set);
the_nodelist->node_addrs = address_set_new(estimated_addresses); the_nodelist->node_addrs = address_set_new(estimated_addresses);
/* Times two here is for both the ORPort and DirPort. We double it again in digestmap_free(the_nodelist->reentry_set, NULL);
* order to minimize as much as possible the false positive when looking up the_nodelist->reentry_set = digestmap_new();
* this set. Reason is that Exit streams that are legitimate but end up a
* false positive against this set will thus be considered reentry and be
* rejected which means a bad UX. */
the_nodelist->reentry_set = addr_port_set_new(estimated_addresses * 2 * 2);
SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) { SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) {
node_t *node = node_get_or_create(rs->identity_digest); node_t *node = node_get_or_create(rs->identity_digest);
@ -902,7 +937,7 @@ nodelist_free_all(void)
address_set_free(the_nodelist->node_addrs); address_set_free(the_nodelist->node_addrs);
the_nodelist->node_addrs = NULL; the_nodelist->node_addrs = NULL;
addr_port_set_free(the_nodelist->reentry_set); digestmap_free(the_nodelist->reentry_set, NULL);
the_nodelist->reentry_set = NULL; the_nodelist->reentry_set = NULL;
tor_free(the_nodelist); tor_free(the_nodelist);

View File

@ -35,8 +35,7 @@ node_t *nodelist_add_microdesc(microdesc_t *md);
void nodelist_set_consensus(const networkstatus_t *ns); void nodelist_set_consensus(const networkstatus_t *ns);
void nodelist_ensure_freshness(const networkstatus_t *ns); void nodelist_ensure_freshness(const networkstatus_t *ns);
int nodelist_probably_contains_address(const tor_addr_t *addr); int nodelist_probably_contains_address(const tor_addr_t *addr);
bool nodelist_reentry_probably_contains(const tor_addr_t *addr, bool nodelist_reentry_contains(const tor_addr_t *addr, uint16_t port);
uint16_t port);
void nodelist_add_addr_to_address_set(const tor_addr_t *addr, void nodelist_add_addr_to_address_set(const tor_addr_t *addr,
uint16_t or_port, uint16_t dir_port); uint16_t or_port, uint16_t dir_port);

View File

@ -215,8 +215,8 @@ test_exit_no_reentry(void *arg)
nodelist_set_consensus(dummy_ns); nodelist_set_consensus(dummy_ns);
/* The address set is empty. Try it anyway */ /* The address set is empty. Try it anyway */
tt_assert(!nodelist_reentry_probably_contains(&addr_v4, 244)); tt_assert(!nodelist_reentry_contains(&addr_v4, 244));
tt_assert(!nodelist_reentry_probably_contains(&addr_v6, 244)); tt_assert(!nodelist_reentry_contains(&addr_v6, 244));
/* Now let's populate the network */ /* Now let's populate the network */
md = tor_malloc_zero(sizeof(*md)); md = tor_malloc_zero(sizeof(*md));
@ -243,12 +243,12 @@ test_exit_no_reentry(void *arg)
/* First let's try an address that is on the no-reentry list, but with a /* First let's try an address that is on the no-reentry list, but with a
different port */ different port */
tt_assert(!nodelist_reentry_probably_contains(&addr_v4, 666)); tt_assert(!nodelist_reentry_contains(&addr_v4, 666));
tt_assert(!nodelist_reentry_probably_contains(&addr_v6, 444)); tt_assert(!nodelist_reentry_contains(&addr_v6, 444));
/* OK now let's try with the right address and right port */ /* OK now let's try with the right address and right port */
tt_assert(nodelist_reentry_probably_contains(&addr_v4, 444)); tt_assert(nodelist_reentry_contains(&addr_v4, 444));
tt_assert(nodelist_reentry_probably_contains(&addr_v6, 666)); tt_assert(nodelist_reentry_contains(&addr_v6, 666));
done: done:
routerstatus_free(rs); routerinfo_free(ri); microdesc_free(md); routerstatus_free(rs); routerinfo_free(ri); microdesc_free(md);