[tor-commits] [metrics-lib/master] Parse flag thresholds in bridge network statuses.

karsten at torproject.org karsten at torproject.org
Thu Dec 10 18:58:35 UTC 2015


commit d5b32d86245a323e0e6b6fde0899d14557438490
Author: Karsten Loesing <karsten.loesing at gmx.net>
Date:   Sat Nov 21 20:46:08 2015 +0100

    Parse flag thresholds in bridge network statuses.
    
    Also parse the "ignoring-advertised-bws" flag threshold in votes that was
    added while we were not looking.
    
    Implements #17617.
---
 CHANGELOG.md                                       |    8 ++
 .../torproject/descriptor/BridgeNetworkStatus.java |   47 ++++++
 .../descriptor/RelayNetworkStatusVote.java         |    6 +
 .../descriptor/impl/BridgeNetworkStatusImpl.java   |  102 +++++++++++++
 .../impl/RelayNetworkStatusVoteImpl.java           |    8 ++
 .../descriptor/impl/BridgeNetworkStatusTest.java   |  151 ++++++++++++++++++++
 6 files changed, 322 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 437f2e6..d9eba21 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,11 @@
+# Changes in version 1.x.x - 201x-xx-xx
+
+ * Medium changes
+    - Parse flag thresholds in bridge network statuses, and parse the
+      "ignoring-advertised-bws" flag threshold in relay network status
+      votes.
+
+
 # Changes in version 1.0.0 - 2015-12-05
 
  * Major changes
diff --git a/src/org/torproject/descriptor/BridgeNetworkStatus.java b/src/org/torproject/descriptor/BridgeNetworkStatus.java
index 296cd22..1a28ebf 100644
--- a/src/org/torproject/descriptor/BridgeNetworkStatus.java
+++ b/src/org/torproject/descriptor/BridgeNetworkStatus.java
@@ -9,6 +9,53 @@ public interface BridgeNetworkStatus extends Descriptor {
   /* Return the published time in milliseconds. */
   public long getPublishedMillis();
 
+  /* Return the minimum uptime in seconds that this authority requires for
+   * assigning the Stable flag, or -1 if the authority doesn't report this
+   * value. */
+  public long getStableUptime();
+
+  /* Return the minimum MTBF (mean time between failure) that this
+   * authority requires for assigning the Stable flag, or -1 if the
+   * authority doesn't report this value. */
+  public long getStableMtbf();
+
+  /* Return the minimum bandwidth that this authority requires for
+   * assigning the Fast flag, or -1 if the authority doesn't report this
+   * value. */
+  public long getFastBandwidth();
+
+  /* Return the minimum WFU (weighted fractional uptime) in percent that
+   * this authority requires for assigning the Guard flag, or -1.0 if the
+   * authority doesn't report this value. */
+  public double getGuardWfu();
+
+  /* Return the minimum weighted time in seconds that this authority needs
+   * to know about a relay before assigning the Guard flag, or -1 if the
+   * authority doesn't report this information. */
+  public long getGuardTk();
+
+  /* Return the minimum bandwidth that this authority requires for
+   * assigning the Guard flag if exits can be guards, or -1 if the
+   * authority doesn't report this value. */
+  public long getGuardBandwidthIncludingExits();
+
+  /* Return the minimum bandwidth that this authority requires for
+   * assigning the Guard flag if exits can not be guards, or -1 if the
+   * authority doesn't report this value. */
+  public long getGuardBandwidthExcludingExits();
+
+  /* Return 1 if the authority has measured enough MTBF info to use the
+   * MTBF requirement instead of the uptime requirement for assigning the
+   * Stable flag, 0 if not, or -1 if the authority doesn't report this
+   * information. */
+  public int getEnoughMtbfInfo();
+
+  /* Return 1 if the authority has enough measured bandwidths that it'll
+   * ignore the advertised bandwidth claims of routers without measured
+   * bandwidth, 0 if not, or -1 if the authority doesn't report this
+   * information. */
+  public int getIgnoringAdvertisedBws();
+
   /* Return status entries, one for each contained bridge. */
   public SortedMap<String, NetworkStatusEntry> getStatusEntries();
 }
