[tor-commits] [tor/release-0.4.4] relay: Add bloomfiter of relay address + {OR|Dir}Port

dgoulet at torproject.org dgoulet at torproject.org
Wed Feb 3 14:50:40 UTC 2021


commit f26950fa7a077de4b6a9329af397fce74182b1e5
Author: David Goulet <dgoulet at torproject.org>
Date:   Wed Jan 27 21:49:56 2021 -0500

    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 at torproject.org>
---
 src/core/or/address_set.c       | 74 +++++++++++++++++++++++++++++++++++
 src/core/or/address_set.h       | 20 +++++++++-
 src/feature/nodelist/dirlist.c  | 36 +++++++++++++++++
 src/feature/nodelist/dirlist.h  |  2 +
 src/feature/nodelist/nodelist.c | 87 +++++++++++++++++++++++++++++++++++------
 src/feature/nodelist/nodelist.h |  4 ++
 6 files changed, 209 insertions(+), 14 deletions(-)

diff --git a/src/core/or/address_set.c b/src/core/or/address_set.c
index 758fba4aac..c2daf89dde 100644
--- a/src/core/or/address_set.c
+++ b/src/core/or/address_set.c
@@ -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));
+}
diff --git a/src/core/or/address_set.h b/src/core/or/address_set.h
index 7a9e71628e..a7b7cb3f78 100644
--- a/src/core/or/address_set.h
+++ b/src/core/or/address_set.h
@@ -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) */
diff --git a/src/feature/nodelist/dirlist.c b/src/feature/nodelist/dirlist.c
index 93baa6e4e0..25f769dd5a 100644
--- a/src/feature/nodelist/dirlist.c
+++ b/src/feature/nodelist/dirlist.c
@@ -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
diff --git a/src/feature/nodelist/dirlist.h b/src/feature/nodelist/dirlist.h
index 9fabd0a44a..9354769bcf 100644
--- a/src/feature/nodelist/dirlist.h
+++ b/src/feature/nodelist/dirlist.h
@@ -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
diff --git a/src/feature/nodelist/nodelist.c b/src/feature/nodelist/nodelist.c
index 8974d95db6..9d553ce1f5 100644
--- a/src/feature/nodelist/nodelist.c
+++ b/src/feature/nodelist/nodelist.c
@@ -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);
 }
diff --git a/src/feature/nodelist/nodelist.h b/src/feature/nodelist/nodelist.h
index c430f497d5..4c4ee6fe83 100644
--- a/src/feature/nodelist/nodelist.h
+++ b/src/feature/nodelist/nodelist.h
@@ -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);





More information about the tor-commits mailing list