[tor-commits] [tor/master] Code to make clients fetch and use microdescriptors for circuit building

nickm at torproject.org nickm at torproject.org
Fri May 6 01:00:27 UTC 2011


commit 4cc348e896f74a4e02ef15a77d22fc636b08afae
Author: Nick Mathewson <nickm at torproject.org>
Date:   Mon Nov 8 14:21:32 2010 -0500

    Code to make clients fetch and use microdescriptors for circuit building
    
    To turn this on, set UseMicrodescriptors to "1" (or "auto" if you
    want it on-if-you're-a-client).  It should go auto-by-default once
    0.2.3.1-alpha is released.
    
    Because of our node logic, directory caches will never use
    microdescriptors when they have the right routerinfo available.
---
 changes/microdesc_use  |   10 +++++++++
 src/or/config.c        |    6 +++-
 src/or/directory.c     |   11 +++++----
 src/or/directory.h     |    1 -
 src/or/microdesc.c     |   53 ++++++++++++++++++++++++++++++++++++++++++++---
 src/or/microdesc.h     |    5 ++++
 src/or/networkstatus.c |   18 +++++++++-------
 src/or/or.h            |   22 +++++++++++++++----
 src/or/router.c        |    3 +-
 src/or/routerlist.c    |   41 +++++++++++++++++++------------------
 src/or/routerlist.h    |    1 -
 src/or/routerparse.c   |    3 ++
 12 files changed, 127 insertions(+), 47 deletions(-)

diff --git a/changes/microdesc_use b/changes/microdesc_use
new file mode 100644
index 0000000..89faf7c
--- /dev/null
+++ b/changes/microdesc_use
@@ -0,0 +1,10 @@
+  o Major features
+    - Clients can now use microdescriptors instead of regular descriptors
+      to build circuits.  Microdescriptors are authority-generated and
+      -authenticated summaries of regular descriptors' contents, designed
+      to change very rarely.  This feature is designed to save bandwidth,
+      especially for clients on slow internet connections.  It's off
+      by default for now, since nearly no caches support it, but it will
+      be on-by-default for clients in a future version.  You can use the
+      UseMicrodescriptors option to turn it on.
+
diff --git a/src/or/config.c b/src/or/config.c
index c936991..09ad51f 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -381,6 +381,7 @@ static config_var_t _option_vars[] = {
   V(UpdateBridgesFromAuthority,  BOOL,     "0"),
   V(UseBridges,                  BOOL,     "0"),
   V(UseEntryGuards,              BOOL,     "1"),
+  V(UseMicrodescriptors,         AUTOBOOL, "0"),
   V(User,                        STRING,   NULL),
   VAR("V1AuthoritativeDirectory",BOOL, V1AuthoritativeDir,   "0"),
   VAR("V2AuthoritativeDirectory",BOOL, V2AuthoritativeDir,   "0"),
@@ -919,7 +920,8 @@ consider_adding_dir_authorities(or_options_t *options,
     if (!options->AlternateBridgeAuthority)
       type |= BRIDGE_DIRINFO;
     if (!options->AlternateDirAuthority)
-      type |= V1_DIRINFO | V2_DIRINFO | V3_DIRINFO;
+      type |= V1_DIRINFO | V2_DIRINFO | V3_DIRINFO | EXTRAINFO_DIRINFO |
+        MICRODESC_DIRINFO;
     if (!options->AlternateHSAuthority)
       type |= HIDSERV_DIRINFO;
     add_default_trusted_dir_authorities(type);
@@ -4605,7 +4607,7 @@ parse_dir_server_line(const char *line, dirinfo_type_t required_type,
         log_warn(LD_CONFIG, "Bad v3 identity digest '%s' on DirServer line",
                  flag);
       } else {
-        type |= V3_DIRINFO;
+        type |= V3_DIRINFO|EXTRAINFO_DIRINFO|MICRODESC_DIRINFO;
       }
     } else {
       log_warn(LD_CONFIG, "Unrecognized flag '%s' on DirServer line",
diff --git a/src/or/directory.c b/src/or/directory.c
index f21dc85..184a6b4 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -147,9 +147,10 @@ purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose)
   return 1;
 }
 
-/** Return a newly allocated string describing <b>auth</b>. */
-char *
-dirinfo_type_to_string(dirinfo_type_t auth)
+/** Return a newly allocated string describing <b>auth</b>. Only describes
+ * authority features. */
+static char *
+authdir_type_to_string(dirinfo_type_t auth)
 {
   char *result;
   smartlist_t *lst = smartlist_create();
@@ -328,7 +329,7 @@ directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose,
                                               NULL, payload, upload_len, 0);
   } SMARTLIST_FOREACH_END(ds);
   if (!found) {
-    char *s = dirinfo_type_to_string(type);
+    char *s = authdir_type_to_string(type);
     log_warn(LD_DIR, "Publishing server descriptor to directory authorities "
              "of type '%s', but no authorities of that type listed!", s);
     tor_free(s);
@@ -379,7 +380,7 @@ directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose,
       type = V3_DIRINFO;
       break;
     case DIR_PURPOSE_FETCH_MICRODESC:
-      type = V3_DIRINFO;
+      type = MICRODESC_DIRINFO;
       break;
     default:
       log_warn(LD_BUG, "Unexpected purpose %d", (int)dir_purpose);
diff --git a/src/or/directory.h b/src/or/directory.h
index 9f4c31d..caff938 100644
--- a/src/or/directory.h
+++ b/src/or/directory.h
@@ -13,7 +13,6 @@
 #define _TOR_DIRECTORY_H
 
 int directories_have_accepted_server_descriptor(void);
-char *dirinfo_type_to_string(dirinfo_type_t auth);
 void directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose,
                                   dirinfo_type_t type, const char *payload,
                                   size_t payload_len, size_t extrainfo_len);
diff --git a/src/or/microdesc.c b/src/or/microdesc.c
index 6209bbf..0e8aa83 100644
--- a/src/or/microdesc.c
+++ b/src/or/microdesc.c
@@ -9,6 +9,7 @@
 #include "networkstatus.h"
 #include "nodelist.h"
 #include "policies.h"
+#include "router.h"
 #include "routerlist.h"
 #include "routerparse.h"
 
@@ -251,6 +252,9 @@ microdescs_add_list_to_cache(microdesc_cache_t *cache,
       SMARTLIST_FOREACH(added, microdesc_t *, md, nodelist_add_microdesc(md));
   }
 
+  if (smartlist_len(added))
+    router_dir_info_changed();
+
   return added;
 }
 