diff --git a/src/org/torproject/descriptor/RelayNetworkStatusVote.java b/src/org/torproject/descriptor/RelayNetworkStatusVote.java
index aeab268..bf4ea6b 100644
--- a/src/org/torproject/descriptor/RelayNetworkStatusVote.java
+++ b/src/org/torproject/descriptor/RelayNetworkStatusVote.java
@@ -85,6 +85,12 @@ public interface RelayNetworkStatusVote extends Descriptor {
    * information. */
   public int getEnoughMtbfInfo();
 
+  /* Return 1 if the authority has enough measured bandwidths that it'll
+   * ignore the advertised bandwidth claims of routers without measured
+   * bandwidth, 0 if not, or -1 if the authority doesn't report this
+   * information. */
+  public int getIgnoringAdvertisedBws();
+
   /* Return consensus parameters. */
   public SortedMap<String, Integer> getConsensusParams();
 
diff --git a/src/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java b/src/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java
index 5935c86..bddf5ab 100644
--- a/src/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java
+++ b/src/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java
@@ -6,7 +6,9 @@ import org.torproject.descriptor.DescriptorParseException;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Map;
 import java.util.Scanner;
+import java.util.SortedMap;
 import java.util.TimeZone;
 
 import org.torproject.descriptor.BridgeNetworkStatus;
@@ -52,6 +54,20 @@ public class BridgeNetworkStatusImpl extends NetworkStatusImpl
 
   protected void parseHeader(byte[] headerBytes)
       throws DescriptorParseException {
+    /* Initialize flag-thresholds values here for the case that the status
+     * doesn't contain those values.  Initializing them in the constructor
+     * or when declaring variables wouldn't work, because those parts are
+     * evaluated later and would overwrite everything we parse here. */
+    this.stableUptime = -1L;
+    this.stableMtbf = -1L;
+    this.fastBandwidth = -1L;
+    this.guardWfu = -1.0;
+    this.guardTk = -1L;
+    this.guardBandwidthIncludingExits = -1L;
+    this.guardBandwidthExcludingExits = -1L;
+    this.enoughMtbfInfo = -1;
+    this.ignoringAdvertisedBws = -1;
+
     Scanner s = new Scanner(new String(headerBytes)).useDelimiter("\n");
     while (s.hasNext()) {
       String line = s.next();
@@ -59,6 +75,8 @@ public class BridgeNetworkStatusImpl extends NetworkStatusImpl
       String keyword = parts[0];
       if (keyword.equals("published")) {
         this.parsePublishedLine(line, parts);
+      } else if (keyword.equals("flag-thresholds")) {
+        this.parseFlagThresholdsLine(line, parts);
       } else if (this.failUnrecognizedDescriptorLines) {
         throw new DescriptorParseException("Unrecognized line '" + line
             + "' in bridge network status.");
@@ -77,6 +95,45 @@ public class BridgeNetworkStatusImpl extends NetworkStatusImpl
         1, 2);
   }
 
+  private void parseFlagThresholdsLine(String line, String[] parts)
+      throws DescriptorParseException {
+    if (parts.length < 2) {
+      throw new DescriptorParseException("No flag thresholds in line '"
+          + line + "'.");
+    }
+    SortedMap<String, String> flagThresholds =
+        ParseHelper.parseKeyValueStringPairs(line, parts, 1, "=");
+    try {
+      for (Map.Entry<String, String> e : flagThresholds.entrySet()) {
+        if (e.getKey().equals("stable-uptime")) {
+          this.stableUptime = Long.parseLong(e.getValue());
+        } else if (e.getKey().equals("stable-mtbf")) {
+          this.stableMtbf = Long.parseLong(e.getValue());
+        } else if (e.getKey().equals("fast-speed")) {
+          this.fastBandwidth = Long.parseLong(e.getValue());
+        } else if (e.getKey().equals("guard-wfu")) {
+          this.guardWfu = Double.parseDouble(e.getValue().
+              replaceAll("%", ""));
+        } else if (e.getKey().equals("guard-tk")) {
+          this.guardTk = Long.parseLong(e.getValue());
+        } else if (e.getKey().equals("guard-bw-inc-exits")) {
+          this.guardBandwidthIncludingExits =
+              Long.parseLong(e.getValue());
+        } else if (e.getKey().equals("guard-bw-exc-exits")) {
+          this.guardBandwidthExcludingExits =
+              Long.parseLong(e.getValue());
+        } else if (e.getKey().equals("enough-mtbf")) {
+          this.enoughMtbfInfo = Integer.parseInt(e.getValue());
+        } else if (e.getKey().equals("ignoring-advertised-bws")) {
+          this.ignoringAdvertisedBws = Integer.parseInt(e.getValue());
+        }
+      }
+    } catch (NumberFormatException ex) {
+      throw new DescriptorParseException("Illegal value in line '"
+          + line + "'.");
+    }
+  }
+
   protected void parseDirSource(byte[] dirSourceBytes)
       throws DescriptorParseException {
     throw new DescriptorParseException("No directory source expected in "
@@ -99,5 +156,50 @@ public class BridgeNetworkStatusImpl extends NetworkStatusImpl
   public long getPublishedMillis() {
     return this.publishedMillis;
   }
+
+  private long stableUptime;
+  public long getStableUptime() {
+    return this.stableUptime;
+  }
+
+  private long stableMtbf;
+  public long getStableMtbf() {
+    return this.stableMtbf;
+  }
+
+  private long fastBandwidth;
+  public long getFastBandwidth() {
+    return this.fastBandwidth;
+  }
+
+  private double guardWfu;
+  public double getGuardWfu() {
+    return this.guardWfu;
+  }
+
+  private long guardTk;
+  public long getGuardTk() {
+    return this.guardTk;
+  }
+
+  private long guardBandwidthIncludingExits;
+  public long getGuardBandwidthIncludingExits() {
+    return this.guardBandwidthIncludingExits;
+  }
+
+  private long guardBandwidthExcludingExits;
+  public long getGuardBandwidthExcludingExits() {
+    return this.guardBandwidthExcludingExits;
+  }
+
+  private int enoughMtbfInfo;
+  public int getEnoughMtbfInfo() {
+    return this.enoughMtbfInfo;
+  }
+
+  private int ignoringAdvertisedBws;
+  public int getIgnoringAdvertisedBws() {
+    return this.ignoringAdvertisedBws;
+  }
 }
 
diff --git a/src/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java b/src/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
index f43733e..410c2f1 100644
--- a/src/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
+++ b/src/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
@@ -72,6 +72,7 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
     this.guardBandwidthIncludingExits = -1L;
     this.guardBandwidthExcludingExits = -1L;
     this.enoughMtbfInfo = -1;
+    this.ignoringAdvertisedBws = -1;
 
     Scanner s = new Scanner(new String(headerBytes)).useDelimiter("\n");
     boolean skipCrypto = false; /* TODO Parse crypto parts. */
@@ -276,6 +277,8 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
               Long.parseLong(e.getValue());
         } else if (e.getKey().equals("enough-mtbf")) {
           this.enoughMtbfInfo = Integer.parseInt(e.getValue());
+        } else if (e.getKey().equals("ignoring-advertised-bws")) {
+          this.ignoringAdvertisedBws = Integer.parseInt(e.getValue());
         }
       }
     } catch (NumberFormatException ex) {
@@ -542,6 +545,11 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
     return this.enoughMtbfInfo;
   }
 
