From f9ea242acabcaec0ab0fbd0de5e9999dfcdb0193 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 30 Sep 2010 18:25:25 -0400 Subject: [PATCH] Implement node-based router family code Also, make the NodeFamily option into a list of routersets. This lets us git rid of router_in_nickname_list (or whatever it was called) without porting it to work with nodes, and also lets people specify country codes and IP ranges in NodeFamily --- doc/nodefamily_routerset | 4 + doc/tor.1.txt | 3 +- src/or/config.c | 23 ++++- src/or/nodelist.c | 13 +++ src/or/nodelist.h | 1 + src/or/or.h | 3 +- src/or/routerlist.c | 182 +++++++++++++++++++++++---------------- 7 files changed, 153 insertions(+), 76 deletions(-) create mode 100644 doc/nodefamily_routerset diff --git a/doc/nodefamily_routerset b/doc/nodefamily_routerset new file mode 100644 index 0000000000..0af62e11f6 --- /dev/null +++ b/doc/nodefamily_routerset @@ -0,0 +1,4 @@ + o Minor features + - The NodeFamily option -- which let you declare that you want to + consider nodes to be part of a family whether they list themselves + that way or not -- now allows IP address ranges and country codes. diff --git a/doc/tor.1.txt b/doc/tor.1.txt index 42eed6e3dd..90a52fe1ef 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -571,7 +571,8 @@ The following options are useful only for clients (that is, if constitute a "family" of similar or co-administered servers, so never use any two of them in the same circuit. Defining a NodeFamily is only needed when a server doesn't list the family itself (with MyFamily). This option - can be used multiple times. + can be used multiple times. In addition to nodes, you can also list + IP address and ranges and country codes in {curly braces}. **EnforceDistinctSubnets** **0**|**1**:: If 1, Tor will not put two servers whose IP addresses are "too close" on diff --git a/src/or/config.c b/src/or/config.c index 4f7077153d..b438626343 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -691,6 +691,11 @@ or_options_free(or_options_t *options) return; routerset_free(options->_ExcludeExitNodesUnion); + if (options->NodeFamilySets) { + SMARTLIST_FOREACH(options->NodeFamilySets, routerset_t *, + rs, routerset_free(rs)); + smartlist_free(options->NodeFamilySets); + } config_free(&options_format, options); } @@ -3084,6 +3089,18 @@ options_validate(or_options_t *old_options, or_options_t *options, routerset_union(options->_ExcludeExitNodesUnion,options->ExcludeNodes); } + if (options->NodeFamilies) { + options->NodeFamilySets = smartlist_create(); + for (cl = options->NodeFamilies; cl; cl = cl->next) { + routerset_t *rs = routerset_new(); + if (routerset_parse(rs, cl->value, cl->key) == 0) { + smartlist_add(options->NodeFamilySets, rs); + } else { + routerset_free(rs); + } + } + } + if (options->ExcludeNodes && options->StrictNodes) { COMPLAIN("You have asked to exclude certain relays from all positions " "in your circuits. Expect hidden services and other Tor " @@ -3549,8 +3566,12 @@ options_validate(or_options_t *old_options, or_options_t *options, if (check_nickname_list(options->MyFamily, "MyFamily", msg)) return -1; for (cl = options->NodeFamilies; cl; cl = cl->next) { - if (check_nickname_list(cl->value, "NodeFamily", msg)) + routerset_t *rs = routerset_new(); + if (routerset_parse(rs, cl->value, cl->key)) { + routerset_free(rs); return -1; + } + routerset_free(rs); } if (validate_addr_policies(options, msg) < 0) diff --git a/src/or/nodelist.c b/src/or/nodelist.c index dd83abf54e..7c036982d5 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -698,6 +698,19 @@ node_is_me(const node_t *node) return router_digest_is_me(node->identity); } +/** Return node declared family (as a list of names), or NULL if + * the node didn't declare a family. */ +const smartlist_t * +node_get_declared_family(const node_t *node) +{ + if (node->ri && node->ri->declared_family) + return node->ri->declared_family; + else if (node->md && node->md->family) + return node->md->family; + else + return NULL; +} + /* KILLTHIS XXXX NM -- it's a dummy to keep UNIMPLEMENTED_NODELIST() * working */ int unimplemented_nodelist_truth = 1; diff --git a/src/or/nodelist.h b/src/or/nodelist.h index 3dceb75731..cb78366508 100644 --- a/src/or/nodelist.h +++ b/src/or/nodelist.h @@ -45,6 +45,7 @@ const char *node_get_platform(const node_t *node); void node_get_address_string(const node_t *node, char *cp, size_t len); long node_get_declared_uptime(const node_t *node); time_t node_get_published_on(const node_t *node); +const smartlist_t *node_get_declared_family(const node_t *node); smartlist_t *nodelist_get_list(void); diff --git a/src/or/or.h b/src/or/or.h index 670cf7dd40..ac9634748e 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2725,7 +2725,8 @@ typedef struct { char *MyFamily; /**< Declared family for this OR. */ config_line_t *NodeFamilies; /**< List of config lines for - * node families */ + * node families */ + smartlist_t *NodeFamilySets; /**< List of parsed NodeFamilies values. */ config_line_t *AuthDirBadDir; /**< Address policy for descriptors to * mark as bad dir mirrors. */ config_line_t *AuthDirBadExit; /**< Address policy for descriptors to diff --git a/src/or/routerlist.c b/src/or/routerlist.c index a082ff410e..73657fca65 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -46,6 +46,8 @@ static const routerstatus_t *router_pick_trusteddirserver_impl( static void mark_all_trusteddirservers_up(void); static int router_nickname_matches(const routerinfo_t *router, const char *nickname); +static int node_nickname_matches(const node_t *router, + const char *nickname); static void trusted_dir_server_free(trusted_dir_server_t *ds); static int signed_desc_digest_is_recognized(signed_descriptor_t *desc); static void update_router_have_minimum_dir_info(void); @@ -1300,72 +1302,70 @@ router_reset_status_download_failures(void) mark_all_trusteddirservers_up(); } -/** Return true iff router1 and router2 have the same /16 network. */ +/** Return true iff router1 and router2 have similar enough network addresses + * that we should treat them as being in the same family */ static INLINE int -routers_in_same_network_family(const routerinfo_t *r1, const routerinfo_t *r2) +addrs_in_same_network_family(const tor_addr_t *a1, + const tor_addr_t *a2) { - return (r1->addr & 0xffff0000) == (r2->addr & 0xffff0000); + /* XXXX MOVE ? */ + return 0 == tor_addr_compare_masked(a1, a2, 16, CMP_SEMANTIC); } -#if 0 -/** Look through the routerlist and identify routers that - * advertise the same /16 network address as router. - * Add each of them to sl. - */ -static void -routerlist_add_network_family(smartlist_t *sl, const routerinfo_t *router) -{ - SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, r, - { - if (router != r && routers_in_same_network_family(router, r)) - smartlist_add(sl, r); - }); -} -#endif - /** Add all the family of router to the smartlist sl. * This is used to make sure we don't pick siblings in a single path, * or pick more than one relay from a family for our entry guard list. */ void -nodelist_add_node_family(smartlist_t *sl, const node_t *router) +nodelist_add_node_family(smartlist_t *sl, const node_t *node) { /* XXXX MOVE */ -#if 0 - const routerinfo_t *r; - config_line_t *cl; + const smartlist_t *all_nodes = nodelist_get_list(); + const smartlist_t *declared_family = node_get_declared_family(node); or_options_t *options = get_options(); - /* First, add any routers with similar network addresses. */ - if (options->EnforceDistinctSubnets) - routerlist_add_network_family(sl, router); + /* First, add any nodes with similar network addresses. */ + if (options->EnforceDistinctSubnets) { + tor_addr_t node_addr; + node_get_addr(node, &node_addr); - if (router->declared_family) { - /* Add every r such that router declares familyness with r, and r + SMARTLIST_FOREACH_BEGIN(all_nodes, const node_t *, node2) { + tor_addr_t a; + node_get_addr(node2, &a); + if (addrs_in_same_network_family(&a, &node_addr)) + smartlist_add(sl, (void*)node2); + } SMARTLIST_FOREACH_END(node); + } + + /* Now, add all nodes in the declared_family of this node, if they + * also declare this node to be in their family. */ + if (declared_family) { + /* Add every r such that router declares familyness with node, and node * declares familyhood with router. */ - SMARTLIST_FOREACH_BEGIN(router->declared_family, const char *, n) { - if (!(r = router_get_by_nickname(n, 0))) - continue; - if (!r->declared_family) - continue; - SMARTLIST_FOREACH(r->declared_family, const char *, n2, - { - if (router_nickname_matches(router, n2)) - smartlist_add(sl, (void*)r); - }); - } SMARTLIST_FOREACH_END(n); + SMARTLIST_FOREACH_BEGIN(declared_family, const char *, name) { + const node_t *node2; + const smartlist_t *family2; + if (!(node2 = node_get_by_nickname(name, 0))) + continue; + if (!(family2 = node_get_declared_family(node2))) + continue; + SMARTLIST_FOREACH(family2, const char *, name2, { + if (node_nickname_matches(node, name2)) { + smartlist_add(sl, (void*)node2); + break; + } + }); + } SMARTLIST_FOREACH_END(name); } /* If the user declared any families locally, honor those too. */ - for (cl = options->NodeFamilies; cl; cl = cl->next) { - if (router_nickname_is_in_list(router, cl->value)) { - add_nickname_list_to_smartlist(sl, cl->value, 0); - } + if (options->NodeFamilySets) { + SMARTLIST_FOREACH(options->NodeFamilySets, const routerset_t *, rs, { + if (routerset_contains_node(rs, node)) { + routerset_get_all_nodes(sl, rs, 0); + } + }); } -#endif - (void)sl; - (void)router; - UNIMPLEMENTED_NODELIST(); } /** Given a router, add every node_t in its family to sl. @@ -1376,19 +1376,28 @@ nodelist_add_node_family(smartlist_t *sl, const node_t *router) static void routerlist_add_nodes_in_family(smartlist_t *sl, const routerinfo_t *router) { - (void)router; - (void)sl; - UNIMPLEMENTED_NODELIST(); + /* XXXX MOVE ? */ + node_t fake_node; + const node_t *node = node_get_by_id(router->cache_info.identity_digest);; + if (node == NULL) { + memset(&fake_node, 0, sizeof(fake_node)); + fake_node.ri = (routerinfo_t *)router; + memcpy(fake_node.identity, router->cache_info.identity_digest, DIGEST_LEN); + node = &fake_node; + } + nodelist_add_node_family(sl, &fake_node); } -/** Return true iff r is named by some nickname in lst. */ +/** Return true iff node is named by some nickname in lst. */ static INLINE int -router_in_nickname_smartlist(smartlist_t *lst, const routerinfo_t *r) +node_in_nickname_smartlist(const smartlist_t *lst, const node_t *node) { + /* XXXX MOVE */ if (!lst) return 0; - SMARTLIST_FOREACH(lst, const char *, name, - if (router_nickname_matches(r, name)) - return 1;); + SMARTLIST_FOREACH(lst, const char *, name, { + if (node_nickname_matches(node, name)) + return 1; + }); return 0; } @@ -1397,27 +1406,38 @@ router_in_nickname_smartlist(smartlist_t *lst, const routerinfo_t *r) int nodes_in_same_family(const node_t *node1, const node_t *node2) { -#if 0 + /* XXXX MOVE */ or_options_t *options = get_options(); - config_line_t *cl; - if (options->EnforceDistinctSubnets && - nodes_in_same_network_family(node1,node2)) - return 1; - - if (router_in_nickname_smartlist(r1->declared_family, r2) && - router_in_nickname_smartlist(r2->declared_family, r1)) - return 1; - - for (cl = options->NodeFamilies; cl; cl = cl->next) { - if (router_nickname_is_in_list(r1, cl->value) && - router_nickname_is_in_list(r2, cl->value)) + /* Are they in the same family because of their addresses? */ + if (options->EnforceDistinctSubnets) { + tor_addr_t a1, a2; + node_get_addr(node1, &a1); + node_get_addr(node2, &a2); + if (addrs_in_same_network_family(&a1, &a2)) return 1; } -#endif - (void)node1; - (void)node2; - UNIMPLEMENTED_NODELIST(); + + /* Are they in the same family because the agree they are? */ + { + const smartlist_t *f1, *f2; + f1 = node_get_declared_family(node1); + f2 = node_get_declared_family(node2); + if (f1 && f2 && + node_in_nickname_smartlist(f1, node2) && + node_in_nickname_smartlist(f2, node1)) + return 1; + } + + /* Are they in the same option because the user says they are? */ + if (options->NodeFamilySets) { + SMARTLIST_FOREACH(options->NodeFamilySets, const routerset_t *, rs, { + if (routerset_contains_node(rs, node1) && + routerset_contains_node(rs, node2)) + return 1; + }); + } + return 0; } @@ -1431,7 +1451,7 @@ nodes_in_same_family(const node_t *node1, const node_t *node2) static void add_nickname_list_to_smartlist(smartlist_t *sl, const char *list, int must_be_running) -{ /*XXXX MOVE */ +{ /*XXXX MOVE or Kill. */ /*XXXX this is only used in one place. Can we kill it?*/ const node_t *node; const routerinfo_t *router; @@ -2301,6 +2321,22 @@ router_nickname_matches(const routerinfo_t *router, const char *nickname) return router_hex_digest_matches(router, nickname); } +/** Return true if node's nickname matches nickname + * (case-insensitive), or if node's identity key digest + * matches a hexadecimal value stored in nickname. Return + * false otherwise. */ +static int +node_nickname_matches(const node_t *node, const char *nickname) +{ + const char *n = node_get_nickname(node); + if (n && nickname[0]!='$' && !strcasecmp(n, nickname)) + return 1; + return hex_digest_nickname_matches(nickname, + node->identity, + n, + node_is_named(node)); +} + /** Return the router in our routerlist whose (case-insensitive) * nickname or (case-sensitive) hexadecimal key digest is * nickname. Return NULL if no such router is known.