mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-10 21:23:58 +01:00
relay: Add bloomfiter of relay address + {OR|Dir}Port
In order to deny re-entry in the network, we now keep a bloomfilter of relay ORPort + address and authorities ORPort + address and DirPort + address combinations. So when an Exit stream is handled, we deny anything connecting back into the network on the ORPorts for relays and on the ORPort+DirPort for the authorities. Related to #2667 Signed-off-by: David Goulet <dgoulet@torproject.org>
This commit is contained in:
parent
9556276f07
commit
f26950fa7a
@ -69,3 +69,77 @@ 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));
|
||||
}
|
||||
|
@ -13,13 +13,14 @@
|
||||
#include "lib/cc/torint.h"
|
||||
#include "lib/container/bloomfilt.h"
|
||||
|
||||
struct tor_addr_t;
|
||||
|
||||
/**
|
||||
* An address_set_t represents a set of tor_addr_t values. The implementation
|
||||
* is probabilistic: false negatives cannot occur but false positives are
|
||||
* possible.
|
||||
*/
|
||||
typedef struct bloomfilt_t address_set_t;
|
||||
struct tor_addr_t;
|
||||
|
||||
address_set_t *address_set_new(int max_addresses_guess);
|
||||
#define address_set_free(set) bloomfilt_free(set)
|
||||
@ -28,4 +29,19 @@ 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);
|
||||
|
||||
#endif
|
||||
/**
|
||||
* 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) */
|
||||
|
@ -49,6 +49,42 @@ static smartlist_t *trusted_dir_servers = NULL;
|
||||
* and all fallback directory servers. */
|
||||
static smartlist_t *fallback_dir_servers = NULL;
|
||||
|
||||
/** Helper: From a given trusted directory entry, add the v4 or/and v6 address
|
||||
* to the nodelist address set. */
|
||||
static void
|
||||
add_trusted_dir_to_nodelist_addr_set(const dir_server_t *dir)
|
||||
{
|
||||
tor_addr_t tmp_addr;
|
||||
|
||||
tor_assert(dir);
|
||||
tor_assert(dir->is_authority);
|
||||
|
||||
/* Add IPv4 and then IPv6 if applicable. For authorities, we add the ORPort
|
||||
* and DirPort so re-entry into the network back to them is not possible. */
|
||||
tor_addr_from_ipv4h(&tmp_addr, dir->addr);
|
||||
nodelist_add_addr_to_address_set(&tmp_addr, dir->or_port, dir->dir_port);
|
||||
if (!tor_addr_is_null(&dir->ipv6_addr)) {
|
||||
/* IPv6 DirPort is not a thing yet for authorities. */
|
||||
nodelist_add_addr_to_address_set(&dir->ipv6_addr, dir->ipv6_orport, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/** Go over the trusted directory server list and add their address(es) to the
|
||||
* nodelist address set. This is called every time a new consensus is set. */
|
||||
void
|
||||
dirlist_add_trusted_dir_addresses(void)
|
||||
{
|
||||
if (!trusted_dir_servers) {
|
||||
return;
|
||||
}
|
||||
|
||||
SMARTLIST_FOREACH_BEGIN(trusted_dir_servers, const dir_server_t *, ent) {
|
||||
if (ent->is_authority) {
|
||||
add_trusted_dir_to_nodelist_addr_set(ent);
|
||||
}
|
||||
} SMARTLIST_FOREACH_END(ent);
|
||||
}
|
||||
|
||||
/** Return the number of directory authorities whose type matches some bit set
|
||||
* in <b>type</b> */
|
||||
int
|
||||
|
@ -44,4 +44,6 @@ void dir_server_add(dir_server_t *ent);
|
||||
void clear_dir_servers(void);
|
||||
void dirlist_free_all(void);
|
||||
|
||||
void dirlist_add_trusted_dir_addresses(void);
|
||||
|
||||
#endif
|
||||
|
@ -134,6 +134,10 @@ typedef struct nodelist_t {
|
||||
/* Set of addresses that belong to nodes we believe in. */
|
||||
address_set_t *node_addrs;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* The valid-after time of the last live consensus that initialized the
|
||||
* nodelist. We use this to detect outdated nodelists that need to be
|
||||
* rebuilt using a newer consensus. */
|
||||
@ -446,27 +450,61 @@ node_addrs_changed(node_t *node)
|
||||
static void
|
||||
node_add_to_address_set(const node_t *node)
|
||||
{
|
||||
if (!the_nodelist || !the_nodelist->node_addrs)
|
||||
tor_addr_t tmp_addr;
|
||||
|
||||
if (!the_nodelist ||
|
||||
!the_nodelist->node_addrs || !the_nodelist->reentry_set)
|
||||
return;
|
||||
|
||||
/* These various address sources can be redundant, but it's likely faster
|
||||
* to add them all than to compare them all for equality. */
|
||||
/* These various address sources can be redundant, but it's likely faster to
|
||||
* add them all than to compare them all for equality.
|
||||
*
|
||||
* For relays, we only add the ORPort in the addr+port set since we want to
|
||||
* allow re-entry into the network to the DirPort so the self reachability
|
||||
* test succeeds and thus the 0 value for the DirPort. */
|
||||
|
||||
if (node->rs) {
|
||||
if (node->rs->addr)
|
||||
address_set_add_ipv4h(the_nodelist->node_addrs, node->rs->addr);
|
||||
if (node->rs->addr) {
|
||||
tor_addr_from_ipv4h(&tmp_addr, node->rs->addr);
|
||||
nodelist_add_addr_to_address_set(&tmp_addr, node->rs->or_port, 0);
|
||||
}
|
||||
if (!tor_addr_is_null(&node->rs->ipv6_addr))
|
||||
address_set_add(the_nodelist->node_addrs, &node->rs->ipv6_addr);
|
||||
nodelist_add_addr_to_address_set(&node->rs->ipv6_addr,
|
||||
node->rs->ipv6_orport, 0);
|
||||
}
|
||||
if (node->ri) {
|
||||
if (node->ri->addr)
|
||||
address_set_add_ipv4h(the_nodelist->node_addrs, node->ri->addr);
|
||||
if (node->ri->addr) {
|
||||
tor_addr_from_ipv4h(&tmp_addr, node->ri->addr);
|
||||
nodelist_add_addr_to_address_set(&tmp_addr, node->ri->or_port, 0);
|
||||
}
|
||||
if (!tor_addr_is_null(&node->ri->ipv6_addr))
|
||||
address_set_add(the_nodelist->node_addrs, &node->ri->ipv6_addr);
|
||||
nodelist_add_addr_to_address_set(&node->ri->ipv6_addr,
|
||||
node->ri->ipv6_orport, 0);
|
||||
}
|
||||
if (node->md) {
|
||||
if (!tor_addr_is_null(&node->md->ipv6_addr))
|
||||
address_set_add(the_nodelist->node_addrs, &node->md->ipv6_addr);
|
||||
nodelist_add_addr_to_address_set(&node->md->ipv6_addr,
|
||||
node->md->ipv6_orport, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/** Add the given address into the nodelist address set. */
|
||||
void
|
||||
nodelist_add_addr_to_address_set(const tor_addr_t *addr,
|
||||
uint16_t or_port, uint16_t dir_port)
|
||||
{
|
||||
if (BUG(!addr) || tor_addr_is_null(addr) ||
|
||||
(!tor_addr_is_v4(addr) && tor_addr_family(addr) != AF_INET6) ||
|
||||
!the_nodelist || !the_nodelist->node_addrs ||
|
||||
!the_nodelist->reentry_set) {
|
||||
return;
|
||||
}
|
||||
address_set_add(the_nodelist->node_addrs, addr);
|
||||
if (or_port != 0) {
|
||||
addr_port_set_add(the_nodelist->reentry_set, addr, or_port);
|
||||
}
|
||||
if (dir_port != 0) {
|
||||
addr_port_set_add(the_nodelist->reentry_set, addr, dir_port);
|
||||
}
|
||||
}
|
||||
|
||||
@ -484,6 +522,21 @@ nodelist_probably_contains_address(const tor_addr_t *addr)
|
||||
return address_set_probably_contains(the_nodelist->node_addrs, addr);
|
||||
}
|
||||
|
||||
/** 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. */
|
||||
bool
|
||||
nodelist_reentry_probably_contains(const tor_addr_t *addr, uint16_t port)
|
||||
{
|
||||
if (BUG(!addr) || BUG(!port))
|
||||
return false;
|
||||
|
||||
if (!the_nodelist || !the_nodelist->reentry_set)
|
||||
return false;
|
||||
|
||||
return addr_port_set_probably_contains(the_nodelist->reentry_set,
|
||||
addr, port);
|
||||
}
|
||||
|
||||
/** Add <b>ri</b> to an appropriate node in the nodelist. If we replace an
|
||||
* old routerinfo, and <b>ri_old_out</b> is not NULL, set *<b>ri_old_out</b>
|
||||
* to the previous routerinfo.
|
||||
@ -612,10 +665,15 @@ nodelist_set_consensus(networkstatus_t *ns)
|
||||
node->rs = NULL);
|
||||
|
||||
/* Conservatively estimate that every node will have 2 addresses. */
|
||||
const int estimated_addresses = smartlist_len(ns->routerstatus_list) *
|
||||
get_estimated_address_per_node();
|
||||
int estimated_addresses = smartlist_len(ns->routerstatus_list) *
|
||||
get_estimated_address_per_node();
|
||||
estimated_addresses += (get_n_authorities(V3_DIRINFO | BRIDGE_DIRINFO) *
|
||||
get_estimated_address_per_node());
|
||||
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. */
|
||||
the_nodelist->reentry_set = addr_port_set_new(estimated_addresses * 2);
|
||||
|
||||
SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) {
|
||||
node_t *node = node_get_or_create(rs->identity_digest);
|
||||
@ -664,6 +722,9 @@ nodelist_set_consensus(networkstatus_t *ns)
|
||||
SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) {
|
||||
node_add_to_address_set(node);
|
||||
} SMARTLIST_FOREACH_END(node);
|
||||
/* Then, add all trusted configured directories. Some might not be in the
|
||||
* consensus so make sure we know them. */
|
||||
dirlist_add_trusted_dir_addresses();
|
||||
|
||||
if (! authdir) {
|
||||
SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) {
|
||||
@ -839,6 +900,8 @@ nodelist_free_all(void)
|
||||
|
||||
address_set_free(the_nodelist->node_addrs);
|
||||
the_nodelist->node_addrs = NULL;
|
||||
addr_port_set_free(the_nodelist->reentry_set);
|
||||
the_nodelist->reentry_set = NULL;
|
||||
|
||||
tor_free(the_nodelist);
|
||||
}
|
||||
|
@ -35,6 +35,10 @@ node_t *nodelist_add_microdesc(microdesc_t *md);
|
||||
void nodelist_set_consensus(networkstatus_t *ns);
|
||||
void nodelist_ensure_freshness(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);
|
||||
void nodelist_add_addr_to_address_set(const tor_addr_t *addr,
|
||||
uint16_t or_port, uint16_t dir_port);
|
||||
|
||||
void nodelist_remove_microdesc(const char *identity_digest, microdesc_t *md);
|
||||
void nodelist_remove_routerinfo(routerinfo_t *ri);
|
||||
|
Loading…
Reference in New Issue
Block a user