[tor-commits] [tor/main] Implement a MiddleOnly flag for vote generation.

ahf at torproject.org ahf at torproject.org
Thu Oct 21 12:58:22 UTC 2021


commit fc542167cb568fc3805275e2405990423cf87e3b
Author: Nick Mathewson <nickm at torproject.org>
Date:   Fri Oct 8 11:14:53 2021 -0400

    Implement a MiddleOnly flag for vote generation.
    
    This proposal implements part of Prop335; it's based on a patch
    from Neel Chauhan.
    
    When configured to do so, authorities will assign a MiddleOnly flag
    to certain relays.  Any relay which an authority gives this flag
    will not get Exit, V2Dir, Guard, or HSDir, and might get BadExit if
    the authority votes for that one.
---
 src/app/config/config.c                 |  3 +++
 src/app/config/or_options_st.h          |  4 ++++
 src/core/or/policies.c                  | 22 ++++++++++++++++++++++
 src/core/or/policies.h                  |  1 +
 src/feature/dirauth/dirauth_options.inc |  4 ++++
 src/feature/dirauth/dirvote.c           | 11 ++++++++---
 src/feature/dirauth/process_descs.c     | 15 +++++++++++++++
 src/feature/dirauth/process_descs.h     |  3 ++-
 src/feature/dirauth/voteflags.c         | 11 ++++++++++-
 src/feature/dirauth/voteflags.h         |  3 ++-
 src/feature/dirparse/ns_parse.c         |  2 ++
 src/feature/nodelist/fmt_routerstatus.c |  3 ++-
 src/feature/nodelist/node_st.h          |  2 ++
 src/feature/nodelist/routerstatus_st.h  |  2 ++
 src/test/test_voting_flags.c            |  3 ++-
 15 files changed, 81 insertions(+), 8 deletions(-)

