[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[or-cvs] [tor/master 03/17] Add parsing+verification for bw weight values.



Author: Mike Perry <mikeperry-git@xxxxxxxxxx>
Date: Fri, 29 Jan 2010 15:40:40 -0800
Subject: Add parsing+verification for bw weight values.
Commit: df1ef2f0f0856af71d92d333bcf2c788c2938703

---
 src/or/dirvote.c       |    6 +-
 src/or/networkstatus.c |   17 ++
 src/or/or.h            |    7 +
 src/or/routerparse.c   |  378 ++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 406 insertions(+), 2 deletions(-)

diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index 360ed87..143c324 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -1784,8 +1784,10 @@ networkstatus_compute_consensus(smartlist_t *votes,
       tor_free(result);
       return NULL;
     }
-    // XXX: Verify balancing parameters here
-    // networkstatus_verify_bw_weights(c);
+    // Verify balancing parameters
+    if (consensus_method >= 9) {
+      networkstatus_verify_bw_weights(c);
+    }
     networkstatus_vote_free(c);
   }
 
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index b5c84d0..d673ef6 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -2074,6 +2074,23 @@ networkstatus_get_param(networkstatus_t *ns, const char *param_name,
   return get_net_param_from_list(ns->net_params, param_name, default_val);
 }
 
+/** Return the value of a integer bw weight parameter from the networkstatus
+ * <b>ns</b> whose name is <b>weight_name</b>.  If <b>ns</b> is NULL, try
+ * loading the latest consensus ourselves. Return <b>default_val</b> if no
+ * latest consensus, or if it has no parameter called <b>param_name</b>. */
+int32_t
+networkstatus_get_bw_weight(networkstatus_t *ns, const char *weight_name,
+                        int32_t default_val)
+{
+  if (!ns) /* if they pass in null, go find it ourselves */
+    ns = networkstatus_get_latest_consensus();
+
+  if (!ns || !ns->weight_params)
+    return default_val;
+
+  return get_net_param_from_list(ns->weight_params, weight_name, default_val);
+}
+
 /** Return the name of the consensus flavor <b>flav</b> as used to identify
  * the flavor in directory documents. */
 const char *