@@ -570,6 +574,8 @@ microdesc_list_missing_digest256(networkstatus_t *ns, microdesc_cache_t *cache,
       continue;
     if (skip && digestmap_get(skip, rs->descriptor_digest))
       continue;
+    if (tor_mem_is_zero(rs->descriptor_digest, DIGEST256_LEN))
+      continue; /* This indicates a bug somewhere XXXX023*/
     /* XXXX Also skip if we're a noncache and wouldn't use this router.
      * XXXX NM Microdesc
      */
@@ -602,11 +608,8 @@ update_microdesc_downloads(time_t now)
   if (!consensus)
     return;
 
-  if (!directory_caches_dir_info(options)) {
-    /* Right now, only caches fetch microdescriptors.
-     * XXXX NM Microdescs */
+  if (!we_fetch_microdescriptors(options))
     return;
-  }
 
   pending = digestmap_new();
   list_pending_microdesc_downloads(pending);
@@ -647,3 +650,45 @@ update_microdescs_from_networkstatus(time_t now)
   } SMARTLIST_FOREACH_END(rs);
 }
 
+/** Return true iff we should prefer to use microdescriptors rather than
+ * routerdescs for building circuits. */
+int
+we_use_microdescriptors_for_circuits(or_options_t *options)
+{
+  int ret = options->UseMicrodescriptors;
+  if (ret == -1) {
+    /* UseMicrodescriptors is "auto"; we need to decide: */
+    /* So we decide that we'll use microdescriptors iff we are not a server */
+    ret = ! server_mode(options);
+  }
+  return ret;
+}
+
+/** Return true iff we should try to download microdescriptors at all. */
+int
+we_fetch_microdescriptors(or_options_t *options)
+{
+  if (directory_caches_dir_info(options))
+    return 1;
+  return we_use_microdescriptors_for_circuits(options);
+}
+
+/** Return true iff we should try to download router descriptors at all. */
+int
+we_fetch_router_descriptors(or_options_t *options)
+{
+  if (directory_caches_dir_info(options))
+    return 1;
+  return ! we_use_microdescriptors_for_circuits(options);
+}
+
+/** Return the consensus flavor we actually want to use to build circuits. */
+int
+usable_consensus_flavor(void)
+{
+  if (we_use_microdescriptors_for_circuits(get_options())) {
+    return FLAV_MICRODESC;
+  } else {
+    return FLAV_NS;
+  }
+}
diff --git a/src/or/microdesc.h b/src/or/microdesc.h
index c967742..94b1ff6 100644
--- a/src/or/microdesc.h
+++ b/src/or/microdesc.h
@@ -43,5 +43,10 @@ void microdesc_free_all(void);
 void update_microdesc_downloads(time_t now);
 void update_microdescs_from_networkstatus(time_t now);
 