diff --git a/src/app/config/config.c b/src/app/config/config.c
index 15b4585954..8df5275cc6 100644
--- a/src/app/config/config.c
+++ b/src/app/config/config.c
@@ -193,6 +193,7 @@ static const config_abbrev_t option_abbrevs_[] = {
   PLURAL(AuthDirBadDirCC),
   PLURAL(AuthDirBadExitCC),
   PLURAL(AuthDirInvalidCC),
+  PLURAL(AuthDirMiddleOnlyCC),
   PLURAL(AuthDirRejectCC),
   PLURAL(EntryNode),
   PLURAL(ExcludeNode),
@@ -331,6 +332,8 @@ static const config_var_t option_vars_[] = {
   V(AuthDirBadExitCCs,           CSV,      ""),
   V(AuthDirInvalid,              LINELIST, NULL),
   V(AuthDirInvalidCCs,           CSV,      ""),
+  V(AuthDirMiddleOnly,           LINELIST, NULL),
+  V(AuthDirMiddleOnlyCCs,        CSV,      ""),
   V(AuthDirReject,               LINELIST, NULL),
   V(AuthDirRejectCCs,            CSV,      ""),
   OBSOLETE("AuthDirRejectUnlisted"),
diff --git a/src/app/config/or_options_st.h b/src/app/config/or_options_st.h
index 812fa92cae..3a1acad044 100644
--- a/src/app/config/or_options_st.h
+++ b/src/app/config/or_options_st.h
@@ -499,6 +499,9 @@ struct or_options_t {
   struct smartlist_t *NodeFamilySets;
   struct config_line_t *AuthDirBadExit; /**< Address policy for descriptors to
                                   * mark as bad exits. */
+  /** Address policy for descriptors to mark as only suitable for the
+   * middle position in circuits. */
+  struct config_line_t *AuthDirMiddleOnly;
   struct config_line_t *AuthDirReject; /**< Address policy for descriptors to
                                  * reject. */
   struct config_line_t *AuthDirInvalid; /**< Address policy for descriptors to
@@ -512,6 +515,7 @@ struct or_options_t {
    */
   struct smartlist_t *AuthDirBadExitCCs;
   struct smartlist_t *AuthDirInvalidCCs;
+  struct smartlist_t *AuthDirMiddleOnlyCCs;
   struct smartlist_t *AuthDirRejectCCs;
   /**@}*/
 
diff --git a/src/core/or/policies.c b/src/core/or/policies.c
index f91c23ad31..a53849b4d0 100644
--- a/src/core/or/policies.c
+++ b/src/core/or/policies.c
@@ -59,6 +59,9 @@ static smartlist_t *authdir_invalid_policy = NULL;
 /** Policy that addresses for incoming router descriptors must <b>not</b>
  * match in order to not be marked as BadExit. */
 static smartlist_t *authdir_badexit_policy = NULL;
+/** Policy that addresses for incoming router descriptors must <b>not</b>
+ * match in order to not be marked as MiddleOnly. */
+static smartlist_t *authdir_middleonly_policy = NULL;
 
 /** Parsed addr_policy_t describing which addresses we believe we can start
  * circuits at. */
@@ -1119,6 +1122,17 @@ authdir_policy_badexit_address(const tor_addr_t *addr, uint16_t port)
   return addr_is_in_cc_list(addr, get_options()->AuthDirBadExitCCs);
 }
 
+/** Return 1 if <b>addr</b>:<b>port</b> should be marked as MiddleOnly,
+ * based on <b>authdir_middleonly_policy</b>. Else return 0.
+ */
+int
+authdir_policy_middleonly_address(const tor_addr_t *addr, uint16_t port)
+{
+  if (!addr_policy_permits_tor_addr(addr, port, authdir_middleonly_policy))
+    return 1;
+  return addr_is_in_cc_list(addr, get_options()->AuthDirMiddleOnlyCCs);
+}
+
 #define REJECT(arg) \
   STMT_BEGIN *msg = tor_strdup(arg); goto err; STMT_END
 
@@ -1173,6 +1187,9 @@ validate_addr_policies(const or_options_t *options, char **msg)
   if (parse_addr_policy(options->AuthDirBadExit, &addr_policy,
                         ADDR_POLICY_REJECT))
     REJECT("Error in AuthDirBadExit entry.");
+  if (parse_addr_policy(options->AuthDirMiddleOnly, &addr_policy,
+                        ADDR_POLICY_REJECT))
+    REJECT("Error in AuthDirMiddleOnly entry.");
 
   if (parse_addr_policy(options->ReachableAddresses, &addr_policy,
                         ADDR_POLICY_ACCEPT))
@@ -1266,6 +1283,9 @@ policies_parse_from_options(const or_options_t *options)
   if (load_policy_from_option(options->AuthDirBadExit, "AuthDirBadExit",
                               &authdir_badexit_policy, ADDR_POLICY_REJECT) < 0)
     ret = -1;
+  if (load_policy_from_option(options->AuthDirMiddleOnly, "AuthDirMiddleOnly",
+                         &authdir_middleonly_policy, ADDR_POLICY_REJECT) < 0)
+    ret = -1;
   if (parse_metrics_port_policy(options) < 0) {
     ret = -1;
   }
@@ -3112,6 +3132,8 @@ policies_free_all(void)
   authdir_invalid_policy = NULL;
   addr_policy_list_free(authdir_badexit_policy);
   authdir_badexit_policy = NULL;
+  addr_policy_list_free(authdir_middleonly_policy);
+  authdir_middleonly_policy = NULL;
 
   if (!HT_EMPTY(&policy_root)) {
     policy_map_ent_t **ent;
diff --git a/src/core/or/policies.h b/src/core/or/policies.h
index a32f50ab1d..e11e1d0ff5 100644
--- a/src/core/or/policies.h
+++ b/src/core/or/policies.h
@@ -106,6 +106,7 @@ int metrics_policy_permits_address(const tor_addr_t *addr);
 int authdir_policy_permits_address(const tor_addr_t *addr, uint16_t port);
 int authdir_policy_valid_address(const tor_addr_t *addr, uint16_t port);
 int authdir_policy_badexit_address(const tor_addr_t *addr, uint16_t port);
+int authdir_policy_middleonly_address(const tor_addr_t *addr, uint16_t port);
 
 int validate_addr_policies(const or_options_t *options, char **msg);
 void policy_expand_private(smartlist_t **policy);
diff --git a/src/feature/dirauth/dirauth_options.inc b/src/feature/dirauth/dirauth_options.inc
index 05726b8c2f..968f8e6c3d 100644
--- a/src/feature/dirauth/dirauth_options.inc
+++ b/src/feature/dirauth/dirauth_options.inc
@@ -27,6 +27,10 @@ CONF_VAR(AuthDirHasIPv6Connectivity, BOOL, 0, "0")
  * good. */
 CONF_VAR(AuthDirListBadExits, BOOL, 0, "0")
 
+/** True iff we should list middle-only relays, and vote for all other
+ * relays as possibly suitable for other positions. */
+CONF_VAR(AuthDirListMiddleOnly, BOOL, 0, "0")
+
 /** Do not permit more than this number of servers per IP address. */
 CONF_VAR(AuthDirMaxServersPerAddr, POSINT, 0, "2")
 
diff --git a/src/feature/dirauth/dirvote.c b/src/feature/dirauth/dirvote.c
index ffaa78b997..4f8cf2b8e6 100644
--- a/src/feature/dirauth/dirvote.c
+++ b/src/feature/dirauth/dirvote.c
@@ -4581,6 +4581,7 @@ const char DIRVOTE_UNIVERSAL_FLAGS[] =
  * depending on our configuration. */
 const char DIRVOTE_OPTIONAL_FLAGS[] =
   "BadExit "
+  "MiddleOnly "
   "Running";
 
 /** Return a new networkstatus_t* containing our current opinion. (For v3
@@ -4598,7 +4599,8 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key,
   smartlist_t *routers, *routerstatuses;
   char identity_digest[DIGEST_LEN];
   char signing_key_digest[DIGEST_LEN];
-  const int listbadexits = d_options->AuthDirListBadExits;
+  const int list_bad_exits = d_options->AuthDirListBadExits;
+  const int list_middle_only = d_options->AuthDirListMiddleOnly;
   routerlist_t *rl = router_get_routerlist();
   time_t now = time(NULL);
   time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH;
@@ -4703,7 +4705,8 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key,
       vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
       rs = &vrs->status;
       dirauth_set_routerstatus_from_routerinfo(rs, node, ri, now,
-                                               listbadexits);
+                                               list_bad_exits,
+                                               list_middle_only);
 
       if (ri->cache_info.signing_key_cert) {
         memcpy(vrs->ed25519_id,
@@ -4827,8 +4830,10 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key,
                          0, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
   if (vote_on_reachability)
     smartlist_add_strdup(v3_out->known_flags, "Running");
-  if (listbadexits)
+  if (list_bad_exits)
     smartlist_add_strdup(v3_out->known_flags, "BadExit");
+  if (list_middle_only)
+    smartlist_add_strdup(v3_out->known_flags, "MiddleOnly");
   smartlist_sort_strings(v3_out->known_flags);
 
   if (d_options->ConsensusParams) {
diff --git a/src/feature/dirauth/process_descs.c b/src/feature/dirauth/process_descs.c
index eca987b8b5..db4eca5329 100644
--- a/src/feature/dirauth/process_descs.c
+++ b/src/feature/dirauth/process_descs.c
@@ -226,6 +226,8 @@ dirserv_load_fingerprint_file(void)
         add_status = RTR_BADEXIT;
     } else if (!strcasecmp(nickname, "!invalid")) {
         add_status = RTR_INVALID;
+    }  else if (!strcasecmp(nickname, "!middleonly")) {
+        add_status = RTR_MIDDLEONLY;
     }
 
     /* Check if fingerprint is RSA or ed25519 by verifying it. */
@@ -496,6 +498,13 @@ dirserv_get_status_impl(const char *id_digest,
     result |= RTR_BADEXIT;
   }
 
+  if (authdir_policy_middleonly_address(ipv4_addr, ipv4_orport)) {
+    log_fn(severity, LD_DIRSERV,
+           "Marking '%s' as middle-only because of address '%s'",
+               nickname, fmt_addr(ipv4_addr));
+    result |= RTR_MIDDLEONLY;
+  }
+
   if (!authdir_policy_permits_address(ipv4_addr, ipv4_orport)) {
     log_fn(severity, LD_DIRSERV, "Rejecting '%s' because of address '%s'",
                nickname, fmt_addr(ipv4_addr));
@@ -630,6 +639,7 @@ dirserv_set_node_flags_from_authoritative_status(node_t *node,
 {
   node->is_valid = (authstatus & RTR_INVALID) ? 0 : 1;
   node->is_bad_exit = (authstatus & RTR_BADEXIT) ? 1 : 0;
+  node->is_middle_only = (authstatus & RTR_MIDDLEONLY) ? 1 : 0;
 }
 
 /** True iff <b>a</b> is more severe than <b>b</b>. */
@@ -963,6 +973,11 @@ directory_remove_invalid(void)
                (r & RTR_BADEXIT) ? "bad" : "good");
       node->is_bad_exit = (r&RTR_BADEXIT) ? 1: 0;
     }
+    if (bool_neq((r & RTR_MIDDLEONLY), node->is_middle_only)) {
+      log_info(LD_DIRSERV, "Router '%s' is now %smiddle-only", description,
+               (r & RTR_MIDDLEONLY) ? "" : "not");
+      node->is_middle_only = (r&RTR_MIDDLEONLY) ? 1: 0;
+    }
   } SMARTLIST_FOREACH_END(node);
 
   routerlist_assert_ok(rl);
diff --git a/src/feature/dirauth/process_descs.h b/src/feature/dirauth/process_descs.h
index 6c056d11dd..a509eb1fbe 100644
--- a/src/feature/dirauth/process_descs.h
+++ b/src/feature/dirauth/process_descs.h
@@ -45,7 +45,8 @@ typedef struct authdir_config_t {
 #define RTR_REJECT  4  /**< We will not publish this router. */
 /*                 8  Historically used to avoid using this as a dir. */
 #define RTR_BADEXIT 16 /**< We'll tell clients not to use this as an exit. */
-/*                 32 Historically used to indicade Unnamed */
+/** We'll vote to only use this router as a midpoint. */
+#define RTR_MIDDLEONLY 32
 
 #endif /* defined(PROCESS_DESCS_PRIVATE) || defined(TOR_UNIT_TESTS) */
 
diff --git a/src/feature/dirauth/voteflags.c b/src/feature/dirauth/voteflags.c
index d755a270be..05c19ff501 100644
--- a/src/feature/dirauth/voteflags.c
+++ b/src/feature/dirauth/voteflags.c
@@ -565,7 +565,8 @@ dirauth_set_routerstatus_from_routerinfo(routerstatus_t *rs,
                                          node_t *node,
                                          const routerinfo_t *ri,
                                          time_t now,
-                                         int listbadexits)
+                                         int listbadexits,
+                                         int listmiddleonly)
 {
   const or_options_t *options = get_options();
   uint32_t routerbw_kb = dirserv_get_credible_bandwidth_kb(ri);
@@ -597,6 +598,14 @@ dirauth_set_routerstatus_from_routerinfo(routerstatus_t *rs,
   /* Override rs->is_bad_exit */
   rs->is_bad_exit = listbadexits && node->is_bad_exit;
 
+  /* Override rs->is_middle_only and related flags. */
+  rs->is_middle_only = listmiddleonly && node->is_middle_only;
+  if (rs->is_middle_only) {
+    if (listbadexits)
+      rs->is_bad_exit = 1;
+    rs->is_exit = rs->is_possible_guard = rs->is_hs_dir = rs->is_v2_dir = 0;
+  }
+
   /* Set rs->is_staledesc. */
   rs->is_staledesc =
     (ri->cache_info.published_on + DESC_IS_STALE_INTERVAL) < now;
diff --git a/src/feature/dirauth/voteflags.h b/src/feature/dirauth/voteflags.h
index 818a0bafd2..8371f1c315 100644
--- a/src/feature/dirauth/voteflags.h
+++ b/src/feature/dirauth/voteflags.h
@@ -22,7 +22,8 @@ void dirauth_set_routerstatus_from_routerinfo(routerstatus_t *rs,
                                               node_t *node,
                                               const routerinfo_t *ri,
                                               time_t now,
-                                              int listbadexits);
+                                              int listbadexits,
+                                              int listmiddleonly);
 
 void dirserv_compute_performance_thresholds(digestmap_t *omit_as_sybil);
 #endif /* defined(HAVE_MODULE_DIRAUTH) */
diff --git a/src/feature/dirparse/ns_parse.c b/src/feature/dirparse/ns_parse.c
index 947b3810a4..cd3e2731be 100644
--- a/src/feature/dirparse/ns_parse.c
+++ b/src/feature/dirparse/ns_parse.c
@@ -434,6 +434,8 @@ routerstatus_parse_entry_from_string(memarea_t *area,
         rs->is_possible_guard = 1;
       else if (!strcmp(tok->args[i], "BadExit"))
         rs->is_bad_exit = 1;
+      else if (!strcmp(tok->args[i], "MiddleOnly"))
+        rs->is_middle_only = 1;
       else if (!strcmp(tok->args[i], "Authority"))
         rs->is_authority = 1;
       else if (!strcmp(tok->args[i], "Unnamed") &&
diff --git a/src/feature/nodelist/fmt_routerstatus.c b/src/feature/nodelist/fmt_routerstatus.c
index 6db40c0b68..95379a7721 100644
--- a/src/feature/nodelist/fmt_routerstatus.c
+++ b/src/feature/nodelist/fmt_routerstatus.c
@@ -87,7 +87,7 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version,
     goto done;
 
   smartlist_add_asprintf(chunks,
-                   "s%s%s%s%s%s%s%s%s%s%s%s%s\n",
+                   "s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
                   /* These must stay in alphabetical order. */
                    rs->is_authority?" Authority":"",
                    rs->is_bad_exit?" BadExit":"",
@@ -95,6 +95,7 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version,
                    rs->is_fast?" Fast":"",
                    rs->is_possible_guard?" Guard":"",
                    rs->is_hs_dir?" HSDir":"",
+                   rs->is_middle_only?" MiddleOnly":"",
                    rs->is_flagged_running?" Running":"",
                    rs->is_stable?" Stable":"",
                    rs->is_staledesc?" StaleDesc":"",
diff --git a/src/feature/nodelist/node_st.h b/src/feature/nodelist/node_st.h
index b15e7154c4..df67a47ada 100644
--- a/src/feature/nodelist/node_st.h
+++ b/src/feature/nodelist/node_st.h
@@ -70,6 +70,8 @@ struct node_t {
   unsigned int is_exit:1; /**< Do we think this is an OK exit? */
   unsigned int is_bad_exit:1; /**< Do we think this exit is censored, borked,
                                * or otherwise nasty? */
+  /** Is this unsuitable for use as anything besides a middle relay? */
+  unsigned int is_middle_only:1;
   unsigned int is_hs_dir:1; /**< True iff this router is a hidden service
                              * directory according to the authorities. */
 
diff --git a/src/feature/nodelist/routerstatus_st.h b/src/feature/nodelist/routerstatus_st.h
index 46ff0bdeac..55b76de581 100644
--- a/src/feature/nodelist/routerstatus_st.h
+++ b/src/feature/nodelist/routerstatus_st.h
@@ -51,6 +51,8 @@ struct routerstatus_t {
                                      * choice as an entry guard. */
   unsigned int is_bad_exit:1; /**< True iff this node is a bad choice for
                                * an exit node. */
+  unsigned int is_middle_only:1; /**< True iff this node is marked as bad
+                                  * for anything besides middle positions. */
   unsigned int is_hs_dir:1; /**< True iff this router is a v2-or-later hidden
                              * service directory. */
   unsigned int is_v2_dir:1; /** True iff this router publishes an open DirPort
diff --git a/src/test/test_voting_flags.c b/src/test/test_voting_flags.c
index 4c5f3a3270..457b0fa796 100644
--- a/src/test/test_voting_flags.c
+++ b/src/test/test_voting_flags.c
@@ -62,7 +62,8 @@ check_result(flag_vote_test_cfg_t *c)
   bool result = false;
   routerstatus_t rs;
   memset(&rs, 0, sizeof(rs));
-  dirauth_set_routerstatus_from_routerinfo(&rs, &c->node, &c->ri, c->now, 0);
+  dirauth_set_routerstatus_from_routerinfo(&rs, &c->node, &c->ri, c->now,
+                                           0, 0);
 
   tt_i64_op(rs.published_on, OP_EQ, c->expected.published_on);
   tt_str_op(rs.nickname, OP_EQ, c->expected.nickname);





More information about the tor-commits mailing list