diff --git a/src/or/or.h b/src/or/or.h
index a1d224a..73ed10f 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -1771,6 +1771,10 @@ typedef struct networkstatus_t {
    * consensus, sorted by key. */
   smartlist_t *net_params;
 
+  /** List of key=value strings for the bw weight parameters in the
+   * consensus. */
+  smartlist_t *weight_params;
+
   /** List of networkstatus_voter_info_t.  For a vote, only one element
    * is included.  For a consensus, one element is included for every voter
    * whose vote contributed to the consensus. */
@@ -4354,6 +4358,8 @@ int32_t networkstatus_get_param(networkstatus_t *ns, const char *param_name,
                                 int32_t default_val);
 int getinfo_helper_networkstatus(control_connection_t *conn,
                                  const char *question, char **answer);
+int32_t networkstatus_get_bw_weight(networkstatus_t *ns, const char *weight,
+                                    int32_t default_val);
 const char *networkstatus_get_flavor_name(consensus_flavor_t flav);
 int networkstatus_parse_flavor_name(const char *flavname);
 void document_signature_free(document_signature_t *sig);
@@ -5182,6 +5188,7 @@ void dump_distinct_digest_count(int severity);
 
 int compare_routerstatus_entries(const void **_a, const void **_b);
 networkstatus_v2_t *networkstatus_v2_parse_from_string(const char *s);
+int networkstatus_verify_bw_weights(networkstatus_t *ns);
 networkstatus_t *networkstatus_parse_vote_from_string(const char *s,
                                                  const char **eos_out,
                                                  networkstatus_type_t ns_type);
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index f4af7cd..1212907 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -11,6 +11,8 @@
 
 #include "or.h"
 #include "memarea.h"
+#undef log
+#include <math.h>
 
 /****************************************************************************/
 
@@ -104,6 +106,7 @@ typedef enum {
 
   K_KNOWN_FLAGS,
   K_PARAMS,
+  K_BW_WEIGHTS,
   K_VOTE_DIGEST,
   K_CONSENSUS_DIGEST,
   K_ADDITIONAL_DIGEST,
@@ -485,6 +488,7 @@ static token_rule_t networkstatus_consensus_token_table[] = {
 /** List of tokens allowable in the footer of v1/v2 directory/networkstatus
  * footers. */
 static token_rule_t networkstatus_vote_footer_token_table[] = {
+  T01("bandwidth-weights",   K_BW_WEIGHTS,          ARGS,        NO_OBJ ),
   T(  "directory-signature", K_DIRECTORY_SIGNATURE, GE(2),   NEED_OBJ ),
   END_OF_TABLE
 };
@@ -2327,6 +2331,360 @@ networkstatus_v2_parse_from_string(const char *s)
   return ns;
 }
 
+/** Verify the bandwidth weights of a network status document */
+int
+networkstatus_verify_bw_weights(networkstatus_t *ns)
+{
+  int64_t weight_scale;
+  int64_t G=0, M=0, E=0, D=0, T=0;
+  double Wgg, Wgm, Wgd, Wmg, Wmm, Wme, Wmd, Weg, Wem, Wee, Wed;
+  double Gtotal=0, Mtotal=0, Etotal=0;
+  const char *casename = NULL;
+  int valid = 1;
+
+  weight_scale = networkstatus_get_param(ns, "bwweightscale", BW_WEIGHT_SCALE);
+  Wgg = networkstatus_get_bw_weight(ns, "Wgg", -1);
+  Wgm = networkstatus_get_bw_weight(ns, "Wgm", -1);
+  Wgd = networkstatus_get_bw_weight(ns, "Wgd", -1);
+  Wmg = networkstatus_get_bw_weight(ns, "Wmg", -1);
+  Wmm = networkstatus_get_bw_weight(ns, "Wmm", -1);
+  Wme = networkstatus_get_bw_weight(ns, "Wme", -1);
+  Wmd = networkstatus_get_bw_weight(ns, "Wmd", -1);
+  Weg = networkstatus_get_bw_weight(ns, "Weg", -1);
+  Wem = networkstatus_get_bw_weight(ns, "Wem", -1);
+  Wee = networkstatus_get_bw_weight(ns, "Wee", -1);
+  Wed = networkstatus_get_bw_weight(ns, "Wed", -1);
+
+  if (Wgg<0 || Wgm<0 || Wgd<0 || Wmg<0 || Wmm<0 || Wme<0 || Wmd<0 || Weg<0
+          || Wem<0 || Wee<0 || Wed<0) {
+    log_warn(LD_BUG, "No bandwidth weights produced in consensus!");
+    return 0;
+  }
+
+  // First, sanity check basic summing properties that hold for all cases
+  // We use > 1 as the check for these because they are computed as integers.
+  // Sometimes there are rounding errors.
+  if (fabs(Wmm - weight_scale) > 1) {
+    log_warn(LD_BUG, "Wmm=%lf != "I64_FORMAT, Wmm, weight_scale);
+    valid = 0;
+  }
+
+  if (fabs(Wem - Wee) > 1) {
+    log_warn(LD_BUG, "Wem=%lf != Wee=%lf", Wem, Wee);
+    valid = 0;
+  }
+
+  if (fabs(Wgm - Wgg) > 1) {
+    log_warn(LD_BUG, "Wgm=%lf != Wgg=%lf", Wgm, Wgg);
+    valid = 0;
+  }
+
+  if (fabs(Weg - Wed) > 1) {
+    log_warn(LD_BUG, "Wed=%lf != Weg=%lf", Wed, Weg);
+    valid = 0;
+  }
+
+  if (fabs(Wgg + Wmg - weight_scale) > 0.001*weight_scale) {
+    log_warn(LD_BUG, "Wgg=%lf != "I64_FORMAT" - Wmg=%lf", Wgg,
+             weight_scale, Wmg);
+    valid = 0;
+  }
+
+  if (fabs(Wee + Wme - weight_scale) > 0.001*weight_scale) {
+    log_warn(LD_BUG, "Wee=%lf != "I64_FORMAT" - Wme=%lf", Wee,
+             weight_scale, Wme);
+    valid = 0;
+  }
+
+  if (fabs(Wgd + Wmd + Wed - weight_scale) > 0.001*weight_scale) {
+    log_warn(LD_BUG, "Wgd=%lf + Wmd=%lf + Wed=%lf != "I64_FORMAT,
+             Wgd, Wmd, Wed, weight_scale);
+    valid = 0;
+  }
+
+  Wgg /= weight_scale;
+  Wgm /= weight_scale;
+  Wgd /= weight_scale;
+
+  Wmg /= weight_scale;
+  Wmm /= weight_scale;
+  Wme /= weight_scale;
+  Wmd /= weight_scale;
+
+  Weg /= weight_scale;
+  Wem /= weight_scale;
+  Wee /= weight_scale;
+  Wed /= weight_scale;
+
+  // Then, gather G, M, E, D, T to determine case
+  SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) {
+    if (rs->has_bandwidth) {
+      T += rs->bandwidth;
+      if (rs->is_exit && rs->is_possible_guard) {
+        D += rs->bandwidth;
+        Gtotal += Wgd*rs->bandwidth;
+        Mtotal += Wmd*rs->bandwidth;
+        Etotal += Wed*rs->bandwidth;
+      } else if (rs->is_exit) {
+        E += rs->bandwidth;
+        Mtotal += Wme*rs->bandwidth;
+        Etotal += Wee*rs->bandwidth;
+      } else if (rs->is_possible_guard) {
+        G += rs->bandwidth;
+        Gtotal += Wgg*rs->bandwidth;
+        Mtotal += Wmg*rs->bandwidth;
+      } else {
+        M += rs->bandwidth;
+        Mtotal += Wmm*rs->bandwidth;
+      }
+    } else {
+      log_warn(LD_BUG, "Missing consensus bandwidth for router %s",
+          rs->nickname);
+    }
+  } SMARTLIST_FOREACH_END(rs);
+
+  // Finally, check equality conditions depending upon case 1, 2 or 3
+  // Full equality cases: 1, 3b
+  // Partial equality cases: 2b (E=G), 3a (M=E)
+  // Fully unknown: 2a
+  if (3*E >= T && 3*G >= T) {
+    // Case 1: Neither are scarce
+    casename = "Case 1";
+    if (fabs(Etotal-Mtotal) > 0.01*MAX(Etotal,Mtotal)) {
+      log_warn(LD_DIR,
+               "Bw Weight Failure for %s: Etotal %lf != Mtotal %lf. "
+               "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
+               " T="I64_FORMAT". "
+               "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+               casename, Etotal, Mtotal, G, M, E, D, T,
+               Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+      valid = 0;
+    }
+    if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
+      log_warn(LD_DIR,
+               "Bw Weight Failure for %s: Etotal %lf != Gtotal %lf. "
+               "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
+               " T="I64_FORMAT". "
+               "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+               casename, Etotal, Gtotal, G, M, E, D, T,
+               Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+      valid = 0;
+    }
+    if (fabs(Gtotal-Mtotal) > 0.01*MAX(Gtotal,Mtotal)) {
+      log_warn(LD_DIR,
+               "Bw Weight Failure for %s: Mtotal %lf != Gtotal %lf. "
+               "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
+               " T="I64_FORMAT". "
+               "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+               casename, Mtotal, Gtotal, G, M, E, D, T,
+               Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+      valid = 0;
+    }
+  } else if (3*E < T && 3*G < T) {
+    int64_t R = MIN(E, G);
+    int64_t S = MAX(E, G);
+    /*
+     * Case 2: Both Guards and Exits are scarce
+     * Balance D between E and G, depending upon
+     * D capacity and scarcity. Devote no extra
+     * bandwidth to middle nodes.
+     */
+    if (R+D < S) { // Subcase a
+      double Rtotal, Stotal;
+      if (E < G) {
+        Rtotal = Etotal;
+        Stotal = Gtotal;
+      } else {
+        Rtotal = Gtotal;
+        Stotal = Etotal;
+      }
+      casename = "Case 2a";
+      // Rtotal < Stotal
+      if (Rtotal > Stotal) {
+        log_warn(LD_DIR,
+                   "Bw Weight Failure for %s: Rtotal %lf > Stotal %lf. "
+                   "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
+                   " T="I64_FORMAT". "
+                   "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+                   casename, Rtotal, Stotal, G, M, E, D, T,
+                   Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+        valid = 0;
+      }
+      // Rtotal < T/3
+      if (3*Rtotal > T) {
+        log_warn(LD_DIR,
+                   "Bw Weight Failure for %s: 3*Rtotal %lf > T "
+                   I64_FORMAT". G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT
+                   " D="I64_FORMAT" T="I64_FORMAT". "
+                   "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+                   casename, Rtotal*3, T, G, M, E, D, T,
+                   Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+        valid = 0;
+      }
+      // Stotal < T/3
+      if (3*Stotal > T) {
+        log_warn(LD_DIR,
+                   "Bw Weight Failure for %s: 3*Stotal %lf > T "
+                   I64_FORMAT". G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT
+                   " D="I64_FORMAT" T="I64_FORMAT". "
+                   "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+                   casename, Stotal*3, T, G, M, E, D, T,
+                   Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+        valid = 0;
+      }
+      // Mtotal > T/3
+      if (3*Mtotal < T) {
+        log_warn(LD_DIR,
+                   "Bw Weight Failure for %s: 3*Mtotal %lf < T "
+                   I64_FORMAT". "
+                   "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
+                   " T="I64_FORMAT". "
+                   "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+                   casename, Mtotal*3, T, G, M, E, D, T,
+                   Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+        valid = 0;
+      }
+    } else { // Subcase b: R+D > S
+      casename = "Case 2b";
+
+      /* Check the rare-M redirect case. */
+      if (D != 0 && 3*M < T) {
+        casename = "Case 2b (balanced)";
+        if (fabs(Etotal-Mtotal) > 0.01*MAX(Etotal,Mtotal)) {
+          log_warn(LD_DIR,
+                   "Bw Weight Failure for %s: Etotal %lf != Mtotal %lf. "
+                   "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
+                   " T="I64_FORMAT". "
+                   "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+                   casename, Etotal, Mtotal, G, M, E, D, T,
+                   Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+          valid = 0;
+        }
+        if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
+          log_warn(LD_DIR,
+                   "Bw Weight Failure for %s: Etotal %lf != Gtotal %lf. "
+                   "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
+                   " T="I64_FORMAT". "
+                   "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+                   casename, Etotal, Gtotal, G, M, E, D, T,
+                   Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+          valid = 0;
+        }
+        if (fabs(Gtotal-Mtotal) > 0.01*MAX(Gtotal,Mtotal)) {
+          log_warn(LD_DIR,
+                   "Bw Weight Failure for %s: Mtotal %lf != Gtotal %lf. "
+                   "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
+                   " T="I64_FORMAT". "
+                   "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+                   casename, Mtotal, Gtotal, G, M, E, D, T,
+                   Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+          valid = 0;
+        }
+      } else {
+        if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
+          log_warn(LD_DIR,
+                   "Bw Weight Failure for %s: Etotal %lf != Gtotal %lf. "
+                   "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
+                   " T="I64_FORMAT". "
+                   "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+                   casename, Etotal, Gtotal, G, M, E, D, T,
+                   Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+          valid = 0;
+        }
+      }
+    }
+  } else { // if (E < T/3 || G < T/3) {
+    int64_t S = MIN(E, G);
+    int64_t NS = MAX(E, G);
+    if (3*(S+D) < T) { // Subcase a:
+      double Stotal;
+      double NStotal;
+      if (G < E) {
+        casename = "Case 3a (G scarce)";
+        Stotal = Gtotal;
+        NStotal = Etotal;
+      } else { // if (G >= E) {
+        casename = "Case 3a (E scarce)";
+        NStotal = Gtotal;
+        Stotal = Etotal;
+      }
+      // Stotal < T/3
+      if (3*Stotal > T) {
+        log_warn(LD_DIR,
+                   "Bw Weight Failure for %s: 3*Stotal %lf > T "
+                   I64_FORMAT". G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT
+                   " D="I64_FORMAT" T="I64_FORMAT". "
+                   "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+                   casename, Stotal*3, T, G, M, E, D, T,
+                   Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+        valid = 0;
+      }
+      if (NS >= M) {
+        if (fabs(NStotal-Mtotal) > 0.01*MAX(NStotal,Mtotal)) {
+          log_warn(LD_DIR,
+                   "Bw Weight Failure for %s: NStotal %lf != Mtotal %lf. "
+                   "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
+                   " T="I64_FORMAT". "
+                   "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+                   casename, NStotal, Mtotal, G, M, E, D, T,
+                   Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+          valid = 0;
+        }
+      } else {
+        // if NS < M, NStotal > T/3 because only one of G or E is scarce
+        if (3*NStotal < T) {
+          log_warn(LD_DIR,
+                     "Bw Weight Failure for %s: 3*NStotal %lf < T "
+                     I64_FORMAT". G="I64_FORMAT" M="I64_FORMAT
+                     " E="I64_FORMAT" D="I64_FORMAT" T="I64_FORMAT". "
+                     "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+                     casename, NStotal*3, T, G, M, E, D, T,
+                     Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+          valid = 0;
+        }
+      }
+    } else { // Subcase b: S+D >= T/3
+      casename = "Case 3b";
+      if (fabs(Etotal-Mtotal) > 0.01*MAX(Etotal,Mtotal)) {
+        log_warn(LD_DIR,
+                 "Bw Weight Failure for %s: Etotal %lf != Mtotal %lf. "
+                 "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
+                 " T="I64_FORMAT". "
+                 "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+                 casename, Etotal, Mtotal, G, M, E, D, T,
+                 Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+        valid = 0;
+      }
+      if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
+        log_warn(LD_DIR,
+                 "Bw Weight Failure for %s: Etotal %lf != Gtotal %lf. "
+                 "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
+                 " T="I64_FORMAT". "
+                 "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+                 casename, Etotal, Gtotal, G, M, E, D, T,
+                 Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+        valid = 0;
+      }
+      if (fabs(Gtotal-Mtotal) > 0.01*MAX(Gtotal,Mtotal)) {
+        log_warn(LD_DIR,
+                 "Bw Weight Failure for %s: Mtotal %lf != Gtotal %lf. "
+                 "G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
+                 " T="I64_FORMAT". "
+                 "Wgg=%lf Wgd=%lf Wmg=%lf Wme=%lf Wmd=%lf Wee=%lf Wed=%lf",
+                 casename, Mtotal, Gtotal, G, M, E, D, T,
+                 Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
+        valid = 0;
+      }
+    }
+  }
+
+  if (valid)
+    log_notice(LD_DIR, "Bandwidth-weight %s is verified and valid.",
+               casename);
+
+  return valid;
+}
+
 /** Parse a v3 networkstatus vote, opinion, or consensus (depending on
  * ns_type), from <b>s</b>, and return the result.  Return NULL on failure. */
 networkstatus_t *
@@ -2675,6 +3033,26 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
     goto err;
   }
 
+  tok = find_opt_by_keyword(footer_tokens, K_BW_WEIGHTS);
+  if (tok) {
+    ns->weight_params = smartlist_create();
+    for (i = 0; i < tok->n_args; ++i) {
+      int ok=0;
+      char *eq = strchr(tok->args[i], '=');
+      if (!eq) {
+        log_warn(LD_DIR, "Bad element '%s' in weight params",
+                 escaped(tok->args[i]));
+        goto err;
+      }
+      tor_parse_long(eq+1, 10, INT32_MIN, INT32_MAX, &ok, NULL);
+      if (!ok) {
+        log_warn(LD_DIR, "Bad element '%s' in params", escaped(tok->args[i]));
+        goto err;
+      }
+      smartlist_add(ns->weight_params, tor_strdup(tok->args[i]));
+    }
+  }
+
   SMARTLIST_FOREACH_BEGIN(footer_tokens, directory_token_t *, _tok) {
     char declared_identity[DIGEST_LEN];
     networkstatus_voter_info_t *v;
-- 
1.6.5