Commit d5b32d86 authored by Karsten Loesing's avatar Karsten Loesing
Browse files

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.
parent f8f2c1bb
# 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
......
......@@ -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();
}
......
......@@ -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();
......
......@@ -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;
}
}
......@@ -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:
......
/* 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());
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment