tor-commits
Threads by month
- ----- 2026 -----
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- 1 participants
- 214783 discussions
commit aa8e063e35b10054917625c31d0ee6a42b0fdcca
Author: David Fifield <david(a)bamsoftware.com>
Date: Sun Oct 30 13:13:22 2011 -0700
Remove obsolete 00 experiment.
Replaced by experiments/throughput/throughput.sh.
---
experiments/00 | 78 --------------------------------------------------------
1 files changed, 0 insertions(+), 78 deletions(-)
diff --git a/experiments/00 b/experiments/00
deleted file mode 100755
index e7994df..0000000
--- a/experiments/00
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/bin/bash
-
-# Get arguments
-if [ $1 ]; then
- NUM_CLIENTS=$1
-else
- NUM_CLIENTS=1
-fi
-
-# Go to flashproxy repo root dir
-cd ..
-
-# Create 1GB dump file
-echo "creating dump file..."
-rm dump
-dd if=/dev/null of=dump bs=1G seek=1
-
-# Start http server
-echo "starting http server..."
-thttpd -p 1080
-
-# Start crossdomain policy server
-echo "starting cross domain policy server..."
-sudo ./crossdomaind.py &
-
-# Start the facilitator
-echo "starting facilitator..."
-./facilitator.py 4701 -r 127.0.0.1:1080
-
-# Start socat adapters
-echo "starting socat adapters..."
-i=1
-until [ $i -gt $NUM_CLIENTS ]; do
- let local_port=2000+i
- let remote_port=9000+i
- socat TCP-LISTEN:${remote_port},reuseaddr,fork TCP-LISTEN:${local_port},reuseaddr &
- let i=i+1
-done
-
-sleep 1
-
-# Register with facilitator
-echo "registering with facilitator..."
-i=1
-until [ $i -gt $NUM_CLIENTS ]; do
- let local_port=9000+i
- echo $'POST / HTTP/1.0\r\n\r\nclient=127.0.0.1:'${local_port} | ncat localhost 4701 --send-only
- let i=i+1
-done
-
-# Start the flash proxy, server side
-echo "starting flash proxy server side..."
-firefox --remote "openURL(http://localhost:1080/swfcat.swf?facilitator=127.0.0.1:4701)"
-
-let query_fac_time=NUM_CLIENTS+2
-sleep ${query_fac_time}
-
-# wget the dump file
-echo "getting the dump file..."
-i=2
-until [ $i -gt $NUM_CLIENTS ]; do
- let local_port=2000+i
- xterm -e "/bin/bash -c 'wget -t1 http://localhost:${local_port}/dump -O - > /dev/null; exec /bin/bash -i'" &
- let i=i+1
-done
-
-# Use this one for delaying the termination of experiment below
-wget -t1 http://localhost:2001/dump -O - > /dev/null
-
-echo "Press enter to continue."
-read
-
-# Slaughter the processes
-ps -ef | sed -n '/thttpd/{/grep/!p;}' | awk '{print$2}' | sudo xargs -i kill {}
-ps -ef | sed -n '/crossdomaind.py/{/grep/!p;}' | awk '{print$2}' | sudo xargs -i kill {}
-ps -ef | sed -n '/facilitator.py/{/grep/!p;}' | awk '{print$2}' | sudo xargs -i kill {}
-ps -ef | sed -n '/socat/{/grep/!p;}' | awk '{print$2}' | sudo xargs -i kill {}
-
1
0
30 Oct '11
commit f600f49bf58f8249de6ff57a53a94a888677fe66
Author: David Fifield <david(a)bamsoftware.com>
Date: Sun Oct 30 01:24:43 2011 -0700
Add a facilitator_poll_interval parameter.
---
swfcat.as | 29 +++++++++++++++++++++++++----
1 files changed, 25 insertions(+), 4 deletions(-)
diff --git a/swfcat.as b/swfcat.as
index f02e0a0..b83dc2f 100644
--- a/swfcat.as
+++ b/swfcat.as
@@ -21,6 +21,10 @@
* How many clients to serve concurrently. The default is
* DEFAULT_MAX_NUM_PROXY_PAIRS.
*
+ * facilitator_poll_interval=<FLOAT>
+ * How often to poll the facilitator, in seconds. The default is
+ * DEFAULT_FACILITATOR_POLL_INTERVAL. There is a sanity-check minimum of 1.0 s.
+ *
* client=1
* If set (to any value), run in client RTMFP mode. In this mode, rather than
* connecting to a facilitator and attempting to serve clients, swfcat starts an
@@ -69,8 +73,9 @@ package
private const DEFAULT_MAX_NUM_PROXY_PAIRS:uint = 10;
- // Milliseconds.
- private const FACILITATOR_POLL_INTERVAL:int = 10000;
+ /* In seconds. */
+ private const DEFAULT_FACILITATOR_POLL_INTERVAL:Number = 10.0;
+ private const MIN_FACILITATOR_POLL_INTERVAL:Number = 1.0;
// Bytes per second. Set to undefined to disable limit.
public static const RATE_LIMIT:Number = undefined;
@@ -89,6 +94,7 @@ package
public var debug:Boolean;
private var fac_addr:Object;
private var max_num_proxy_pairs:uint;
+ private var facilitator_poll_interval:Number;
private var local_addr:Object;
public var rate_limit:RateLimit;
@@ -153,6 +159,13 @@ package
}
max_num_proxy_pairs = uint(tmp);
+ tmp = get_param_timespec("facilitator_poll_interval", DEFAULT_FACILITATOR_POLL_INTERVAL);
+ if (tmp == null || tmp < MIN_FACILITATOR_POLL_INTERVAL) {
+ puts("Error: facilitator_poll_interval must be a nonnegative number at least " + MIN_FACILITATOR_POLL_INTERVAL + ".");
+ return;
+ }
+ facilitator_poll_interval = Number(tmp);
+
local_addr = get_param_addr("local", DEFAULT_LOCAL_TOR_CLIENT_ADDR);
if (!local_addr) {
puts("Error: Local spec must be in the form \"host:port\".");
@@ -198,6 +211,14 @@ package
}
}
+ /* Get a floating-point number of seconds from a time specification. The
+ only time specification format is a decimal number of seconds.
+ Returns null on error. */
+ private function get_param_timespec(param:String, default_val:Number):Object
+ {
+ return get_param_number(param, default_val);
+ }
+
/* The main logic begins here, after start-up issues are taken care of. */
private function proxy_main():void
{
@@ -205,7 +226,7 @@ package
var loader:URLLoader;
if (proxy_pairs.length >= max_num_proxy_pairs) {
- setTimeout(proxy_main, FACILITATOR_POLL_INTERVAL);
+ setTimeout(proxy_main, uint(facilitator_poll_interval * 1000));
return;
}
@@ -233,7 +254,7 @@ package
var relay_spec:String;
var proxy_pair:Object;
- setTimeout(proxy_main, FACILITATOR_POLL_INTERVAL);
+ setTimeout(proxy_main, uint(facilitator_poll_interval * 1000));
loader = e.target as URLLoader;
client_spec = loader.data.client;
1
0
30 Oct '11
commit dfe72ac1ed223ccb43b9d92115a28307caf5fa7e
Author: David Fifield <david(a)bamsoftware.com>
Date: Sun Oct 30 00:38:02 2011 -0700
Document Flash parameters (e.g. debug=1).
---
swfcat.as | 31 +++++++++++++++++++++++++++++++
1 files changed, 31 insertions(+), 0 deletions(-)
diff --git a/swfcat.as b/swfcat.as
index b1dabc1..0c2f200 100644
--- a/swfcat.as
+++ b/swfcat.as
@@ -1,3 +1,34 @@
+/* Flash parameters. These can change how the program runs from the outside.
+ * They can be set as a URL query string, for example:
+ * http://www.example.com/swfcat.swf?facilitator=127.0.0.1:9002&debug=1
+ * or in the HTML markup for the embedding of the movie:
+ * <object>
+ * <param name="movie" value="http://www.example.com/swfcat.swf">
+ * <param name="flashvars" value="facilitator=127.0.0.1:9002&debug=1">
+ * <embed src="http://www.example.com/swfcat.swf"
+ * flashvars="facilitator=127.0.0.1:9002&debug=1"></embed>
+ * </object>
+ *
+ * debug=1
+ * If set (to any value), show verbose terminal-like output instead of the
+ * badge.
+ *
+ * facilitator=<HOST>:<PORT>
+ * The address of the facilitator to use. By default it is
+ * DEFAULT_FACILITATOR_ADDR. Both <HOST> and <PORT> must be present.
+ *
+ * client=1
+ * If set (to any value), run in client RTMFP mode. In this mode, rather than
+ * connecting to a facilitator and attempting to serve clients, swfcat starts an
+ * RTMFP socket and connects to a local address (to the connector, to be exact).
+ * See README for more on running in client RTMFP mode. No argument is required
+ * to use RTMFP when in the usual server mode.
+ *
+ * local=<HOST>:<PORT>
+ * When in client RTMFP mode (client=1), connect to this local address. The
+ * default is DEFAULT_LOCAL_TOR_CLIENT_ADDR.
+ */
+
package
{
import flash.display.Sprite;
1
0
[flashproxy/master] Change DEFAULT_MAX_NUM_PROXY_PAIRS from 100 to 10.
by dcf@torproject.org 30 Oct '11
by dcf@torproject.org 30 Oct '11
30 Oct '11
commit 74dd4fbfad5aca0f4f89e61f8c43f90f86ff6897
Author: David Fifield <david(a)bamsoftware.com>
Date: Sun Oct 30 01:09:28 2011 -0700
Change DEFAULT_MAX_NUM_PROXY_PAIRS from 100 to 10.
---
swfcat.as | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/swfcat.as b/swfcat.as
index 938def9..f02e0a0 100644
--- a/swfcat.as
+++ b/swfcat.as
@@ -67,7 +67,7 @@ package
port: 9002
};
- private const DEFAULT_MAX_NUM_PROXY_PAIRS:uint = 100;
+ private const DEFAULT_MAX_NUM_PROXY_PAIRS:uint = 10;
// Milliseconds.
private const FACILITATOR_POLL_INTERVAL:int = 10000;
1
0
commit 5c8da04943e88e9c9ebcf9810173430287c9e33b
Author: David Fifield <david(a)bamsoftware.com>
Date: Sun Oct 30 00:39:54 2011 -0700
Light rearrangement.
---
swfcat.as | 4 +---
1 files changed, 1 insertions(+), 3 deletions(-)
diff --git a/swfcat.as b/swfcat.as
index 0c2f200..9fcf0db 100644
--- a/swfcat.as
+++ b/swfcat.as
@@ -82,12 +82,10 @@ package
/* Proxy pairs currently connected (up to MAX_NUM_PROXY_PAIRS). */
private var proxy_pairs:Array;
+ public var debug:Boolean;
private var fac_addr:Object;
-
private var local_addr:Object;
- public var debug:Boolean;
-
public var rate_limit:RateLimit;
public function puts(s:String):void
1
0
commit a239d995242dd9205f7826a29ccf793bfc7eb803
Author: David Fifield <david(a)bamsoftware.com>
Date: Sun Oct 30 03:29:47 2011 -0700
Add throughput measurement script.
---
experiments/README | 45 ++++++++++++-
experiments/common.sh | 34 ++++++++++
experiments/throughput/throughput-all.sh | 5 ++
experiments/throughput/throughput.sh | 105 ++++++++++++++++++++++++++++++
4 files changed, 185 insertions(+), 4 deletions(-)
diff --git a/experiments/README b/experiments/README
index 5d65b28..ff8b416 100644
--- a/experiments/README
+++ b/experiments/README
@@ -1,6 +1,43 @@
-Scripts for running experiments.
+This directory contains scripts for testing and benchmarking the flash
+proxy.
-This README contains descriptions of each numbered experiment.
+== Preparation
-Experiment
-00 - Includes socat, crossdomaind, swfcat, httpd, and facilitator
+You need to have installed certain software before running the tests.
+ Firefox
+ socat
+ Wget
+ Python
+ thttpd
+ Flash Player
+Firefox, socat, Wget, and Python are easily installed on most GNU/Linux
+distributions. thttpd can be compiled from the packages at
+http://acme.com/software/thttpd/. A debug version of Flash Player can be
+downloaded from http://www.adobe.com/support/flashplayer/downloads.html.
+
+You need to create some dedicated Firefox profiles. Create profiles
+named flashexp1 and flashexp2 by running
+ firefox -ProfileManager -no-remote
+Start the browsers with
+ firefox -P flashexp1 -no-remote &
+ firefox -P flashexp2 -no-remote &
+and in each one, set this about:config variable:
+ browser.link.open_newwindow=1 (default is 3)
+This allows the scripts to clear the contents of a tab and replace them
+with another page.
+
+Before running any tests, start the crossdomain server as root:
+ sudo crossdomaind.py --daemon
+The tests themselves shouldn't be run as root.
+
+I personally run these tests in an Arch Linux VM.
+ useradd -m user
+ passwd user
+ pacman -Sy
+ pacman -Su
+ pacman -S firefox socat python2 xorg xorg-xinit xterm flashplugin gcc make
+Download thttpd, compile it (you have to rename the getline function to
+avoid a naming conflict), and install it in /usr/local/bin. Symlink
+/usr/bin/python to /usr/bin/python2. Also you have to install the
+ttf-ms-fonts package from the AUR for text to show up in Flash Player.
+Add a window manager, run "startx", and you should be set.
diff --git a/experiments/common.sh b/experiments/common.sh
new file mode 100644
index 0000000..485e908
--- /dev/null
+++ b/experiments/common.sh
@@ -0,0 +1,34 @@
+# This file contains common variables and subroutines used by the experiment
+# scripts.
+
+FIREFOX=firefox
+SOCAT=socat
+THTTPD=thttpd
+
+visible_sleep() {
+ N="$1"
+ echo -n "sleep $N"
+ while [ "$N" -gt 0 ]; do
+ sleep 1
+ N=$((N-1))
+ echo -ne "\rsleep $N"
+ done
+ echo -ne "\n"
+}
+
+ensure_browser_started() {
+ local PROFILE="$1"
+ ("$FIREFOX" -P "$PROFILE" -remote "ping()" || "$FIREFOX" -P "$PROFILE" -no-remote & visible_sleep 5) 2>/dev/null
+}
+
+browser_clear() {
+ local PROFILE="$1"
+ ("$FIREFOX" -P "$PROFILE" -remote "ping()" && "$FIREFOX" -P "$PROFILE" -remote "openurl(about:blank)" &) 2>/dev/null
+}
+
+browser_goto() {
+ local PROFILE="$1"
+ local URL="$2"
+ ensure_browser_started "$PROFILE"
+ "$FIREFOX" -P "$PROFILE" -remote "openurl($URL)" 2>/dev/null
+}
diff --git a/experiments/throughput/throughput-all.sh b/experiments/throughput/throughput-all.sh
new file mode 100755
index 0000000..015e3e1
--- /dev/null
+++ b/experiments/throughput/throughput-all.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+for n in $(seq 1 50); do
+ ./throughput.sh -n $n
+done
diff --git a/experiments/throughput/throughput.sh b/experiments/throughput/throughput.sh
new file mode 100755
index 0000000..5e2fd4b
--- /dev/null
+++ b/experiments/throughput/throughput.sh
@@ -0,0 +1,105 @@
+#!/bin/sh
+
+# Usage: ./throughput.sh [-n NUM_CLIENTS]
+#
+# Tests the raw throughput of a single proxy. This script starts a web server
+# serving swfcat.swf and a large data file, starts a facilitator, connector, and
+# socat shim, and then starts multiple downloads through the proxy at once.
+# Results are saved in a file called results-NUM_CLIENTS-DATE, where DATE is the
+# current date.
+
+. ../common.sh
+
+FLASHPROXY_DIR=../../../flashproxy
+
+NUM_CLIENTS=1
+
+while getopts "n:" OPTNAME; do
+ if [ "$OPTNAME" == n ]; then
+ NUM_CLIENTS="$OPTARG"
+ fi
+done
+
+PROFILE=flashexp1
+PROXY_URL="http://localhost:8000/swfcat.swf?facilitator=127.0.0.1:9002&max_clients=$NU…"
+DATA_FILE_NAME="$FLASHPROXY_DIR/dump"
+RESULTS_FILE_NAME="results-$NUM_CLIENTS-$(date --iso)"
+
+# Declare an array.
+declare -a PIDS_TO_KILL
+stop() {
+ browser_clear "$PROFILE"
+ if [ -n "${PIDS_TO_KILL[*]}" ]; then
+ echo "Kill pids ${PIDS_TO_KILL[@]}."
+ kill "${PIDS_TO_KILL[@]}"
+ fi
+ echo "Delete data file."
+ rm -f "$DATA_FILE_NAME"
+ exit
+}
+trap stop EXIT
+
+echo "Create data file."
+dd if=/dev/null of="$DATA_FILE_NAME" bs=1M seek=100 2>/dev/null || exit
+
+echo "Start web server."
+"$THTTPD" -D -d "$FLASHPROXY_DIR" -p 8000 &
+PIDS_TO_KILL+=($!)
+
+echo "Start facilitator."
+"$FLASHPROXY_DIR"/facilitator.py -d --relay 127.0.0.1:8000 >/dev/null &
+PIDS_TO_KILL+=($!)
+visible_sleep 1
+
+echo "Start connector."
+"$FLASHPROXY_DIR"/connector.py >/dev/null &
+PIDS_TO_KILL+=($!)
+visible_sleep 1
+
+echo "Start browser."
+browser_goto "$PROFILE" "$PROXY_URL"
+visible_sleep 2
+
+# Create sufficiently many client registrations.
+i=0
+while [ $i -lt $NUM_CLIENTS ]; do
+ echo -ne "\rRegister client $((i + 1))."
+ echo $'POST / HTTP/1.0\r\n\r\nclient=127.0.0.1:9000' | socat STDIN TCP-CONNECT:localhost:9002
+ sleep 1
+ i=$((i + 1))
+done
+echo
+visible_sleep 2
+
+echo "Start socat."
+"$SOCAT" TCP-LISTEN:2000,fork,reuseaddr SOCKS4A:localhost:dummy:0,socksport=9001 &
+PIDS_TO_KILL+=($!)
+visible_sleep 1
+
+
+# Extract and parse the "real" part of "time" output and return as a count of
+# seconds.
+extract_real_time() {
+ perl -n -e '($m, $s) = ($_ =~ m/real\s+(\d+)m([\d\.]+)s/); if (defined($m)) { print $m*60 + $s . "\n"; }'
+}
+
+> "$RESULTS_FILE_NAME"
+
+declare -a WAIT_PIDS
+i=0
+while [ $i -lt $NUM_CLIENTS ]; do
+ echo "Start Wget $((i + 1))."
+ (
+ times=$((time wget http://localhost:2000/dump -q --timeout 30 -t 1 -O /dev/null) 2>&1)
+ if [ $? -eq 0 ]; then
+ echo "$times" | extract_real_time >> "$RESULTS_FILE_NAME"
+ else
+ echo "error" >> "$RESULTS_FILE_NAME"
+ fi
+ ) &
+ WAIT_PIDS+=($!)
+ i=$((i + 1))
+done
+for pid in "${WAIT_PIDS[@]}"; do
+ wait "$pid"
+done
1
0
[gettor/master] See http://www.copyright.gov/circs/circ03.pdf. Thanks, Andrew
by kaner@torproject.org 30 Oct '11
by kaner@torproject.org 30 Oct '11
30 Oct '11
commit 57c846f88ce01f0e0da1d2294fc3cd6088f21cd9
Author: Christian Fromme <kaner(a)strace.org>
Date: Sun Oct 30 13:53:06 2011 +0100
See http://www.copyright.gov/circs/circ03.pdf. Thanks, Andrew
---
lib/GetTor.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/lib/GetTor.py b/lib/GetTor.py
index 8a25e6f..d9c378d 100644
--- a/lib/GetTor.py
+++ b/lib/GetTor.py
@@ -4,7 +4,7 @@
__program__ = 'GetTor.py'
__url__ = 'https://gitweb.torproject.org/gettor.git'
__author__ = 'Jacob Appelbaum <jacob(a)appelbaum.net>, Christian Fromme <kaner(a)strace.org>'
-__copyright__ = 'Copyright (c) 2008-2011, The Tor Project'
+__copyright__ = 'Copyright (c) 2008, The Tor Project'
__license__ = 'See LICENSE for licensing information'
try:
1
0
[metrics-tasks/master] Add bridge stability report draft (#4255).
by karsten@torproject.org 30 Oct '11
by karsten@torproject.org 30 Oct '11
30 Oct '11
commit 85a545e66a8ba85d512e30b955c28c1b90e915ea
Author: Karsten Loesing <karsten.loesing(a)gmx.net>
Date: Sun Oct 30 07:11:56 2011 +0100
Add bridge stability report draft (#4255).
---
task-4255/.gitignore | 11 +
task-4255/README | 29 ++
task-4255/SimulateBridgeStability.java | 770 ++++++++++++++++++++++++++++++++
task-4255/report.bib | 26 ++
task-4255/report.tex | 414 +++++++++++++++++
task-4255/stability.R | 220 +++++++++
task-4255/stale-bridge-tarballs.csv | 760 +++++++++++++++++++++++++++++++
7 files changed, 2230 insertions(+), 0 deletions(-)
diff --git a/task-4255/.gitignore b/task-4255/.gitignore
new file mode 100644
index 0000000..4251d3d
--- /dev/null
+++ b/task-4255/.gitignore
@@ -0,0 +1,11 @@
+*.pdf
+*.log
+stability.csv
+stable-fingerprints-and-addresses
+*.class
+*.aux
+*.bbl
+*.blg
+bridge-statuses/
+future-stability/
+
diff --git a/task-4255/README b/task-4255/README
new file mode 100644
index 0000000..7ff10c9
--- /dev/null
+++ b/task-4255/README
@@ -0,0 +1,29 @@
+Extract sanitized bridge network statuses (not server descriptors or
+extra-info descriptors) to bridge-statuses/, e.g.
+bridge-statuses/bridge-descriptors-2010-01/statuses/. Leaving server or
+extra-info descriptors in the directory may lead to errors or at least
+delay the evaluation significantly.
+
+Compile the Java class:
+
+ $ javac SimulateBridgeStability.java
+
+Run the Java class:
+
+ $ java SimulateBridgeStability
+
+Before re-running, delete the following files/directories to re-generate
+them:
+
+ - stable-fingerprints-and-addresses
+ - future-stability/
+ - stability.csv
+
+Plot the results:
+
+ $ R --slave -f stability.R
+
+Compile the report:
+
+ $ pdflatex report.tex
+
diff --git a/task-4255/SimulateBridgeStability.java b/task-4255/SimulateBridgeStability.java
new file mode 100644
index 0000000..6deb318
--- /dev/null
+++ b/task-4255/SimulateBridgeStability.java
@@ -0,0 +1,770 @@
+import java.io.*;
+import java.text.*;
+import java.util.*;
+
+/**
+ * Simulate different requirements for stable bridges that BridgeDB can
+ * use to include at least one such stable bridge in its responses to
+ * bridge users.
+ *
+ * The two bridge stability metrics used here are weighted mean time
+ * between address changes (WMTBAC) and weighted fractional uptime (WFU).
+ * Different requirements based on these two metrics are simulated by
+ * comparing the time on same address (TOSA) and weighted fractional
+ * uptime in the future (WFU).
+ */
+public class SimulateBridgeStability {
+
+ /* Bridge history entry for the third step. Once we teach BridgeDB
+ * how to keep track of bridge stability, it's going to keep records
+ * similar to this one. */
+ private static class BridgeHistoryElement {
+ /* Weighted uptime in seconds that is used for WFU calculation. */
+ public long weightedUptime;
+ /* Weighted time in seconds that is used for WFU calculation. */
+ public long weightedTime;
+ /* Weighted run length of previously used addresses or ports in
+ * seconds. */
+ public double weightedRunLength;
+ /* Total run weights of previously used addresses or ports. */
+ public double totalRunWeights;
+ /* Currently known address. */
+ public String address;
+ /* Month string (YYYY-MM) that was used as input to the bridge
+ * descriptor sanitizer. */
+ public String month;
+ /* Currently known port. */
+ public int port;
+ /* Timestamp in milliseconds when this bridge was last seen with a
+ * different address or port. When adding a history entry, this
+ * timestamp is initialized with the publication time of the previous
+ * status. */
+ public long lastSeenWithDifferentAddressAndPort;
+ /* Timestamp in milliseconds when this bridge was last seen with this
+ * address and port. */
+ public long lastSeenWithThisAddressAndPort;
+ }
+
+ /* Run the analysis in three steps:
+ *
+ * In the first step, we parse sanitized bridge network statuses from
+ * first to last to determine stable addresses that have changed by the
+ * sanitizing process only. In the second step, we parse statuses from
+ * last to first to calculate TOSA and future WFU, and write future
+ * stability metrics to disk as one file per network status in
+ * future-stability/$filename. In the third step, we parse the statuses
+ * again from first to last, calculate past stability metrics for all
+ * bridges, select stable bridges, look up future stability of these
+ * bridges, and write results to stability.csv.
+ */
+ public static void main(String[] args) throws Exception {
+
+ /* Measure how long this execution takes. */
+ long started = System.currentTimeMillis();
+
+ /* Prepare timestamp parsing. */
+ SimpleDateFormat isoFormat = new SimpleDateFormat(
+ "yyyy-MM-dd HH:mm:ss");
+ isoFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ SimpleDateFormat statusFileNameFormat = new SimpleDateFormat(
+ "yyyyMMdd-HHmmss");
+ statusFileNameFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ SimpleDateFormat futureStabilityFileNameFormat = new SimpleDateFormat(
+ "yyyy-MM-dd-HH-mm-ss");
+ futureStabilityFileNameFormat.setTimeZone(TimeZone.getTimeZone(
+ "UTC"));
+
+ /* Define analysis interval. */
+ String analyzeFrom = "2010-07-01 00:00:00",
+ analyzeTo = "2011-06-30 23:00:00";
+ long analyzeFromMillis = isoFormat.parse(analyzeFrom).getTime(),
+ analyzeToMillis = isoFormat.parse(analyzeTo).getTime();
+
+ /* Scan existing status files. */
+ SortedSet<File> allStatuses = new TreeSet<File>();
+ Stack<File> files = new Stack<File>();
+ files.add(new File("bridge-statuses"));
+ while (!files.isEmpty()) {
+ File file = files.pop();
+ if (file.isDirectory()) {
+ files.addAll(Arrays.asList(file.listFiles()));
+ } else {
+ if (file.getName().endsWith(
+ "-4A0CCD2DDC7995083D73F5D667100C8A5831F16D")) {
+ allStatuses.add(file);
+ }
+ }
+ }
+ System.out.println("Scanning " + allStatuses.size() + " bridge "
+ + "network statuses.");
+
+ /* Parse statuses in forward order to detect stable fingerprint/
+ * address combinations to correct some of the IP address changes from
+ * one month to the next. */
+ SortedSet<String> stableFingerprintsAndAddresses =
+ new TreeSet<String>();
+ File stableFingerprintsAndAddressesFile =
+ new File("stable-fingerprints-and-addresses");
+ if (stableFingerprintsAndAddressesFile.exists()) {
+ System.out.println("Reading stable fingerprints and addresses from "
+ + "disk...");
+ BufferedReader br = new BufferedReader(new FileReader(
+ stableFingerprintsAndAddressesFile));
+ String line;
+ while ((line = br.readLine()) != null) {
+ stableFingerprintsAndAddresses.add(line);
+ }
+ br.close();
+ } else {
+ System.out.println("Parsing bridge network statuses and writing "
+ + "stable fingerprints and addresses to disk...");
+ Map<String, Long> firstSeenFingerprintAndAddress =
+ new HashMap<String, Long>();
+ for (File status : allStatuses) {
+ Set<String> fingerprints = new HashSet<String>();
+ BufferedReader br = new BufferedReader(new FileReader(status));
+ String line, rLine = null;
+ while ((line = br.readLine()) != null) {
+ if (line.startsWith("r ")) {
+ String[] parts = line.split(" ");
+ if (parts.length < 8) {
+ System.out.println("Illegal line '" + rLine + "' in "
+ + status + ". Skipping status.");
+ break;
+ }
+ String fingerprint = parts[2];
+ String address = parts[6];
+ String fingerprintAndAddress = fingerprint + " " + address;
+ if (stableFingerprintsAndAddresses.contains(
+ fingerprintAndAddress)) {
+ continue;
+ } else {
+ String date = parts[4];
+ String time = parts[5];
+ long published = isoFormat.parse(date + " " + time).
+ getTime();
+ if (!firstSeenFingerprintAndAddress.containsKey(
+ fingerprintAndAddress)) {
+ firstSeenFingerprintAndAddress.put(fingerprintAndAddress,
+ published);
+ } else if (published - firstSeenFingerprintAndAddress.get(
+ fingerprintAndAddress) > 36L * 60L * 60L * 1000L) {
+ stableFingerprintsAndAddresses.add(
+ fingerprintAndAddress);
+ }
+ }
+ }
+ }
+ br.close();
+ }
+ BufferedWriter bw = new BufferedWriter(new FileWriter(
+ stableFingerprintsAndAddressesFile));
+ for (String stableFingerprintAndAddress :
+ stableFingerprintsAndAddresses) {
+ bw.write(stableFingerprintAndAddress + "\n");
+ }
+ bw.close();
+ }
+ System.out.println("We know about "
+ + stableFingerprintsAndAddresses.size() + " stable fingerprints "
+ + "and addresses.");
+
+ /* Now parse statuses in reverse direction to calculate time until
+ * next address change and weighted fractional uptime for all bridges.
+ * Whenever we find a bridge published in a month for the first time,
+ * we look if we identified the bridge fingerprint and address (either
+ * new or old) as stable before. If we did, ignore this address
+ * change. */
+ File futureStabilityDirectory = new File("future-stability");
+ if (futureStabilityDirectory.exists()) {
+ System.out.println("Not overwriting files in "
+ + futureStabilityDirectory.getAbsolutePath());
+ } else {
+
+ /* Track weighted uptime and total weighted time in a long[2]. */
+ SortedMap<String, long[]> wfuHistory =
+ new TreeMap<String, long[]>();
+
+ /* Track timestamps of next address changes in a long. */
+ SortedMap<String, Long> nacHistory = new TreeMap<String, Long>();
+
+ /* Store the last known r lines by fingerprint to be able to decide
+ * whether a bridge has changed its IP address. */
+ Map<String, String> lastKnownRLines = new HashMap<String, String>();
+
+ /* Parse bridge network statuses in reverse order. */
+ SortedSet<File> allStatusesReverseOrder =
+ new TreeSet<File>(Collections.reverseOrder());
+ allStatusesReverseOrder.addAll(allStatuses);
+ long nextWeightingInterval = -1L, lastStatusPublicationMillis = -1L;
+ for (File status : allStatusesReverseOrder) {
+
+ /* Parse status publication time from file name. */
+ long statusPublicationMillis = statusFileNameFormat.parse(
+ status.getName().substring(0, "yyyyMMdd-HHmmss".length())).
+ getTime();
+
+ /* We're just looking at the first status outside the analysis
+ * interval. Stop parsing here. */
+ if (statusPublicationMillis < analyzeFromMillis) {
+ break;
+ }
+
+ /* Calculate the seconds since the last parsed status. If this is
+ * the first status or we haven't seen a status for more than 60
+ * minutes, assume 60 minutes. */
+ long secondsSinceLastStatusPublication =
+ lastStatusPublicationMillis < 0L ||
+ lastStatusPublicationMillis - statusPublicationMillis
+ > 60L * 60L * 1000L ? 60L * 60L
+ : (lastStatusPublicationMillis - statusPublicationMillis)
+ / 1000L;
+ lastStatusPublicationMillis = statusPublicationMillis;
+
+ /* Before parsing the next bridge network status, see if 12 hours
+ * have passed since we last discounted wfu history values. If
+ * so, discount variables for all known bridges by factor 0.95 (or
+ * 19/20 since these are long integers) and remove those bridges
+ * with a weighted fractional uptime below 1/10000 from the
+ * history. */
+ long weightingInterval = statusPublicationMillis
+ / (12L * 60L * 60L * 1000L);
+ if (nextWeightingInterval < 0L) {
+ nextWeightingInterval = weightingInterval;
+ }
+ while (weightingInterval < nextWeightingInterval) {
+ Set<String> bridgesToRemove = new HashSet<String>();
+ for (Map.Entry<String, long[]> e : wfuHistory.entrySet()) {
+ long[] w = e.getValue();
+ w[0] *= 19L;
+ w[0] /= 20L;
+ w[1] *= 19L;
+ w[1] /= 20L;
+ if (((10000L * w[0]) / w[1]) < 1L) {
+ bridgesToRemove.add(e.getKey());
+ }
+ }
+ for (String fingerprint : bridgesToRemove) {
+ wfuHistory.remove(fingerprint);
+ }
+ nextWeightingInterval -= 1L;
+ }
+
+ /* Increment total weighted time for all bridges by seconds
+ * since the last status was published. */
+ for (long[] w : wfuHistory.values()) {
+ w[1] += secondsSinceLastStatusPublication;
+ }
+
+ /* If the status falls within our analysis interval, write future
+ * WFUs and TOSAs for all running bridges to disk. */
+ BufferedWriter bw = null;
+ if (statusPublicationMillis <= analyzeToMillis) {
+ File futureStabilityFile = new File("future-stability",
+ futureStabilityFileNameFormat.format(
+ statusPublicationMillis));
+ futureStabilityFile.getParentFile().mkdirs();
+ bw = new BufferedWriter(new FileWriter(futureStabilityFile));
+ }
+
+
+ /* Parse r lines of all bridges with the Running flag from the
+ * current bridge network status. */
+ BufferedReader br = new BufferedReader(new FileReader(status));
+ String line, rLine = null;
+ SortedMap<String, String> runningBridges =
+ new TreeMap<String, String>();
+ while ((line = br.readLine()) != null) {
+ if (line.startsWith("r ")) {
+ rLine = line;
+ } else if (line.startsWith("s ") && line.contains(" Running")) {
+ String[] parts = rLine.split(" ");
+ if (parts.length < 8) {
+ System.out.println("Illegal line '" + rLine + "' in "
+ + status + ". Skipping line.");
+ continue;
+ }
+ String fingerprint = parts[2];
+ runningBridges.put(fingerprint, rLine);
+ }
+ }
+ br.close();
+
+ /* If this status doesn't contain a single bridge with the Running
+ * flag, ignore it. This is a problem with the bridge authority
+ * and doesn't mean we should consider all bridges as down. */
+ if (runningBridges.isEmpty()) {
+ continue;
+ }
+
+ /* Find out if a bridge changed its IP address or port. */
+ for (Map.Entry<String, String> e : runningBridges.entrySet()) {
+ String fingerprint = e.getKey();
+ String brLine = e.getValue();
+ String[] brParts = brLine.split(" ");
+
+ /* Increment weighted uptime by seconds since last status
+ * publication time. */
+ if (!wfuHistory.containsKey(fingerprint)) {
+ wfuHistory.put(fingerprint, new long[] {
+ secondsSinceLastStatusPublication,
+ secondsSinceLastStatusPublication });
+ } else {
+ wfuHistory.get(fingerprint)[0] +=
+ secondsSinceLastStatusPublication;
+ }
+
+ /* Check for address or port change. */
+ String address = brParts[6];
+ String port = brParts[7];
+ boolean sameAddressAndPort = false;
+ if (lastKnownRLines.containsKey(fingerprint)) {
+ String[] lastKnownRLineParts =
+ lastKnownRLines.get(fingerprint).split(" ");
+ String lastAddress = lastKnownRLineParts[6];
+ String lastPort = lastKnownRLineParts[7];
+ if (!port.equals(lastPort)) {
+ /* The port changed. It doesn't matter whether the
+ * address changed or not. */
+ } else if (address.equals(lastAddress)) {
+ /* The bridge's address and port are still the same. */
+ sameAddressAndPort = true;
+ } else {
+ String month = brParts[4].substring(0, "yyyy-MM".length());
+ String lastMonth = lastKnownRLineParts[4].substring(0,
+ "yyyy-MM".length());
+ if (!lastMonth.equals(month) &&
+ stableFingerprintsAndAddresses.contains(
+ fingerprint + " " + address) &&
+ stableFingerprintsAndAddresses.contains(
+ fingerprint + " " + lastAddress)) {
+ /* The last time we saw this bridge was in a different
+ * month. This bridge was seen with both addresses in
+ * intervals of 36 hours or more. Consider this
+ * address change an artifact from the sanitizing
+ * process. */
+ sameAddressAndPort = true;
+ } else {
+ /* Either the month did not change or the address or
+ * port did change. */
+ }
+ }
+ } else {
+ /* We're seeing this bridge for the first time. */
+ }
+ if (!sameAddressAndPort) {
+ nacHistory.put(fingerprint, statusPublicationMillis);
+ }
+ lastKnownRLines.put(fingerprint, brLine);
+
+ /* Write WFU and TOSA to disk. */
+ if (bw != null) {
+ long[] wfu = wfuHistory.get(fingerprint);
+ long tosa = (nacHistory.get(fingerprint)
+ - statusPublicationMillis) / 1000L;
+ bw.write(fingerprint + " " + tosa + " "
+ + ((10000L * wfu[0]) / wfu[1]) + " " + "\n");
+ }
+ }
+ br.close();
+ if (bw != null) {
+ bw.close();
+ }
+ }
+ }
+
+ /* Finally, parse statuses in forward order to calculate weighted mean
+ * time between address change (WMTBAC) and weighted fractional uptime
+ * (WFU) and simulate how stable bridges meeting given requirements
+ * are. */
+ File stabilityFile = new File("stability.csv");
+ if (stabilityFile.exists()) {
+ System.out.println("Not overwriting output file "
+ + stabilityFile.getAbsolutePath());
+ } else {
+
+ /* Run the simulation for the following WFU and WMTBAC
+ * percentiles: */
+ List<Integer> wfuPercentiles = new ArrayList<Integer>();
+ for (int l : new int[] { 0, 30, 40, 50, 60, 70 }) {
+ wfuPercentiles.add(l);
+ }
+ List<Integer> wmtbacPercentiles = new ArrayList<Integer>();
+ for (int l : new int[] { 0, 30, 40, 50, 60, 70 }) {
+ wmtbacPercentiles.add(l);
+ }
+
+ /* Add column headers to output file. */
+ SortedSet<String> columns = new TreeSet<String>();
+ columns.add("running");
+ columns.add("minwta");
+ columns.add("minwtb");
+ for (int wfuPercentile : wfuPercentiles) {
+ columns.add("minwfua" + wfuPercentile + "wfu");
+ columns.add("minwfub" + wfuPercentile + "wfu");
+ for (int wmtbacPercentile : wmtbacPercentiles) {
+ String simulation = wfuPercentile + "wfu" + wmtbacPercentile
+ + "wmtbac";
+ columns.add("stablebridge" + simulation);
+ columns.add("perc15wfu" + simulation);
+ columns.add("perc10wfu" + simulation);
+ columns.add("perc5wfu" + simulation);
+ columns.add("perc15tosa" + simulation);
+ columns.add("perc10tosa" + simulation);
+ columns.add("perc5tosa" + simulation);
+ }
+ }
+ for (int wmtbacPercentile : wmtbacPercentiles) {
+ columns.add("minwmtbaca" + wmtbacPercentile + "wmtbac");
+ columns.add("minwmtbacb" + wmtbacPercentile + "wmtbac");
+ }
+ BufferedWriter bw = new BufferedWriter(new FileWriter(
+ stabilityFile));
+ bw.write("time");
+ for (String column : columns) {
+ bw.write("," + column);
+ }
+ bw.write("\n");
+
+ SortedMap<String, BridgeHistoryElement> history =
+ new TreeMap<String, BridgeHistoryElement>();
+
+ /* Parse previously exported network status entries again, but this
+ * time in forward order. */
+ long nextWeightingInterval = -1L, lastStatusPublicationMillis = -1L;
+ for (File status : allStatuses) {
+
+ /* Parse status publication time from file name. */
+ long statusPublicationMillis = statusFileNameFormat.parse(
+ status.getName().substring(0, "yyyyMMdd-HHmmss".length())).
+ getTime();
+
+ /* Calculate the seconds since the last parsed status. If this is
+ * the first status or we haven't seen a status for more than 60
+ * minutes, assume 60 minutes. */
+ long secondsSinceLastStatusPublication =
+ lastStatusPublicationMillis < 0L ||
+ statusPublicationMillis - lastStatusPublicationMillis
+ > 60L * 60L * 1000L ? 60L * 60L
+ : (statusPublicationMillis - lastStatusPublicationMillis)
+ / 1000L;
+
+ /* Before parsing the next bridge network status, see if 12 hours
+ * have passed since we last discounted wfu and wmtbac history
+ * values. If so, discount variables for all known bridges by
+ * factor 0.95 (or 19/20 since these are long integers) and remove
+ * those bridges with a weighted fractional uptime below 1/10000
+ * from the history. Also, discount weighted run length and total
+ * run weights for all known relays by factor 0.95. */
+ long weightingInterval = statusPublicationMillis
+ / (12L * 60L * 60L * 1000L);
+ if (nextWeightingInterval < 0L) {
+ nextWeightingInterval = weightingInterval;
+ }
+ while (weightingInterval > nextWeightingInterval) {
+ Set<String> bridgesToRemove = new HashSet<String>();
+ for (Map.Entry<String, BridgeHistoryElement> e :
+ history.entrySet()) {
+ BridgeHistoryElement historyElement = e.getValue();
+ historyElement.weightedUptime =
+ (historyElement.weightedUptime * 19L) / 20L;
+ historyElement.weightedTime =
+ (historyElement.weightedTime * 19L) / 20L;
+ if (((10000L * historyElement.weightedUptime)
+ / historyElement.weightedTime) < 1L) {
+ String fingerprint = e.getKey();
+ bridgesToRemove.add(fingerprint);
+ }
+ historyElement.weightedRunLength *= 0.95;
+ historyElement.totalRunWeights *= 0.95;
+ }
+ for (String fingerprint : bridgesToRemove) {
+ history.remove(fingerprint);
+ }
+ nextWeightingInterval += 1L;
+ }
+
+ /* Parse r lines of all bridges with the Running flag from the
+ * current bridge network status. */
+ BufferedReader br = new BufferedReader(new FileReader(status));
+ String line, rLine = null;
+ SortedMap<String, String> runningBridges =
+ new TreeMap<String, String>();
+ while ((line = br.readLine()) != null) {
+ if (line.startsWith("r ")) {
+ rLine = line;
+ } else if (line.startsWith("s ") && line.contains(" Running")) {
+ String[] parts = rLine.split(" ");
+ if (parts.length < 8) {
+ System.out.println("Illegal line '" + rLine + "' in "
+ + status + ". Skipping line.");
+ continue;
+ }
+ String fingerprint = parts[2];
+ runningBridges.put(fingerprint, rLine);
+ }
+ }
+ br.close();
+
+ /* If this status doesn't contain a single bridge with the Running
+ * flag, ignore it. This is a problem with the bridge authority
+ * and doesn't mean we should consider all bridges as down. */
+ if (runningBridges.isEmpty()) {
+ continue;
+ }
+
+ /* Add new bridges to history or update history if it already
+ * contains a bridge. */
+ for (Map.Entry<String, String> e : runningBridges.entrySet()) {
+ String fingerprint = e.getKey();
+ String brLine = e.getValue();
+ String[] brParts = brLine.split(" ");
+ String address = brParts[6];
+ int port = Integer.parseInt(brParts[7]);
+ String month = brParts[4].substring(0, "yyyy-MM".length());
+ BridgeHistoryElement bhe = null;
+ if (!history.containsKey(fingerprint)) {
+
+ /* Add new bridge to history. */
+ bhe = new BridgeHistoryElement();
+ bhe.lastSeenWithDifferentAddressAndPort =
+ lastStatusPublicationMillis;
+ history.put(fingerprint, bhe);
+ } else {
+
+ /* Update bridge in history. */
+ bhe = history.get(fingerprint);
+
+ /* If the port changed, ... */
+ if (port != bhe.port ||
+
+ /* ... or the address changed and ... */
+ (!address.equals(bhe.address) &&
+
+ /* ... either the month is the same ... */
+ (month.equals(bhe.month) ||
+
+ /* ... or this address is not stable ... */
+ !stableFingerprintsAndAddresses.contains(
+ fingerprint + " " + address) ||
+
+ /* ... or the last address is not stable, ... */
+ !stableFingerprintsAndAddresses.contains(
+ fingerprint + " " + bhe.address)))) {
+
+ /* ... assume that the bridge changed its address or
+ * port. */
+ bhe.weightedRunLength += (double)
+ ((bhe.lastSeenWithThisAddressAndPort -
+ bhe.lastSeenWithDifferentAddressAndPort) / 1000L);
+ bhe.totalRunWeights += 1.0;
+ bhe.lastSeenWithDifferentAddressAndPort =
+ bhe.lastSeenWithThisAddressAndPort;
+ }
+ }
+
+ /* Regardless of whether the bridge is new, kept or changed
+ * its address and port, raise its WFU times and note its
+ * current address, month, and port, and that we saw it using
+ * them. */
+ bhe.weightedUptime += secondsSinceLastStatusPublication;
+ bhe.weightedTime += secondsSinceLastStatusPublication;
+ bhe.lastSeenWithThisAddressAndPort = statusPublicationMillis;
+ bhe.address = address;
+ bhe.month = month;
+ bhe.port = port;
+ }
+
+ /* Update weighted times (not uptimes) of non-running bridges. */
+ for (Map.Entry<String, BridgeHistoryElement> e :
+ history.entrySet()) {
+ String fingerprint = e.getKey();
+ if (!runningBridges.containsKey(fingerprint)) {
+ BridgeHistoryElement bhe = e.getValue();
+ bhe.weightedTime += secondsSinceLastStatusPublication;
+ }
+ }
+
+ /* Prepare writing results. */
+ Map<String, String> results = new HashMap<String, String>();
+ results.put("running", "" + runningBridges.size());
+
+ /* If we reached the analysis interval, read previously calculated
+ * future WFUs and TOSAs from disk and run the simulations. */
+ Map<String, Long> fwfus = new HashMap<String, Long>(),
+ tosas = new HashMap<String, Long>();
+ File futureStabilityFile = new File("future-stability",
+ futureStabilityFileNameFormat.format(
+ statusPublicationMillis));
+ if (statusPublicationMillis < analyzeFromMillis ||
+ statusPublicationMillis > analyzeToMillis) {
+ /* Outside of our analysis interval. Skip simulation. */
+ } else if (!futureStabilityFile.exists()) {
+ System.out.println("Could not find file "
+ + futureStabilityFile + ". Skipping simulation!");
+ } else {
+ BufferedReader fsBr = new BufferedReader(new FileReader(
+ futureStabilityFile));
+ String fsLine;
+ while ((fsLine = fsBr.readLine()) != null) {
+ String[] fsParts = fsLine.split(" ");
+ tosas.put(fsParts[0], Long.parseLong(fsParts[1]));
+ fwfus.put(fsParts[0], Long.parseLong(fsParts[2]));
+ }
+ fsBr.close();
+
+ /* Prepare calculating thresholds for selecting stable bridges
+ * in simulations. */
+ List<Long> totalWeightedTimes = new ArrayList<Long>();
+ for (String fingerprint : runningBridges.keySet()) {
+ totalWeightedTimes.add(history.get(fingerprint).weightedTime);
+ }
+ Collections.sort(totalWeightedTimes);
+ long minimumTotalWeightedTime = totalWeightedTimes.get(
+ totalWeightedTimes.size() / 8);
+ results.put("minwta", String.valueOf(minimumTotalWeightedTime));
+ if (minimumTotalWeightedTime > 8L * 24L * 60L * 60L) {
+ minimumTotalWeightedTime = 8L * 24L * 60L * 60L;
+ }
+ results.put("minwtb", String.valueOf(minimumTotalWeightedTime));
+ List<Long> weightedFractionalUptimesFamiliar =
+ new ArrayList<Long>();
+ for (String fingerprint : runningBridges.keySet()) {
+ BridgeHistoryElement bhe = history.get(fingerprint);
+ if (bhe.weightedTime >= minimumTotalWeightedTime) {
+ long weightedFractionalUptime =
+ (10000L * bhe.weightedUptime) / bhe.weightedTime;
+ weightedFractionalUptimesFamiliar.add(
+ weightedFractionalUptime);
+ }
+ }
+ Collections.sort(weightedFractionalUptimesFamiliar);
+ List<Long> wmtbacs = new ArrayList<Long>();
+ for (String fingerprint : runningBridges.keySet()) {
+ BridgeHistoryElement bhe = history.get(fingerprint);
+ double totalRunLength = bhe.weightedRunLength + (double)
+ ((bhe.lastSeenWithThisAddressAndPort -
+ bhe.lastSeenWithDifferentAddressAndPort) / 1000L);
+ double totalWeights = bhe.totalRunWeights + 1.0;
+ long wmtbac = totalWeights < 0.0001 ? 0L
+ : (long) (totalRunLength / totalWeights);
+ wmtbacs.add(wmtbac);
+ }
+ Collections.sort(wmtbacs);
+
+ /* Run simulation for the bridges in the current status for
+ * various WFU and WMTBAC percentiles. */
+ for (int wmtbacPercentile : wmtbacPercentiles) {
+ for (int wfuPercentile : wfuPercentiles) {
+ String simulation = wfuPercentile + "wfu" + wmtbacPercentile
+ + "wmtbac";
+ long minimumWeightedMeanTimeBetweenAddressChange =
+ wmtbacs.get((wmtbacPercentile * wmtbacs.size()) / 100);
+ results.put("minwmtbaca" + wmtbacPercentile + "wmtbac",
+ String.valueOf(
+ minimumWeightedMeanTimeBetweenAddressChange));
+ if (minimumWeightedMeanTimeBetweenAddressChange >
+ 30L * 24L * 60L * 60L) {
+ minimumWeightedMeanTimeBetweenAddressChange =
+ 30L * 24L * 60L * 60L;
+ }
+ results.put("minwmtbacb" + wmtbacPercentile + "wmtbac",
+ String.valueOf(
+ minimumWeightedMeanTimeBetweenAddressChange));
+ long minimumWeightedFractionalUptime =
+ weightedFractionalUptimesFamiliar.get((wfuPercentile
+ * weightedFractionalUptimesFamiliar.size()) / 100);
+ results.put("minwfua" + wfuPercentile + "wfu",
+ String.valueOf(minimumWeightedFractionalUptime));
+ if (minimumWeightedFractionalUptime > 9800) {
+ minimumWeightedFractionalUptime = 9800;
+ }
+ results.put("minwfub" + wfuPercentile + "wfu",
+ String.valueOf(minimumWeightedFractionalUptime));
+ List<Long> fwfuList = new ArrayList<Long>(),
+ tosaList = new ArrayList<Long>();
+ Set<String> selectedBridges = new HashSet<String>();
+ for (String fingerprint : runningBridges.keySet()) {
+ BridgeHistoryElement bhe = history.get(fingerprint);
+ double totalRunLength = bhe.weightedRunLength + (double)
+ ((bhe.lastSeenWithThisAddressAndPort -
+ bhe.lastSeenWithDifferentAddressAndPort) / 1000L);
+ double totalWeights = bhe.totalRunWeights + 1.0;
+ long wmtbac = totalWeights < 0.0001 ? 0L
+ : (long) (totalRunLength / totalWeights);
+ if (wmtbac < minimumWeightedMeanTimeBetweenAddressChange) {
+ continue;
+ }
+ if (wfuPercentile > 0 &&
+ bhe.weightedTime < minimumTotalWeightedTime) {
+ continue;
+ }
+ long weightedFractionalUptime =
+ (10000L * bhe.weightedUptime) / bhe.weightedTime;
+ if (weightedFractionalUptime <
+ minimumWeightedFractionalUptime) {
+ continue;
+ }
+ long fwfu = fwfus.get(fingerprint);
+ fwfuList.add(fwfu);
+ long tosa = tosas.get(fingerprint);
+ tosaList.add(tosa);
+ selectedBridges.add(fingerprint);
+ }
+ /* Calculate percentiles of future WFU and of TOSA as the
+ * simulation results. */
+ results.put("stablebridge" + simulation,
+ String.valueOf(selectedBridges.size()));
+ if (tosaList.size() > 0L) {
+ Collections.sort(tosaList);
+ results.put("perc15tosa" + simulation,
+ "" + tosaList.get((15 * tosaList.size()) / 100));
+ results.put("perc10tosa" + simulation,
+ "" + tosaList.get((10 * tosaList.size()) / 100));
+ results.put("perc5tosa" + simulation,
+ "" + tosaList.get((5 * tosaList.size()) / 100));
+ }
+ if (fwfuList.size() > 0) {
+ Collections.sort(fwfuList);
+ results.put("perc15wfu" + simulation,
+ "" + fwfuList.get((15 * fwfuList.size()) / 100));
+ results.put("perc10wfu" + simulation,
+ "" + fwfuList.get((10 * fwfuList.size()) / 100));
+ results.put("perc5wfu" + simulation,
+ "" + fwfuList.get((5 * fwfuList.size()) / 100));
+ }
+ }
+ }
+ }
+
+ /* Write results, regardless of whether we ran simulations or
+ * not. */
+ SortedSet<String> missingColumns = new TreeSet<String>();
+ for (String resultColumn : results.keySet()) {
+ if (!columns.contains(resultColumn)) {
+ missingColumns.add(resultColumn);
+ }
+ }
+ if (!missingColumns.isEmpty()) {
+ System.out.println("We are missing the following columns:");
+ for (String missingColumn : missingColumns) {
+ System.out.print(" " + missingColumn);
+ }
+ System.out.println(". Ignoring.");
+ }
+ bw.write(isoFormat.format(statusPublicationMillis));
+ for (String column : columns) {
+ if (results.containsKey(column)) {
+ bw.write("," + results.get(column));
+ } else {
+ bw.write(",NA");
+ }
+ }
+ bw.write("\n");
+ lastStatusPublicationMillis = statusPublicationMillis;
+ }
+ bw.close();
+ }
+ }
+}
+
diff --git a/task-4255/report.bib b/task-4255/report.bib
new file mode 100644
index 0000000..427e27c
--- /dev/null
+++ b/task-4255/report.bib
@@ -0,0 +1,26 @@
+@misc{dirspec,
+ author = {Roger Dingledine and Nick Mathewson},
+ title = {Tor directory protocol, version 3},
+ note = {\url{https://gitweb.torproject.org/tor.git/blob_plain/HEAD:/doc/spec/dir-spec.txt}},
+}
+
+@techreport{loesing2011analysis,
+ title = {An Analysis of {Tor} Relay Stability},
+ author = {Karsten Loesing},
+ institution = {The Tor Project},
+ year = {2011},
+ month = {June},
+ type = {Technical Report},
+ note = {\url{https://metrics.torproject.org/papers/relay-stability-2011-06-30.pdf}},
+}
+
+@techreport{loesing2011overview,
+ title = {Overview of Statistical Data in the {Tor} Network},
+ author = {Karsten Loesing},
+ institution = {The Tor Project},
+ year = {2011},
+ month = {March},
+ type = {Technical Report},
+ note = {\url{https://metrics.torproject.org/papers/data-2011-03-14.pdf}},
+}
+
diff --git a/task-4255/report.tex b/task-4255/report.tex
new file mode 100644
index 0000000..0dfd437
--- /dev/null
+++ b/task-4255/report.tex
@@ -0,0 +1,414 @@
+\documentclass{article}
+\usepackage{url}
+\usepackage[pdftex]{graphicx}
+\usepackage{graphics}
+\usepackage{color}
+\begin{document}
+\title{An Analysis of Tor Bridge Stability\\
+{\large --- Making BridgeDB give out at least one stable bridge per
+user ---}}
+\author{Karsten Loesing\\{\tt karsten(a)torproject.org}}
+
+\maketitle
+
+\section{Introducing the instable bridges problem}
+
+As of October 2011, the Tor network consists of a few hundred thousand
+clients, 2\,400 public relays, and about 600 non-public bridge relays.
+Bridge relays (in the following: bridges) are entry points which are not
+publicly listed to prevent censors from blocking access to the Tor
+network.
+Censored users request a small number of typically three bridge addresses
+from the BridgeDB service via email or http and then connect to the Tor
+network only via these bridges.
+If all bridges that a user knows about suddenly stop working, the user
+needs to request a new set of bridge addresses from BridgeDB.
+However, BridgeDB memorizes the user's email or IP address and only gives
+out new bridges every 24 hours to slow down enumeration attempts.
+The result is that a user who is unlucky enough to receive only unreliable
+bridges from BridgeDB won't be able to connect to the Tor network for up
+to 24 hours before requesting a new set of bridges.
+
+In this report we propose that BridgeDB keeps bridge stability records,
+similar to how the directory authorities keep relay stability records, and
+includes at least one stable bridge in its responses to users.
+In fact, BridgeDB currently attempts to do this by including at least one
+bridge with the Stable flag assigned by the bridge authority in its
+results.
+This approach is broken for two reasons:
+The first reason is that the algorithm that the bridge authority uses to
+assign the Stable flag is broken to the extent that almost every bridge
+has the Stable flag assigned.
+The second reason is that the Stable flag was designed for clients to
+select relays for long-running streams, not for BridgeDB to select
+reliable entry points into the Tor network.
+A better metric for stable bridges would be based on bridge uptime and on
+the frequency of IP address changes.
+We propose such a metric and evaluate its effectiveness for selecting
+stable bridges based on archived bridge directories.
+
+\section{Defining a new bridge stability metric}
+\label{sec:defining}
+
+The directory authorities implement a few relay stability metrics to
+decide which of the relays to assign the Guard and Stable
+flag~\cite{dirspec, loesing2011analysis}.
+The requirements for stable bridges that we propose here are similar to
+the entry guard requirements.
+That is, stable bridges should have a higher fractional uptime than
+non-stable ones.
+Further, a stable bridge should be available under the same IP address and
+TCP port.
+Otherwise, bridge users who only know a bridge address won't be able to
+connect to the bridge once it changes its address or port.
+We propose the following requirements for a bridge to be considered
+stable in the style of the Guard and Stable flag definition:
+
+\label{def:bridgestability}
+\begin{quote}
+A bridge is considered stable if its \emph{Weighted Mean Time Between
+Address Change} is at least the median for known active bridges or at
+least 30~days, if it is `familiar', and if its \emph{Weighted Fractional
+Uptime} is at least the median for `familiar' active bridges or at least
+98~\%.
+A bridge is `familiar' if 1/8 of all active bridges have appeared more
+recently than it, or if it has been around for a \emph{Weighted Time} of
+8~days.
+\end{quote}
+
+This bridge stability definition contains three main requirements:
+
+\begin{itemize}
+\item The \emph{Weighted Mean Time Between Address Change (WMTBAC)}
+metric is used to track the time that a bridge typically uses the same IP
+address and TCP port.
+The (unweighted) MTBAC measures the average time between last using
+address and port $a_0$ to last using address and port $a_1$.
+This metric is weighted to put more emphasis on recent events than on past
+events.
+Therefore, past address sessions are discounted by factor $0.95$ every
+12~hours.
+The current session is not discounted, so that a WMTBAC value of 30 days
+can be reached after 30 days at the earliest.
+\item The \emph{Weighted Fractional Uptime (WFU)} metric measures the
+fraction of bridge uptime in the past.
+Similar to WMTBAC, WFU values are discounted by factor $0.95$ every
+12~hours, but in this case including the current uptime session.
+\item The \emph{Weighted Time (WT)} metric is used to calculate a bridge's
+WFU and to decide whether a bridge is around long enough to be considered
+`familiar.'
+WT is discounted similar to WMTBAC and WFU, so that a WT of 8 days can be
+reached after around 16 days at the earliest.
+\end{itemize}
+
+All three requirements consist of a dynamic part that depends on the
+stability of other bridges (e.g., ``A bridge is familiar if 1/8 of all
+active bridges have appeared more recently than it, \ldots'') and a static
+part that is independent of other bridges (e.g., ``\ldots or if it has
+been around for a Weighted Time of 8 days.'').
+The dynamic parts ensure that a certain fraction of bridges is considered
+stable even in a rather instable network.
+The static parts ensures that rather stable bridges are not excluded even
+when most other bridges in the network are stable.
+
+\section{Extending BridgeDB to track bridge stability}
+
+There are at least two code bases that could be extended to track bridge
+stability and include at least one stable bridge in BridgeDB results: the
+bridge authority and BridgeDB.
+The decision for extending either code base affects the available data
+for tracking bridge stability and is therefore discussed here.
+
+The bridge authority maintains a list of all active bridges.
+Bridges register at the bridge authority when joining the network, and the
+bridge authority periodically performs reachability tests to confirm that
+a bridge is still active.
+The bridge authority takes snapshots of the list of active bridges every
+30~minutes and copies these snapshots to BridgeDB.
+BridgeDB parses these half-hourly snapshots and gives out bridges to users
+based on the most recently known snapshot.
+
+The bridge stability history can be implemented either in the bridge
+authority code or in BridgeDB.
+On the one hand, an implementation in BridgeDB has the disadvantage that
+bridge reachability data has a resolution of 30 minutes whereas the bridge
+authority would learn about bridges joining or leaving the network
+immediately.
+On the other hand, the bridge stability information is not used by
+anything in the Tor software, but only by BridgeDB.
+Implementing this feature in BridgeDB makes more sense from a software
+architecture point of view.
+In the following we assume that BridgeDB will track bridge stability based
+on half-hourly snapshots of active bridge lists, the bridge network
+statuses.
+
+\section{Simulating bridge stability using archived data}
+
+We can analyze how BridgeDB would track bridge stability and give out
+stable bridges by using archived bridge descriptors.
+These archives contain the same descriptors that BridgeDB uses, but they
+are public and don't contain any IP addresses or sensitive pieces of
+information.
+In Section~\ref{sec:missingdata} we look at the problem of missing data
+due to either the bridge authority or BridgeDB failing and at the effect
+on tracking bridge stability.
+We then touch the topic of how bridge descriptors are sanitized and how we
+can glue them back together for our analysis in
+Section~\ref{sec:sanitizing}.
+Next, we examine typical bridge stability values as requirements for
+considering a bridge as stable in Section~\ref{sec:requirements}.
+In Section~\ref{sec:fractions} we estimate what fraction of bridges would
+be considered as stable depending on the chosen stability requirements.
+Finally, in Section~\ref{sec:selectedstability} we evaluate how effective
+different requirement combinations are for selecting stable bridges.
+Result metrics are how soon selected bridges change their address or what
+fractional uptime selected bridges have in the future.
+
+\subsection{Handling missing bridge status data}
+\label{sec:missingdata}
+
+The bridge status data that we use in this analysis and that would also be
+used by BridgeDB to track bridge stability is generated by the bridge
+authority and copied over to BridgeDB every 30~minutes.
+Figure~\ref{fig:runningbridge} shows the number of running bridges
+contained in these snapshots from July 2010 to June 2011.
+
+\begin{figure}[t]
+\includegraphics[width=\textwidth]{runningbridge.pdf}
+\caption{Median number of running bridges as reported by the bridge
+authority}
+\label{fig:runningbridge}
+\end{figure}
+
+For most of the time the number of bridges is relatively stable.
+But there are at least two irregularities, one in July 2010 and another
+one in February 2011, resulting from problems with the bridge authority or
+the data transfer to the BridgeDB host.
+Figure~\ref{fig:runningbridge-detail} shows these two intervals in more
+detail.
+
+\begin{figure}[t]
+\includegraphics[width=\textwidth]{runningbridge-detail.pdf}
+\caption{Number of Running bridges during phases when either the bridge
+authority or the BridgeDB host were broken}
+\label{fig:runningbridge-detail}
+\end{figure}
+
+The missing data from July 14 to 27, 2010 comes from BridgeDB host not
+accepting new descriptors from the bridge authority because of an
+operating system upgrade of the BridgeDB host.
+During this time, the bridge authority continued to work, but BridgeDB was
+unable to learn about new bridge descriptors from it.
+
+During the time from January 31 to February 16, 2011, the \verb+tor+
+process running the bridge authority silently died, but the script to copy
+descriptors to BridgeDB kept running.
+In this case, BridgeDB received fresh tarballs containing stale
+descriptors with a constant number of 687 relays, visualized in light
+gray.
+These stale descriptors have been excluded from the sanitized descriptors
+and the subsequent analysis.
+The bridge authority was restarted on February 16, 2011, resulting in the
+number of running bridges slowly stabilizing throughout the day.
+
+Both this analysis and a later implementation in BridgeDB need to take
+extended phases of missing or stale data into account.
+
+\subsection{Detecting address changes in sanitized descriptors}
+\label{sec:sanitizing}
+
+The bridge descriptor archives that we use in this analysis have been
+sanitized to remove all addresses and otherwise sensitive
+parts~\cite{loesing2011overview}.
+Part of this sanitizing process is that bridge IP addresses are replaced
+with keyed hashes using a fresh key every month.
+More precisely, every bridge IP address is replaced with the private IP
+address \verb+10.x.x.x+ with \verb+x.x.x+ being the 3 most significant
+bytes of \verb+SHA-256(IP address | bridge identity | secret)+.
+
+A side-effect of this sanitizing step is that a bridge's sanitized IP
+address changes at least once per month, even if the bridge's real IP
+address stays the same.
+We need to detect these artificial address changes and distinguish them
+from real IP address changes.
+
+In this analysis we use a simple heuristic to distinguish between real IP
+address changes and artifacts from the sanitizing process:
+Whenever we find that a bridge has changed its IP address from one month
+to the next, we look up how long both IP addresses were in use in either
+month.
+If both addresses were contained in bridge descriptors that were published
+at least 36~hours apart, we consider them stable IP addresses and
+attribute the apparent IP address change to the sanitizing process.
+Otherwise, we assume the bridge has really changed its IP address.
+Obviously, this simple heuristic might lead us to false conclusions in
+some cases.
+But it helps us handle cases when bridges rarely or never change their IP
+address which would otherwise suffer from monthly address changes in this
+analysis.
+
+\subsection{Examining typical stability metric values}
+\label{sec:requirements}
+
+The definition of bridge stability on page~\pageref{sec:defining} contains
+three different metrics, each of which having a dynamic and a static part.
+The dynamic parts compares the value of a bridge's stability metric to the
+whole set of running bridges.
+Only those bridges are considered as stable that exceed the median value
+(or the 12.5th percentile) of all running bridges.
+The static requirement parts are fixed values for all stability metrics
+that don't rely on the stability of other bridges.
+
+Figure~\ref{fig:requirements} visualizes the dynamic (solid lines) and
+static parts (dashed lines) of all three requirements.
+The dynamic WMTBAC requirements are higher than previously expected.
+A value of 60 means that, on average, bridges keep their IP address and
+port for 60 days.
+The dynamic values are cut off at 30 days by the static requirement which
+should be a high enough value.
+The goal here is to give blocked users a stable enough set of bridges so
+that they don't have to wait another 24~hours before receiving new ones.
+
+We can further see that the dynamic requirements are relatively stable
+over time except for the two phases of missing bridge status data.
+The first phase in July 2010 mostly affects WT, but neither WMTBAC nor
+WFU.
+The second phase in February 2011 affects all three metrics.
+We can expect the selection of stable bridges during February 2010 to be
+more random than at other times.
+
+\begin{figure}[t]
+\includegraphics[width=\textwidth]{requirements.pdf}
+\caption{Dynamic requirements for considering a bridge as stable}
+\label{fig:requirements}
+\end{figure}
+
+\subsection{Estimating fractions of bridges considered as stable}
+\label{sec:fractions}
+
+Requiring a bridge to meet or exceed either or both WMTBF or WFU metric
+results in considering only a subset of all bridges as stable.
+The first result of this analysis is to outline what fraction of bridges
+would be considered as stable if BridgeDB used either or both
+requirements.
+In theory, all parameters in the bridge stability definition on
+page~\pageref{def:bridgestability} could be adjusted to change the set of
+stable bridges or focus more on address changes or on fractional uptime.
+We're leaving the fine-tuning for future work when specifying and
+implementing the BridgeDB extension.
+
+Figure~\ref{fig:stablebridge} shows the fraction of stable bridges over
+time.
+If we only require bridges to meet or exceed the median WMTBAC or the
+fixed value of 30 days, roughly 55~\% of the bridges are considered as
+stable.
+If bridges are only required to meet or exceed the WT and WFU values,
+about $7/8 \times 1/2 = 43.75~\%$ of bridges are considered as stable.
+Requiring both WFU and WMTBAC leads to a fraction of roughly 35~\% stable
+bridges.
+
+\begin{figure}[t]
+\includegraphics[width=\textwidth]{stablebridge.pdf}
+\caption{Impact of requiring stable bridges to meet or exceed the median
+WFU and/or WMTBAC on the fraction of running bridges considered as stable}
+\label{fig:stablebridge}
+\end{figure}
+
+The fraction of 33~\% stable bridges seems appropriate if 1 out of
+3~bridges in the BridgeDB results is supposed to be a stable bridge.
+If more than 1~bridge should be a stable bridge, the requirements need to
+be lowered, so that a higher fraction of bridges is considered stable.
+Otherwise, the load on stable bridges might become too high.
+
+\subsection{Evaluating different requirements on stable bridges}
+\label{sec:selectedstability}
+
+The main purpose of this analysis is to compare the quality of certain
+requirements and requirement combinations on the stability of selected
+bridges.
+Similar to the previous section, we only compare whether or not the WMTBAC
+or WFU requirement is used, but don't change their parameters.
+
+The first result is the future uptime that we can expect from a bridge
+that we consider stable.
+We calculate future uptime similar to past uptime by weighting events in
+the near future more than those happening later.
+We are particularly interested in the almost worst-case scenario here,
+which is why we're looking at the 10th percentile weighted fractional
+uptime in the future.
+This number means that 10~\% of bridges have a weighted fractional uptime
+at most this high and 90~\% of bridges have a value at least this high.
+
+Figure~\ref{fig:fwfu-sim} visualizes the four possible combinations of
+using or not using the WMTBAC and WFU requirements.
+In this plot, the ``WFU \& WMTBAC'' and ``WFU'' lines almost entirely
+overlap, meaning that the WMTBAC requirement doesn't add anything to
+future uptime of selected bridges.
+If the WFU requirement is not used, requiring bridges to meet the WMTBAC
+requirement increases future uptime from roughly 35~\% to maybe 55~\%.
+That means that there is a slight correlation between the two metrics,
+which is plausible.
+
+\begin{figure}[t]
+\includegraphics[width=\textwidth]{fwfu-sim.pdf}
+\caption{Impact of requiring stable bridges to meet or exceed the median
+WFU and/or WMTBAC on the 10th percentile weighted fractional uptime in the
+future}
+\label{fig:fwfu-sim}
+\end{figure}
+
+The second result is the time that a selected bridge stays on the same
+address and port.
+We simply measure the time that the bridge will keep using its current
+address in days.
+Again, we look at the 10th percentile.
+90~\% of selected bridges keep their address longer than this time.
+
+Figure~\ref{fig:tosa-sim} shows for how long bridges keep their address
+and port.
+Bridges meeting both WFU and WTMBAC requirements keep their address for 2
+to 5~weeks.
+This value decreases to 1 to 3~weeks when taking away the WFU requirement,
+which is also a result of the two metrics beeing correlated.
+The bridges that only meet the WFU requirement and not the WMTBAC
+requirement change their address within the first week.
+If we don't use any requirement at all, which is what BridgeDB does today,
+10~\% of all bridges change their address within a single day.
+
+\begin{figure}[t]
+\includegraphics[width=\textwidth]{tosa-sim.pdf}
+\caption{Impact of requiring stable bridges to meet or exceed the median
+WFU and/or WMTBAC on the 10th percentile time on the same address}
+\label{fig:tosa-sim}
+\end{figure}
+
+\section{Concluding the bridge stability analysis}
+
+In this report we propose to extend BridgeDB to make it give out at least
+one stable bridge per user.
+Bridge stability can be calculated based on bridge status information over
+time, similar to how the directory authorities calculate relay stability.
+The bridge stability metric proposed here is based on a bridge's past
+uptime and the frequency of changing its address and/or port.
+Requiring at least 1 bridge of the 3 to be given out to users greatly
+reduces the worst case probability of all bridges being offline or
+changing their addresses or ports.
+The price for this increase in stability is that stable bridges will be
+given out more often than non-stable bridges and will therefore see more
+usage.
+
+We suggest to implement the described bridge stability metric in BridgeDB
+and make it configurable to tweak the requirement parameters if needed.
+Maybe it turns out to be more useful to lower the requirements for a
+bridge to become stable and give out two stable bridges per response.
+It's also possible that the requirement for a bridge to keep its address
+becomes less important in the future when bridge clients can request a
+bridge's current address from the bridge authority.
+All these scenarios can be analyzed before deploying them using archived
+data as done in this report.
+
+\bibliography{report}
+\bibliographystyle{plain}
+
+\end{document}
+
diff --git a/task-4255/stability.R b/task-4255/stability.R
new file mode 100644
index 0000000..b2cd487
--- /dev/null
+++ b/task-4255/stability.R
@@ -0,0 +1,220 @@
+library(ggplot2)
+stability <- read.csv("stability.csv", stringsAsFactors = FALSE)
+
+d <- stability[stability$time > '2010-07' & stability$time < '2011-07', ]
+d <- d[, c("time", "running")]
+d <- na.omit(d)
+d_mean <- aggregate(list(running = d$running),
+ by = list(date = as.Date(d$time)), quantile, probs = 0.5)
+d_max <- aggregate(list(running = d$running),
+ by = list(date = as.Date(d$time)), quantile, probs = 0.75)
+d_min <- aggregate(list(running = d$running),
+ by = list(date = as.Date(d$time)), quantile, probs = 0.25)
+d <- data.frame(x = d_mean$date, y = d_mean$running, ymin = d_min$running,
+ ymax = d_max$running)
+d <- rbind(d,
+ data.frame(x = as.Date(setdiff(seq(from = min(d$x, na.rm = TRUE),
+ to = max(d$x, na.rm = TRUE), by="1 day"), d$x), origin = "1970-01-01"),
+ y = NA, ymin = NA, ymax = NA))
+ggplot(d, aes(x = as.Date(x), y = y, ymin = ymin, ymax = ymax)) +
+geom_line() +
+scale_x_date(name = "", major = "3 months", minor = "1 month",
+ format = "%b %Y") +
+scale_y_continuous(name = "Running \nbridges ",
+ limits = c(0, max(d_mean$running, na.rm = TRUE))) +
+opts(axis.title.x = theme_text(size = 12 * 0.8, face = "bold",
+ hjust = 0.5),
+ axis.title.y = theme_text(size = 12 * 0.8, face = "bold", vjust = 0.5,
+ hjust = 1))
+ggsave(filename = "runningbridge.pdf", width = 7, height = 3, dpi = 100)
+
+pdf("runningbridge-detail.pdf", width = 7, height = 4)
+grid.newpage()
+pushViewport(viewport(layout = grid.layout(2, 1)))
+d <- stability[stability$time > '2010-07-10' &
+ stability$time < '2010-07-31', ]
+a <- ggplot(d, aes(x = as.POSIXct(time), y = running)) +
+geom_point(size = 0.75) +
+scale_x_datetime(name = "", major = "1 week", minor = "1 day",
+ format = "%b %d, %Y") +
+scale_y_continuous(name = "Running \nbridges ",
+ limits = c(0, max(d$running, na.rm = TRUE))) +
+opts(axis.title.x = theme_text(size = 12 * 0.8, face = "bold",
+ hjust = 0.5),
+ axis.title.y = theme_text(size = 12 * 0.8, face = "bold", vjust = 0.5,
+ hjust = 1),
+ legend.position = "none")
+d <- stability[stability$time > '2011-01-29' &
+ stability$time < '2011-02-19', ]
+e <- read.csv("stale-bridge-tarballs.csv", stringsAsFactors = FALSE,
+ col.names = c("time"))
+d <- rbind(
+ data.frame(time = d$time, running = d$running, colour = "black"),
+ data.frame(time = e$time, running = 687, colour = "grey"))
+b <- ggplot(d, aes(x = as.POSIXct(time), y = running, colour = colour)) +
+geom_point(size = 0.75) +
+scale_x_datetime(name = "", major = "1 week", minor = "1 day",
+ format = "%b %d, %Y") +
+scale_y_continuous(name = "Running \nbridges ",
+ limits = c(0, max(d$running, na.rm = TRUE))) +
+scale_colour_manual(values = c("black", "grey60")) +
+opts(axis.title.x = theme_text(size = 12 * 0.8, face = "bold",
+ hjust = 0.5),
+ axis.title.y = theme_text(size = 12 * 0.8, face = "bold", vjust = 0.5,
+ hjust = 1),
+ legend.position = "none")
+print(a, vp = viewport(layout.pos.row = 1, layout.pos.col = 1))
+print(b, vp = viewport(layout.pos.row = 2, layout.pos.col = 1))
+dev.off()
+
+d <- stability[stability$time > '2010-07' & stability$time < '2011-07', ]
+d <- d[, c("time", "minwmtbaca50wmtbac", "minwta", "minwfua50wfu")]
+d <- na.omit(d)
+d_mean <- aggregate(d[, 2:length(d)], by = list(date = as.Date(d$time)),
+ quantile, probs = 0.5)
+d_max <- aggregate(d[, 2:length(d)], by = list(date = as.Date(d$time)),
+ quantile, probs = 0.75)
+d_min <- aggregate(d[, 2:length(d)], by = list(date = as.Date(d$time)),
+ quantile, probs = 0.25)
+d <- rbind(
+ data.frame(x = d_mean$date,
+ y = d_mean$minwmtbaca50wmtbac / (24 * 60 * 60),
+ ymin = d_min$minwmtbaca50wmtbac / (24 * 60 * 60),
+ ymax = d_max$minwmtbaca50wmtbac / (24 * 60 * 60),
+ var = "Median WMTBAC"),
+ data.frame(x = d_mean$date, y = d_mean$minwta / (24 * 60 * 60),
+ ymin = d_min$minwta / (24 * 60 * 60),
+ ymax = d_max$minwta / (24 * 60 * 60),
+ var = "12.5th perc. WT"),
+ data.frame(x = d_mean$date, y = d_mean$minwfua50wfu / 10000,
+ ymin = d_min$minwfua50wfu / 10000,
+ ymax = d_max$minwfua50wfu / 10000,
+ var = "Median WFU"))
+missing_dates <- as.Date(setdiff(seq(from = min(d$x, na.rm = TRUE),
+ to = max(d$x, na.rm = TRUE), by="1 day"), d$x), origin = "1970-01-01")
+d <- rbind(d,
+ data.frame(x = missing_dates, y = NA, ymin = NA, ymax = NA,
+ var = "Median WMTBAC"),
+ data.frame(x = missing_dates, y = NA, ymin = NA, ymax = NA,
+ var = "12.5th perc. WT"),
+ data.frame(x = missing_dates, y = NA, ymin = NA, ymax = NA,
+ var = "Median WFU"))
+e <- data.frame(
+ yintercept = c(30, 8, 0.98),
+ var = c("Median WMTBAC", "12.5th perc. WT", "Median WFU"))
+ggplot(d, aes(x = as.Date(x), y = y, ymin = ymin, ymax = ymax)) +
+geom_line() +#colour = "grey30") +
+#geom_ribbon(alpha = 0.3) +
+geom_hline(data = e, aes(yintercept = yintercept), colour = "gray40",
+ linetype = 2) +
+facet_grid(var ~ ., scales = "free_y") +
+scale_x_date(name = "", major = "3 months", minor = "1 month",
+ format = "%b %Y") +
+scale_y_continuous(name = "") +
+opts(axis.title.x = theme_text(size = 12 * 0.8, face = "bold",
+ hjust = 0.5),
+ axis.title.y = theme_text(size = 12 * 0.8, face = "bold", vjust = 0.5,
+ hjust = 1))
+ggsave(filename = "requirements.pdf", width = 7, height = 5, dpi = 100)
+
+d <- stability[stability$time > '2010-07' & stability$time < '2011-07', ]
+d <- d[, c("time", "perc10wfu0wfu0wmtbac", "perc10wfu0wfu50wmtbac",
+ "perc10wfu50wfu0wmtbac", "perc10wfu50wfu50wmtbac")]
+d <- na.omit(d)
+d <- aggregate(d[, 2:length(d)], by = list(date = as.Date(d$time)),
+ quantile, probs = 0.5)
+d <- rbind(d,
+ data.frame(date = as.Date(setdiff(seq(from = min(d$date),
+ to = max(d$date), by="1 day"), d$date), origin = "1970-01-01"),
+ perc10wfu0wfu0wmtbac = NA, perc10wfu0wfu50wmtbac = NA,
+ perc10wfu50wfu0wmtbac = NA, perc10wfu50wfu50wmtbac = NA))
+d <- melt(d, id = "date")
+ggplot(d, aes(x = date, y = value / 10000, linetype = variable)) +
+geom_line() +
+scale_y_continuous(name = paste("10th perc. \nWFU in \n",
+ "the future ", sep = ""), formatter = "percent", limits = c(0, 1)) +
+scale_x_date(name = "", major = "3 months", minor = "1 month",
+ format = "%b %Y") +
+scale_linetype_manual(name = paste("Requirements for\nconsidering",
+ "a\nbridge as stable\n"), breaks = c("perc10wfu50wfu50wmtbac",
+ "perc10wfu50wfu0wmtbac", "perc10wfu0wfu50wmtbac",
+ "perc10wfu0wfu0wmtbac"), labels = c("WFU & WMTBAC", "WFU", "WMTBAC",
+ "None"), values = c(1, 3, 2, 4)) +
+opts(plot.title = theme_text(size = 14 * 0.8, face = "bold"),
+ axis.title.x = theme_text(size = 12 * 0.8, face = "bold",
+ hjust = 0.5),
+ axis.title.y = theme_text(size = 12 * 0.8, face = "bold", vjust = 0.5,
+ hjust = 1))
+ggsave(filename = "fwfu-sim.pdf", width = 7, height = 3, dpi = 100)
+
+d <- stability[stability$time > '2010-07' & stability$time < '2011-07', ]
+d <- d[, c("time", "perc10tosa0wfu0wmtbac", "perc10tosa0wfu50wmtbac",
+ "perc10tosa50wfu0wmtbac", "perc10tosa50wfu50wmtbac")]
+d <- na.omit(d)
+d <- aggregate(d[, 2:length(d)], by = list(date = as.Date(d$time)),
+ quantile, probs = 0.5)
+d <- rbind(d,
+ data.frame(date = as.Date(setdiff(seq(from = min(d$date),
+ to = max(d$date), by="1 day"), d$date), origin = "1970-01-01"),
+ perc10tosa0wfu0wmtbac = NA, perc10tosa0wfu50wmtbac = NA,
+ perc10tosa50wfu0wmtbac = NA, perc10tosa50wfu50wmtbac = NA))
+d <- melt(d, id = "date")
+ggplot(d, aes(x = date, y = value / 86400, linetype = variable)) +
+geom_line() +
+scale_y_continuous(name = paste("10th perc. \ntime on \nthe same \n",
+ "address \nin days ", sep = ""),
+ breaks = seq(0, max(d$value / 86400, na.rm = TRUE), 7),
+ minor = seq(0, max(d$value / 86400, na.rm = TRUE), 1),
+ limits = c(0, max(d$value / 86400, na.rm = TRUE))) +
+scale_x_date(name = "", major = "3 months", minor = "1 month",
+ format = "%b %Y") +
+scale_linetype_manual(name = paste("Requirements for\nconsidering",
+ "a\nbridge as stable\n"), breaks = c("perc10tosa50wfu50wmtbac",
+ "perc10tosa0wfu50wmtbac", "perc10tosa50wfu0wmtbac",
+ "perc10tosa0wfu0wmtbac"), labels = c("WFU & WMTBAC", "WMTBAC", "WFU",
+ "None"), values = c(1, 3, 2, 4)) +
+opts(plot.title = theme_text(size = 14 * 0.8, face = "bold"),
+ axis.title.x = theme_text(size = 12 * 0.8, face = "bold",
+ hjust = 0.5),
+ axis.title.y = theme_text(size = 12 * 0.8, face = "bold", vjust = 0.5,
+ hjust = 1))
+ggsave(filename = "tosa-sim.pdf", width = 7, height = 3, dpi = 100)
+
+d <- stability[stability$time > '2010-07' & stability$time < '2011-07', ]
+d <- d[, c("time", "stablebridge0wfu50wmtbac", "stablebridge50wfu0wmtbac",
+ "stablebridge50wfu50wmtbac", "running")]
+d <- na.omit(d)
+#d <- aggregate(d[, 2:length(d)], by = list(date = as.Date(d$time)),
+# quantile, probs = 0.5)
+d <- rbind(
+ data.frame(time = d$time, y = d$stablebridge0wfu50wmtbac / d$running,
+ variable = "WMTBAC"),
+ data.frame(time = d$time, y = d$stablebridge50wfu0wmtbac / d$running,
+ variable = "WFU"),
+ data.frame(time = d$time, y = d$stablebridge50wfu50wmtbac / d$running,
+ variable = "WFU & WMTBAC"))
+d <- aggregate(list(y = d$y), by = list(x = as.Date(d$time),
+ variable = d$variable), quantile, probs = 0.5)
+missing_dates <- as.Date(setdiff(seq(from = min(d$x, na.rm = TRUE),
+ to = max(d$x, na.rm = TRUE), by="1 day"), d$x), origin = "1970-01-01")
+d <- rbind(d,
+ data.frame(x = missing_dates, y = NA,
+ variable = "WMTBAC"),
+ data.frame(x = missing_dates, y = NA,
+ variable = "WFU"),
+ data.frame(x = missing_dates, y = NA,
+ variable = "WFU & WMTBAC"))
+ggplot(d, aes(x = x, y = y, linetype = variable)) +
+geom_line() +
+scale_y_continuous(name = "Fraction of \nRunning \nbridges ",
+ formatter = "percent", limits = c(0, max(d$y, na.rm = TRUE))) +
+scale_x_date(name = "", major = "3 months", minor = "1 month",
+ format = "%b %Y") +
+scale_linetype_manual(name = paste("\nRequirements for\nconsidering",
+ "a\nbridge as stable\n"), values = c(3, 2, 4)) +
+opts(axis.title.x = theme_text(size = 12 * 0.8, face = "bold",
+ hjust = 0.5),
+ axis.title.y = theme_text(size = 12 * 0.8, face = "bold", vjust = 0.5,
+ hjust = 1))
+ggsave(filename = "stablebridge.pdf", width = 7, height = 3, dpi = 100)
+
diff --git a/task-4255/stale-bridge-tarballs.csv b/task-4255/stale-bridge-tarballs.csv
new file mode 100644
index 0000000..ff430a4
--- /dev/null
+++ b/task-4255/stale-bridge-tarballs.csv
@@ -0,0 +1,760 @@
+2011-01-31 06:07:03
+2011-01-31 06:37:03
+2011-01-31 07:07:03
+2011-01-31 07:37:02
+2011-01-31 08:07:02
+2011-01-31 08:37:03
+2011-01-31 09:07:03
+2011-01-31 09:37:04
+2011-01-31 10:07:03
+2011-01-31 10:37:03
+2011-01-31 11:07:03
+2011-01-31 11:37:03
+2011-01-31 12:07:02
+2011-01-31 12:37:03
+2011-01-31 13:07:03
+2011-01-31 13:37:03
+2011-01-31 14:07:02
+2011-01-31 14:37:03
+2011-01-31 15:07:02
+2011-01-31 15:37:03
+2011-01-31 16:07:02
+2011-01-31 16:37:03
+2011-01-31 17:07:03
+2011-01-31 17:37:02
+2011-01-31 18:07:04
+2011-01-31 18:37:03
+2011-01-31 19:07:03
+2011-01-31 19:37:03
+2011-01-31 20:07:03
+2011-01-31 20:37:02
+2011-01-31 21:07:03
+2011-01-31 21:37:02
+2011-01-31 22:07:02
+2011-01-31 22:37:03
+2011-01-31 23:07:02
+2011-01-31 23:37:03
+2011-02-01 00:07:02
+2011-02-01 00:37:02
+2011-02-01 01:07:02
+2011-02-01 01:37:03
+2011-02-01 02:07:02
+2011-02-01 02:37:02
+2011-02-01 03:07:02
+2011-02-01 03:37:03
+2011-02-01 04:07:02
+2011-02-01 04:37:02
+2011-02-01 05:07:03
+2011-02-01 05:37:03
+2011-02-01 06:07:03
+2011-02-01 06:37:03
+2011-02-01 07:07:03
+2011-02-01 07:37:03
+2011-02-01 08:07:02
+2011-02-01 08:37:03
+2011-02-01 09:07:02
+2011-02-01 09:37:03
+2011-02-01 10:07:02
+2011-02-01 10:37:03
+2011-02-01 11:07:02
+2011-02-01 11:37:02
+2011-02-01 12:07:03
+2011-02-01 12:37:03
+2011-02-01 13:07:03
+2011-02-01 13:37:03
+2011-02-01 14:07:03
+2011-02-01 14:37:03
+2011-02-01 15:07:03
+2011-02-01 15:37:02
+2011-02-01 16:07:02
+2011-02-01 16:37:03
+2011-02-01 17:07:03
+2011-02-01 17:37:03
+2011-02-01 18:07:02
+2011-02-01 18:37:03
+2011-02-01 19:07:03
+2011-02-01 19:37:03
+2011-02-01 20:07:03
+2011-02-01 20:37:02
+2011-02-01 21:07:08
+2011-02-01 21:37:03
+2011-02-01 22:07:03
+2011-02-01 22:37:03
+2011-02-01 23:07:02
+2011-02-01 23:37:03
+2011-02-02 00:07:02
+2011-02-02 00:37:03
+2011-02-02 01:07:02
+2011-02-02 01:37:02
+2011-02-02 02:07:03
+2011-02-02 02:37:02
+2011-02-02 03:07:03
+2011-02-02 03:37:03
+2011-02-02 04:07:02
+2011-02-02 04:37:03
+2011-02-02 05:07:02
+2011-02-02 05:37:03
+2011-02-02 06:07:03
+2011-02-02 06:37:02
+2011-02-02 07:07:03
+2011-02-02 07:37:03
+2011-02-02 08:07:03
+2011-02-02 08:37:02
+2011-02-02 09:07:03
+2011-02-02 09:37:03
+2011-02-02 10:07:03
+2011-02-02 10:37:02
+2011-02-02 11:07:02
+2011-02-02 11:37:02
+2011-02-02 12:07:03
+2011-02-02 12:37:03
+2011-02-02 13:07:02
+2011-02-02 13:37:03
+2011-02-02 14:07:02
+2011-02-02 14:37:03
+2011-02-02 15:07:03
+2011-02-02 15:37:02
+2011-02-02 16:07:03
+2011-02-02 16:37:03
+2011-02-02 17:07:03
+2011-02-02 17:37:03
+2011-02-02 18:07:03
+2011-02-02 18:37:02
+2011-02-02 19:07:03
+2011-02-02 19:37:03
+2011-02-02 20:07:03
+2011-02-02 20:37:03
+2011-02-02 21:07:03
+2011-02-02 21:37:03
+2011-02-02 22:07:03
+2011-02-02 22:37:03
+2011-02-02 23:07:03
+2011-02-02 23:37:03
+2011-02-03 00:07:03
+2011-02-03 00:37:02
+2011-02-03 01:07:03
+2011-02-03 01:37:02
+2011-02-03 02:07:03
+2011-02-03 02:37:02
+2011-02-03 03:07:02
+2011-02-03 03:37:02
+2011-02-03 04:07:03
+2011-02-03 04:37:02
+2011-02-03 05:07:02
+2011-02-03 05:37:03
+2011-02-03 06:07:03
+2011-02-03 06:37:03
+2011-02-03 07:07:02
+2011-02-03 07:37:02
+2011-02-03 08:07:02
+2011-02-03 08:37:02
+2011-02-03 09:07:03
+2011-02-03 09:37:03
+2011-02-03 10:07:02
+2011-02-03 10:37:03
+2011-02-03 11:07:02
+2011-02-03 11:37:03
+2011-02-03 12:07:02
+2011-02-03 12:37:02
+2011-02-03 13:07:03
+2011-02-03 13:37:03
+2011-02-03 14:07:02
+2011-02-03 14:37:03
+2011-02-03 15:07:02
+2011-02-03 15:37:02
+2011-02-03 16:07:03
+2011-02-03 16:37:03
+2011-02-03 17:07:02
+2011-02-03 17:37:03
+2011-02-03 18:07:03
+2011-02-03 18:37:03
+2011-02-03 19:07:02
+2011-02-03 19:37:03
+2011-02-03 20:07:03
+2011-02-03 20:37:03
+2011-02-03 21:07:03
+2011-02-03 21:37:03
+2011-02-03 22:07:03
+2011-02-03 22:37:03
+2011-02-03 23:07:02
+2011-02-03 23:37:03
+2011-02-04 00:07:02
+2011-02-04 00:37:03
+2011-02-04 01:07:03
+2011-02-04 01:37:02
+2011-02-04 02:07:03
+2011-02-04 02:37:03
+2011-02-04 03:07:03
+2011-02-04 03:37:02
+2011-02-04 04:07:02
+2011-02-04 04:37:03
+2011-02-04 05:07:02
+2011-02-04 05:37:03
+2011-02-04 06:07:03
+2011-02-04 06:37:03
+2011-02-04 07:07:03
+2011-02-04 07:37:03
+2011-02-04 08:07:03
+2011-02-04 08:37:03
+2011-02-04 09:07:02
+2011-02-04 09:37:03
+2011-02-04 10:07:03
+2011-02-04 10:37:03
+2011-02-04 11:07:03
+2011-02-04 11:37:02
+2011-02-04 12:07:03
+2011-02-04 12:37:02
+2011-02-04 13:07:03
+2011-02-04 13:37:03
+2011-02-04 14:07:03
+2011-02-04 14:37:02
+2011-02-04 15:07:03
+2011-02-04 15:37:03
+2011-02-04 16:07:03
+2011-02-04 16:37:02
+2011-02-04 17:07:03
+2011-02-04 17:37:03
+2011-02-04 18:07:03
+2011-02-04 18:37:03
+2011-02-04 19:07:03
+2011-02-04 19:37:03
+2011-02-04 20:07:03
+2011-02-04 20:37:03
+2011-02-04 21:07:02
+2011-02-04 21:37:02
+2011-02-04 22:07:02
+2011-02-04 22:37:03
+2011-02-04 23:07:03
+2011-02-04 23:37:03
+2011-02-05 00:07:03
+2011-02-05 00:37:02
+2011-02-05 01:07:03
+2011-02-05 01:37:03
+2011-02-05 02:07:03
+2011-02-05 02:37:02
+2011-02-05 03:07:03
+2011-02-05 03:37:02
+2011-02-05 04:07:03
+2011-02-05 04:37:03
+2011-02-05 05:07:03
+2011-02-05 05:37:05
+2011-02-05 06:07:02
+2011-02-05 06:37:02
+2011-02-05 07:07:02
+2011-02-05 07:37:03
+2011-02-05 08:07:03
+2011-02-05 08:37:03
+2011-02-05 09:07:03
+2011-02-05 09:37:02
+2011-02-05 10:07:03
+2011-02-05 10:37:03
+2011-02-05 11:07:02
+2011-02-05 11:37:02
+2011-02-05 12:07:03
+2011-02-05 12:37:03
+2011-02-05 13:07:02
+2011-02-05 13:37:03
+2011-02-05 14:07:04
+2011-02-05 14:37:03
+2011-02-05 15:07:03
+2011-02-05 15:37:03
+2011-02-05 16:07:02
+2011-02-05 16:37:03
+2011-02-05 17:07:03
+2011-02-05 17:37:03
+2011-02-05 18:07:03
+2011-02-05 18:37:03
+2011-02-05 19:07:02
+2011-02-05 19:37:03
+2011-02-05 20:07:03
+2011-02-05 20:37:03
+2011-02-05 21:07:02
+2011-02-05 21:37:03
+2011-02-05 22:07:03
+2011-02-05 22:37:03
+2011-02-05 23:07:03
+2011-02-05 23:37:02
+2011-02-06 00:07:03
+2011-02-06 00:37:02
+2011-02-06 01:07:03
+2011-02-06 01:37:02
+2011-02-06 02:07:03
+2011-02-06 02:37:03
+2011-02-06 03:07:03
+2011-02-06 03:37:06
+2011-02-06 04:07:03
+2011-02-06 04:37:03
+2011-02-06 05:07:03
+2011-02-06 05:37:03
+2011-02-06 06:07:03
+2011-02-06 06:37:03
+2011-02-06 07:07:02
+2011-02-06 07:37:02
+2011-02-06 08:07:02
+2011-02-06 08:37:03
+2011-02-06 09:07:02
+2011-02-06 09:37:02
+2011-02-06 10:07:02
+2011-02-06 10:37:02
+2011-02-06 11:07:02
+2011-02-06 11:37:02
+2011-02-06 12:07:03
+2011-02-06 12:37:02
+2011-02-06 13:07:02
+2011-02-06 13:37:03
+2011-02-06 14:07:03
+2011-02-06 14:37:02
+2011-02-06 15:07:02
+2011-02-06 15:37:03
+2011-02-06 16:07:03
+2011-02-06 16:37:03
+2011-02-06 17:07:03
+2011-02-06 17:37:02
+2011-02-06 18:07:03
+2011-02-06 18:37:02
+2011-02-06 19:07:02
+2011-02-06 19:37:02
+2011-02-06 20:07:03
+2011-02-06 20:37:02
+2011-02-06 21:07:03
+2011-02-06 21:37:03
+2011-02-06 22:07:03
+2011-02-06 22:37:02
+2011-02-06 23:07:02
+2011-02-06 23:37:03
+2011-02-07 00:07:03
+2011-02-07 00:37:02
+2011-02-07 01:07:03
+2011-02-07 01:37:02
+2011-02-07 02:07:02
+2011-02-07 02:37:02
+2011-02-07 03:07:02
+2011-02-07 03:37:03
+2011-02-07 04:07:02
+2011-02-07 04:37:02
+2011-02-07 05:07:02
+2011-02-07 05:37:03
+2011-02-07 06:07:03
+2011-02-07 06:37:02
+2011-02-07 07:07:03
+2011-02-07 07:37:03
+2011-02-07 08:07:03
+2011-02-07 08:37:03
+2011-02-07 09:07:03
+2011-02-07 09:37:02
+2011-02-07 10:07:02
+2011-02-07 10:37:02
+2011-02-07 11:07:02
+2011-02-07 11:37:02
+2011-02-07 12:07:03
+2011-02-07 12:37:03
+2011-02-07 13:07:03
+2011-02-07 13:37:02
+2011-02-07 14:07:03
+2011-02-07 14:37:03
+2011-02-07 15:07:03
+2011-02-07 15:37:03
+2011-02-07 16:07:02
+2011-02-07 16:37:03
+2011-02-07 17:07:02
+2011-02-07 17:37:02
+2011-02-07 18:07:03
+2011-02-07 18:37:03
+2011-02-07 19:07:02
+2011-02-07 19:37:02
+2011-02-07 20:07:03
+2011-02-07 20:37:03
+2011-02-07 21:07:06
+2011-02-07 21:37:02
+2011-02-07 22:07:02
+2011-02-07 22:37:03
+2011-02-07 23:07:03
+2011-02-07 23:37:02
+2011-02-08 00:07:02
+2011-02-08 00:37:02
+2011-02-08 01:07:03
+2011-02-08 01:37:03
+2011-02-08 02:07:02
+2011-02-08 02:37:03
+2011-02-08 03:07:03
+2011-02-08 03:37:03
+2011-02-08 04:07:02
+2011-02-08 04:37:03
+2011-02-08 05:07:02
+2011-02-08 05:37:03
+2011-02-08 06:07:03
+2011-02-08 06:37:02
+2011-02-08 07:07:03
+2011-02-08 07:37:03
+2011-02-08 08:07:02
+2011-02-08 08:37:03
+2011-02-08 09:07:03
+2011-02-08 09:37:02
+2011-02-08 10:07:02
+2011-02-08 10:37:03
+2011-02-08 11:07:02
+2011-02-08 11:37:02
+2011-02-08 12:07:02
+2011-02-08 12:37:02
+2011-02-08 13:07:03
+2011-02-08 13:37:02
+2011-02-08 14:07:03
+2011-02-08 14:37:02
+2011-02-08 15:07:03
+2011-02-08 15:37:02
+2011-02-08 16:07:03
+2011-02-08 16:37:03
+2011-02-08 17:07:03
+2011-02-08 17:37:03
+2011-02-08 18:07:03
+2011-02-08 18:37:02
+2011-02-08 19:07:03
+2011-02-08 19:37:03
+2011-02-08 20:07:03
+2011-02-08 20:37:03
+2011-02-08 21:07:03
+2011-02-08 21:37:03
+2011-02-08 22:07:03
+2011-02-08 22:37:05
+2011-02-08 23:07:03
+2011-02-08 23:37:02
+2011-02-09 00:07:02
+2011-02-09 00:37:02
+2011-02-09 01:07:02
+2011-02-09 01:37:03
+2011-02-09 02:07:02
+2011-02-09 02:37:03
+2011-02-09 03:07:03
+2011-02-09 03:37:03
+2011-02-09 04:07:03
+2011-02-09 04:37:03
+2011-02-09 05:07:02
+2011-02-09 05:37:03
+2011-02-09 06:07:03
+2011-02-09 06:37:03
+2011-02-09 07:07:03
+2011-02-09 07:37:03
+2011-02-09 08:07:02
+2011-02-09 08:37:02
+2011-02-09 09:07:03
+2011-02-09 09:37:03
+2011-02-09 10:07:03
+2011-02-09 10:37:02
+2011-02-09 11:07:02
+2011-02-09 11:37:03
+2011-02-09 12:07:03
+2011-02-09 12:37:03
+2011-02-09 13:07:02
+2011-02-09 13:37:02
+2011-02-09 14:07:03
+2011-02-09 14:37:02
+2011-02-09 15:07:03
+2011-02-09 15:37:02
+2011-02-09 16:07:03
+2011-02-09 16:37:02
+2011-02-09 17:07:02
+2011-02-09 17:37:03
+2011-02-09 18:07:03
+2011-02-09 18:37:03
+2011-02-09 19:07:03
+2011-02-09 19:37:03
+2011-02-09 20:07:03
+2011-02-09 20:37:02
+2011-02-09 21:07:02
+2011-02-09 21:37:03
+2011-02-09 22:07:03
+2011-02-09 22:37:03
+2011-02-09 23:07:02
+2011-02-09 23:37:03
+2011-02-10 00:07:03
+2011-02-10 00:37:03
+2011-02-10 01:07:02
+2011-02-10 01:37:02
+2011-02-10 02:07:03
+2011-02-10 02:37:02
+2011-02-10 03:07:02
+2011-02-10 03:37:03
+2011-02-10 04:07:03
+2011-02-10 04:37:02
+2011-02-10 05:07:03
+2011-02-10 05:37:03
+2011-02-10 06:07:03
+2011-02-10 06:37:02
+2011-02-10 07:07:02
+2011-02-10 07:37:03
+2011-02-10 08:07:03
+2011-02-10 08:37:03
+2011-02-10 09:07:02
+2011-02-10 09:37:02
+2011-02-10 10:07:02
+2011-02-10 10:37:03
+2011-02-10 11:07:03
+2011-02-10 11:37:03
+2011-02-10 12:07:03
+2011-02-10 12:37:02
+2011-02-10 13:07:03
+2011-02-10 13:37:02
+2011-02-10 14:07:03
+2011-02-10 14:37:03
+2011-02-10 15:07:02
+2011-02-10 15:37:03
+2011-02-10 16:07:03
+2011-02-10 16:37:03
+2011-02-10 17:07:02
+2011-02-10 17:37:03
+2011-02-10 18:07:02
+2011-02-10 18:37:03
+2011-02-10 19:07:02
+2011-02-10 19:37:02
+2011-02-10 20:07:03
+2011-02-10 20:37:03
+2011-02-10 21:07:03
+2011-02-10 21:37:03
+2011-02-10 22:07:03
+2011-02-10 22:37:03
+2011-02-10 23:07:03
+2011-02-10 23:37:05
+2011-02-11 00:07:03
+2011-02-11 00:37:03
+2011-02-11 01:07:03
+2011-02-11 01:37:03
+2011-02-11 02:07:03
+2011-02-11 02:37:02
+2011-02-11 03:07:02
+2011-02-11 03:37:03
+2011-02-11 04:07:03
+2011-02-11 04:37:03
+2011-02-11 05:07:02
+2011-02-11 05:37:02
+2011-02-11 06:07:03
+2011-02-11 06:37:03
+2011-02-11 07:07:03
+2011-02-11 07:37:03
+2011-02-11 08:07:03
+2011-02-11 08:37:03
+2011-02-11 09:07:03
+2011-02-11 09:37:03
+2011-02-11 10:07:03
+2011-02-11 10:37:02
+2011-02-11 11:07:02
+2011-02-11 11:37:03
+2011-02-11 12:07:03
+2011-02-11 12:37:03
+2011-02-11 13:07:03
+2011-02-11 13:37:02
+2011-02-11 14:07:03
+2011-02-11 14:37:03
+2011-02-11 15:07:03
+2011-02-11 15:37:04
+2011-02-11 16:07:03
+2011-02-11 16:37:03
+2011-02-11 17:07:03
+2011-02-11 17:37:03
+2011-02-11 18:07:03
+2011-02-11 18:37:03
+2011-02-11 19:07:03
+2011-02-11 19:37:03
+2011-02-11 20:07:03
+2011-02-11 20:37:03
+2011-02-11 21:07:03
+2011-02-11 21:37:03
+2011-02-11 22:07:02
+2011-02-11 22:37:02
+2011-02-11 23:07:02
+2011-02-11 23:37:03
+2011-02-12 00:07:02
+2011-02-12 00:37:02
+2011-02-12 01:07:03
+2011-02-12 01:37:03
+2011-02-12 02:07:02
+2011-02-12 02:37:02
+2011-02-12 03:07:02
+2011-02-12 03:37:03
+2011-02-12 04:07:02
+2011-02-12 04:37:03
+2011-02-12 05:07:02
+2011-02-12 05:37:02
+2011-02-12 06:07:03
+2011-02-12 06:37:03
+2011-02-12 07:07:02
+2011-02-12 07:37:03
+2011-02-12 08:07:03
+2011-02-12 08:37:03
+2011-02-12 09:07:03
+2011-02-12 09:37:03
+2011-02-12 10:07:02
+2011-02-12 10:37:03
+2011-02-12 11:07:03
+2011-02-12 11:37:03
+2011-02-12 12:07:03
+2011-02-12 12:37:03
+2011-02-12 13:07:03
+2011-02-12 13:37:03
+2011-02-12 14:07:03
+2011-02-12 14:37:02
+2011-02-12 15:07:02
+2011-02-12 15:37:03
+2011-02-12 16:07:03
+2011-02-12 16:37:03
+2011-02-12 17:07:03
+2011-02-12 17:37:03
+2011-02-12 18:07:03
+2011-02-12 18:37:02
+2011-02-12 19:07:02
+2011-02-12 19:37:03
+2011-02-12 20:07:03
+2011-02-12 20:37:03
+2011-02-12 21:07:03
+2011-02-12 21:37:03
+2011-02-12 22:07:03
+2011-02-12 22:37:03
+2011-02-12 23:07:03
+2011-02-12 23:37:03
+2011-02-13 00:07:02
+2011-02-13 00:37:03
+2011-02-13 01:07:03
+2011-02-13 01:37:02
+2011-02-13 02:07:02
+2011-02-13 02:37:03
+2011-02-13 03:07:02
+2011-02-13 03:37:03
+2011-02-13 04:07:03
+2011-02-13 04:37:03
+2011-02-13 05:07:03
+2011-02-13 05:37:03
+2011-02-13 06:07:03
+2011-02-13 06:37:03
+2011-02-13 07:07:02
+2011-02-13 07:37:02
+2011-02-13 08:07:03
+2011-02-13 08:37:03
+2011-02-13 09:07:03
+2011-02-13 09:37:03
+2011-02-13 10:07:03
+2011-02-13 10:37:02
+2011-02-13 11:07:03
+2011-02-13 11:37:03
+2011-02-13 12:07:03
+2011-02-13 12:37:02
+2011-02-13 13:07:02
+2011-02-13 13:37:03
+2011-02-13 14:07:03
+2011-02-13 14:37:02
+2011-02-13 15:07:02
+2011-02-13 15:37:03
+2011-02-13 16:07:04
+2011-02-13 16:37:03
+2011-02-13 17:07:03
+2011-02-13 17:37:03
+2011-02-13 18:07:02
+2011-02-13 18:37:03
+2011-02-13 19:07:03
+2011-02-13 19:37:03
+2011-02-13 20:07:03
+2011-02-13 20:37:03
+2011-02-13 21:07:03
+2011-02-13 21:37:02
+2011-02-13 22:07:03
+2011-02-13 22:37:03
+2011-02-13 23:07:02
+2011-02-13 23:37:02
+2011-02-14 00:07:03
+2011-02-14 00:37:02
+2011-02-14 01:07:03
+2011-02-14 01:37:02
+2011-02-14 02:07:03
+2011-02-14 02:37:03
+2011-02-14 03:07:03
+2011-02-14 03:37:03
+2011-02-14 04:07:02
+2011-02-14 04:37:02
+2011-02-14 05:07:02
+2011-02-14 05:37:03
+2011-02-14 06:07:03
+2011-02-14 06:37:02
+2011-02-14 07:07:02
+2011-02-14 07:37:02
+2011-02-14 08:07:02
+2011-02-14 08:37:03
+2011-02-14 09:07:02
+2011-02-14 09:37:02
+2011-02-14 10:07:03
+2011-02-14 10:37:03
+2011-02-14 11:07:03
+2011-02-14 11:37:02
+2011-02-14 12:07:03
+2011-02-14 12:37:03
+2011-02-14 13:07:03
+2011-02-14 13:37:02
+2011-02-14 14:07:02
+2011-02-14 14:37:03
+2011-02-14 15:07:03
+2011-02-14 15:37:02
+2011-02-14 16:07:02
+2011-02-14 16:37:03
+2011-02-14 17:07:02
+2011-02-14 17:37:03
+2011-02-14 18:07:03
+2011-02-14 18:37:03
+2011-02-14 19:07:02
+2011-02-14 19:37:03
+2011-02-14 20:07:03
+2011-02-14 20:37:03
+2011-02-14 21:07:03
+2011-02-14 21:37:03
+2011-02-14 22:07:03
+2011-02-14 22:37:03
+2011-02-14 23:07:03
+2011-02-14 23:37:02
+2011-02-15 00:07:03
+2011-02-15 00:37:02
+2011-02-15 01:07:03
+2011-02-15 01:37:03
+2011-02-15 02:07:03
+2011-02-15 02:37:03
+2011-02-15 03:07:03
+2011-02-15 03:37:02
+2011-02-15 04:07:03
+2011-02-15 04:37:03
+2011-02-15 05:07:02
+2011-02-15 05:37:02
+2011-02-15 06:07:02
+2011-02-15 06:37:02
+2011-02-15 07:07:03
+2011-02-15 07:37:02
+2011-02-15 08:07:03
+2011-02-15 08:37:02
+2011-02-15 09:07:02
+2011-02-15 09:37:03
+2011-02-15 10:07:02
+2011-02-15 10:37:02
+2011-02-15 11:07:03
+2011-02-15 11:37:03
+2011-02-15 12:07:03
+2011-02-15 12:37:02
+2011-02-15 13:07:02
+2011-02-15 13:37:02
+2011-02-15 14:07:03
+2011-02-15 14:37:02
+2011-02-15 15:07:02
+2011-02-15 15:37:02
+2011-02-15 16:07:02
+2011-02-15 16:37:02
+2011-02-15 17:07:03
+2011-02-15 17:37:03
+2011-02-15 18:07:03
+2011-02-15 18:37:03
+2011-02-15 19:07:03
+2011-02-15 19:37:02
+2011-02-15 20:07:03
+2011-02-15 20:37:03
+2011-02-15 21:07:03
+2011-02-15 21:37:03
+2011-02-15 22:07:03
+2011-02-15 22:37:03
+2011-02-15 23:07:02
+2011-02-15 23:37:02
+2011-02-16 00:07:03
+2011-02-16 00:37:02
+2011-02-16 01:07:04
+2011-02-16 01:37:02
1
0
30 Oct '11
commit 46296b3fa45c9e78bf0af944a52e649f8e27c238
Author: Roger Dingledine <arma(a)torproject.org>
Date: Sun Oct 30 01:58:05 2011 -0400
there will be a new alpha release today (oct 30)
---
ChangeLog | 20 +++++++++++++-------
changes/bug4348 | 7 -------
2 files changed, 13 insertions(+), 14 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index ac23606..5198350 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,19 +1,25 @@
-Changes in version 0.2.3.7-alpha - 2011-10-??
- o Major bugfix:
+Changes in version 0.2.3.7-alpha - 2011-10-30
+ o Major bugfixes:
- If we mark an OR connection for close based on a cell we process,
- don't process any further cells on it. We already avoided reading
- on marked-for-close connections, but now we also discard the cells
- we'd already read. Fixes bug 4299; bugfix on 0.2.0.10-alpha, which
- was the first version where we might mark a connection for close
- based on processing a cell on it.
+ don't process any further cells on it. We already avoid further
+ reads on marked-for-close connections, but now we also discard the
+ cells we'd already read. Fixes bug 4299; bugfix on 0.2.0.10-alpha,
+ which was the first version where we might mark a connection for
+ close based on processing a cell on it.
- Fix a double-free bug that would occur when we received an invalid
certificate in a CERT cell in the new v3 handshake. Fixes bug 4343;
bugfix on 0.2.3.6-alpha.
+ - Bridges no longer include their address in NETINFO cells on outgoing
+ OR connections, to allow them to blend in better with clients.
+ Removes another avenue for enumerating bridges. Reported by
+ "troll_un". Fixes bug 4348; bugfix on 0.2.0.10-alpha, when NETINFO
+ cells were introduced.
o Trivial fixes:
- Fixed a typo in a hibernation-related log message. Fixes bug 4331;
bugfix on 0.2.2.23-alpha; found by "tmpname0901".
+
Changes in version 0.2.3.6-alpha - 2011-10-26
Tor 0.2.3.6-alpha includes the fix from 0.2.2.34 for a critical
anonymity vulnerability where an attacker can deanonymize Tor
diff --git a/changes/bug4348 b/changes/bug4348
deleted file mode 100644
index d2b226d..0000000
--- a/changes/bug4348
+++ /dev/null
@@ -1,7 +0,0 @@
- Privacy fixes:
- - Bridges no longer include their address in NETINFO cells on outgoing
- OR connections, to allow them to blend in better with clients.
- Removes another avenue for enumerating bridges. Reported by
- "troll_un". Fixes bug 4348; bugfix on 0.2.0.10-alpha, when NETINFO
- cells were introduced.
-
1
0
[tor/master] bridges send netinfo cells like clients on outgoing conns
by arma@torproject.org 30 Oct '11
by arma@torproject.org 30 Oct '11
30 Oct '11
commit eeb6588389397d855512c1973a9d139b55339af6
Author: Roger Dingledine <arma(a)torproject.org>
Date: Sat Oct 29 21:43:23 2011 -0400
bridges send netinfo cells like clients on outgoing conns
fixes bug 4348
---
changes/bug4348 | 7 +++++++
src/or/connection_or.c | 7 +++++--
2 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/changes/bug4348 b/changes/bug4348
new file mode 100644
index 0000000..d2b226d
--- /dev/null
+++ b/changes/bug4348
@@ -0,0 +1,7 @@
+ Privacy fixes:
+ - Bridges no longer include their address in NETINFO cells on outgoing
+ OR connections, to allow them to blend in better with clients.
+ Removes another avenue for enumerating bridges. Reported by
+ "troll_un". Fixes bug 4348; bugfix on 0.2.0.10-alpha, when NETINFO
+ cells were introduced.
+
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index 4c0960c..202548a 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -1928,8 +1928,11 @@ connection_or_send_netinfo(or_connection_t *conn)
return -1;
out += len;
- /* My address. */
- if ((me = router_get_my_routerinfo())) {
+ /* My address -- only include it if I'm a public relay, or if I'm a
+ * bridge and this is an incoming connection. If I'm a bridge and this
+ * is an outgoing connection, act like a normal client and omit it. */
+ if ((public_server_mode(get_options()) || !conn->is_outgoing) &&
+ (me = router_get_my_routerinfo())) {
tor_addr_t my_addr;
*out++ = 1; /* only one address is supported. */
1
0