diff --git a/src/core/or/address_set.c b/src/core/or/address_set.c index fcddc55e9f..9bd3cc0f2d 100644 --- a/src/core/or/address_set.c +++ b/src/core/or/address_set.c @@ -15,7 +15,6 @@ #include "lib/net/address.h" #include "lib/container/bloomfilt.h" #include "lib/crypt_ops/crypto_rand.h" -#include "siphash.h" /** Wrap our hash function to have the signature that the bloom filter * needs. */ @@ -69,77 +68,3 @@ address_set_probably_contains(const address_set_t *set, { return bloomfilt_probably_contains(set, addr); } - -/* Length of the item is an address (IPv4 or IPv6) and a 2 byte port. We use - * 16 bytes for the address here (IPv6) since we do not know which family - * the given address in the item thus in the case of IPv4, the extra bytes - * are simply zeroes to accomodate. */ -#define BLOOMFILT_ADDR_PORT_ITEM_LEN (16 + sizeof(uint16_t)) - -/** Build an item for the bloomfilter 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 const uint8_t * -build_addr_port_item(const tor_addr_t *addr, const uint16_t port) -{ - static uint8_t data[BLOOMFILT_ADDR_PORT_ITEM_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; -} - -/** Return a hash value for the given item that the bloomfilter will use. */ -static uint64_t -bloomfilt_addr_port_hash(const struct sipkey *key, - const void *item) -{ - return siphash24(item, BLOOMFILT_ADDR_PORT_ITEM_LEN, key); -} - -/** Allocate and return an addr_port_set_t, suitable for holding up to - * max_address_guess distinct values. */ -addr_port_set_t * -addr_port_set_new(int max_addresses_guess) -{ - uint8_t k[BLOOMFILT_KEY_LEN]; - crypto_rand((void*)k, sizeof(k)); - return bloomfilt_new(max_addresses_guess, bloomfilt_addr_port_hash, k); -} - -/** Add an address and port pair to the given set. */ -void -addr_port_set_add(addr_port_set_t *set, const tor_addr_t *addr, uint16_t port) -{ - bloomfilt_add(set, build_addr_port_item(addr, port)); -} - -/** Return true if the given address and port pair are in the set. Of course, - * this is a bloomfilter and thus in rare occasion, a false positive happens - * thus the "probably". */ -bool -addr_port_set_probably_contains(const addr_port_set_t *set, - const tor_addr_t *addr, uint16_t port) -{ - return !!bloomfilt_probably_contains(set, build_addr_port_item(addr, port)); -} diff --git a/src/core/or/address_set.h b/src/core/or/address_set.h index 2c78ab0576..b4d94b65a9 100644 --- a/src/core/or/address_set.h +++ b/src/core/or/address_set.h @@ -29,19 +29,4 @@ void address_set_add_ipv4h(address_set_t *set, uint32_t addr); int address_set_probably_contains(const address_set_t *set, const struct tor_addr_t *addr); -/** - * An addr_port_set_t represents a set of tor_addr_t values with a uint16_t - * port value. The implementation is probabilistic: false negatives cannot - * occur but false positives are possible. - */ -typedef struct bloomfilt_t addr_port_set_t; - -addr_port_set_t *addr_port_set_new(int max_addresses_guess); -#define addr_port_set_free(s) bloomfilt_free(s) -void addr_port_set_add(addr_port_set_t *set, - const struct tor_addr_t *addr, uint16_t port); -bool addr_port_set_probably_contains(const addr_port_set_t *set, - const struct tor_addr_t *addr, - uint16_t port); - #endif /* !defined(TOR_ADDRESS_SET_H) */ diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c index 04005b29a6..a33c64fe19 100644 --- a/src/core/or/connection_edge.c +++ b/src/core/or/connection_edge.c @@ -4290,7 +4290,7 @@ connection_exit_connect(edge_connection_t *edge_conn) * case of an attack so this is a small price to pay. */ if (!connection_edge_is_rendezvous_stream(edge_conn) && !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. " "Closing.", connection_describe(conn)); connection_edge_end(edge_conn, END_STREAM_REASON_CONNECTREFUSED); diff --git a/src/feature/nodelist/nodelist.c b/src/feature/nodelist/nodelist.c index 6a6f277849..03b158e68d 100644 --- a/src/feature/nodelist/nodelist.c +++ b/src/feature/nodelist/nodelist.c @@ -137,7 +137,7 @@ typedef struct nodelist_t { /* Set of addresses + port that belong to nodes we know and that we don't * 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 * 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. */ void 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); 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) { - 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 addr is the address of some node in the nodelist and * corresponds also to the given port. If not, probably return false. */ 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)) 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) return false; - return addr_port_set_probably_contains(the_nodelist->reentry_set, - addr, port); + return digestmap_get(the_nodelist->reentry_set, + build_addr_port_item(addr, port)) != NULL; } /** Add ri 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(); estimated_addresses += (get_n_authorities(V3_DIRINFO | BRIDGE_DIRINFO) * 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); - addr_port_set_free(the_nodelist->reentry_set); the_nodelist->node_addrs = address_set_new(estimated_addresses); - /* Times two here is for both the ORPort and DirPort. We double it again in - * order to minimize as much as possible the false positive when looking up - * 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); + digestmap_free(the_nodelist->reentry_set, NULL); + the_nodelist->reentry_set = digestmap_new(); SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) { 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); 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; tor_free(the_nodelist); diff --git a/src/feature/nodelist/nodelist.h b/src/feature/nodelist/nodelist.h index ada0a640a1..44b8918b06 100644 --- a/src/feature/nodelist/nodelist.h +++ b/src/feature/nodelist/nodelist.h @@ -35,8 +35,7 @@ node_t *nodelist_add_microdesc(microdesc_t *md); void nodelist_set_consensus(const networkstatus_t *ns); void nodelist_ensure_freshness(const networkstatus_t *ns); int nodelist_probably_contains_address(const tor_addr_t *addr); -bool nodelist_reentry_probably_contains(const tor_addr_t *addr, - uint16_t port); +bool nodelist_reentry_contains(const tor_addr_t *addr, uint16_t port); void nodelist_add_addr_to_address_set(const tor_addr_t *addr, uint16_t or_port, uint16_t dir_port); diff --git a/src/test/test_address_set.c b/src/test/test_address_set.c index 55a0b106d2..a73fbab76a 100644 --- a/src/test/test_address_set.c +++ b/src/test/test_address_set.c @@ -215,8 +215,8 @@ test_exit_no_reentry(void *arg) nodelist_set_consensus(dummy_ns); /* The address set is empty. Try it anyway */ - tt_assert(!nodelist_reentry_probably_contains(&addr_v4, 244)); - tt_assert(!nodelist_reentry_probably_contains(&addr_v6, 244)); + tt_assert(!nodelist_reentry_contains(&addr_v4, 244)); + tt_assert(!nodelist_reentry_contains(&addr_v6, 244)); /* Now let's populate the network */ 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 different port */ - tt_assert(!nodelist_reentry_probably_contains(&addr_v4, 666)); - tt_assert(!nodelist_reentry_probably_contains(&addr_v6, 444)); + tt_assert(!nodelist_reentry_contains(&addr_v4, 666)); + tt_assert(!nodelist_reentry_contains(&addr_v6, 444)); /* 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_probably_contains(&addr_v6, 666)); + tt_assert(nodelist_reentry_contains(&addr_v4, 444)); + tt_assert(nodelist_reentry_contains(&addr_v6, 666)); done: routerstatus_free(rs); routerinfo_free(ri); microdesc_free(md);