+int usable_consensus_flavor(void);
+int we_fetch_microdescriptors(or_options_t *options);
+int we_fetch_router_descriptors(or_options_t *options);
+int we_use_microdescriptors_for_circuits(or_options_t *options);
+
 #endif
 
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index b191f57..663d1ad 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -50,7 +50,9 @@ static strmap_t *unnamed_server_map = NULL;
  * of whichever type we are using for our own circuits.  This will be the same
  * as one of current_ns_consensus or current_md_consensus.
  */
-#define current_consensus current_ns_consensus
+#define current_consensus                                       \
+  (we_use_microdescriptors_for_circuits(get_options()) ?        \
+   current_md_consensus : current_ns_consensus)
 
 /** Most recently received and validated v3 "ns"-flavored consensus network
  * status. */
@@ -1187,7 +1189,7 @@ we_want_to_fetch_flavor(or_options_t *options, int flavor)
   }
   /* Otherwise, we want the flavor only if we want to use it to build
    * circuits. */
-  return (flavor == USABLE_CONSENSUS_FLAVOR);
+  return flavor == usable_consensus_flavor();
 }
 
 /** How many times will we try to fetch a consensus before we give up? */
@@ -1392,7 +1394,7 @@ update_certificate_downloads(time_t now)
 int
 consensus_is_waiting_for_certs(void)
 {
-  return consensus_waiting_for_certs[USABLE_CONSENSUS_FLAVOR].consensus
+  return consensus_waiting_for_certs[usable_consensus_flavor()].consensus
     ? 1 : 0;
 }
 
@@ -1621,7 +1623,7 @@ networkstatus_set_current_consensus(const char *consensus,
     flavor = networkstatus_get_flavor_name(flav);
   }
 
