]> git.ipfire.org Git - thirdparty/tor.git/commitdiff
Have directory authorities vote on IPv6 OR ports according to the spec
authorLinus Nordberg <linus@torproject.org>
Tue, 14 Aug 2012 12:03:58 +0000 (14:03 +0200)
committerNick Mathewson <nickm@torproject.org>
Tue, 4 Sep 2012 15:52:22 +0000 (11:52 -0400)
Define new new consensus method 14 adding "a" lines to vote and
consensus documents.

From proposal 186:

  As with other data in the vote derived from the descriptor, the
  consensus will include whichever set of "a" lines are given by the
  most authorities who voted for the descriptor digest that will be
  used for the router.

This patch implements this.

changes/bug6363 [new file with mode: 0644]
src/or/dirserv.c
src/or/dirvote.c
src/or/routerparse.c
src/test/test_dir.c

diff --git a/changes/bug6363 b/changes/bug6363
new file mode 100644 (file)
index 0000000..de99b72
--- /dev/null
@@ -0,0 +1,3 @@
+  o Major features:
+    - Directory authorities vote on IPv6 OR ports using new consensus
+      method 14. Implements ticket 6363.
index f4ba02b4adc35c1a079a9588a8165c72b10482ab..5814171c5be947a2a76a167951623006baa5b93b 100644 (file)
@@ -2053,7 +2053,7 @@ version_from_platform(const char *platform)
  * non-NULL, add a "v" line for the platform.  Return 0 on success, -1 on
  * failure.
  *
- * The format argument has three possible values:
+ * The format argument has one of the following values:
  *   NS_V2 - Output an entry suitable for a V2 NS opinion document
  *   NS_V3_CONSENSUS - Output the first portion of a V3 NS consensus entry
  *   NS_V3_CONSENSUS_MICRODESC - Output the first portion of a V3 microdesc
@@ -2092,17 +2092,18 @@ routerstatus_format_entry(char *buf, size_t buf_len,
     log_warn(LD_BUG, "Not enough space in buffer.");
     return -1;
   }
+  cp = buf + strlen(buf);
 
   /* TODO: Maybe we want to pass in what we need to build the rest of
    * this here, instead of in the caller. Then we could use the
    * networkstatus_type_t values, with an additional control port value
    * added -MP */
-  if (format == NS_V3_CONSENSUS || format == NS_V3_CONSENSUS_MICRODESC)
-    return 0;
 
-  cp = buf + strlen(buf);
+  /* V3 microdesc consensuses don't have "a" lines. */
+  if (format == NS_V3_CONSENSUS_MICRODESC)
+    return 0;
 