+  private int ignoringAdvertisedBws;
+  public int getIgnoringAdvertisedBws() {
+    return this.ignoringAdvertisedBws;
+  }
+
   private SortedMap<String, Integer> consensusParams;
   public SortedMap<String, Integer> getConsensusParams() {
     return this.consensusParams == null ? null:
diff --git a/test/org/torproject/descriptor/impl/BridgeNetworkStatusTest.java b/test/org/torproject/descriptor/impl/BridgeNetworkStatusTest.java
new file mode 100644
index 0000000..c8e95bf
--- /dev/null
+++ b/test/org/torproject/descriptor/impl/BridgeNetworkStatusTest.java
@@ -0,0 +1,151 @@
+/* Copyright 2015 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+import org.torproject.descriptor.BridgeNetworkStatus;
+import org.torproject.descriptor.DescriptorParseException;
+
+/* Test parsing of bridge network statuses.  Some of the parsing code is
+ * already tested in the consensus/vote-parsing tests. */
+public class BridgeNetworkStatusTest {
+
+  /* Helper class to build a bridge network status based on default data
+   * and modifications requested by test methods. */
+  private static class StatusBuilder {
+    private String fileName = "20151121-173936-"
+        + "4A0CCD2DDC7995083D73F5D667100C8A5831F16D";
+    private static BridgeNetworkStatus
+        createWithFileName(String fileName)
+        throws DescriptorParseException {
+      StatusBuilder sb = new StatusBuilder();
+      sb.fileName = fileName;
+      return new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName,
+          true);
+    }
+    private String publishedLine = "published 2015-11-21 17:39:36";
+    private static BridgeNetworkStatus
+        createWithPublishedLine(String line)
+        throws DescriptorParseException {
+      StatusBuilder sb = new StatusBuilder();
+      sb.publishedLine = line;
+      return new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName,
+          true);
+    }
+    private String flagThresholdsLine = "flag-thresholds "
+        + "stable-uptime=3105080 stable-mtbf=2450615 fast-speed=55000 "
+        + "guard-wfu=98.000% guard-tk=691200 guard-bw-inc-exits=337000 "
+        + "guard-bw-exc-exits=339000 enough-mtbf=1 "
+        + "ignoring-advertised-bws=0";
+    private static BridgeNetworkStatus
+        createWithFlagThresholdsLine(String line)
+        throws DescriptorParseException {
+      StatusBuilder sb = new StatusBuilder();
+      sb.flagThresholdsLine = line;
+      return new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName,
+          true);
+    }
+    private List<String> statusEntries = new ArrayList<String>();
+    private String unrecognizedHeaderLine = null;
+    protected static BridgeNetworkStatus
+        createWithUnrecognizedHeaderLine(String line,
+        boolean failUnrecognizedDescriptorLines)
+        throws DescriptorParseException {
+      StatusBuilder sb = new StatusBuilder();
+      sb.unrecognizedHeaderLine = line;
+      return new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName,
+          failUnrecognizedDescriptorLines);
+    }
+    private String unrecognizedStatusEntryLine = null;
+    protected static BridgeNetworkStatus
+        createWithUnrecognizedStatusEntryLine(String line,
+        boolean failUnrecognizedDescriptorLines)
+        throws DescriptorParseException {
+      StatusBuilder sb = new StatusBuilder();
+      sb.unrecognizedStatusEntryLine = line;
+      return new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName,
+          failUnrecognizedDescriptorLines);
+    }
+
+    private StatusBuilder() {
+      this.statusEntries.add("r Unnamed ABk0wg4j6BLCdZKleVtmNrfzJGI "
+          + "bh7gVU1Cz6+JG+7j4qGsF4prDi8 2015-11-21 15:46:25 "
+          + "10.153.163.200 443 0\ns Fast Running Stable Valid\n"
+          + "w Bandwidth=264\np reject 1-65535");
+    }
+    private byte[] buildStatus() {
+      StringBuilder sb = new StringBuilder();
+      this.appendHeader(sb);
+      this.appendStatusEntries(sb);
+      return sb.toString().getBytes();
+    }
+    private void appendHeader(StringBuilder sb) {
+      if (this.publishedLine != null) {
+        sb.append(this.publishedLine + "\n");
+      }
+      if (this.flagThresholdsLine != null) {
+        sb.append(this.flagThresholdsLine + "\n");
+      }
+      if (this.unrecognizedHeaderLine != null) {
+        sb.append(this.unrecognizedHeaderLine + "\n");
+      }
+    }
+    private void appendStatusEntries(StringBuilder sb) {
+      for (String statusEntry : this.statusEntries) {
+        sb.append(statusEntry + "\n");
+      }
+      if (this.unrecognizedStatusEntryLine != null) {
+        sb.append(this.unrecognizedStatusEntryLine + "\n");
+      }
+    }
+  }
+
+  @Test()
+  public void testSampleStatus() throws DescriptorParseException {
+    StatusBuilder sb = new StatusBuilder();
+    BridgeNetworkStatus status =
+        new BridgeNetworkStatusImpl(sb.buildStatus(), sb.fileName, true);
+    assertEquals(1448127576000L, status.getPublishedMillis());
+    assertEquals(3105080L, status.getStableUptime());
+    assertEquals(2450615L, status.getStableMtbf());
+    assertEquals(55000L, status.getFastBandwidth());
+    assertEquals(98.0, status.getGuardWfu(), 0.001);
+    assertEquals(691200L, status.getGuardTk());
+    assertEquals(337000L, status.getGuardBandwidthIncludingExits());
+    assertEquals(339000L, status.getGuardBandwidthExcludingExits());
+    assertEquals(1, status.getEnoughMtbfInfo());
+    assertEquals(0, status.getIgnoringAdvertisedBws());
+    assertEquals(264, status.getStatusEntries().get(
+        "001934C20E23E812C27592A5795B6636B7F32462").getBandwidth());
+    assertTrue(status.getUnrecognizedLines().isEmpty());
+  }
+
+  @Test()
+  public void testPublishedNoLine() throws DescriptorParseException {
+    BridgeNetworkStatus status =
+        StatusBuilder.createWithPublishedLine(null);
+    assertEquals(1448127576000L, status.getPublishedMillis());
+  }
+
+  @Test()
+  public void testFlagThresholdsNoLine() throws DescriptorParseException {
+    BridgeNetworkStatus status =
+        StatusBuilder.createWithFlagThresholdsLine(null);
+    assertEquals(-1L, status.getStableUptime());
+    assertEquals(-1L, status.getStableMtbf());
+    assertEquals(-1L, status.getFastBandwidth());
+    assertEquals(-1.0, status.getGuardWfu(), 0.001);
+    assertEquals(-1L, status.getGuardTk());
+    assertEquals(-1L, status.getGuardBandwidthIncludingExits());
+    assertEquals(-1L, status.getGuardBandwidthExcludingExits());
+    assertEquals(-1, status.getEnoughMtbfInfo());
+    assertEquals(-1, status.getIgnoringAdvertisedBws());
+  }
+}
+



More information about the tor-commits mailing list