-  if (flav != USABLE_CONSENSUS_FLAVOR &&
+  if (flav != usable_consensus_flavor() &&
       !directory_caches_dir_info(options)) {
     /* This consensus is totally boring to us: we won't use it, and we won't
      * serve it.  Drop it. */
@@ -1726,14 +1728,14 @@ networkstatus_set_current_consensus(const char *consensus,
     }
   }
 
-  if (!from_cache && flav == USABLE_CONSENSUS_FLAVOR)
+  if (!from_cache && flav == usable_consensus_flavor())
     control_event_client_status(LOG_NOTICE, "CONSENSUS_ARRIVED");
 
   /* Are we missing any certificates at all? */
   if (r != 1 && dl_certs)
     authority_certs_fetch_missing(c, now);
 
-  if (flav == USABLE_CONSENSUS_FLAVOR) {
+  if (flav == usable_consensus_flavor()) {
     notify_control_networkstatus_changed(current_consensus, c);
   }
   if (flav == FLAV_NS) {
@@ -1780,8 +1782,8 @@ networkstatus_set_current_consensus(const char *consensus,
       download_status_failed(&consensus_dl_status[flav], 0);
   }
 
-  if (flav == USABLE_CONSENSUS_FLAVOR) {
-    /* XXXXNM Microdescs: needs a non-ns variant. */
+  if (flav == usable_consensus_flavor()) {
+    /* XXXXNM Microdescs: needs a non-ns variant. ???? NM*/
     update_consensus_networkstatus_fetch_time(now);
 
     nodelist_set_consensus(current_consensus);
diff --git a/src/or/or.h b/src/or/or.h
index a976916..f45ccb7 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -1641,6 +1641,9 @@ typedef struct routerstatus_t {
   /** True iff this router is a version that, if it caches directory info,
    * we can get v3 downloads from. */
   unsigned int version_supports_v3_dir:1;
+  /** True iff this router is a version that, if it caches directory info,
+   * we can get microdescriptors from. */
+  unsigned int version_supports_microdesc_cache:1;
 
   unsigned int has_bandwidth:1; /**< The vote/consensus had bw info */
   unsigned int has_exitsummary:1; /**< The vote/consensus had exit summaries */
@@ -1918,9 +1921,6 @@ typedef enum {
   FLAV_MICRODESC = 1,
 } consensus_flavor_t;
 
-/** Which consensus flavor do we actually want to use to build circuits? */
-#define USABLE_CONSENSUS_FLAVOR FLAV_NS
-
 /** How many different consensus flavors are there? */
 #define N_CONSENSUS_FLAVORS ((int)(FLAV_MICRODESC)+1)
 
@@ -2092,6 +2092,12 @@ typedef struct authority_cert_t {
 
 /** Bitfield enum type listing types of information that directory authorities
  * can be authoritative about, and that directory caches may or may not cache.
+ *
+ * Note that the granularity here is based on authority granularity and on
+ * cache capabilities.  Thus, one particular bit may correspond in practice to
+ * a few types of directory info, so long as every authority that pronounces
+ * officially about one of the types prounounces officially about all of them,
+ * and so long as every cache that caches one of them caches all of them.
  */
 typedef enum {
   NO_DIRINFO      = 0,
@@ -2107,7 +2113,9 @@ typedef enum {
   /** Serves bridge descriptors. */
   BRIDGE_DIRINFO  = 1 << 4,
   /** Serves extrainfo documents. */
-  EXTRAINFO_DIRINFO   = 1 << 5,
+  EXTRAINFO_DIRINFO=1 << 5,
+  /** Serves microdescriptors. */
+  MICRODESC_DIRINFO=1 << 6,
 } dirinfo_type_t;
 
 #define CRYPT_PATH_MAGIC 0x70127012u
@@ -2642,7 +2650,7 @@ typedef struct {
   /** To what authority types do we publish our descriptor? Choices are
    * "v1", "v2", "v3", "bridge", or "". */
   smartlist_t *PublishServerDescriptor;
-  /** An authority type, derived from PublishServerDescriptor. */
+  /** A bitfield of authority types, derived from PublishServerDescriptor. */
   dirinfo_type_t _PublishServerDescriptor;
   /** Boolean: do we publish hidden service descriptors to the HS auths? */
   int PublishHidServDescriptors;
@@ -3043,6 +3051,10 @@ typedef struct {
    * the defaults have changed. */
   int _UsingTestNetworkDefaults;
 
+  /** If 1, we try to use microdescriptors to build circuits.  If 0, we don't.
+   * If -1, Tor decides. */
+  int UseMicrodescriptors;
+
 } or_options_t;
 
 /** Persistent state for an onion router, as saved to disk. */
diff --git a/src/or/router.c b/src/or/router.c
index e4dab0e..6de069f 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -699,7 +699,8 @@ init_keys(void)
   crypto_pk_get_digest(get_server_identity_key(), digest);
   type = ((options->V1AuthoritativeDir ? V1_DIRINFO : NO_DIRINFO) |
           (options->V2AuthoritativeDir ? V2_DIRINFO : NO_DIRINFO) |
-          (options->V3AuthoritativeDir ? V3_DIRINFO : NO_DIRINFO) |
+          (options->V3AuthoritativeDir ?
+               (V3_DIRINFO|MICRODESC_DIRINFO|EXTRAINFO_DIRINFO) : NO_DIRINFO) |
           (options->BridgeAuthoritativeDir ? BRIDGE_DIRINFO : NO_DIRINFO) |
           (options->HSAuthoritativeDir ? HIDSERV_DIRINFO : NO_DIRINFO));
 
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index 8bcfa05..6f90a8b 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -1127,6 +1127,9 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags)
     if ((type & EXTRAINFO_DIRINFO) &&
         !router_supports_extrainfo(node->identity, 0))
       continue;
+    if ((type & MICRODESC_DIRINFO) && !is_trusted &&
+        !node->rs->version_supports_microdesc_cache)
+      continue;
     if (try_excluding && options->ExcludeNodes &&
         routerset_contains_routerstatus(options->ExcludeNodes, status,
                                         country)) {
@@ -2443,18 +2446,6 @@ router_get_by_nickname(const char *nickname, int warn_if_unnamed)
 #endif
 }
 
-/** Try to find a routerinfo for <b>digest</b>. If we don't have one,
- * return 1. If we do, ask tor_version_as_new_as() for the answer.
- */
-int
-router_digest_version_as_new_as(const char *digest, const char *cutoff)
-{
-  const routerinfo_t *router = router_get_by_id_digest(digest);
-  if (!router)
-    return 1;
-  return tor_version_as_new_as(router->platform, cutoff);
-}
-
 /** Return true iff <b>digest</b> is the digest of the identity key of a
  * trusted directory matching at least one bit of <b>type</b>.  If <b>type</b>
  * is zero, any authority is okay. */
@@ -4726,6 +4717,8 @@ update_router_descriptor_downloads(time_t now)
   static time_t last_dummy_download = 0;
   if (should_delay_dir_fetches(options))
     return;
+  if (!we_fetch_router_descriptors(options))
+    return;
   if (directory_fetches_dir_info_early(options)) {
     update_router_descriptor_cache_downloads_v2(now);
   }
@@ -4879,20 +4872,28 @@ count_usable_descriptors(int *num_present, int *num_usable,
                          or_options_t *options, time_t now,
                          routerset_t *in_set)
 {
+  const int md = (consensus->flavor == FLAV_MICRODESC);
   *num_present = 0, *num_usable=0;
 
-  SMARTLIST_FOREACH(consensus->routerstatus_list, routerstatus_t *, rs,
-     {
+  SMARTLIST_FOREACH_BEGIN(consensus->routerstatus_list, routerstatus_t *, rs)
+    {
        if (in_set && ! routerset_contains_routerstatus(in_set, rs, -1))
          continue;
        if (client_would_use_router(rs, now, options)) {
+         const char * const digest = rs->descriptor_digest;
+         int present;
          ++*num_usable; /* the consensus says we want it. */
-         if (router_get_by_descriptor_digest(rs->descriptor_digest)) {
+         if (md)
+           present = NULL != (microdesc_cache_lookup_by_digest256(NULL, digest));
+         else
+           present = NULL != router_get_by_descriptor_digest(digest);
+         if (present) {
            /* we have the descriptor listed in the consensus. */
            ++*num_present;
          }
        }
-     });
+     }
+  SMARTLIST_FOREACH_END(rs);
 
   log_debug(LD_DIR, "%d usable, %d present.", *num_usable, *num_present);
 }
@@ -4906,7 +4907,7 @@ count_loading_descriptors_progress(void)
   int num_present = 0, num_usable=0;
   time_t now = time(NULL);
   const networkstatus_t *consensus =
-    networkstatus_get_reasonably_live_consensus(now, FLAV_NS);
+    networkstatus_get_reasonably_live_consensus(now, usable_consensus_flavor());
   double fraction;
 
   if (!consensus)
@@ -4936,14 +4937,14 @@ update_router_have_minimum_dir_info(void)
   int res;
   or_options_t *options = get_options();
   const networkstatus_t *consensus =
-    networkstatus_get_reasonably_live_consensus(now, FLAV_NS);
+    networkstatus_get_reasonably_live_consensus(now, usable_consensus_flavor());
 
   if (!consensus) {
     if (!networkstatus_get_latest_consensus())
-      strlcpy(dir_info_status, "We have no network-status consensus.",
+      strlcpy(dir_info_status, "We have no usable consensus.",
               sizeof(dir_info_status));
     else
-      strlcpy(dir_info_status, "We have no recent network-status consensus.",
+      strlcpy(dir_info_status, "We have no recent usable consensus.",
               sizeof(dir_info_status));
     res = 0;
     goto done;
diff --git a/src/or/routerlist.h b/src/or/routerlist.h
index 41a4c90..940e206 100644
--- a/src/or/routerlist.h
+++ b/src/or/routerlist.h
@@ -56,7 +56,6 @@ const node_t *router_choose_random_node(smartlist_t *excludedsmartlist,
 
 const routerinfo_t *router_get_by_nickname(const char *nickname,
                                      int warn_if_unnamed);
-int router_digest_version_as_new_as(const char *digest, const char *cutoff);
 int router_digest_is_trusted_dir_type(const char *digest,
                                       dirinfo_type_t type);
 #define router_digest_is_trusted_dir(d) \
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 80214b3..163cc37 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -2085,6 +2085,7 @@ routerstatus_parse_entry_from_string(memarea_t *area,
       rs->version_supports_begindir = 1;
       rs->version_supports_extrainfo_upload = 1;
       rs->version_supports_conditional_consensus = 1;
+      rs->version_supports_microdesc_cache = 1;
     } else {
       rs->version_supports_begindir =
         tor_version_as_new_as(tok->args[0], "0.2.0.1-alpha");
@@ -2094,6 +2095,8 @@ routerstatus_parse_entry_from_string(memarea_t *area,
         tor_version_as_new_as(tok->args[0], "0.2.0.8-alpha");
       rs->version_supports_conditional_consensus =
         tor_version_as_new_as(tok->args[0], "0.2.1.1-alpha");
+      rs->version_supports_microdesc_cache =
+        tor_version_as_new_as(tok->args[0], "0.2.3.0-alpha");
     }
     if (vote_rs) {
       vote_rs->version = tor_strdup(tok->args[0]);





More information about the tor-commits mailing list