-  /* Possible "a" line, not included in consensus for now. */
+  /* Possible "a" line. At most one for now. */
   if (!tor_addr_is_null(&rs->ipv6_addr)) {
     const char *addr_str = fmt_and_decorate_addr(&rs->ipv6_addr);
     r = tor_snprintf(cp, buf_len - (cp-buf),
@@ -2116,6 +2117,9 @@ routerstatus_format_entry(char *buf, size_t buf_len,
     cp += strlen(cp);
   }
 
+  if (format == NS_V3_CONSENSUS)
+    return 0;
+
   /* NOTE: Whenever this list expands, be sure to increase MAX_FLAG_LINE_LEN*/
   r = tor_snprintf(cp, buf_len - (cp-buf),
                    "s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
index b3de90b5c0811043ec224cbac139b86c226e1c69..9056cf59231af1eb04649a8fdb1db67cd8501176 100644 (file)
@@ -54,7 +54,7 @@ static int dirvote_publish_consensus(void);
 static char *make_consensus_method_list(int low, int high, const char *sep);
 
 /** The highest consensus method that we currently support. */
-#define MAX_SUPPORTED_CONSENSUS_METHOD 13
+#define MAX_SUPPORTED_CONSENSUS_METHOD 14
 
 /** Lowest consensus method that contains a 'directory-footer' marker */
 #define MIN_METHOD_FOR_FOOTER 9
@@ -76,6 +76,9 @@ static char *make_consensus_method_list(int low, int high, const char *sep);
  * with no microdesc. */
 #define MIN_METHOD_FOR_MANDATORY_MICRODESC 13
 
+/** Lowest consensus method that contains "a" lines. */
+#define MIN_METHOD_FOR_A_LINES 14
+
 /* =====
  * Voting
  * =====*/
@@ -430,6 +433,21 @@ _compare_vote_rs(const void **_a, const void **_b)
   return compare_vote_rs(a,b);
 }
 
+/** Helper for sorting OR ports. */
+static int
+_compare_orports(const void **_a, const void **_b)
+{
+  const tor_addr_port_t *a = *_a, *b = *_b;
+  int r;
+
+  if ((r = tor_addr_compare(&a->addr, &b->addr, CMP_EXACT)))
+    return r;
+  if ((r = (((int) b->port) - ((int) a->port))))
+    return r;
+
+  return 0;
+}
+
 /** Given a list of vote_routerstatus_t, all for the same router identity,
  * return whichever is most frequent, breaking ties in favor of more
  * recently published vote_routerstatus_t and in case of ties there,
@@ -437,7 +455,8 @@ _compare_vote_rs(const void **_a, const void **_b)
  */
 static vote_routerstatus_t *
 compute_routerstatus_consensus(smartlist_t *votes, int consensus_method,
-                               char *microdesc_digest256_out)
+                               char *microdesc_digest256_out,
+                               tor_addr_port_t *best_alt_orport_out)
 {
   vote_routerstatus_t *most = NULL, *cur = NULL;
   int most_n = 0, cur_n = 0;
@@ -473,6 +492,42 @@ compute_routerstatus_consensus(smartlist_t *votes, int consensus_method,
 
   tor_assert(most);
 
+  /* If we're producing "a" lines, vote on potential alternative (sets
+   * of) OR port(s) in the winning routerstatuses.
+   *
+   * XXX prop186 There's at most one alternative OR port (_the_ IPv6
+   * port) for now. */
+  if (consensus_method >= MIN_METHOD_FOR_A_LINES && best_alt_orport_out) {
+    smartlist_t *alt_orports = smartlist_new();
+    const tor_addr_port_t *most_alt_orport = NULL;
+
+    SMARTLIST_FOREACH_BEGIN(votes, vote_routerstatus_t *, rs) {
+      if (compare_vote_rs(most, rs) == 0 &&
+          !tor_addr_is_null(&rs->status.ipv6_addr)
+          && rs->status.ipv6_orport) {
+        smartlist_add(alt_orports, tor_addr_port_alloc(&rs->status.ipv6_addr,
+                                                       rs->status.ipv6_orport));
+        log_debug(LD_DIR, "picking %s:%d (%s) for voting on \"a\" lines", /* FIXME: remove */
+                  fmt_and_decorate_addr(&rs->status.ipv6_addr), rs->status.ipv6_orport,
+                  rs->status.nickname);
+      }
+    } SMARTLIST_FOREACH_END(rs);
+
+    smartlist_sort(alt_orports, _compare_orports);
+    most_alt_orport = smartlist_get_most_frequent(alt_orports, _compare_orports);
+    if (most_alt_orport) {
+      memcpy(best_alt_orport_out, most_alt_orport, sizeof(tor_addr_port_t));
+
+      log_debug(LD_DIR, "\"a\" line winner for %s is %s:%d",
+                most->status.nickname,
+                fmt_and_decorate_addr(&most_alt_orport->addr),
+                most_alt_orport->port);
+    }
+
+    SMARTLIST_FOREACH(alt_orports, tor_addr_port_t *, ap, tor_free(ap));
+    smartlist_free(alt_orports);
+  }
+
   if (consensus_method >= MIN_METHOD_FOR_MICRODESC &&
       microdesc_digest256_out) {
     smartlist_t *digests = smartlist_new();
@@ -1685,6 +1740,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
       int n_listing = 0;
       int i;
       char microdesc_digest[DIGEST256_LEN];
+      tor_addr_port_t alt_orport = {TOR_ADDR_NULL, 0};
 
       /* Of the next-to-be-considered digest in each voter, which is first? */
       SMARTLIST_FOREACH(votes, networkstatus_t *, v, {
@@ -1754,7 +1810,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
        * routerinfo and its contents are. */
       memset(microdesc_digest, 0, sizeof(microdesc_digest));
       rs = compute_routerstatus_consensus(matching_descs, consensus_method,
-                                          microdesc_digest);
+                                          microdesc_digest, &alt_orport);
       /* Copy bits of that into rs_out. */
       memset(&rs_out, 0, sizeof(rs_out));
       tor_assert(fast_memeq(lowest_id, rs->status.identity_digest,DIGEST_LEN));
@@ -1765,6 +1821,10 @@ networkstatus_compute_consensus(smartlist_t *votes,
       rs_out.published_on = rs->status.published_on;
       rs_out.dir_port = rs->status.dir_port;
       rs_out.or_port = rs->status.or_port;
+      if (consensus_method >= MIN_METHOD_FOR_A_LINES) {
+        tor_addr_copy(&rs_out.ipv6_addr, &alt_orport.addr);
+        rs_out.ipv6_orport = alt_orport.port;
+      }
       rs_out.has_bandwidth = 0;
       rs_out.has_exitsummary = 0;
 
index 60a2eae75f63a699142c7fceafdda4655da0d56c..cda1c00664eac4e18503607a11da97dcfecfb296 100644 (file)
@@ -67,6 +67,7 @@ typedef enum {
   K_OR_ADDRESS,
   K_P,
   K_R,
+  K_A,
   K_S,
   K_V,
   K_W,
@@ -338,6 +339,7 @@ static token_rule_t extrainfo_token_table[] = {
 static token_rule_t rtrstatus_token_table[] = {
   T01("p",                   K_P,               CONCAT_ARGS, NO_OBJ ),
   T1( "r",                   K_R,                   GE(7),   NO_OBJ ),
+  T0N("a",                   K_A,                   GE(1),   NO_OBJ ),
   T1( "s",                   K_S,                   ARGS,    NO_OBJ ),
   T01("v",                   K_V,               CONCAT_ARGS, NO_OBJ ),
   T01("w",                   K_W,                   ARGS,    NO_OBJ ),
@@ -1257,6 +1259,42 @@ dump_distinct_digest_count(int severity)
 #endif
 }
 
+/** Try to find an IPv6 OR port in <b>list</b> of directory_token_t's
+ * with at least one argument (use GE(1) in setup). If found, store
+ * address and port number to <b>addr_out</b> and
+ * <b>port_out</b>. Return number of OR ports found. */
+static int
+find_single_ipv6_orport(const smartlist_t *list,
+                        tor_addr_t *addr_out,
+                        uint16_t *port_out)
+{
+  int ret = 0;
+  tor_assert(list != NULL);
+  tor_assert(addr_out != NULL);
+  tor_assert(port_out != NULL);
+
+  SMARTLIST_FOREACH_BEGIN(list, directory_token_t *, t) {
+    tor_addr_t a;
+    maskbits_t bits;
+    uint16_t port_min, port_max;
+    tor_assert(t->n_args >= 1);
+    /* XXXX Prop186 the full spec allows much more than this. */
+    if (tor_addr_parse_mask_ports(t->args[0], &a, &bits, &port_min,
+                                  &port_max) == AF_INET6 &&
+        bits == 128 &&
+        port_min == port_max) {
+      /* Okay, this is one we can understand. Use it and ignore
+         any potential more addresses in list. */
+      tor_addr_copy(addr_out, &a);
+      *port_out = port_min;
+      ret = 1;
+      break;
+    }
+  } SMARTLIST_FOREACH_END(t);
+
+  return ret;
+}
+
 /** Helper function: reads a single router entry from *<b>s</b> ...
  * *<b>end</b>.  Mallocs a new router and returns it if all goes well, else
  * returns NULL.  If <b>cache_copy</b> is true, duplicate the contents of
@@ -1513,21 +1551,8 @@ router_parse_entry_from_string(const char *s, const char *end,
   {
     smartlist_t *or_addresses = find_all_by_keyword(tokens, K_OR_ADDRESS);
     if (or_addresses) {
-      SMARTLIST_FOREACH_BEGIN(or_addresses, directory_token_t *, t) {
-        tor_addr_t a;
-        maskbits_t bits;
-        uint16_t port_min, port_max;
-        /* XXXX Prop186 the full spec allows much more than this. */
-        if (tor_addr_parse_mask_ports(t->args[0], &a, &bits, &port_min,
-                                      &port_max) == AF_INET6 &&
-            bits == 128 &&
-            port_min == port_max) {
-          /* Okay, this is one we can understand. */
-          tor_addr_copy(&router->ipv6_addr, &a);
-          router->ipv6_orport = port_min;
-          break;
-        }
-      } SMARTLIST_FOREACH_END(t);
+      find_single_ipv6_orport(or_addresses, &router->ipv6_addr,
+                              &router->ipv6_orport);
       smartlist_free(or_addresses);
     }
   }
@@ -2060,6 +2085,14 @@ routerstatus_parse_entry_from_string(memarea_t *area,
   rs->dir_port = (uint16_t) tor_parse_long(tok->args[7+offset],
                                            10,0,65535,NULL,NULL);
 
+  {
+    smartlist_t *a_lines = find_all_by_keyword(tokens, K_A);
+    if (a_lines) {
+      find_single_ipv6_orport(a_lines, &rs->ipv6_addr, &rs->ipv6_orport);
+      smartlist_free(a_lines);
+    }
+  }
+
   tok = find_opt_by_keyword(tokens, K_S);
   if (tok && vote) {
     int i;
index 84705d0e0254aa28be0bef6ef5ac923f36b06fa0..af878a696f01ec6ec7ce3ff4d9dbcb72393b01e6 100644 (file)
@@ -797,6 +797,7 @@ test_dir_v3_networkstatus(void)
   networkstatus_t *vote=NULL, *v1=NULL, *v2=NULL, *v3=NULL, *con=NULL,
     *con_md=NULL;
   vote_routerstatus_t *vrs;
+  tor_addr_t addr_ipv6;
   routerstatus_t *rs;
   char *v1_text=NULL, *v2_text=NULL, *v3_text=NULL, *consensus_text=NULL, *cp;
   smartlist_t *votes = smartlist_new();
@@ -893,6 +894,9 @@ test_dir_v3_networkstatus(void)
   rs->addr = 0x99009901;
   rs->or_port = 443;
   rs->dir_port = 0;
+  tor_addr_parse(&addr_ipv6, "[1:2:3::4]");
+  tor_addr_copy(&rs->ipv6_addr, &addr_ipv6);
+  rs->ipv6_orport = 4711;
   rs->is_exit = rs->is_stable = rs->is_fast = rs->is_flagged_running =
     rs->is_valid = rs->is_v2_dir = rs->is_possible_guard = 1;
   smartlist_add(vote->routerstatus_list, vrs);
@@ -987,6 +991,8 @@ test_dir_v3_networkstatus(void)
   test_eq(rs->addr, 0x99009901);
   test_eq(rs->or_port, 443);
   test_eq(rs->dir_port, 0);
+  test_assert(tor_addr_eq(&rs->ipv6_addr, &addr_ipv6));
+  test_eq(rs->ipv6_orport, 4711);
   test_eq(vrs->flags, U64_LITERAL(254)); // all flags except "authority."
 
   {
@@ -1169,6 +1175,8 @@ test_dir_v3_networkstatus(void)
   test_eq(rs->addr, 0x99009901);
   test_eq(rs->or_port, 443);
   test_eq(rs->dir_port, 0);
+  test_assert(tor_addr_eq(&rs->ipv6_addr, &addr_ipv6));
+  test_eq(rs->ipv6_orport, 4711);
   test_assert(!rs->is_authority);
   test_assert(rs->is_exit);
   test_assert(rs->is_fast);