commit 62820c22c53b412fa6965c35795e33e5caa39e3c
parent e73c3928d3315186b6bdb26c687efdb6011379b6
Author: Nick Mathewson <nickm@torproject.org>
Date: Tue, 11 Feb 2025 09:04:53 -0500
nodelist: Additionally use family IDs to decide family membership.
This implements the client side of happy families.
Diffstat:
2 files changed, 58 insertions(+), 3 deletions(-)
diff --git a/changes/happy-families-client b/changes/happy-families-client
@@ -0,0 +1,4 @@
+ o Major features (client):
+ - Clients now respect "happy families" per proposal 321.
+ This feature will eventually allow a much more compact representation
+ for relay families, for a significant savings in directory download size.
diff --git a/src/feature/nodelist/nodelist.c b/src/feature/nodelist/nodelist.c
@@ -2172,6 +2172,38 @@ node_has_declared_family_list(const node_t *node)
}
/**
+ * Return the listed family IDs of `a`, if it has any.
+ */
+static const smartlist_t *
+node_get_family_ids(const node_t *node)
+{
+ if (node->ri && node->ri->family_ids) {
+ return node->ri->family_ids;
+ } else if (node->md && node->md->family_ids) {
+ return node->md->family_ids;
+ } else {
+ return NULL;
+ }
+}
+
+/**
+ * Return true iff `a` and `b` have any family ID in common.
+ **/
+static bool
+nodes_have_common_family_id(const node_t *a, const node_t *b)
+{
+ const smartlist_t *ids_a = node_get_family_ids(a);
+ const smartlist_t *ids_b = node_get_family_ids(b);
+ if (ids_a == NULL || ids_b == NULL)
+ return false;
+ SMARTLIST_FOREACH(ids_a, const char *, id, {
+ if (smartlist_contains_string(ids_b, id))
+ return true;
+ });
+ return false;
+}
+
+/**
* Add to <b>out</b> every node_t that is listed by <b>node</b> as being in
* its family. (Note that these nodes are not in node's family unless they
* also agree that node is in their family.)
@@ -2217,12 +2249,20 @@ nodes_in_same_family(const node_t *node1, const node_t *node2)
return 1;
}
- /* Are they in the same family because the agree they are? */
- if (node_family_list_contains(node1, node2) &&
+ /* Are they in the same family because they agree they are? */
+ if (use_family_lists &&
+ node_family_list_contains(node1, node2) &&
node_family_list_contains(node2, node1)) {
return 1;
}
+ /* Are they in the same family because they have a common
+ * verified family ID? */
+ if (use_family_ids &&
+ nodes_have_common_family_id(node1, node2)) {
+ return 1;
+ }
+
/* Are they in the same family because the user says they are? */
if (options->NodeFamilySets) {
SMARTLIST_FOREACH(options->NodeFamilySets, const routerset_t *, rs, {
@@ -2279,7 +2319,8 @@ nodelist_add_node_and_family(smartlist_t *sl, const node_t *node)
/* Now, add all nodes in the declared family of this node, if they
* also declare this node to be in their family. */
- if (node_has_declared_family_list(node)) {
+ if (use_family_lists &&
+ node_has_declared_family_list(node)) {
smartlist_t *declared_family = smartlist_new();
node_lookup_declared_family_list(declared_family, node);
@@ -2293,6 +2334,16 @@ nodelist_add_node_and_family(smartlist_t *sl, const node_t *node)
smartlist_free(declared_family);
}
+ /* Now add all the nodes that share a verified family ID with this node. */
+ if (use_family_ids &&
+ node_get_family_ids(node)) {
+ SMARTLIST_FOREACH(all_nodes, const node_t *, node2, {
+ if (nodes_have_common_family_id(node, node2)) {
+ smartlist_add(sl, (void *)node2);
+ }
+ });
+ }
+
/* If the user declared any families locally, honor those too. */
if (options->NodeFamilySets) {
SMARTLIST_FOREACH(options->NodeFamilySets, const routerset_t *, rs, {