├── .bumpversion.cfg ├── .flake8 ├── .github └── workflows │ ├── cancel_prev_run_all_steps.yml │ ├── ci.yml │ ├── parse_and_plot.yml │ ├── parse_and_plot_input │ ├── README │ ├── free_rusage.json.xz │ ├── oniontrace.analysis.json.xz │ ├── shadow_rusage.json.xz │ ├── tgen.analysis.json.xz │ ├── tornettools.archive.2022-02-08.20.47.46.log.xz │ ├── tornettools.generate.2022-02-08.20.40.27.log.xz │ ├── tornettools.parse.2022-02-08.20.47.30.log.xz │ └── tornettools.simulate.2022-02-08.20.40.33.log.xz │ ├── parse_and_plot_output │ └── tornet.plot.data │ │ ├── error_rate.exit.json │ │ ├── error_rate.onionservice.json │ │ ├── perfclient_circuit_build_time.exit.json │ │ ├── perfclient_circuit_build_time.onionservice.json │ │ ├── perfclient_goodput.exit.json │ │ ├── perfclient_goodput.onionservice.json │ │ ├── perfclient_goodput_5MiB.exit.json │ │ ├── perfclient_goodput_5MiB.onionservice.json │ │ ├── relay_goodput.json │ │ ├── resource_usage.json │ │ ├── round_trip_time.exit.json │ │ ├── round_trip_time.onionservice.json │ │ ├── simulation_info.json │ │ ├── time_to_first_byte_recv.exit.json │ │ ├── time_to_first_byte_recv.onionservice.json │ │ ├── time_to_last_byte_recv.exit.json │ │ └── time_to_last_byte_recv.onionservice.json │ ├── run_all_steps.yml │ └── run_all_steps_output │ ├── README │ └── tornet.plot.data │ ├── error_rate.exit.json │ ├── error_rate.onionservice.json │ ├── perfclient_circuit_build_time.exit.json │ ├── perfclient_circuit_build_time.onionservice.json │ ├── perfclient_goodput.exit.json │ ├── perfclient_goodput.onionservice.json │ ├── perfclient_goodput_5MiB.exit.json │ ├── perfclient_goodput_5MiB.onionservice.json │ ├── relay_goodput.json │ ├── resource_usage.json │ ├── round_trip_time.exit.json │ ├── round_trip_time.onionservice.json │ ├── simulation_info.json │ ├── time_to_first_byte_recv.exit.json │ ├── time_to_first_byte_recv.onionservice.json │ ├── time_to_last_byte_recv.exit.json │ └── time_to_last_byte_recv.onionservice.json ├── .gitignore ├── LICENSE ├── README.md ├── requirements.txt ├── setup.py ├── test └── run_integration_tests.sh └── tornettools ├── __init__.py ├── _version.py ├── archive.py ├── generate.py ├── generate_defaults.py ├── generate_tgen.py ├── generate_tor.py ├── parse.py ├── parse_onionperf.py ├── parse_oniontrace.py ├── parse_rusage.py ├── parse_tgen.py ├── plot.py ├── plot_common.py ├── plot_oniontrace.py ├── plot_tgen.py ├── simulate.py ├── stage.py ├── tornettools ├── util.py └── util_geoip.py /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 2.0.0 3 | commit = False 4 | tag = False 5 | 6 | [bumpversion:file:setup.py] 7 | 8 | [bumpversion:file:tornettools/_version.py] 9 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = 3 | # expected 2 blank lines, found 1. 4 | E302, 5 | # line too long. 6 | E501, 7 | # at least two spaces before inline comment. 8 | E261, 9 | # block comment should start with '# ' 10 | E265, 11 | # multiple statements on one line 12 | E704, 13 | # ambiguous variable name 14 | E741, 15 | # line break before binary operator 16 | W503, 17 | -------------------------------------------------------------------------------- /.github/workflows/cancel_prev_run_all_steps.yml: -------------------------------------------------------------------------------- 1 | # Syntax reference: 2 | # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions 3 | # 4 | # Since the run_all_steps workflow is relatively expensive, only allow one 5 | # instance to run at once per branch, canceling obsolete runs. 6 | # 7 | # Based on https://github.com/styfle/cancel-workflow-action#advanced-pull-requests-from-forks 8 | # 9 | # TODO: should we replace this with 'cancel-in-progress'? 10 | # https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/control-the-concurrency-of-workflows-and-jobs 11 | name: Cancel previous run-all-steps 12 | permissions: read-all 13 | on: 14 | workflow_run: 15 | workflows: ["Run all steps"] 16 | types: 17 | - requested 18 | jobs: 19 | cancel: 20 | runs-on: ubuntu-latest 21 | permissions: 22 | actions: write 23 | steps: 24 | # Pin to the current sha of tag 0.9.1. Being a bit extra careful here 25 | # since this job gets elevated permission. 26 | - uses: styfle/cancel-workflow-action@a40b8845c0683271d9f53dfcb887a7e181d3918b 27 | with: 28 | workflow_id: ${{ github.event.workflow.id }} 29 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # Syntax reference: 2 | # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions 3 | 4 | name: Build 5 | permissions: read-all 6 | 7 | on: 8 | push: 9 | paths-ignore: 10 | - '**.md' 11 | - 'LICENSE' 12 | pull_request: 13 | types: [opened, synchronize] 14 | 15 | jobs: 16 | building: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v4 21 | with: 22 | persist-credentials: false 23 | 24 | - name: Update packages 25 | run: sudo apt-get update 26 | 27 | - name: Install tornettools dependencies 28 | run: sudo apt-get install -y 29 | python3 30 | python3-dev 31 | python3-pip 32 | libxml2 33 | libxml2-dev 34 | libxslt1.1 35 | libxslt1-dev 36 | libpng16-16 37 | libpng-dev 38 | libfreetype6 39 | libfreetype6-dev 40 | libblas-dev 41 | liblapack-dev 42 | 43 | - name: Build tornettools 44 | run: | 45 | mkdir build 46 | python3 -m venv build/tornettoolsenv 47 | source build/tornettoolsenv/bin/activate 48 | pip3 install wheel 49 | pip3 install -r requirements.txt 50 | pip3 install -I . 51 | 52 | lint: 53 | runs-on: ubuntu-latest 54 | steps: 55 | - name: Checkout 56 | uses: actions/checkout@v4 57 | with: 58 | persist-credentials: false 59 | 60 | - name: Update packages 61 | run: sudo apt-get update 62 | 63 | - name: Install tornettools dependencies 64 | run: sudo apt-get install -y 65 | python3 66 | flake8 67 | 68 | - name: Run flake8 69 | run: flake8 tornettools 70 | -------------------------------------------------------------------------------- /.github/workflows/parse_and_plot.yml: -------------------------------------------------------------------------------- 1 | # Syntax reference: 2 | # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions 3 | 4 | name: Parse and plot 5 | permissions: read-all 6 | 7 | on: 8 | push: 9 | paths-ignore: 10 | - '**.md' 11 | - 'LICENSE' 12 | pull_request: 13 | types: [opened, synchronize] 14 | 15 | env: 16 | DEBIAN_FRONTEND: noninteractive 17 | 18 | jobs: 19 | parse-and-plot: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: Checkout 23 | uses: actions/checkout@v4 24 | with: 25 | persist-credentials: false 26 | 27 | - name: Update packages 28 | run: sudo apt-get update 29 | 30 | - name: Install tornettools dependencies 31 | run: sudo apt-get install -y 32 | python3 33 | python3-dev 34 | python3-pip 35 | libxml2 36 | libxml2-dev 37 | libxslt1.1 38 | libxslt1-dev 39 | libpng16-16 40 | libpng-dev 41 | libfreetype6 42 | libfreetype6-dev 43 | libblas-dev 44 | liblapack-dev 45 | 46 | - name: Build tornettools 47 | run: | 48 | pip3 install wheel 49 | pip3 install -r requirements.txt 50 | pip3 install -I . 51 | 52 | - name: Create output/working directory 53 | run: mv .github/workflows/parse_and_plot_input parse_and_plot_output 54 | 55 | - name: Re-parse 56 | # -c must match the convergence time used to generate the golden data, 57 | # in run_all_steps.yml 58 | # 59 | # We use --skip-raw to skip trying to parse the raw logs again, so that 60 | # we don't need to check the raw logs into version control, and since 61 | # the tgen and oniontrace parsing are done by those respective tools, 62 | # which we're not trying to test here. 63 | run: tornettools parse -c 300 --skip-raw parse_and_plot_output 64 | 65 | # The json files in tornet.plot.data are the fully processed data used to 66 | # generate graphs in the `plot` step. They should always be the same when 67 | # parsing and plotting the same simulation data with a given version of 68 | # tornettools. 69 | # 70 | # When this test fails due to an *intentional* change in processing, the 71 | # golden data in `.github/workflows/parse_and_plot_output` can be updated 72 | # using the actual output from this workflow's artifacts. 73 | - name: Diff 74 | run: | 75 | for f in .github/workflows/parse_and_plot_output/tornet.plot.data/*.json 76 | do 77 | echo "Diffing $f" 78 | diff $f parse_and_plot_output/tornet.plot.data/$(basename $f) 79 | done 80 | 81 | - name: Plot 82 | # Run even if the diff step failed. In some cases the diff may be 83 | # expected, in which case it'll still be helpful to compare the plots. 84 | if: always() 85 | # Plot reparsed against golden 86 | run: tornettools plot --prefix pdfs -l golden reparsed -- .github/workflows/parse_and_plot_output parse_and_plot_output 87 | 88 | - name: Upload pdfs 89 | if: always() 90 | uses: actions/upload-artifact@v4 91 | with: 92 | name: pdfs 93 | path: pdfs 94 | 95 | # This artifact can be used to update 96 | # .github/workflows/parse_and_plot_output to a new expected output. 97 | - name: Upload plot data 98 | if: always() 99 | uses: actions/upload-artifact@v4 100 | with: 101 | name: parse_and_plot_output 102 | path: parse_and_plot_output/tornet.plot.data 103 | -------------------------------------------------------------------------------- /.github/workflows/parse_and_plot_input/README: -------------------------------------------------------------------------------- 1 | Generated by run_all_steps workflow. To update, unpack the 2 | artifacts from a more recent run of this workflow into this 3 | directory. 4 | env: 5 | GITHUB_REF: refs/pull/59/merge 6 | GITHUB_SHA: 3c5697bc2cbefd0c20d83feafa79114d6f0c1f7a 7 | SHADOW_COMMIT: 9eef66b4da7c78c31fa7d93c2982311a55f0abd4 8 | TOR_REPO: https://git.torproject.org/tor.git 9 | TOR_BRANCH: release-0.4.6 10 | TOR_COMMIT: f728e09ebe611d6858e721eaa37637025bfbf259 11 | TGEN_COMMIT: 8cec86f46c8ca719ff9c023e381363098b99586d 12 | ONIONTRACE_COMMIT: bc26be3c4737a8a367a156f12bab2975cd811855 13 | NETDATA_MONTH: 2020-11 14 | NETDATA_LAST_DAY: 30 15 | nproc: 2 16 | free: 17 | total used free shared buff/cache available 18 | Mem: 7114112 649028 3173900 9528 3291184 6145432 19 | Swap: 4194300 524 4193776 20 | -------------------------------------------------------------------------------- /.github/workflows/parse_and_plot_input/free_rusage.json.xz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadow/tornettools/df6ada5e74c1eda22899610e4d1bed13a37878eb/.github/workflows/parse_and_plot_input/free_rusage.json.xz -------------------------------------------------------------------------------- /.github/workflows/parse_and_plot_input/oniontrace.analysis.json.xz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadow/tornettools/df6ada5e74c1eda22899610e4d1bed13a37878eb/.github/workflows/parse_and_plot_input/oniontrace.analysis.json.xz -------------------------------------------------------------------------------- /.github/workflows/parse_and_plot_input/shadow_rusage.json.xz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadow/tornettools/df6ada5e74c1eda22899610e4d1bed13a37878eb/.github/workflows/parse_and_plot_input/shadow_rusage.json.xz -------------------------------------------------------------------------------- /.github/workflows/parse_and_plot_input/tgen.analysis.json.xz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadow/tornettools/df6ada5e74c1eda22899610e4d1bed13a37878eb/.github/workflows/parse_and_plot_input/tgen.analysis.json.xz -------------------------------------------------------------------------------- /.github/workflows/parse_and_plot_input/tornettools.archive.2022-02-08.20.47.46.log.xz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadow/tornettools/df6ada5e74c1eda22899610e4d1bed13a37878eb/.github/workflows/parse_and_plot_input/tornettools.archive.2022-02-08.20.47.46.log.xz -------------------------------------------------------------------------------- /.github/workflows/parse_and_plot_input/tornettools.generate.2022-02-08.20.40.27.log.xz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadow/tornettools/df6ada5e74c1eda22899610e4d1bed13a37878eb/.github/workflows/parse_and_plot_input/tornettools.generate.2022-02-08.20.40.27.log.xz -------------------------------------------------------------------------------- /.github/workflows/parse_and_plot_input/tornettools.parse.2022-02-08.20.47.30.log.xz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadow/tornettools/df6ada5e74c1eda22899610e4d1bed13a37878eb/.github/workflows/parse_and_plot_input/tornettools.parse.2022-02-08.20.47.30.log.xz -------------------------------------------------------------------------------- /.github/workflows/parse_and_plot_input/tornettools.simulate.2022-02-08.20.40.33.log.xz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadow/tornettools/df6ada5e74c1eda22899610e4d1bed13a37878eb/.github/workflows/parse_and_plot_input/tornettools.simulate.2022-02-08.20.40.33.log.xz -------------------------------------------------------------------------------- /.github/workflows/parse_and_plot_output/tornet.plot.data/error_rate.exit.json: -------------------------------------------------------------------------------- 1 | { 2 | "ALL": [ 3 | [ 4 | 0.0, 5 | 11.11111111111111 6 | ], 7 | [ 8 | 0.0, 9 | 11.11111111111111 10 | ], 11 | [ 12 | 0.0, 13 | 11.11111111111111 14 | ], 15 | [ 16 | 0.0, 17 | 11.11111111111111 18 | ], 19 | [ 20 | 0.0, 21 | 11.11111111111111 22 | ], 23 | [ 24 | 0.0, 25 | 11.11111111111111 26 | ], 27 | [ 28 | 11.11111111111111, 29 | 11.11111111111111 30 | ], 31 | [ 32 | 11.11111111111111, 33 | 11.11111111111111 34 | ], 35 | [ 36 | 0.0, 37 | 11.11111111111111 38 | ], 39 | [ 40 | 0.0, 41 | 11.11111111111111 42 | ], 43 | [ 44 | 0.0, 45 | 11.11111111111111 46 | ], 47 | [ 48 | 0.0, 49 | 11.11111111111111 50 | ], 51 | [ 52 | 11.11111111111111, 53 | 11.11111111111111 54 | ], 55 | [ 56 | 11.11111111111111, 57 | 11.11111111111111 58 | ], 59 | [ 60 | 0.0, 61 | 11.11111111111111 62 | ], 63 | [ 64 | 0.0, 65 | 11.11111111111111 66 | ] 67 | ], 68 | "TIMEOUT": [ 69 | [ 70 | 11.11111111111111, 71 | 11.11111111111111 72 | ], 73 | [ 74 | 11.11111111111111, 75 | 11.11111111111111 76 | ] 77 | ] 78 | } -------------------------------------------------------------------------------- /.github/workflows/parse_and_plot_output/tornet.plot.data/error_rate.onionservice.json: -------------------------------------------------------------------------------- 1 | { 2 | "ALL": [ 3 | [ 4 | 77.77777777777777, 5 | 11.11111111111111 6 | ], 7 | [ 8 | 77.77777777777777, 9 | 11.11111111111111 10 | ], 11 | [ 12 | 88.88888888888889, 13 | 11.11111111111111 14 | ], 15 | [ 16 | 88.88888888888889, 17 | 11.11111111111111 18 | ] 19 | ], 20 | "TIMEOUT": [ 21 | [ 22 | 77.77777777777777, 23 | 11.11111111111111 24 | ], 25 | [ 26 | 88.88888888888889, 27 | 11.11111111111111 28 | ] 29 | ] 30 | } -------------------------------------------------------------------------------- /.github/workflows/parse_and_plot_output/tornet.plot.data/perfclient_circuit_build_time.exit.json: -------------------------------------------------------------------------------- 1 | [ 2 | 0.658302, 3 | 0.666712, 4 | 0.488289, 5 | 0.46296, 6 | 0.274498, 7 | 0.403133, 8 | 0.400468, 9 | 0.396532, 10 | 0.377199, 11 | 0.685712, 12 | 0.655756, 13 | 0.385033, 14 | 0.667963, 15 | 0.406712, 16 | 0.39349, 17 | 0.886241, 18 | 0.399917, 19 | 0.416624, 20 | 0.393345, 21 | 0.477006, 22 | 0.420462, 23 | 0.411872, 24 | 0.411533, 25 | 0.404747, 26 | 0.419821, 27 | 0.413429, 28 | 0.389048, 29 | 0.44546, 30 | 0.404713, 31 | 0.67851, 32 | 0.415507, 33 | 0.389671, 34 | 0.390483, 35 | 0.377577, 36 | 0.403018, 37 | 0.591, 38 | 0.952414, 39 | 0.983766, 40 | 0.99371, 41 | 1.267346, 42 | 0.974079, 43 | 1.249117, 44 | 1.251729, 45 | 2.897577, 46 | 0.975673, 47 | 1.113577, 48 | 0.94594, 49 | 0.954342, 50 | 0.95471, 51 | 0.972253, 52 | 1.235598, 53 | 1.233337, 54 | 1.246971, 55 | 1.331002, 56 | 0.991603, 57 | 0.973309, 58 | 1.223825, 59 | 0.980122, 60 | 0.984962, 61 | 1.251823, 62 | 0.997, 63 | 1.24671, 64 | 0.981237, 65 | 1.237242, 66 | 0.991887, 67 | 0.98844, 68 | 1.258665, 69 | 0.983561, 70 | 1.238868, 71 | 0.962, 72 | 0.364773, 73 | 0.383603, 74 | 0.392395, 75 | 0.671191, 76 | 0.822608, 77 | 0.654486, 78 | 0.393964, 79 | 0.38772, 80 | 0.349571, 81 | 0.348603, 82 | 0.398335, 83 | 0.397891, 84 | 0.377029, 85 | 0.659866, 86 | 0.365988, 87 | 0.691098, 88 | 0.382221, 89 | 0.641134, 90 | 0.366204, 91 | 0.663615, 92 | 0.392951, 93 | 0.377598, 94 | 0.641666, 95 | 0.436909, 96 | 0.384, 97 | 0.595393, 98 | 0.070714, 99 | 0.384614, 100 | 0.62359, 101 | 0.375615, 102 | 0.383098, 103 | 0.407583, 104 | 0.370608, 105 | 0.380899, 106 | 0.377457, 107 | 0.393625, 108 | 0.385421, 109 | 0.861789, 110 | 0.84042, 111 | 0.90542, 112 | 1.788434, 113 | 0.871008, 114 | 0.846497, 115 | 0.837098, 116 | 0.74296, 117 | 0.886237, 118 | 1.131812, 119 | 0.85281, 120 | 1.10979, 121 | 0.835337, 122 | 0.841973, 123 | 0.85152, 124 | 0.82852, 125 | 0.849273, 126 | 1.256284, 127 | 0.833697, 128 | 0.839628, 129 | 0.821198, 130 | 0.860661, 131 | 0.828704, 132 | 0.952523, 133 | 0.868, 134 | 0.855604, 135 | 0.830697, 136 | 0.854219, 137 | 0.824388, 138 | 1.83537, 139 | 0.839, 140 | 0.231676, 141 | 0.826189, 142 | 0.870423, 143 | 1.11814, 144 | 0.666017, 145 | 0.636389, 146 | 0.929389, 147 | 1.094468, 148 | 1.159478, 149 | 0.646105, 150 | 0.648841, 151 | 0.924021, 152 | 0.653564, 153 | 0.666696, 154 | 0.640804, 155 | 0.647645, 156 | 0.664875, 157 | 0.666529, 158 | 0.664407, 159 | 0.923351, 160 | 0.930086, 161 | 0.666167, 162 | 0.659861, 163 | 0.702213, 164 | 0.922168, 165 | 0.670686, 166 | 0.64429, 167 | 0.640815, 168 | 0.94997, 169 | 0.736389, 170 | 0.654701, 171 | 0.667253, 172 | 0.934549, 173 | 0.654621, 174 | 0.924069, 175 | 0.668598, 176 | 0.666015, 177 | 0.931015, 178 | 0.667879, 179 | 0.136667, 180 | 0.854656, 181 | 0.604298, 182 | 0.884927, 183 | 0.608016, 184 | 1.255746, 185 | 0.886943, 186 | 0.598439, 187 | 0.600792, 188 | 0.599439, 189 | 0.594259, 190 | 0.571316, 191 | 0.578014, 192 | 0.882907, 193 | 0.607757, 194 | 0.576058, 195 | 0.93443, 196 | 0.620094, 197 | 0.606535, 198 | 0.622936, 199 | 0.871453, 200 | 0.62405, 201 | 0.882442, 202 | 0.585403, 203 | 0.61879, 204 | 0.613187, 205 | 0.582462, 206 | 0.579489, 207 | 0.887258, 208 | 0.88306, 209 | 0.601197, 210 | 0.588888, 211 | 0.872813, 212 | 0.611433, 213 | 0.585545, 214 | 0.61037, 215 | 0.614, 216 | 0.406996, 217 | 0.701566, 218 | 0.532, 219 | 0.46973, 220 | 0.703, 221 | 1.331896, 222 | 0.625562, 223 | 0.701781, 224 | 0.389595, 225 | 0.427421, 226 | 0.428714, 227 | 0.419255, 228 | 0.433846, 229 | 0.419657, 230 | 0.686082, 231 | 0.691876, 232 | 0.432267, 233 | 0.419194, 234 | 0.428579, 235 | 0.707438, 236 | 0.630162, 237 | 0.705738, 238 | 0.402439, 239 | 0.578519, 240 | 0.436791, 241 | 0.420482, 242 | 0.833616, 243 | 0.744535, 244 | 0.415715, 245 | 0.422076, 246 | 0.419079, 247 | 0.452, 248 | 0.386243, 249 | 0.437658, 250 | 0.403741, 251 | 0.405391, 252 | 0.609023, 253 | 0.626951, 254 | 0.327343, 255 | 0.348819, 256 | 0.634585, 257 | 0.334846, 258 | 0.341973, 259 | 0.333612, 260 | 0.355143, 261 | 0.539397, 262 | 0.312087, 263 | 0.356856, 264 | 0.355675, 265 | 0.620091, 266 | 0.355913, 267 | 0.349396, 268 | 0.63746, 269 | 0.328063, 270 | 0.323158, 271 | 0.336, 272 | 0.387847, 273 | 0.606284, 274 | 0.04776, 275 | 0.336149, 276 | 0.624131, 277 | 0.637936, 278 | 0.349215, 279 | 0.349849, 280 | 0.349681, 281 | 0.352, 282 | 0.62101, 283 | 0.346, 284 | 0.365462, 285 | 0.365583, 286 | 0.605326, 287 | 0.320622 288 | ] -------------------------------------------------------------------------------- /.github/workflows/parse_and_plot_output/tornet.plot.data/perfclient_circuit_build_time.onionservice.json: -------------------------------------------------------------------------------- 1 | [ 2 | 0.898649, 3 | 1.42717, 4 | 114.421077, 5 | 0.945596, 6 | 0.996, 7 | 116.443066, 8 | 0.89783, 9 | 117.421938, 10 | 1.19117, 11 | 11.002332, 12 | 128.556159, 13 | 1.183668, 14 | 129.772159, 15 | 1.742327, 16 | 1.69417, 17 | 48.882668, 18 | 59.49617, 19 | 0.911, 20 | 49.016385, 21 | 0.907215, 22 | 0.889362, 23 | 10.00081, 24 | 11.542415, 25 | 0.916479, 26 | 0.90317, 27 | 0.931664, 28 | 0.904914, 29 | 0.905729, 30 | 0.878044, 31 | 45.97902, 32 | 45.384801, 33 | 0.992515, 34 | 0.992515, 35 | 1.187232, 36 | 1.562655, 37 | 58.579785, 38 | 1.71625, 39 | 1.193672, 40 | 13.002272, 41 | 0.818965, 42 | 1.428279, 43 | 2.490306, 44 | 4.399202, 45 | 17.003542, 46 | 1.182344, 47 | 1.811797, 48 | 0.89769, 49 | 1.837159, 50 | 1.174677, 51 | 1.600768, 52 | 1.159843, 53 | 0.919113, 54 | 1.183558, 55 | 38.964784, 56 | 0.896622, 57 | 1.376038, 58 | 2.470025, 59 | 4.40417, 60 | 45.015695, 61 | 1.156423, 62 | 1.771883, 63 | 0.882618, 64 | 1.790673, 65 | 0.891421, 66 | 0.893412, 67 | 52.94883, 68 | 54.348955, 69 | 1.17617, 70 | 55.205942, 71 | 0.932737, 72 | 1.483357, 73 | 4.00217, 74 | 0.88477, 75 | 3.899015, 76 | 1.121198, 77 | 1.185165, 78 | 1.107276, 79 | 0.919768, 80 | 55.93583, 81 | 176.608343, 82 | 0.894206, 83 | 0.910913, 84 | 178.042672, 85 | 1.654782, 86 | 6.002176, 87 | 0.893, 88 | 1.389671, 89 | 2.737463, 90 | 0.880328, 91 | 3.899229, 92 | 66.604, 93 | 0.894172, 94 | 1.179723, 95 | 0.912002, 96 | 0.939713, 97 | 0.920747, 98 | 50.923367, 99 | 1.240787, 100 | 1.172098, 101 | 2.000895, 102 | 0.903275, 103 | 62.018541, 104 | 2.084275, 105 | 1.184063, 106 | 1.469803, 107 | 0.922232, 108 | 55.909253, 109 | 0.895747, 110 | 1.214057, 111 | 2.167794, 112 | 3.400605, 113 | 61.012697, 114 | 1.114213, 115 | 1.247167, 116 | 1.638453, 117 | 1.338869, 118 | 0.891174, 119 | 56.897909, 120 | 56.258761, 121 | 0.931736, 122 | 57.19219, 123 | 0.894434, 124 | 2.44917, 125 | 2.000434, 126 | 1.181, 127 | 0.899312, 128 | 1.292464, 129 | 2.397751, 130 | 0.877233, 131 | 1.478246, 132 | 0.880007, 133 | 1.17275, 134 | 0.223373, 135 | 1.035113, 136 | 0.972242, 137 | 0.951028, 138 | 110.687996, 139 | 1.258604, 140 | 1.379473, 141 | 113.948335, 142 | 1.000412, 143 | 1.245, 144 | 9.001763, 145 | 10.567091, 146 | 0.974044, 147 | 0.983, 148 | 48.558237, 149 | 0.96671, 150 | 1.137425, 151 | 172.847689, 152 | 1.220715, 153 | 1.265237, 154 | 10.001, 155 | 9.669814, 156 | 0.961227, 157 | 0.979773, 158 | 11.143874, 159 | 12.00285, 160 | 71.443588, 161 | 0.97615, 162 | 72.435869, 163 | 0.977415, 164 | 2.000565, 165 | 0.962592, 166 | 1.420758, 167 | 2.830019, 168 | 4.964331, 169 | 0.971373, 170 | 1.231712, 171 | 10.00277, 172 | 27.450734, 173 | 0.965847, 174 | 28.516609, 175 | 1.255083, 176 | 2.708304, 177 | 3.002424, 178 | 1.257821, 179 | 0.964548, 180 | 1.758298, 181 | 18.025143, 182 | 5.14872, 183 | 1.265885, 184 | 0.946301, 185 | 11.001886, 186 | 14.558152, 187 | 1.257637, 188 | 1.95829, 189 | 0.970754, 190 | 1.496612, 191 | 1.504913, 192 | 2.516287, 193 | 4.704921, 194 | 7.003284, 195 | 2.001247, 196 | 2.564779, 197 | 0.963301, 198 | 1.949705, 199 | 1.24919, 200 | 0.957631, 201 | 3.223012, 202 | 1.239872, 203 | 22.428057, 204 | 0.976718, 205 | 0.966058, 206 | 1.253187, 207 | 1.26643, 208 | 0.9723, 209 | 1.244279, 210 | 48.952809, 211 | 53.58344, 212 | 1.958666, 213 | 1.958666, 214 | 0.971421, 215 | 54.397482, 216 | 0.965573, 217 | 0.966077, 218 | 2.290609, 219 | 1.029542, 220 | 57.94429, 221 | 57.39204, 222 | 1.248347, 223 | 58.348336, 224 | 1.240487, 225 | 61.560421, 226 | 4.002062, 227 | 2.14145, 228 | 3.78325, 229 | 0.982585, 230 | 1.859677, 231 | 1.243757, 232 | 1.253592, 233 | 53.930261, 234 | 58.404651, 235 | 0.965201, 236 | 59.621179, 237 | 1.497126, 238 | 2.931306, 239 | 0.994981, 240 | 0.994981, 241 | 59.660323, 242 | 1.25642, 243 | 1.251563, 244 | 1.541752, 245 | 0.965192, 246 | 12.967712, 247 | 16.652884, 248 | 0.949249, 249 | 18.09602, 250 | 1.249983, 251 | 12.002478, 252 | 11.677612, 253 | 1.234233, 254 | 1.261417, 255 | 1.514417, 256 | 5.002423, 257 | 1.199996, 258 | 1.491789, 259 | 3.095664, 260 | 0.352763, 261 | 0.980233, 262 | 10.001767, 263 | 1.255002, 264 | 1.622973, 265 | 3.204283, 266 | 4.638587, 267 | 1.21643, 268 | 2.000595, 269 | 24.111427, 270 | 1.494542, 271 | 0.978254, 272 | 1.466383, 273 | 5.901527, 274 | 7.638805, 275 | 0.970287, 276 | 9.232591, 277 | 1.256237, 278 | 4.43194, 279 | 0.968303, 280 | 0.968303, 281 | 4.616201, 282 | 1.261717, 283 | 1.247859, 284 | 1.476, 285 | 0.971491, 286 | 0.959266, 287 | 0.98907, 288 | 0.974926, 289 | 54.897224, 290 | 0.97371, 291 | 1.508017, 292 | 2.700632, 293 | 2.186892, 294 | 7.003878, 295 | 0.972409, 296 | 1.397922, 297 | 2.672661, 298 | 0.963083, 299 | 1.547097, 300 | 0.969363, 301 | 0.976859, 302 | 49.878461, 303 | 1.217704, 304 | 1.751473, 305 | 3.026612, 306 | 1.245583, 307 | 118.672057, 308 | 0.956892, 309 | 2.001221, 310 | 1.238289, 311 | 0.971994, 312 | 1.460055, 313 | 1.243944, 314 | 2.845328, 315 | 0.947807, 316 | 1.566977, 317 | 1.240115, 318 | 0.961639, 319 | 1.000639, 320 | 188.694138, 321 | 1.239328, 322 | 0.93923, 323 | 7.003335, 324 | 1.252026, 325 | 1.245023, 326 | 2.079693, 327 | 1.498387, 328 | 4.556826, 329 | 0.978274, 330 | 5.807788, 331 | 0.97463, 332 | 1.24909, 333 | 4.000994, 334 | 0.957231, 335 | 1.790313, 336 | 0.951245, 337 | 2.418807, 338 | 0.941855, 339 | 3.000855, 340 | 0.986327, 341 | 1.695767, 342 | 2.766258, 343 | 4.001767, 344 | 12.846188, 345 | 1.336591, 346 | 1.185769, 347 | 0.962673, 348 | 1.681697, 349 | 4.001331, 350 | 3.568461, 351 | 0.966192, 352 | 4.781062, 353 | 0.974344 354 | ] -------------------------------------------------------------------------------- /.github/workflows/parse_and_plot_output/tornet.plot.data/perfclient_goodput.exit.json: -------------------------------------------------------------------------------- 1 | [ 2 | 2.7992027186882464, 3 | 3.8066416656515614, 4 | 2.9135828653214775, 5 | 9.3251708428246, 6 | 3.7715710833472293, 7 | 5.330403645833335, 8 | 6.416536050156736, 9 | 6.422334270438517, 10 | 5.9415820029027575, 11 | 6.396484374999999, 12 | 7.94902912621359, 13 | 4.867717003567181, 14 | 8.691613588110402, 15 | 10.46994884910486, 16 | 4.799975142724146, 17 | 10.15818858560794 18 | ] -------------------------------------------------------------------------------- /.github/workflows/parse_and_plot_output/tornet.plot.data/perfclient_goodput.onionservice.json: -------------------------------------------------------------------------------- 1 | [ 2 | 2.4660580059010697, 3 | 0.9377318559909589 4 | ] -------------------------------------------------------------------------------- /.github/workflows/parse_and_plot_output/tornet.plot.data/perfclient_goodput_5MiB.exit.json: -------------------------------------------------------------------------------- 1 | [ 2 | 6.938421509106675, 3 | 6.477732793522265, 4 | 6.890611541774329, 5 | 4.698759586203738 6 | ] -------------------------------------------------------------------------------- /.github/workflows/parse_and_plot_output/tornet.plot.data/perfclient_goodput_5MiB.onionservice.json: -------------------------------------------------------------------------------- 1 | [ 2 | 2.533382860968268 3 | ] -------------------------------------------------------------------------------- /.github/workflows/parse_and_plot_output/tornet.plot.data/relay_goodput.json: -------------------------------------------------------------------------------- 1 | { 2 | "300": 133847, 3 | "301": 278016, 4 | "302": 652345, 5 | "303": 801268, 6 | "304": 725316, 7 | "305": 511867, 8 | "306": 549370, 9 | "307": 970346, 10 | "308": 917951, 11 | "309": 1184691, 12 | "310": 507552, 13 | "311": 512904, 14 | "312": 1068546, 15 | "313": 1181534, 16 | "314": 2119766, 17 | "315": 1421306, 18 | "316": 2145334, 19 | "317": 2380902, 20 | "318": 1051001, 21 | "319": 1210242, 22 | "320": 1222764, 23 | "321": 2082967, 24 | "322": 261268, 25 | "323": 409198, 26 | "324": 739542, 27 | "325": 886736, 28 | "326": 1743420, 29 | "327": 2405630, 30 | "328": 2640390, 31 | "329": 2435422, 32 | "330": 2580682, 33 | "331": 3762866, 34 | "332": 3543348, 35 | "333": 3715303, 36 | "334": 4383870, 37 | "335": 5115032, 38 | "336": 5913664, 39 | "337": 4184178, 40 | "338": 4317020, 41 | "339": 3220434, 42 | "340": 4254424, 43 | "341": 4234848, 44 | "342": 2986878, 45 | "343": 5390333, 46 | "344": 4307540, 47 | "345": 4181442, 48 | "346": 4269462, 49 | "347": 3932962, 50 | "348": 5080610, 51 | "349": 3953466, 52 | "350": 2957880, 53 | "351": 2951408, 54 | "352": 3977772, 55 | "353": 3423550, 56 | "354": 5267676, 57 | "355": 4197115, 58 | "356": 4651531, 59 | "357": 4811064, 60 | "358": 4107778, 61 | "359": 3596111, 62 | "360": 4049154, 63 | "361": 4348158, 64 | "362": 7224932, 65 | "363": 5553041, 66 | "364": 4201092, 67 | "365": 5077150, 68 | "366": 4451430, 69 | "367": 6380350, 70 | "368": 5232982, 71 | "369": 3266447, 72 | "370": 3574700, 73 | "371": 4084436, 74 | "372": 2170136, 75 | "373": 3009780, 76 | "374": 5291246, 77 | "375": 5124033, 78 | "376": 5355367, 79 | "377": 4426475, 80 | "378": 4115967, 81 | "379": 2752709, 82 | "380": 1562372, 83 | "381": 1732237, 84 | "382": 2817817, 85 | "383": 2215493, 86 | "384": 5174999, 87 | "385": 4067644, 88 | "386": 2996721, 89 | "387": 3053788, 90 | "388": 4675024, 91 | "389": 4191568, 92 | "390": 4560066, 93 | "391": 4619108, 94 | "392": 4681491, 95 | "393": 4134115, 96 | "394": 2683911, 97 | "395": 5348624, 98 | "396": 4183297, 99 | "397": 3877595, 100 | "398": 4626356, 101 | "399": 3808870, 102 | "400": 2547681, 103 | "401": 2245392, 104 | "402": 3279318, 105 | "403": 3230950, 106 | "404": 4540588, 107 | "405": 4054130, 108 | "406": 3071541, 109 | "407": 2596901, 110 | "408": 1767644, 111 | "409": 2685401, 112 | "410": 2050215, 113 | "411": 2587050, 114 | "412": 2154664, 115 | "413": 1360358, 116 | "414": 2718862, 117 | "415": 3796660, 118 | "416": 2617801, 119 | "417": 1607303, 120 | "418": 2492674, 121 | "419": 1917942, 122 | "420": 2902148, 123 | "421": 4737203, 124 | "422": 8226918, 125 | "423": 7007998, 126 | "424": 7319191, 127 | "425": 6545930, 128 | "426": 6029437, 129 | "427": 5160852, 130 | "428": 3936801, 131 | "429": 3356028, 132 | "430": 2525851, 133 | "431": 2599728, 134 | "432": 2040886, 135 | "433": 4305583, 136 | "434": 3780015, 137 | "435": 4150490, 138 | "436": 5033682, 139 | "437": 2910705, 140 | "438": 2551671, 141 | "439": 1931074, 142 | "440": 1354449, 143 | "441": 1625240, 144 | "442": 2489465, 145 | "443": 2321139, 146 | "444": 1230243, 147 | "445": 2296029, 148 | "446": 2166126, 149 | "447": 1778299, 150 | "448": 535343, 151 | "449": 917808, 152 | "450": 755583, 153 | "451": 1188865, 154 | "452": 1864267, 155 | "453": 1409989, 156 | "454": 1630857, 157 | "455": 2710507, 158 | "456": 1569549, 159 | "457": 1071382, 160 | "458": 2275455, 161 | "459": 2803752, 162 | "460": 2450005, 163 | "461": 2638538, 164 | "462": 1678815, 165 | "463": 1519261, 166 | "464": 1738467, 167 | "465": 1100421, 168 | "466": 1234029, 169 | "467": 2578415, 170 | "468": 3702231, 171 | "469": 2146969, 172 | "470": 1352507, 173 | "471": 2224499, 174 | "472": 1475903, 175 | "473": 1752070, 176 | "474": 2006954, 177 | "475": 3619159, 178 | "476": 2080401, 179 | "477": 3565083, 180 | "478": 3507215, 181 | "479": 3466093, 182 | "480": 2282377, 183 | "481": 4916065, 184 | "482": 3653841, 185 | "483": 2436862, 186 | "484": 1157401, 187 | "485": 2364809, 188 | "486": 1431393, 189 | "487": 1670043, 190 | "488": 1920493, 191 | "489": 1230100, 192 | "490": 1134261, 193 | "491": 809477, 194 | "492": 1293565, 195 | "493": 2111653, 196 | "494": 4522457, 197 | "495": 1634519, 198 | "496": 1270261, 199 | "497": 521787, 200 | "498": 1386110, 201 | "499": 3040811, 202 | "500": 1956878, 203 | "501": 2673209, 204 | "502": 1862875, 205 | "503": 1502103, 206 | "504": 2638926, 207 | "505": 3053852, 208 | "506": 2735163, 209 | "507": 2517451, 210 | "508": 1987683, 211 | "509": 3783303, 212 | "510": 4705119, 213 | "511": 3201913, 214 | "512": 2026443, 215 | "513": 3246823, 216 | "514": 4342340, 217 | "515": 5495085, 218 | "516": 4231189, 219 | "517": 3851239, 220 | "518": 3159097, 221 | "519": 3897115, 222 | "520": 4183380, 223 | "521": 3250031, 224 | "522": 1240025, 225 | "523": 2165883, 226 | "524": 1624177, 227 | "525": 1434603, 228 | "526": 2593681, 229 | "527": 2300008, 230 | "528": 1804105, 231 | "529": 1012513, 232 | "530": 1008242, 233 | "531": 1345388, 234 | "532": 877252, 235 | "533": 1448917, 236 | "534": 1018451, 237 | "535": 1265382, 238 | "536": 2486032, 239 | "537": 2518410, 240 | "538": 940717, 241 | "539": 1230301, 242 | "540": 1929967, 243 | "541": 3062713, 244 | "542": 3214514, 245 | "543": 1943378, 246 | "544": 2157241, 247 | "545": 1466026, 248 | "546": 1923598, 249 | "547": 2162144, 250 | "548": 1142554, 251 | "549": 1617433, 252 | "550": 1455376, 253 | "551": 1209395, 254 | "552": 1974466, 255 | "553": 950968, 256 | "554": 1263976, 257 | "555": 2384050, 258 | "556": 2692993, 259 | "557": 2257352, 260 | "558": 3624114, 261 | "559": 3206212, 262 | "560": 2480249, 263 | "561": 2350373, 264 | "562": 2054733, 265 | "563": 1296864, 266 | "564": 1726609, 267 | "565": 3814419, 268 | "566": 3186897, 269 | "567": 3656591, 270 | "568": 3025591, 271 | "569": 4014687, 272 | "570": 3947125, 273 | "571": 2104947, 274 | "572": 4049327, 275 | "573": 3044008, 276 | "574": 2769036, 277 | "575": 3784000, 278 | "576": 2563630, 279 | "577": 2148692, 280 | "578": 1387826, 281 | "579": 3340357, 282 | "580": 1717303, 283 | "581": 1185187, 284 | "582": 2006840, 285 | "583": 1365088, 286 | "584": 1694412, 287 | "585": 1284494, 288 | "586": 3228538, 289 | "587": 2989458, 290 | "588": 1939314, 291 | "589": 1073430, 292 | "590": 781662, 293 | "591": 923933, 294 | "592": 2069069, 295 | "593": 2503020, 296 | "594": 1749235, 297 | "595": 2157329, 298 | "596": 2734589, 299 | "597": 3144319, 300 | "598": 2490039, 301 | "599": 679743, 302 | "600": 836420, 303 | "601": 3580117, 304 | "602": 4383029, 305 | "603": 3203704, 306 | "604": 2395190, 307 | "605": 2751518, 308 | "606": 3087368, 309 | "607": 2659242, 310 | "608": 1544950, 311 | "609": 1867837, 312 | "610": 1381067, 313 | "611": 1049287, 314 | "612": 724635, 315 | "613": 2081105, 316 | "614": 1598850, 317 | "615": 2667306, 318 | "616": 2059593, 319 | "617": 1940072, 320 | "618": 1910128, 321 | "619": 1566025, 322 | "620": 1201111, 323 | "621": 1906220, 324 | "622": 1888168, 325 | "623": 3369054, 326 | "624": 1883681, 327 | "625": 1714161, 328 | "626": 3034398, 329 | "627": 2818765, 330 | "628": 3236205, 331 | "629": 3097300, 332 | "630": 3624010, 333 | "631": 1862535, 334 | "632": 3037431, 335 | "633": 1972740, 336 | "634": 4365677, 337 | "635": 2940225, 338 | "636": 1441061, 339 | "637": 2300723, 340 | "638": 3730674, 341 | "639": 4427467, 342 | "640": 5351818, 343 | "641": 3151642, 344 | "642": 2307051, 345 | "643": 2654608, 346 | "644": 3656765, 347 | "645": 2942816, 348 | "646": 2391917, 349 | "647": 3626602, 350 | "648": 3722738, 351 | "649": 2871232, 352 | "650": 3730490, 353 | "651": 3791560, 354 | "652": 4725317, 355 | "653": 3669043, 356 | "654": 2086712, 357 | "655": 4686002, 358 | "656": 3086114, 359 | "657": 3081259, 360 | "658": 6075278, 361 | "659": 5806832, 362 | "660": 4101841, 363 | "661": 2843672, 364 | "662": 3270521, 365 | "663": 5341940, 366 | "664": 4153409, 367 | "665": 2438556, 368 | "666": 5083152, 369 | "667": 3964086, 370 | "668": 3729487, 371 | "669": 3473247, 372 | "670": 2048502, 373 | "671": 1363747, 374 | "672": 3919582, 375 | "673": 4181411, 376 | "674": 3822408, 377 | "675": 2781085, 378 | "676": 2593732, 379 | "677": 1602178, 380 | "678": 872702, 381 | "679": 593632, 382 | "680": 1750685, 383 | "681": 1721180, 384 | "682": 618872, 385 | "683": 1416068, 386 | "684": 1094944, 387 | "685": 2259262, 388 | "686": 2709782, 389 | "687": 1286276, 390 | "688": 1617710, 391 | "689": 2373883, 392 | "690": 2223822, 393 | "691": 3544474, 394 | "692": 2230772, 395 | "693": 1734005, 396 | "694": 996585, 397 | "695": 642250, 398 | "696": 822718, 399 | "697": 1556937, 400 | "698": 3472174, 401 | "699": 4956456, 402 | "700": 4929202, 403 | "701": 3690539, 404 | "702": 4757327, 405 | "703": 4153094, 406 | "704": 4967005, 407 | "705": 4267670, 408 | "706": 4962110, 409 | "707": 6056766, 410 | "708": 5444116, 411 | "709": 5872471, 412 | "710": 5168649, 413 | "711": 4267438, 414 | "712": 3460486, 415 | "713": 3319514, 416 | "714": 1558574, 417 | "715": 1278063, 418 | "716": 2159337, 419 | "717": 3612650, 420 | "718": 3645470, 421 | "719": 5257955, 422 | "720": 7613971, 423 | "721": 6174233, 424 | "722": 7977534, 425 | "723": 9369340, 426 | "724": 5345940, 427 | "725": 3328536, 428 | "726": 3176821, 429 | "727": 3147753, 430 | "728": 4704867, 431 | "729": 5168980, 432 | "730": 5898923, 433 | "731": 3705676, 434 | "732": 3115509, 435 | "733": 2931185, 436 | "734": 3721651, 437 | "735": 2347305, 438 | "736": 2338687, 439 | "737": 2748026, 440 | "738": 4346409, 441 | "739": 4758498, 442 | "740": 2880625, 443 | "741": 4399205, 444 | "742": 4206883, 445 | "743": 6609628, 446 | "744": 3014292, 447 | "745": 4753054, 448 | "746": 5355768, 449 | "747": 3132144, 450 | "748": 3739675, 451 | "749": 2182498, 452 | "750": 2252699, 453 | "751": 3003696, 454 | "752": 2337828, 455 | "753": 2462111, 456 | "754": 4336448, 457 | "755": 5425916, 458 | "756": 5784908, 459 | "757": 1803498, 460 | "758": 1856236, 461 | "759": 3756403, 462 | "760": 1867452, 463 | "761": 3156732, 464 | "762": 5776554, 465 | "763": 2823186, 466 | "764": 1209609, 467 | "765": 1459222, 468 | "766": 3467742, 469 | "767": 3579848, 470 | "768": 4806999, 471 | "769": 4487296, 472 | "770": 2076857, 473 | "771": 2794090, 474 | "772": 2396874, 475 | "773": 2778858, 476 | "774": 2015306, 477 | "775": 2185836, 478 | "776": 914546, 479 | "777": 1756884, 480 | "778": 1838947, 481 | "779": 560932, 482 | "780": 772001, 483 | "781": 2240503, 484 | "782": 1825212, 485 | "783": 462790, 486 | "784": 1502098, 487 | "785": 3679687, 488 | "786": 2271065, 489 | "787": 2870294, 490 | "788": 1339079, 491 | "789": 1697745, 492 | "790": 1756867, 493 | "791": 1147093, 494 | "792": 1750319, 495 | "793": 2228277, 496 | "794": 1826809, 497 | "795": 1144041, 498 | "796": 2120543, 499 | "797": 853478, 500 | "798": 1109947, 501 | "799": 2701199, 502 | "800": 3087986, 503 | "801": 1955156, 504 | "802": 1910201, 505 | "803": 1991693, 506 | "804": 3638033, 507 | "805": 4250044, 508 | "806": 2180325, 509 | "807": 3439337, 510 | "808": 2562823, 511 | "809": 1437083, 512 | "810": 1564899, 513 | "811": 987480, 514 | "812": 2056542, 515 | "813": 2367987, 516 | "814": 1659156, 517 | "815": 2523732, 518 | "816": 3124757, 519 | "817": 3082864, 520 | "818": 941606, 521 | "819": 1084554, 522 | "820": 1226830, 523 | "821": 3241932, 524 | "822": 3663746, 525 | "823": 4099394, 526 | "824": 2717863, 527 | "825": 1670732, 528 | "826": 1368035, 529 | "827": 2115926, 530 | "828": 3017732, 531 | "829": 3204548, 532 | "830": 2274517, 533 | "831": 1336811, 534 | "832": 2016197, 535 | "833": 3280433, 536 | "834": 2749210, 537 | "835": 1052367, 538 | "836": 1516016, 539 | "837": 2127423, 540 | "838": 1596788, 541 | "839": 2158148, 542 | "840": 3639838, 543 | "841": 4739104, 544 | "842": 5128694, 545 | "843": 3495610, 546 | "844": 1451337, 547 | "845": 829774, 548 | "846": 1852292, 549 | "847": 2094422, 550 | "848": 2915617, 551 | "849": 1224995, 552 | "850": 994511, 553 | "851": 2544746, 554 | "852": 2789638, 555 | "853": 2197148, 556 | "854": 956459, 557 | "855": 773795, 558 | "856": 1439248, 559 | "857": 2133526, 560 | "858": 4155616, 561 | "859": 3014973, 562 | "860": 1955364, 563 | "861": 1435334, 564 | "862": 1146504, 565 | "863": 1721502, 566 | "864": 886565, 567 | "865": 724251, 568 | "866": 946550, 569 | "867": 1207515, 570 | "868": 1796187, 571 | "869": 1833170, 572 | "870": 1054465, 573 | "871": 1014052, 574 | "872": 1786351, 575 | "873": 3917872, 576 | "874": 1469497, 577 | "875": 535405, 578 | "876": 787692, 579 | "877": 1846374, 580 | "878": 2153567, 581 | "879": 1232391, 582 | "880": 896963, 583 | "881": 2326323, 584 | "882": 4049908, 585 | "883": 3376918, 586 | "884": 3739572, 587 | "885": 3911139, 588 | "886": 3588944, 589 | "887": 3674368, 590 | "888": 4217358, 591 | "889": 3082378, 592 | "890": 1729829, 593 | "891": 1386970, 594 | "892": 3473383, 595 | "893": 2850760, 596 | "894": 3548368, 597 | "895": 4413962, 598 | "896": 5819733, 599 | "897": 5885850, 600 | "898": 4250494, 601 | "899": 3770374 602 | } -------------------------------------------------------------------------------- /.github/workflows/parse_and_plot_output/tornet.plot.data/round_trip_time.exit.json: -------------------------------------------------------------------------------- 1 | [ 2 | 0.360913, 3 | 0.47, 4 | 0.287331, 5 | 0.627992, 6 | 0.26736, 7 | 0.417222, 8 | 0.438423, 9 | 0.292981, 10 | 0.355586, 11 | 0.546244, 12 | 0.457672, 13 | 0.493423, 14 | 0.631, 15 | 0.413466, 16 | 0.813139, 17 | 0.619584, 18 | 0.482852, 19 | 0.748, 20 | 0.391423, 21 | 0.283799, 22 | 0.230684, 23 | 0.406254, 24 | 0.471871, 25 | 0.623514, 26 | 0.417297, 27 | 0.271803, 28 | 0.32726, 29 | 0.51809, 30 | 0.44304, 31 | 0.567896, 32 | 0.413423, 33 | 0.592014, 34 | 0.580367, 35 | 0.450304, 36 | 0.776799, 37 | 0.469455, 38 | 0.372879, 39 | 0.574075, 40 | 0.511426, 41 | 0.324999, 42 | 0.516022, 43 | 0.549422, 44 | 0.363423, 45 | 0.421139, 46 | 0.449602, 47 | 0.353385, 48 | 0.344443, 49 | 0.698898, 50 | 0.364578, 51 | 0.706525, 52 | 0.695982, 53 | 0.35359, 54 | 0.796871, 55 | 0.380139, 56 | 0.503749, 57 | 0.356753, 58 | 0.287036, 59 | 0.436906, 60 | 0.638963, 61 | 0.313874, 62 | 0.373471, 63 | 0.332336, 64 | 0.451, 65 | 0.268356, 66 | 0.338, 67 | 0.461787, 68 | 0.320066, 69 | 0.420423, 70 | 0.27365, 71 | 0.335945 72 | ] -------------------------------------------------------------------------------- /.github/workflows/parse_and_plot_output/tornet.plot.data/round_trip_time.onionservice.json: -------------------------------------------------------------------------------- 1 | [ 2 | 0.647766, 3 | 1.015262, 4 | 0.85474 5 | ] -------------------------------------------------------------------------------- /.github/workflows/parse_and_plot_output/tornet.plot.data/simulation_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "net_scale": 0.0001, 3 | "num_dir_authorities": 3, 4 | "num_exit_circuits_ten_minutes": 150, 5 | "num_hs_circuits_ten_minutes": 40, 6 | "num_public_relays": 6764, 7 | "num_sampled_relays": 4, 8 | "num_tgen_exit_emulated_users": 75.05, 9 | "num_tgen_exit_markov_clients": 75, 10 | "num_tgen_exit_perf_clients": 8, 11 | "num_tgen_exit_servers": 8, 12 | "num_tgen_hs_emulated_users": 19.75, 13 | "num_tgen_hs_markov_clients": 20, 14 | "num_tgen_hs_perf_clients": 2, 15 | "num_tgen_onionservice_servers": 2, 16 | "tornettools_generate_seed": 1 17 | } -------------------------------------------------------------------------------- /.github/workflows/parse_and_plot_output/tornet.plot.data/time_to_first_byte_recv.exit.json: -------------------------------------------------------------------------------- 1 | { 2 | "1048576": [ 3 | 0.679423, 4 | 0.56, 5 | 0.926, 6 | 0.656182, 7 | 0.540463, 8 | 0.810116, 9 | 0.562139, 10 | 0.568, 11 | 0.535222, 12 | 0.34549, 13 | 0.55021, 14 | 0.390662 15 | ], 16 | "51200": [ 17 | 0.54, 18 | 0.555424, 19 | 0.373879, 20 | 0.866375, 21 | 0.352197, 22 | 0.675814, 23 | 0.411457, 24 | 0.507062, 25 | 0.72, 26 | 0.534291, 27 | 0.863439, 28 | 0.461043, 29 | 1.051571, 30 | 0.851584, 31 | 0.590333, 32 | 0.577199, 33 | 0.364222, 34 | 0.288051, 35 | 0.635674, 36 | 0.552987, 37 | 0.863421, 38 | 0.389867, 39 | 0.470399, 40 | 0.711, 41 | 0.817623, 42 | 0.489523, 43 | 0.805498, 44 | 0.549702, 45 | 0.815815, 46 | 0.634308, 47 | 0.451429, 48 | 0.65259, 49 | 0.769423, 50 | 0.38373, 51 | 0.750248, 52 | 0.775283, 53 | 0.479487, 54 | 0.432773, 55 | 0.92386, 56 | 0.930525, 57 | 0.939375, 58 | 0.459763, 59 | 0.835871, 60 | 0.592307, 61 | 0.508945, 62 | 0.383464, 63 | 0.66179, 64 | 0.420854, 65 | 0.521423, 66 | 0.548, 67 | 0.488813, 68 | 0.484048, 69 | 0.638423, 70 | 0.502599 71 | ], 72 | "5242880": [ 73 | 0.615693, 74 | 0.441385, 75 | 0.441466, 76 | 0.871192 77 | ], 78 | "ALL": [ 79 | 0.54, 80 | 0.555424, 81 | 0.373879, 82 | 0.866375, 83 | 0.352197, 84 | 0.675814, 85 | 0.411457, 86 | 0.507062, 87 | 0.679423, 88 | 0.72, 89 | 0.534291, 90 | 0.863439, 91 | 0.461043, 92 | 1.051571, 93 | 0.851584, 94 | 0.590333, 95 | 0.56, 96 | 0.926, 97 | 0.577199, 98 | 0.364222, 99 | 0.288051, 100 | 0.635674, 101 | 0.552987, 102 | 0.863421, 103 | 0.389867, 104 | 0.470399, 105 | 0.656182, 106 | 0.711, 107 | 0.817623, 108 | 0.489523, 109 | 0.805498, 110 | 0.549702, 111 | 0.815815, 112 | 0.540463, 113 | 0.810116, 114 | 0.634308, 115 | 0.451429, 116 | 0.65259, 117 | 0.769423, 118 | 0.38373, 119 | 0.750248, 120 | 0.775283, 121 | 0.479487, 122 | 0.562139, 123 | 0.432773, 124 | 0.92386, 125 | 0.930525, 126 | 0.939375, 127 | 0.459763, 128 | 0.835871, 129 | 0.615693, 130 | 0.441385, 131 | 0.441466, 132 | 0.592307, 133 | 0.508945, 134 | 0.383464, 135 | 0.66179, 136 | 0.420854, 137 | 0.568, 138 | 0.535222, 139 | 0.871192, 140 | 0.521423, 141 | 0.548, 142 | 0.488813, 143 | 0.484048, 144 | 0.638423, 145 | 0.502599, 146 | 0.34549, 147 | 0.55021, 148 | 0.390662 149 | ] 150 | } -------------------------------------------------------------------------------- /.github/workflows/parse_and_plot_output/tornet.plot.data/time_to_first_byte_recv.onionservice.json: -------------------------------------------------------------------------------- 1 | { 2 | "1048576": [ 3 | 0.883423 4 | ], 5 | "51200": [ 6 | 1.03538 7 | ], 8 | "5242880": [ 9 | 0.658239 10 | ], 11 | "ALL": [ 12 | 1.03538, 13 | 0.658239, 14 | 0.883423 15 | ] 16 | } -------------------------------------------------------------------------------- /.github/workflows/parse_and_plot_output/tornet.plot.data/time_to_last_byte_recv.exit.json: -------------------------------------------------------------------------------- 1 | { 2 | "1048576": [ 3 | 4.63447, 4 | 3.256423, 5 | 4.017057, 6 | 2.75666, 7 | 3.304463, 8 | 3.378423, 9 | 2.446, 10 | 2.389, 11 | 2.266222, 12 | 1.484529, 13 | 2.473079, 14 | 1.639982 15 | ], 16 | "51200": [ 17 | 1.253, 18 | 0.917, 19 | 0.724716, 20 | 1.797375, 21 | 0.700197, 22 | 1.618423, 23 | 0.833532, 24 | 1.194423, 25 | 1.412, 26 | 0.893083, 27 | 1.804215, 28 | 0.66335, 29 | 1.99084, 30 | 1.793059, 31 | 1.022063, 32 | 1.294423, 33 | 0.712318, 34 | 0.481908, 35 | 1.595553, 36 | 0.919126, 37 | 1.797677, 38 | 0.82329, 39 | 1.05126, 40 | 1.41, 41 | 1.756815, 42 | 0.848423, 43 | 1.744498, 44 | 0.976206, 45 | 1.484222, 46 | 1.361819, 47 | 0.797, 48 | 1.005173, 49 | 1.691223, 50 | 0.575378, 51 | 1.697423, 52 | 1.705283, 53 | 0.906487, 54 | 0.774299, 55 | 1.873493, 56 | 1.86514, 57 | 1.876875, 58 | 0.88591, 59 | 1.329448, 60 | 0.946334, 61 | 1.136131, 62 | 0.733352, 63 | 1.610837, 64 | 0.842976, 65 | 1.256423, 66 | 0.894074, 67 | 1.117, 68 | 1.104398, 69 | 1.589423, 70 | 1.193 71 | ], 72 | "5242880": [ 73 | 7.470693, 74 | 6.868385, 75 | 6.801466, 76 | 10.683 77 | ], 78 | "ALL": [ 79 | 1.253, 80 | 0.917, 81 | 0.724716, 82 | 1.797375, 83 | 0.700197, 84 | 1.618423, 85 | 0.833532, 86 | 1.194423, 87 | 4.63447, 88 | 1.412, 89 | 0.893083, 90 | 1.804215, 91 | 0.66335, 92 | 1.99084, 93 | 1.793059, 94 | 1.022063, 95 | 3.256423, 96 | 4.017057, 97 | 1.294423, 98 | 0.712318, 99 | 0.481908, 100 | 1.595553, 101 | 0.919126, 102 | 1.797677, 103 | 0.82329, 104 | 1.05126, 105 | 2.75666, 106 | 1.41, 107 | 1.756815, 108 | 0.848423, 109 | 1.744498, 110 | 0.976206, 111 | 1.484222, 112 | 3.304463, 113 | 3.378423, 114 | 1.361819, 115 | 0.797, 116 | 1.005173, 117 | 1.691223, 118 | 0.575378, 119 | 1.697423, 120 | 1.705283, 121 | 0.906487, 122 | 2.446, 123 | 0.774299, 124 | 1.873493, 125 | 1.86514, 126 | 1.876875, 127 | 0.88591, 128 | 1.329448, 129 | 7.470693, 130 | 6.868385, 131 | 6.801466, 132 | 0.946334, 133 | 1.136131, 134 | 0.733352, 135 | 1.610837, 136 | 0.842976, 137 | 2.389, 138 | 2.266222, 139 | 10.683, 140 | 1.256423, 141 | 0.894074, 142 | 1.117, 143 | 1.104398, 144 | 1.589423, 145 | 1.193, 146 | 1.484529, 147 | 2.473079, 148 | 1.639982 149 | ] 150 | } -------------------------------------------------------------------------------- /.github/workflows/parse_and_plot_output/tornet.plot.data/time_to_last_byte_recv.onionservice.json: -------------------------------------------------------------------------------- 1 | { 2 | "1048576": [ 3 | 7.09112 4 | ], 5 | "51200": [ 6 | 1.08224 7 | ], 8 | "5242880": [ 9 | 18.196239 10 | ], 11 | "ALL": [ 12 | 1.08224, 13 | 18.196239, 14 | 7.09112 15 | ] 16 | } -------------------------------------------------------------------------------- /.github/workflows/run_all_steps.yml: -------------------------------------------------------------------------------- 1 | # Syntax reference: 2 | # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions 3 | 4 | # When changing name, also change `workflows` in 5 | # `.github/workflows/cancel_prev_run_all_steps.yml` to match. 6 | name: Run all steps 7 | permissions: read-all 8 | 9 | on: 10 | push: 11 | paths-ignore: 12 | - '**.md' 13 | - 'LICENSE' 14 | pull_request: 15 | types: [opened, synchronize] 16 | 17 | env: 18 | DEBIAN_FRONTEND: noninteractive 19 | SHADOW_COMMIT: 7ba17fb598d930c1368a6af08bb701ac7d16051d 20 | TOR_REPO: https://git.torproject.org/tor.git 21 | TOR_BRANCH: release-0.4.7 22 | TOR_COMMIT: tor-0.4.7.13 23 | # Optimization - this must be older than $TOR_COMMIT, but ideally not much 24 | # older. 25 | TOR_SHALLOW_SINCE: '2023-01-01' 26 | TGEN_COMMIT: 3d7788bad362b4487d1145da93ab2fdb73c9b639 27 | ONIONTRACE_COMMIT: 9e0e83ceb0765bc915a2888bcc02e68a5a9b848f 28 | NETDATA_MONTH: '2020-11' 29 | NETDATA_LAST_DAY: '30' 30 | # Increment to invalidate caches 31 | CACHE_VERSION: 1 32 | 33 | jobs: 34 | run-all-steps: 35 | # Keep synchronized with `CONTAINER` below 36 | runs-on: ubuntu-22.04 37 | env: 38 | CC: gcc 39 | CXX: g++ 40 | # Keep synchronized with `runs-on`, above 41 | CONTAINER: ubuntu:22.04 42 | BUILDTYPE: release 43 | RUSTPROFILE: minimal 44 | steps: 45 | - name: Checkout 46 | uses: actions/checkout@v4 47 | with: 48 | persist-credentials: false 49 | 50 | - name: Update packages 51 | run: sudo apt-get update 52 | 53 | - name: Install tornettools dependencies 54 | run: sudo apt-get install -y 55 | python3 56 | python3-dev 57 | python3-pip 58 | libxml2 59 | libxml2-dev 60 | libxslt1.1 61 | libxslt1-dev 62 | libpng16-16 63 | libpng-dev 64 | libfreetype6 65 | libfreetype6-dev 66 | libblas-dev 67 | liblapack-dev 68 | stow 69 | 70 | - name: Build tornettools 71 | run: | 72 | pip3 install wheel 73 | pip3 install -r requirements.txt 74 | pip3 install -I . 75 | 76 | - name: Restore shadow build cache 77 | id: restore-shadow-build-cache 78 | uses: actions/cache@v4 79 | with: 80 | path: | 81 | ~/opt/shadow 82 | key: shadow-${{ env.SHADOW_COMMIT }}-${{ env.CACHE_VERSION }} 83 | 84 | - name: Checkout shadow 85 | uses: actions/checkout@v4 86 | with: 87 | repository: shadow/shadow 88 | ref: ${{ env.SHADOW_COMMIT }} 89 | path: shadow 90 | persist-credentials: false 91 | 92 | - name: Install shadow deps 93 | run: | 94 | cd shadow 95 | sudo --preserve-env ci/container_scripts/install_deps.sh 96 | sudo --preserve-env ci/container_scripts/install_extra_deps.sh 97 | 98 | - name: Build shadow 99 | if: steps.restore-shadow-build-cache.outputs.cache-hit != 'true' 100 | run: | 101 | cd shadow 102 | ./setup build -j`nproc` -p ~/opt/shadow 103 | ./setup install 104 | 105 | - name: Install tor deps 106 | run: sudo apt-get install -y autoconf automake gcc libevent-dev libssl-dev make 107 | 108 | - name: Restore tor build cache 109 | id: restore-tor-build-cache 110 | uses: actions/cache@v4 111 | with: 112 | path: | 113 | ~/opt/tor 114 | key: tor-${{ env.TOR_COMMIT }}-${{ env.CACHE_VERSION }} 115 | 116 | - name: Build tor 117 | if: steps.restore-tor-build-cache.outputs.cache-hit != 'true' 118 | run: | 119 | git clone --shallow-since=$TOR_SHALLOW_SINCE -b $TOR_BRANCH $TOR_REPO tor 120 | cd tor 121 | git checkout $TOR_COMMIT 122 | ./autogen.sh 123 | ./configure --disable-asciidoc --disable-unittests --disable-manpage --disable-html-manual --disable-lzma --disable-zstd --prefix=$HOME/opt/tor 124 | make -j$(nproc) 125 | make install 126 | 127 | - name: Checkout tgen 128 | uses: actions/checkout@v4 129 | with: 130 | repository: shadow/tgen 131 | ref: ${{ env.TGEN_COMMIT }} 132 | path: tgen 133 | persist-credentials: false 134 | 135 | - name: Build tgen deps 136 | run: sudo apt-get install -y cmake gcc libglib2.0-dev libigraph-dev make 137 | 138 | - name: Build tgen 139 | run: | 140 | cd tgen 141 | mkdir build 142 | cd build 143 | cmake .. -DCMAKE_INSTALL_PREFIX=$HOME/opt/tgen 144 | make -j`nproc` 145 | make install 146 | pip3 install -r ../tools/requirements.txt 147 | pip3 install -I ../tools 148 | 149 | - name: Checkout oniontrace 150 | uses: actions/checkout@v4 151 | with: 152 | repository: shadow/oniontrace 153 | ref: ${{ env.ONIONTRACE_COMMIT }} 154 | path: oniontrace 155 | persist-credentials: false 156 | 157 | - name: Install oniontrace deps 158 | run: sudo apt-get install -y cmake gcc libglib2.0-0 libglib2.0-dev make 159 | 160 | - name: Build oniontrace 161 | run: | 162 | cd oniontrace 163 | mkdir build 164 | cd build 165 | cmake .. -DCMAKE_INSTALL_PREFIX=$HOME/opt/oniontrace 166 | make -j`nproc` 167 | make install 168 | pip3 install -r ../tools/requirements.txt 169 | pip3 install -I ../tools 170 | 171 | - name: Restore net data cache 172 | id: restore-net-data-cache 173 | uses: actions/cache@v4 174 | with: 175 | path: netdata 176 | key: netdata-${{ env.NETDATA_MONTH }}-${{ env.NETDATA_LAST_DAY}}-${{ env.CACHE_VERSION }} 177 | 178 | - name: Download net data 179 | if: steps.restore-net-data-cache.outputs.cache-hit != 'true' 180 | run: | 181 | mkdir -p netdata 182 | cd netdata 183 | wget -O - https://collector.torproject.org/archive/relay-descriptors/consensuses/consensuses-$NETDATA_MONTH.tar.xz | tar xJ 184 | wget -O - https://collector.torproject.org/archive/relay-descriptors/server-descriptors/server-descriptors-$NETDATA_MONTH.tar.xz | tar xJ 185 | wget https://metrics.torproject.org/userstats-relay-country.csv 186 | wget -O - https://collector.torproject.org/archive/onionperf/onionperf-$NETDATA_MONTH.tar.xz | tar xJ 187 | wget -O bandwidth-$NETDATA_MONTH.csv "https://metrics.torproject.org/bandwidth.csv?start=$NETDATA_MONTH-01&end=$NETDATA_MONTH-$NETDATA_LAST_DAY" 188 | # Drop most of the consensuses. This saves about ~6m in the `stage` step on GitHub's CI runner. 189 | find consensuses-*/ -type f | tail -n +100 | xargs rm 190 | 191 | - name: Checkout tmodel 192 | uses: actions/checkout@v4 193 | with: 194 | repository: tmodel-ccs2018/tmodel-ccs2018.github.io 195 | path: tmodel-ccs2018.github.io 196 | persist-credentials: false 197 | 198 | - name: Install custom deps 199 | run: | 200 | stow -d ~/opt -t ~/.local shadow 201 | stow -d ~/opt -t ~/.local tor 202 | stow -d ~/opt -t ~/.local tgen 203 | stow -d ~/opt -t ~/.local oniontrace 204 | 205 | - name: Stage 206 | run: tornettools 207 | --seed 1 208 | stage 209 | netdata/consensuses-* 210 | netdata/server-descriptors-* 211 | netdata/userstats-relay-country.csv 212 | tmodel-ccs2018.github.io 213 | --onionperf_data_path netdata/onionperf-* 214 | --bandwidth_data_path netdata/bandwidth-*.csv 215 | --geoip_path $HOME/opt/tor/share/tor/geoip 216 | 217 | - name: Generate 218 | run: | 219 | # Needed for fake certificate timestamps 220 | sudo apt-get install -y faketime 221 | # Simulate a very small network to keep this test fast. 222 | NETWORK_SCALE=0.0001 223 | # Likewise use a relatively small number of perf nodes, but still 224 | # enough to get somewhat meaningful data. 225 | TORPERF_N=10 226 | # At this network size, 1.0 would only give us ~4 onion service 227 | # markov clients, and 1 perf client. 228 | ONION_SERVICE_USER_SCALE=5.0 229 | tornettools \ 230 | --seed 1 \ 231 | generate \ 232 | relayinfo_staging_*.json \ 233 | userinfo_staging_*.json \ 234 | networkinfo_staging.gml \ 235 | tmodel-ccs2018.github.io \ 236 | --network_scale $NETWORK_SCALE \ 237 | --torperf_num_exit $TORPERF_N \ 238 | --torperf_num_onion_service $TORPERF_N \ 239 | --onion_service_user_scale $ONION_SERVICE_USER_SCALE \ 240 | --prefix tornet 241 | 242 | - name: Simulate 243 | run: | 244 | # Use short simulation time 245 | sed -i 's/stop_time:.*/stop_time: "15m"/' tornet/shadow.config.yaml 246 | tornettools --seed 1 simulate --args "--parallelism=$(nproc) --seed=1 --template-directory=shadow.data.template --progress=true" tornet 247 | 248 | - name: Parse 249 | # We use a relatively low convergence time (-c) since we're only 250 | # simulating 10m. 251 | run: tornettools parse -c 300 tornet 252 | 253 | - name: Plot 254 | # Plot against saved artifacts from a previous run, for comparison. 255 | run: tornettools plot --tor_metrics_path tor_metrics_*.json --prefix pdfs -l golden current -- .github/workflows/run_all_steps_output tornet 256 | 257 | - name: Generate README 258 | run: | 259 | echo "Generated by run_all_steps workflow. To update, unpack the 260 | artifacts from a more recent run of this workflow into this 261 | directory." > tornet/README 262 | echo "env:" >> tornet/README 263 | for v in \ 264 | GITHUB_REF \ 265 | GITHUB_SHA \ 266 | SHADOW_COMMIT \ 267 | TOR_REPO \ 268 | TOR_BRANCH \ 269 | TOR_COMMIT \ 270 | TGEN_COMMIT \ 271 | ONIONTRACE_COMMIT \ 272 | NETDATA_MONTH \ 273 | NETDATA_LAST_DAY \ 274 | ; do echo " $v: ${!v:-}" >> tornet/README; done 275 | echo "nproc: " $(nproc) >> tornet/README 276 | echo "free: " >> tornet/README 277 | free >> tornet/README 278 | 279 | - name: Archive 280 | run: tornettools archive tornet 281 | 282 | - name: Upload tornet 283 | uses: actions/upload-artifact@v4 284 | if: failure() 285 | with: 286 | name: tornet 287 | path: tornet 288 | retention-days: 5 289 | 290 | # Use this artifact to update .github/workflow/parse_and_plot_input when 291 | # necessary - e.g. when the output of the tgen or oniontrace parsers 292 | # changes in some significant way. 293 | - name: Upload parse_and_plot input 294 | uses: actions/upload-artifact@v4 295 | with: 296 | name: parse_and_plot_input 297 | path: | 298 | tornet/README 299 | tornet/tornettools*.log* 300 | tornet/*.json.xz 301 | 302 | # Use this artifact to update .github/workflow/run_all_steps_output, to 303 | # update the "golden" baseline in comparison graphs of future runs. 304 | # Typically this is only needed when the output changes in some 305 | # significant way. 306 | - name: Upload plot data 307 | uses: actions/upload-artifact@v4 308 | with: 309 | name: run_all_steps_output 310 | path: | 311 | tornet/README 312 | tornet/tornet.plot.data/*.json 313 | 314 | # This artifact is for a human to judge whether the plots are 315 | # "reasonable", and whether significant changes from the previous output 316 | # are expected. Since the simulations are currently not deterministic, 317 | # the lines from the current run are not expected to be identical to the 318 | # lines from the previous run. 319 | - name: Upload plots 320 | uses: actions/upload-artifact@v4 321 | with: 322 | name: plots 323 | path: pdfs/*.pdf 324 | -------------------------------------------------------------------------------- /.github/workflows/run_all_steps_output/README: -------------------------------------------------------------------------------- 1 | Generated by run_all_steps workflow. To update, unpack the 2 | artifacts from a more recent run of this workflow into this 3 | directory. 4 | env: 5 | GITHUB_REF: refs/pull/87/merge 6 | GITHUB_SHA: c86d7aa56a66570504e2267e14076cbf900714f6 7 | SHADOW_COMMIT: 4226cf2b8309218376f5db43b9ac739a5592e767 8 | TOR_REPO: https://git.torproject.org/tor.git 9 | TOR_BRANCH: release-0.4.6 10 | TOR_COMMIT: f728e09ebe611d6858e721eaa37637025bfbf259 11 | TGEN_COMMIT: 8cec86f46c8ca719ff9c023e381363098b99586d 12 | ONIONTRACE_COMMIT: bc26be3c4737a8a367a156f12bab2975cd811855 13 | NETDATA_MONTH: 2020-11 14 | NETDATA_LAST_DAY: 30 15 | nproc: 2 16 | free: 17 | total used free shared buff/cache available 18 | Mem: 7113128 679128 3047600 9552 3386400 6112480 19 | Swap: 4194300 5924 4188376 20 | -------------------------------------------------------------------------------- /.github/workflows/run_all_steps_output/tornet.plot.data/error_rate.exit.json: -------------------------------------------------------------------------------- 1 | { 2 | "ALL": [ 3 | [ 4 | 0.0, 5 | 11.11111111111111 6 | ], 7 | [ 8 | 0.0, 9 | 11.11111111111111 10 | ], 11 | [ 12 | 0.0, 13 | 11.11111111111111 14 | ], 15 | [ 16 | 0.0, 17 | 11.11111111111111 18 | ], 19 | [ 20 | 0.0, 21 | 11.11111111111111 22 | ], 23 | [ 24 | 0.0, 25 | 11.11111111111111 26 | ], 27 | [ 28 | 0.0, 29 | 11.11111111111111 30 | ], 31 | [ 32 | 0.0, 33 | 11.11111111111111 34 | ], 35 | [ 36 | 0.0, 37 | 11.11111111111111 38 | ], 39 | [ 40 | 0.0, 41 | 11.11111111111111 42 | ], 43 | [ 44 | 0.0, 45 | 11.11111111111111 46 | ], 47 | [ 48 | 0.0, 49 | 11.11111111111111 50 | ], 51 | [ 52 | 0.0, 53 | 11.11111111111111 54 | ], 55 | [ 56 | 0.0, 57 | 11.11111111111111 58 | ], 59 | [ 60 | 0.0, 61 | 11.11111111111111 62 | ], 63 | [ 64 | 0.0, 65 | 11.11111111111111 66 | ], 67 | [ 68 | 0.0, 69 | 11.11111111111111 70 | ], 71 | [ 72 | 0.0, 73 | 11.11111111111111 74 | ], 75 | [ 76 | 0.0, 77 | 11.11111111111111 78 | ], 79 | [ 80 | 0.0, 81 | 11.11111111111111 82 | ] 83 | ] 84 | } -------------------------------------------------------------------------------- /.github/workflows/run_all_steps_output/tornet.plot.data/error_rate.onionservice.json: -------------------------------------------------------------------------------- 1 | { 2 | "ALL": [ 3 | [ 4 | 33.333333333333336, 5 | 11.11111111111111 6 | ], 7 | [ 8 | 33.333333333333336, 9 | 11.11111111111111 10 | ], 11 | [ 12 | 33.333333333333336, 13 | 11.11111111111111 14 | ], 15 | [ 16 | 33.333333333333336, 17 | 11.11111111111111 18 | ], 19 | [ 20 | 44.44444444444444, 21 | 11.11111111111111 22 | ], 23 | [ 24 | 44.44444444444444, 25 | 11.11111111111111 26 | ], 27 | [ 28 | 33.333333333333336, 29 | 11.11111111111111 30 | ], 31 | [ 32 | 33.333333333333336, 33 | 11.11111111111111 34 | ], 35 | [ 36 | 44.44444444444444, 37 | 11.11111111111111 38 | ], 39 | [ 40 | 44.44444444444444, 41 | 11.11111111111111 42 | ], 43 | [ 44 | 44.44444444444444, 45 | 11.11111111111111 46 | ], 47 | [ 48 | 44.44444444444444, 49 | 11.11111111111111 50 | ], 51 | [ 52 | 66.66666666666667, 53 | 11.11111111111111 54 | ], 55 | [ 56 | 66.66666666666667, 57 | 11.11111111111111 58 | ], 59 | [ 60 | 44.44444444444444, 61 | 11.11111111111111 62 | ], 63 | [ 64 | 44.44444444444444, 65 | 11.11111111111111 66 | ], 67 | [ 68 | 44.44444444444444, 69 | 11.11111111111111 70 | ], 71 | [ 72 | 44.44444444444444, 73 | 11.11111111111111 74 | ], 75 | [ 76 | 44.44444444444444, 77 | 11.11111111111111 78 | ], 79 | [ 80 | 44.44444444444444, 81 | 11.11111111111111 82 | ] 83 | ], 84 | "PROXY-STATUS": [ 85 | [ 86 | 33.333333333333336, 87 | 11.11111111111111 88 | ], 89 | [ 90 | 22.22222222222222, 91 | 11.11111111111111 92 | ], 93 | [ 94 | 22.22222222222222, 95 | 11.11111111111111 96 | ], 97 | [ 98 | 33.333333333333336, 99 | 11.11111111111111 100 | ], 101 | [ 102 | 22.22222222222222, 103 | 11.11111111111111 104 | ], 105 | [ 106 | 11.11111111111111, 107 | 11.11111111111111 108 | ], 109 | [ 110 | 33.333333333333336, 111 | 11.11111111111111 112 | ], 113 | [ 114 | 22.22222222222222, 115 | 11.11111111111111 116 | ], 117 | [ 118 | 33.333333333333336, 119 | 11.11111111111111 120 | ], 121 | [ 122 | 22.22222222222222, 123 | 11.11111111111111 124 | ] 125 | ], 126 | "TIMEOUT": [ 127 | [ 128 | 11.11111111111111, 129 | 11.11111111111111 130 | ], 131 | [ 132 | 22.22222222222222, 133 | 11.11111111111111 134 | ], 135 | [ 136 | 22.22222222222222, 137 | 11.11111111111111 138 | ], 139 | [ 140 | 33.333333333333336, 141 | 11.11111111111111 142 | ], 143 | [ 144 | 33.333333333333336, 145 | 11.11111111111111 146 | ], 147 | [ 148 | 22.22222222222222, 149 | 11.11111111111111 150 | ], 151 | [ 152 | 11.11111111111111, 153 | 11.11111111111111 154 | ], 155 | [ 156 | 22.22222222222222, 157 | 11.11111111111111 158 | ] 159 | ] 160 | } -------------------------------------------------------------------------------- /.github/workflows/run_all_steps_output/tornet.plot.data/perfclient_circuit_build_time.exit.json: -------------------------------------------------------------------------------- 1 | [ 2 | 1.087481, 3 | 0.969182, 4 | 1.100099, 5 | 1.037279, 6 | 0.876007, 7 | 1.110286, 8 | 1.129426, 9 | 0.849479, 10 | 0.854174, 11 | 0.855673, 12 | 1.026728, 13 | 1.025721, 14 | 0.892673, 15 | 1.036192, 16 | 0.916045, 17 | 1.041057, 18 | 1.112528, 19 | 1.167206, 20 | 0.936009, 21 | 1.105112, 22 | 0.856, 23 | 1.097992, 24 | 0.87876, 25 | 0.881673, 26 | 0.283154, 27 | 1.122399, 28 | 0.959645, 29 | 1.110447, 30 | 1.09801, 31 | 1.113232, 32 | 1.025092, 33 | 0.883303, 34 | 0.847064, 35 | 1.101741, 36 | 2.213354, 37 | 1.128405, 38 | 0.880151, 39 | 0.878753, 40 | 0.868979, 41 | 0.840662, 42 | 1.221845, 43 | 1.498007, 44 | 1.110286, 45 | 1.038007, 46 | 1.098067, 47 | 1.992775, 48 | 0.993671, 49 | 0.843231, 50 | 0.895686, 51 | 0.833057, 52 | 1.035863, 53 | 0.91522, 54 | 1.118667, 55 | 0.885721, 56 | 0.931337, 57 | 0.838684, 58 | 0.890789, 59 | 1.054132, 60 | 0.882577, 61 | 0.857879, 62 | 0.850786, 63 | 1.033643, 64 | 0.854245, 65 | 1.116011, 66 | 1.014652, 67 | 0.83909, 68 | 1.116007, 69 | 0.281344, 70 | 1.028348, 71 | 0.985964, 72 | 1.127232, 73 | 0.845975, 74 | 0.984032, 75 | 0.971658, 76 | 0.882536, 77 | 1.024824, 78 | 0.99, 79 | 1.070007, 80 | 0.87505, 81 | 1.125141, 82 | 0.873792, 83 | 1.122848, 84 | 2.196182, 85 | 1.1, 86 | 0.875951, 87 | 0.876007, 88 | 0.973201, 89 | 1.036034, 90 | 0.859311, 91 | 0.88118, 92 | 0.936337, 93 | 1.046007, 94 | 0.845441, 95 | 0.876966, 96 | 0.847202, 97 | 0.958873, 98 | 0.996823, 99 | 1.037466, 100 | 1.108981, 101 | 1.120992, 102 | 0.283344, 103 | 0.855279, 104 | 1.037964, 105 | 1.026092, 106 | 0.880303, 107 | 0.841523, 108 | 0.876634, 109 | 0.897651, 110 | 0.921639, 111 | 2.293849, 112 | 0.881787, 113 | 0.867076, 114 | 1.038855, 115 | 0.880007, 116 | 1.190672, 117 | 0.880007, 118 | 0.878322, 119 | 1.091881, 120 | 1.103559, 121 | 0.878721, 122 | 0.850605, 123 | 1.060765, 124 | 1.025406, 125 | 1.101039, 126 | 1.038322, 127 | 0.856279, 128 | 1.076286, 129 | 1.109286, 130 | 1.116305, 131 | 0.857, 132 | 1.099, 133 | 0.853152, 134 | 0.887219, 135 | 1.024462, 136 | 1.015831, 137 | 1.017342, 138 | 0.847341, 139 | 0.838088, 140 | 0.856286, 141 | 0.898303, 142 | 0.833733, 143 | 0.887013, 144 | 0.88877, 145 | 1.031, 146 | 1.10836, 147 | 1.024374, 148 | 1.018467, 149 | 1.182464, 150 | 0.988455, 151 | 1.447007, 152 | 1.018007, 153 | 1.573286, 154 | 1.093772, 155 | 0.859734, 156 | 0.880232, 157 | 1.091873, 158 | 1.072749, 159 | 1.195914, 160 | 1.023941, 161 | 1.097516, 162 | 0.977, 163 | 1.041721, 164 | 1.227672, 165 | 1.023286, 166 | 1.113136, 167 | 1.122729, 168 | 1.900204, 169 | 0.874989, 170 | 1.002548, 171 | 0.859598, 172 | 1.008502, 173 | 1.087121, 174 | 0.840699, 175 | 1.109023, 176 | 0.963208, 177 | 1.105078, 178 | 1.091954, 179 | 0.854694, 180 | 0.855512, 181 | 0.968723, 182 | 1.101918, 183 | 1.022327, 184 | 1.115258, 185 | 0.869205, 186 | 1.115007, 187 | 1.031286, 188 | 1.555286, 189 | 0.858347, 190 | 1.102892, 191 | 2.138372, 192 | 1.120912, 193 | 0.850419, 194 | 1.092607, 195 | 0.898139, 196 | 1.10769, 197 | 1.113983, 198 | 0.860877, 199 | 1.027007, 200 | 0.992286, 201 | 1.102136, 202 | 1.111363, 203 | 1.054056, 204 | 0.876919, 205 | 0.845364, 206 | 0.846869, 207 | 1.099039, 208 | 1.121271, 209 | 1.102742, 210 | 0.862704, 211 | 1.030118, 212 | 0.875511, 213 | 1.819961, 214 | 0.911537, 215 | 0.883154, 216 | 0.878797, 217 | 1.088993, 218 | 1.09556, 219 | 1.122962, 220 | 1.113761, 221 | 0.85303, 222 | 1.112437, 223 | 0.852716, 224 | 1.087512, 225 | 0.873909, 226 | 2.17255, 227 | 1.812815, 228 | 1.029926, 229 | 1.01801, 230 | 1.083806, 231 | 0.903099, 232 | 0.878263, 233 | 0.893244, 234 | 1.046278, 235 | 1.037286, 236 | 0.886905, 237 | 0.882433, 238 | 1.932073, 239 | 2.107671, 240 | 0.856221, 241 | 0.879016, 242 | 1.033571, 243 | 1.114557, 244 | 0.852442, 245 | 1.022762, 246 | 0.83855, 247 | 1.118007, 248 | 1.031007, 249 | 0.890973, 250 | 1.101497, 251 | 0.849556, 252 | 1.046147, 253 | 0.266696, 254 | 0.254248, 255 | 0.261773, 256 | 0.535059, 257 | 0.52914, 258 | 0.324754, 259 | 0.534754, 260 | 0.496754, 261 | 0.29706, 262 | 0.292912, 263 | 0.297658, 264 | 0.274805, 265 | 0.303703, 266 | 0.311888, 267 | 0.446966, 268 | 0.462, 269 | 0.305381, 270 | 0.456154, 271 | 0.527754, 272 | 0.525015, 273 | 0.3, 274 | 0.305669, 275 | 1.422384, 276 | 0.333764, 277 | 0.056228, 278 | 0.54147, 279 | 0.514759, 280 | 0.518906, 281 | 0.450868, 282 | 0.396249, 283 | 0.456766, 284 | 0.28737, 285 | 0.383211, 286 | 0.805341, 287 | 0.278106, 288 | 0.530284, 289 | 0.491222, 290 | 0.296147, 291 | 0.333303, 292 | 0.244261, 293 | 0.41919, 294 | 0.431798, 295 | 0.293811, 296 | 0.507781, 297 | 0.256811, 298 | 0.311785, 299 | 0.314641, 300 | 0.519552, 301 | 0.275048, 302 | 0.504855, 303 | 0.439449, 304 | 0.283285, 305 | 0.266, 306 | 0.252098, 307 | 0.440992, 308 | 0.499043, 309 | 0.461082, 310 | 0.243, 311 | 0.29, 312 | 0.508271, 313 | 0.289334, 314 | 0.419444, 315 | 0.420225, 316 | 0.232853, 317 | 0.427294, 318 | 0.515665, 319 | 0.501887, 320 | 0.294427, 321 | 0.436657, 322 | 0.282924, 323 | 0.263583, 324 | 0.694652, 325 | 0.503528, 326 | 0.540187, 327 | 0.358651, 328 | 0.764424, 329 | 0.584126, 330 | 0.62308, 331 | 0.641471, 332 | 0.381296, 333 | 0.738602, 334 | 0.617848, 335 | 0.545505, 336 | 0.858016, 337 | 0.524174, 338 | 0.620551, 339 | 0.391217, 340 | 0.404089, 341 | 0.416439, 342 | 0.461089, 343 | 0.397102, 344 | 0.613148, 345 | 0.47088, 346 | 0.61808, 347 | 0.623892, 348 | 0.343926, 349 | 0.388657, 350 | 0.543805, 351 | 0.62608, 352 | 0.360989, 353 | 0.362014, 354 | 0.359502, 355 | 0.601083, 356 | 0.398498, 357 | 0.350996, 358 | 0.374042, 359 | 0.483729, 360 | 0.080568, 361 | 0.354428 362 | ] -------------------------------------------------------------------------------- /.github/workflows/run_all_steps_output/tornet.plot.data/perfclient_circuit_build_time.onionservice.json: -------------------------------------------------------------------------------- 1 | [ 2 | 0.273892, 3 | 114.298364, 4 | 0.343245, 5 | 0.587605, 6 | 0.621065, 7 | 0.35533, 8 | 58.984534, 9 | 0.28302, 10 | 0.53367, 11 | 1.112713, 12 | 0.259732, 13 | 2.269532, 14 | 1.000014, 15 | 0.249986, 16 | 178.455526, 17 | 0.922986, 18 | 0.512846, 19 | 0.500468, 20 | 0.292, 21 | 0.428, 22 | 0.394926, 23 | 0.406784, 24 | 0.415311, 25 | 55.97633, 26 | 53.278522, 27 | 53.960343, 28 | 1.181575, 29 | 0.617474, 30 | 56.970433, 31 | 0.458728, 32 | 2.459677, 33 | 1.080723, 34 | 0.277272, 35 | 0.625884, 36 | 0.272797, 37 | 0.267869, 38 | 0.402279, 39 | 0.385037, 40 | 0.353498, 41 | 0.276178, 42 | 59.327459, 43 | 0.67776, 44 | 56.969586, 45 | 0.281414, 46 | 0.282, 47 | 58.957744, 48 | 0.466957, 49 | 0.493122, 50 | 116.301574, 51 | 0.495678, 52 | 116.948526, 53 | 3.001065, 54 | 0.48192, 55 | 61.570009, 56 | 1.309673, 57 | 0.580943, 58 | 2.359774, 59 | 0.571178, 60 | 0.267653, 61 | 2.374202, 62 | 2.001309, 63 | 0.502743, 64 | 0.607888, 65 | 0.227544, 66 | 0.41598, 67 | 0.609488, 68 | 0.266597, 69 | 0.407389, 70 | 0.559228, 71 | 0.069692, 72 | 113.944059, 73 | 0.28567, 74 | 119.314678, 75 | 120.176367, 76 | 0.736866, 77 | 1.95167, 78 | 0.276, 79 | 0.276, 80 | 1.514844, 81 | 0.284014, 82 | 0.610807, 83 | 0.669198, 84 | 0.522731, 85 | 0.35874, 86 | 56.924048, 87 | 0.261286, 88 | 56.38074, 89 | 0.279743, 90 | 57.404377, 91 | 2.001107, 92 | 0.281769, 93 | 1.438884, 94 | 1.010814, 95 | 0.499072, 96 | 0.48382, 97 | 0.509035, 98 | 0.25926, 99 | 56.90633, 100 | 0.341613, 101 | 56.390816, 102 | 0.244154, 103 | 57.541287, 104 | 3.253983, 105 | 0.579951, 106 | 0.579951, 107 | 44.918937, 108 | 0.24475, 109 | 0.462375, 110 | 0.369279, 111 | 0.428271, 112 | 0.307677, 113 | 0.284977, 114 | 0.250516, 115 | 0.405153, 116 | 0.255657, 117 | 75.362279, 118 | 76.735859, 119 | 0.802784, 120 | 74.942991, 121 | 0.627716, 122 | 0.498685, 123 | 59.983, 124 | 0.501901, 125 | 0.795901, 126 | 1.560901, 127 | 2.365901, 128 | 0.501773, 129 | 1.000986, 130 | 0.29, 131 | 0.556, 132 | 1.378014, 133 | 0.500922, 134 | 9.002454, 135 | 0.258664, 136 | 0.558546, 137 | 74.412444, 138 | 0.61824, 139 | 45.964645, 140 | 44.333472, 141 | 0.432746, 142 | 1.346595, 143 | 0.713742, 144 | 4.002212, 145 | 0.240569, 146 | 3.726595, 147 | 1.442508, 148 | 0.491801, 149 | 0.429306, 150 | 0.36118, 151 | 0.344087, 152 | 0.249333, 153 | 0.578585, 154 | 54.954621, 155 | 54.269516, 156 | 0.278901, 157 | 0.27318, 158 | 180.429306, 159 | 181.251655, 160 | 0.255019, 161 | 2.000398, 162 | 1.355143, 163 | 0.618443, 164 | 3.895579, 165 | 0.281714, 166 | 0.484506, 167 | 0.328279, 168 | 0.406345, 169 | 0.232, 170 | 56.229993, 171 | 0.492901, 172 | 0.500901, 173 | 58.001867, 174 | 1.033929, 175 | 59.057652, 176 | 0.433446, 177 | 57.924662, 178 | 115.295193, 179 | 0.641787, 180 | 116.214803, 181 | 0.229114, 182 | 1.259114, 183 | 0.276632, 184 | 3.001901, 185 | 0.48, 186 | 62.676714, 187 | 0.427922, 188 | 0.563328, 189 | 0.476721, 190 | 56.911099, 191 | 58.178911, 192 | 0.37311, 193 | 1.000487, 194 | 57.33183, 195 | 0.506414, 196 | 1.000818, 197 | 0.377726, 198 | 0.572769, 199 | 0.609126, 200 | 1.654569, 201 | 0.514712, 202 | 0.498931, 203 | 0.402291, 204 | 0.263433, 205 | 0.457436, 206 | 0.282516, 207 | 55.89631, 208 | 0.264877, 209 | 0.666253, 210 | 1.338583, 211 | 2.00078, 212 | 59.515208, 213 | 0.619123, 214 | 0.242062, 215 | 1.346664, 216 | 2.41927, 217 | 0.496674, 218 | 0.240506, 219 | 55.882463, 220 | 57.144783, 221 | 0.480813, 222 | 57.916783, 223 | 0.493833, 224 | 1.961834, 225 | 0.271337, 226 | 1.000936, 227 | 0.361191, 228 | 118.531973, 229 | 0.480248, 230 | 0.073568, 231 | 0.243721, 232 | 0.246967, 233 | 0.330555, 234 | 0.496977, 235 | 0.26038, 236 | 0.427798, 237 | 74.32052, 238 | 0.311811, 239 | 0.639395, 240 | 0.291672, 241 | 1.045416, 242 | 1.864759, 243 | 11.00281, 244 | 87.262262, 245 | 0.317321, 246 | 0.386484, 247 | 47.981051, 248 | 136.242694, 249 | 0.270811, 250 | 136.877504, 251 | 0.542727, 252 | 1.543088, 253 | 0.364513, 254 | 0.718, 255 | 1.0285, 256 | 0.526275, 257 | 10.001426, 258 | 12.392727, 259 | 0.53557, 260 | 0.310723, 261 | 45.967912, 262 | 47.30657, 263 | 0.295811, 264 | 48.04257, 265 | 0.274002, 266 | 0.290097, 267 | 2.290972, 268 | 1.00057, 269 | 0.277591, 270 | 2.434723, 271 | 1.021613, 272 | 0.24488, 273 | 1.698243, 274 | 0.24216, 275 | 112.610546, 276 | 0.286555, 277 | 0.365002, 278 | 0.40345, 279 | 0.387543, 280 | 0.276447, 281 | 54.95591, 282 | 0.317711, 283 | 0.845811, 284 | 2.001265, 285 | 0.279258, 286 | 58.434343, 287 | 0.983534, 288 | 0.496376, 289 | 0.375735, 290 | 0.291349, 291 | 0.43079, 292 | 0.422188, 293 | 114.221241, 294 | 57.484478, 295 | 0.501811, 296 | 0.603002, 297 | 0.29, 298 | 10.000661, 299 | 0.243339, 300 | 0.571807, 301 | 1.209437, 302 | 2.000651, 303 | 0.187998, 304 | 13.485556, 305 | 1.107914, 306 | 0.281279, 307 | 0.280365, 308 | 44.928275, 309 | 116.269295, 310 | 0.470311, 311 | 0.236266, 312 | 1.326339, 313 | 0.28382, 314 | 2.390721, 315 | 3.001176, 316 | 0.50709, 317 | 0.539, 318 | 0.239411, 319 | 0.425891, 320 | 56.292365, 321 | 0.533811, 322 | 0.039926, 323 | 0.416723, 324 | 0.418377, 325 | 0.449803, 326 | 0.297049, 327 | 0.2871, 328 | 59.904, 329 | 118.20482, 330 | 0.591399, 331 | 118.861986, 332 | 0.523403, 333 | 1.523811, 334 | 168.144862, 335 | 0.258376, 336 | 0.653356, 337 | 0.504804, 338 | 0.48791, 339 | 56.889993, 340 | 0.31599, 341 | 0.552815, 342 | 1.154811, 343 | 2.000051, 344 | 0.27727, 345 | 61.507935, 346 | 1.166462, 347 | 0.307722, 348 | 0.386104, 349 | 0.273, 350 | 0.649904, 351 | 0.737921, 352 | 0.657233, 353 | 0.509414, 354 | 1.199032, 355 | 76.511905, 356 | 75.966873, 357 | 0.51, 358 | 0.779, 359 | 9.0, 360 | 10.268, 361 | 1.14987, 362 | 0.490576, 363 | 46.988316, 364 | 0.48256, 365 | 0.940311, 366 | 2.346032, 367 | 0.742014, 368 | 3.143375, 369 | 2.000293, 370 | 140.671279, 371 | 0.90423, 372 | 53.377767, 373 | 0.501, 374 | 0.497183, 375 | 55.983689, 376 | 54.269183, 377 | 0.488145, 378 | 54.972183, 379 | 0.487064, 380 | 56.773695, 381 | 0.737695, 382 | 2.737727, 383 | 2.213453, 384 | 0.501, 385 | 0.861724, 386 | 1.729, 387 | 0.736555, 388 | 0.47627, 389 | 1.900547, 390 | 0.665059, 391 | 0.461139, 392 | 0.644721, 393 | 0.642243, 394 | 0.657866, 395 | 55.316042, 396 | 58.654, 397 | 0.672811, 398 | 55.171574, 399 | 0.677237, 400 | 0.518949, 401 | 0.478204, 402 | 0.724447, 403 | 0.533205, 404 | 59.965, 405 | 58.247012, 406 | 0.774321, 407 | 0.774321, 408 | 0.518647, 409 | 0.78099, 410 | 1.248926, 411 | 0.485, 412 | 2.170647, 413 | 0.912896, 414 | 2.205, 415 | 0.771524, 416 | 1.77242, 417 | 1.40081, 418 | 0.516677, 419 | 0.496799, 420 | 54.31739, 421 | 0.586815, 422 | 0.729723, 423 | 0.132698, 424 | 0.532838, 425 | 59.947, 426 | 0.680838, 427 | 0.779032, 428 | 1.407032, 429 | 2.411492, 430 | 0.523721, 431 | 1.000059, 432 | 118.636634, 433 | 0.893517, 434 | 1.315056, 435 | 0.516968, 436 | 0.764913, 437 | 9.001622, 438 | 10.355677, 439 | 0.592772, 440 | 0.508387, 441 | 0.491843, 442 | 0.743088, 443 | 0.510813, 444 | 0.507977, 445 | 45.932952, 446 | 45.289005, 447 | 0.673426, 448 | 46.127005, 449 | 0.751606, 450 | 1.752032, 451 | 1.416965, 452 | 0.494622, 453 | 1.246222, 454 | 0.479734, 455 | 2.068001, 456 | 0.702528, 457 | 0.516409, 458 | 0.611136, 459 | 56.912968, 460 | 56.450388, 461 | 0.757424, 462 | 0.532738, 463 | 57.616334, 464 | 3.442032, 465 | 0.493145, 466 | 1.000957, 467 | 0.483438, 468 | 3.48543, 469 | 0.653818, 470 | 0.669788, 471 | 0.372707, 472 | 0.359499, 473 | 0.523839, 474 | 0.409584, 475 | 1.00908, 476 | 76.425952, 477 | 75.760532, 478 | 0.397154, 479 | 0.356, 480 | 9.000173, 481 | 88.371734, 482 | 0.381164, 483 | 0.379805, 484 | 47.324526, 485 | 0.41108, 486 | 0.65408, 487 | 0.379911, 488 | 0.978, 489 | 11.0016, 490 | 70.253154, 491 | 0.400284, 492 | 61.322827, 493 | 0.416, 494 | 0.407, 495 | 1.052, 496 | 45.97892, 497 | 0.35708, 498 | 1.06508, 499 | 2.015359, 500 | 0.561678, 501 | 4.00108, 502 | 0.404279, 503 | 51.50522, 504 | 1.035, 505 | 0.383363, 506 | 0.890504, 507 | 0.485873, 508 | 0.610645, 509 | 0.470327, 510 | 0.529474, 511 | 54.97092, 512 | 108.281284, 513 | 0.599937, 514 | 109.043018, 515 | 0.365515, 516 | 2.360801, 517 | 0.40243, 518 | 1.00056, 519 | 0.406302, 520 | 57.567, 521 | 1.3142, 522 | 0.357148, 523 | 0.605, 524 | 0.602982, 525 | 0.551073, 526 | 0.543282, 527 | 55.962165, 528 | 0.36986, 529 | 166.728022, 530 | 57.394, 531 | 0.608366, 532 | 1.000595, 533 | 3.59323, 534 | 63.192143, 535 | 1.296032, 536 | 0.367585, 537 | 63.851191, 538 | 0.625, 539 | 2.625986, 540 | 0.604172, 541 | 0.959218, 542 | 3.573586, 543 | 0.485169, 544 | 53.95381, 545 | 52.506643, 546 | 0.629896, 547 | 0.359537, 548 | 0.375564, 549 | 2.54608, 550 | 3.72408, 551 | 3.001899, 552 | 0.386505, 553 | 4.406537, 554 | 0.63, 555 | 0.082568, 556 | 0.53575, 557 | 0.620638, 558 | 55.169505, 559 | 0.36208, 560 | 0.354137, 561 | 0.530997, 562 | 0.611381, 563 | 0.468838, 564 | 0.349025, 565 | 59.928, 566 | 0.611044, 567 | 0.70408, 568 | 1.374404, 569 | 2.001528, 570 | 0.377969, 571 | 116.73252, 572 | 1.364705, 573 | 0.690756, 574 | 0.611955, 575 | 0.34517, 576 | 56.9134, 577 | 0.374537, 578 | 56.412439, 579 | 57.490977, 580 | 0.633193, 581 | 3.001683, 582 | 0.369695, 583 | 2.58409, 584 | 1.299087, 585 | 0.625675, 586 | 0.564957, 587 | 0.389583, 588 | 0.337721, 589 | 0.300065, 590 | 0.521281, 591 | 0.513842, 592 | 0.528, 593 | 0.448342, 594 | 66.62933, 595 | 64.952073, 596 | 0.331629, 597 | 0.547, 598 | 57.984658, 599 | 125.276378, 600 | 0.504312, 601 | 126.003492, 602 | 0.494409, 603 | 0.494605, 604 | 3.001145, 605 | 0.302197, 606 | 0.506, 607 | 1.308197, 608 | 0.51301, 609 | 9.00001, 610 | 0.299078, 611 | 0.904924, 612 | 1.710078, 613 | 45.975736, 614 | 59.318475, 615 | 0.512469, 616 | 60.10882, 617 | 0.286045, 618 | 2.000616, 619 | 0.302887, 620 | 0.607726, 621 | 2.101766, 622 | 0.513, 623 | 2.616766, 624 | 0.287587, 625 | 1.353821, 626 | 0.237945, 627 | 9.002123, 628 | 10.226721, 629 | 0.544311, 630 | 0.544311, 631 | 0.843529, 632 | 0.524409, 633 | 0.280822, 634 | 0.416525, 635 | 0.245736, 636 | 0.290721, 637 | 44.965, 638 | 44.238229, 639 | 0.421708, 640 | 44.848, 641 | 0.52859, 642 | 180.858502, 643 | 0.301942, 644 | 2.303284, 645 | 0.839, 646 | 0.417619, 647 | 0.621654, 648 | 0.323423, 649 | 0.314998, 650 | 0.48373, 651 | 0.241109, 652 | 0.275822, 653 | 56.277579, 654 | 0.291001, 655 | 0.592147, 656 | 58.027281, 657 | 0.896406, 658 | 58.931281, 659 | 0.396275, 660 | 11.002337, 661 | 0.245333, 662 | 0.576897, 663 | 1.000465, 664 | 0.299713, 665 | 11.450479, 666 | 1.083147, 667 | 0.258051, 668 | 0.480905, 669 | 44.944379, 670 | 46.2502, 671 | 0.518581, 672 | 46.949772, 673 | 0.245, 674 | 2.000342, 675 | 1.501, 676 | 0.539, 677 | 0.54816, 678 | 0.55452, 679 | 1.156934, 680 | 3.001281, 681 | 0.183945, 682 | 1.175934, 683 | 2.183031, 684 | 0.50184, 685 | 0.534532, 686 | 0.437045, 687 | 56.934318, 688 | 0.487937, 689 | 0.891342, 690 | 1.871342, 691 | 0.257613, 692 | 3.328718, 693 | 1.000411, 694 | 0.252923, 695 | 183.520818, 696 | 0.520432, 697 | 0.260429, 698 | 0.286541, 699 | 0.327919, 700 | 0.278741, 701 | 55.914247, 702 | 0.578745, 703 | 0.479924, 704 | 1.259342, 705 | 2.001246, 706 | 0.30846, 707 | 57.677, 708 | 0.262279, 709 | 1.380689, 710 | 2.148481, 711 | 0.544854, 712 | 0.261639, 713 | 0.038634, 714 | 55.89913, 715 | 57.164288, 716 | 0.268374, 717 | 1.000853, 718 | 0.480074, 719 | 174.56484, 720 | 0.256632, 721 | 1.326703, 722 | 0.282858, 723 | 0.359405, 724 | 0.328971, 725 | 0.981787, 726 | 0.988983, 727 | 0.917, 728 | 0.901624, 729 | 1.412549, 730 | 76.860931, 731 | 76.331929, 732 | 0.912389, 733 | 0.882611, 734 | 9.001977, 735 | 0.89895, 736 | 88.930922, 737 | 1.151009, 738 | 90.913061, 739 | 48.407634, 740 | 47.831009, 741 | 0.889549, 742 | 59.255, 743 | 0.91616, 744 | 1.877301, 745 | 9.00284, 746 | 10.428, 747 | 0.914, 748 | 1.232662, 749 | 1.324237, 750 | 2.283237, 751 | 48.972451, 752 | 1.821763, 753 | 2.073549, 754 | 3.360358, 755 | 4.001549, 756 | 64.880868, 757 | 0.909279, 758 | 1.767391, 759 | 0.926669, 760 | 0.881936, 761 | 1.065933, 762 | 1.142664, 763 | 1.059949, 764 | 0.918869, 765 | 53.963503, 766 | 55.39006, 767 | 0.903549, 768 | 56.338721, 769 | 1.147887, 770 | 2.444892, 771 | 0.799356, 772 | 1.00082, 773 | 0.900993, 774 | 2.952, 775 | 0.904, 776 | 2.055307, 777 | 1.128154, 778 | 3.244204, 779 | 1.197549, 780 | 0.915627, 781 | 1.041913, 782 | 54.944451, 783 | 56.40179, 784 | 0.887828, 785 | 57.506, 786 | 0.909497, 787 | 3.001326, 788 | 0.817389, 789 | 2.827335, 790 | 4.554163, 791 | 1.136279, 792 | 56.933674, 793 | 47.469597, 794 | 0.926411, 795 | 0.635024, 796 | 48.682279, 797 | 1.274895, 798 | 5.001716, 799 | 0.811077, 800 | 62.319204, 801 | 0.908035, 802 | 1.785676, 803 | 0.932241, 804 | 1.047729, 805 | 1.282329, 806 | 54.914284, 807 | 52.47508, 808 | 53.68808, 809 | 0.884097, 810 | 1.889452, 811 | 3.000549, 812 | 0.832918, 813 | 2.989097, 814 | 1.145279, 815 | 0.895562, 816 | 0.946241, 817 | 0.921848, 818 | 1.011161, 819 | 56.907451, 820 | 0.931353, 821 | 56.476, 822 | 0.910979, 823 | 0.889383, 824 | 1.890362, 825 | 0.922948, 826 | 1.84143, 827 | 57.896902, 828 | 59.421193, 829 | 0.921549, 830 | 60.560561, 831 | 1.143172, 832 | 3.001721, 833 | 0.865155, 834 | 2.913774, 835 | 0.560846, 836 | 2.001335, 837 | 1.139039, 838 | 0.144904, 839 | 1.138057, 840 | 1.148803, 841 | 0.487056, 842 | 0.494, 843 | 0.286356, 844 | 0.352723, 845 | 74.331817, 846 | 0.513781, 847 | 0.669781, 848 | 75.927326, 849 | 1.009366, 850 | 0.288129, 851 | 9.000129, 852 | 0.243213, 853 | 10.322783, 854 | 0.434608, 855 | 48.990219, 856 | 0.288781, 857 | 0.641857, 858 | 138.211653, 859 | 0.514783, 860 | 138.743925, 861 | 4.002781, 862 | 0.264651, 863 | 2.526798, 864 | 0.284, 865 | 0.536378, 866 | 55.979219, 867 | 55.283522, 868 | 0.66999, 869 | 55.965343, 870 | 0.591242, 871 | 0.925323, 872 | 0.427656, 873 | 2.428103, 874 | 0.38125, 875 | 0.512356, 876 | 0.51122, 877 | 11.003543, 878 | 121.275688, 879 | 0.502791, 880 | 121.884106, 881 | 0.346924, 882 | 1.705069, 883 | 0.504102, 884 | 0.500813, 885 | 0.299307, 886 | 0.418952, 887 | 0.479895, 888 | 0.246781, 889 | 45.313781, 890 | 0.653781, 891 | 46.958912, 892 | 0.270354, 893 | 0.345017, 894 | 0.428554, 895 | 0.354452, 896 | 0.479751, 897 | 59.223404, 898 | 0.23106, 899 | 0.50099, 900 | 58.794869, 901 | 0.509195, 902 | 0.528, 903 | 0.507279, 904 | 10.002279, 905 | 0.235721, 906 | 0.574533, 907 | 1.000065, 908 | 0.252857, 909 | 0.523589, 910 | 1.218935, 911 | 0.249, 912 | 58.940414, 913 | 46.113279, 914 | 0.50202, 915 | 46.931, 916 | 0.514, 917 | 1.994781, 918 | 0.522, 919 | 0.522, 920 | 1.894684, 921 | 0.350229, 922 | 0.507571, 923 | 0.419007, 924 | 0.288791, 925 | 0.399, 926 | 57.240069, 927 | 0.337068, 928 | 0.311669, 929 | 0.505159, 930 | 0.220999, 931 | 0.392231, 932 | 59.909, 933 | 0.41751, 934 | 0.605781, 935 | 1.286781, 936 | 2.001108, 937 | 0.236298, 938 | 0.963579, 939 | 1.816219, 940 | 0.532904, 941 | 0.245587, 942 | 55.897357, 943 | 176.364854, 944 | 0.340835, 945 | 177.383283, 946 | 0.501377, 947 | 2.001014, 948 | 0.317505, 949 | 0.516397, 950 | 0.269353, 951 | 1.271368, 952 | 0.25249, 953 | 0.473642, 954 | 0.649466, 955 | 0.670573, 956 | 0.734657, 957 | 0.677926, 958 | 1.451523, 959 | 76.870491, 960 | 78.247508, 961 | 0.606459, 962 | 0.527945, 963 | 9.001567, 964 | 0.504895, 965 | 0.758745, 966 | 1.448337, 967 | 47.983437, 968 | 58.401718, 969 | 0.49486, 970 | 0.578919, 971 | 1.922523, 972 | 2.001627, 973 | 0.481398, 974 | 0.853882, 975 | 2.203843, 976 | 1.730124, 977 | 0.512, 978 | 57.975477, 979 | 55.351, 980 | 0.735264, 981 | 56.255, 982 | 1.41761, 983 | 2.457523, 984 | 0.739669, 985 | 1.000457, 986 | 0.737933, 987 | 0.91818, 988 | 0.917345, 989 | 0.510508, 990 | 0.74302, 991 | 0.730641, 992 | 0.643949, 993 | 0.504223, 994 | 51.335185, 995 | 0.484523, 996 | 0.698523, 997 | 60.275469, 998 | 0.512, 999 | 0.506679, 1000 | 0.725671, 1001 | 0.499, 1002 | 0.588988, 1003 | 58.960477, 1004 | 117.389807, 1005 | 1.030044, 1006 | 118.327933, 1007 | 0.519962, 1008 | 1.373, 1009 | 0.454414, 1010 | 3.0007, 1011 | 0.506785, 1012 | 1.14477, 1013 | 2.277045, 1014 | 0.476986, 1015 | 3.086823, 1016 | 2.303986, 1017 | 0.732813, 1018 | 0.732813, 1019 | 0.488141, 1020 | 1.114789, 1021 | 0.746343, 1022 | 0.65639, 1023 | 0.75139, 1024 | 111.946618, 1025 | 118.197426, 1026 | 0.489523, 1027 | 119.239034, 1028 | 0.742, 1029 | 2.072802, 1030 | 0.731253, 1031 | 0.731253, 1032 | 2.169376, 1033 | 0.491944, 1034 | 0.936761, 1035 | 0.490986, 1036 | 0.738, 1037 | 0.503486, 1038 | 0.742062, 1039 | 0.750669, 1040 | 0.658344, 1041 | 0.520667, 1042 | 55.917463, 1043 | 0.602241, 1044 | 0.9267, 1045 | 1.758509, 1046 | 3.002459, 1047 | 0.478016, 1048 | 0.930091, 1049 | 1.936704, 1050 | 0.586655, 1051 | 0.654546, 1052 | 0.129382, 1053 | 54.899837, 1054 | 115.481, 1055 | 0.755163, 1056 | 0.547514, 1057 | 116.793633, 1058 | 2.338421, 1059 | 0.740225, 1060 | 3.741748, 1061 | 1.453299, 1062 | 0.583142, 1063 | 0.724, 1064 | 0.506525, 1065 | 0.485417, 1066 | 0.639388, 1067 | 0.65383, 1068 | 0.656132, 1069 | 75.360382, 1070 | 0.480299, 1071 | 0.703469, 1072 | 77.060572, 1073 | 0.412792, 1074 | 0.723159, 1075 | 9.00082, 1076 | 0.49206, 1077 | 0.772958, 1078 | 49.353455, 1079 | 0.463299, 1080 | 0.707299, 1081 | 0.687027, 1082 | 0.74138, 1083 | 58.978671, 1084 | 0.718299, 1085 | 0.858299, 1086 | 2.050578, 1087 | 0.795279, 1088 | 5.002176, 1089 | 124.666677, 1090 | 0.703514, 1091 | 200.334038, 1092 | 0.450841, 1093 | 0.715994, 1094 | 0.701029, 1095 | 0.502707, 1096 | 0.720868, 1097 | 0.629452, 1098 | 54.971824, 1099 | 54.346755, 1100 | 0.397147, 1101 | 55.227343, 1102 | 0.479688, 1103 | 2.295274, 1104 | 0.495949, 1105 | 1.000303, 1106 | 2.550588, 1107 | 0.99522, 1108 | 57.339009, 1109 | 0.465426, 1110 | 58.337016, 1111 | 0.480852, 1112 | 2.324426, 1113 | 0.892174, 1114 | 0.724134, 1115 | 0.46683, 1116 | 0.704792, 1117 | 56.962347, 1118 | 54.293141, 1119 | 0.70965, 1120 | 0.376078, 1121 | 0.718513, 1122 | 1.303983, 1123 | 2.000513, 1124 | 0.45841, 1125 | 3.603308, 1126 | 1.441159, 1127 | 0.457103, 1128 | 0.438055, 1129 | 55.952692, 1130 | 55.272927, 1131 | 0.48557, 1132 | 1.000117, 1133 | 116.611905, 1134 | 0.726182, 1135 | 0.466, 1136 | 0.442036, 1137 | 0.6362, 1138 | 0.723343, 1139 | 58.943883, 1140 | 58.19, 1141 | 0.383336, 1142 | 59.040228, 1143 | 0.505, 1144 | 2.000512, 1145 | 0.567038, 1146 | 1.053071, 1147 | 0.058938, 1148 | 0.471958, 1149 | 0.480402, 1150 | 0.495513, 1151 | 0.405508, 1152 | 57.936488, 1153 | 59.289, 1154 | 60.111986, 1155 | 0.323832, 1156 | 1.799731, 1157 | 1.799731, 1158 | 1.054537, 1159 | 0.463568, 1160 | 0.815924, 1161 | 0.468169, 1162 | 0.508315, 1163 | 57.924269, 1164 | 57.31935, 1165 | 0.327469, 1166 | 58.23284, 1167 | 0.455843, 1168 | 2.001592, 1169 | 0.494576, 1170 | 0.803167, 1171 | 1.681707, 1172 | 0.472445, 1173 | 0.460553, 1174 | 0.648764 1175 | ] -------------------------------------------------------------------------------- /.github/workflows/run_all_steps_output/tornet.plot.data/perfclient_goodput.exit.json: -------------------------------------------------------------------------------- 1 | [ 2 | 4.013527610128748, 3 | 3.68099621357556, 4 | 4.397153598281418, 5 | 4.710874568469505, 6 | 3.8012266064970204, 7 | 3.6004837291116973, 8 | 3.928795588424414, 9 | 3.411458333333334, 10 | 5.330403645833333, 11 | 5.562160326086958, 12 | 3.457559121621621, 13 | 3.928795588424414, 14 | 5.436586985391767, 15 | 3.587861524978089, 16 | 3.5597826086956523, 17 | 3.801067780872795, 18 | 5.915823699421968, 19 | 6.55 20 | ] -------------------------------------------------------------------------------- /.github/workflows/run_all_steps_output/tornet.plot.data/perfclient_goodput.onionservice.json: -------------------------------------------------------------------------------- 1 | [ 2 | 4.345806794055205, 3 | 3.2668615943960404, 4 | 1.900854229380346, 5 | 2.819387052341601, 6 | 2.038142578124028, 7 | 0.9779898238889678, 8 | 2.4855798421372217, 9 | 0.9777924378637538, 10 | 1.3208900516127196, 11 | 0.9765440966164999, 12 | 0.9744703641990001, 13 | 2.1671702883666577, 14 | 1.9580565747414398, 15 | 1.9940331222601098, 16 | 0.9762990948768352, 17 | 1.6641260162601632 18 | ] -------------------------------------------------------------------------------- /.github/workflows/run_all_steps_output/tornet.plot.data/perfclient_goodput_5MiB.exit.json: -------------------------------------------------------------------------------- 1 | [ 2 | 3.8797284190106702, 3 | 3.7278657968313125, 4 | 3.8277511961722492, 5 | 6.633499170812601 6 | ] -------------------------------------------------------------------------------- /.github/workflows/run_all_steps_output/tornet.plot.data/perfclient_goodput_5MiB.onionservice.json: -------------------------------------------------------------------------------- 1 | [ 2 | 2.2120932928225314, 3 | 2.338306814498322, 4 | 2.485310263026597, 5 | 2.308788982458974, 6 | 2.2922636103151874, 7 | 2.095053082098058, 8 | 3.337400790546821 9 | ] -------------------------------------------------------------------------------- /.github/workflows/run_all_steps_output/tornet.plot.data/relay_goodput.json: -------------------------------------------------------------------------------- 1 | { 2 | "300": 19913, 3 | "301": 210061, 4 | "302": 763433, 5 | "303": 521562, 6 | "304": 1197604, 7 | "305": 2015684, 8 | "306": 1764278, 9 | "307": 1376738, 10 | "308": 1455450, 11 | "309": 1865588, 12 | "310": 1842652, 13 | "311": 1537538, 14 | "312": 2337344, 15 | "313": 1906602, 16 | "314": 2351770, 17 | "315": 2518174, 18 | "316": 3940622, 19 | "317": 3256896, 20 | "318": 1347572, 21 | "319": 3412556, 22 | "320": 2912106, 23 | "321": 2454778, 24 | "322": 2223224, 25 | "323": 1246680, 26 | "324": 1422474, 27 | "325": 2014466, 28 | "326": 3766073, 29 | "327": 4608060, 30 | "328": 4018710, 31 | "329": 3041210, 32 | "330": 2677670, 33 | "331": 4178164, 34 | "332": 4890101, 35 | "333": 4812532, 36 | "334": 3571370, 37 | "335": 3870352, 38 | "336": 5767592, 39 | "337": 5663732, 40 | "338": 5891694, 41 | "339": 5241793, 42 | "340": 5513520, 43 | "341": 5549810, 44 | "342": 5282060, 45 | "343": 5378488, 46 | "344": 4905442, 47 | "345": 3587284, 48 | "346": 4272267, 49 | "347": 5123100, 50 | "348": 6773779, 51 | "349": 6370785, 52 | "350": 5392667, 53 | "351": 5234907, 54 | "352": 5071082, 55 | "353": 4658692, 56 | "354": 5721959, 57 | "355": 7354991, 58 | "356": 5197943, 59 | "357": 3591331, 60 | "358": 6417321, 61 | "359": 5397180, 62 | "360": 7420300, 63 | "361": 11382358, 64 | "362": 9139608, 65 | "363": 7280644, 66 | "364": 7426606, 67 | "365": 6785194, 68 | "366": 5982596, 69 | "367": 5502324, 70 | "368": 4918495, 71 | "369": 5921301, 72 | "370": 6073271, 73 | "371": 4017536, 74 | "372": 4200981, 75 | "373": 4711942, 76 | "374": 6470176, 77 | "375": 5134100, 78 | "376": 7758526, 79 | "377": 6127298, 80 | "378": 4942519, 81 | "379": 5224552, 82 | "380": 1751743, 83 | "381": 2323793, 84 | "382": 5030903, 85 | "383": 3423368, 86 | "384": 4891506, 87 | "385": 5780603, 88 | "386": 5330862, 89 | "387": 3162330, 90 | "388": 5715262, 91 | "389": 3183206, 92 | "390": 2877392, 93 | "391": 3351399, 94 | "392": 3868051, 95 | "393": 4711490, 96 | "394": 2738542, 97 | "395": 5597044, 98 | "396": 5384980, 99 | "397": 3643673, 100 | "398": 4607414, 101 | "399": 3149658, 102 | "400": 2975015, 103 | "401": 5227110, 104 | "402": 5318131, 105 | "403": 2917546, 106 | "404": 3244416, 107 | "405": 3086054, 108 | "406": 3184116, 109 | "407": 3492108, 110 | "408": 4248900, 111 | "409": 1992262, 112 | "410": 2620246, 113 | "411": 1963108, 114 | "412": 1235146, 115 | "413": 2579536, 116 | "414": 3439344, 117 | "415": 3395492, 118 | "416": 1846936, 119 | "417": 1066280, 120 | "418": 2300364, 121 | "419": 2620182, 122 | "420": 3261706, 123 | "421": 5330681, 124 | "422": 6036837, 125 | "423": 7058196, 126 | "424": 6513124, 127 | "425": 5257930, 128 | "426": 5344752, 129 | "427": 5438524, 130 | "428": 6534465, 131 | "429": 7292117, 132 | "430": 6259300, 133 | "431": 7479098, 134 | "432": 5543133, 135 | "433": 6077304, 136 | "434": 9869660, 137 | "435": 9420562, 138 | "436": 6979510, 139 | "437": 4498072, 140 | "438": 5933834, 141 | "439": 6331324, 142 | "440": 6623270, 143 | "441": 5873681, 144 | "442": 5133733, 145 | "443": 3466243, 146 | "444": 3312095, 147 | "445": 2478681, 148 | "446": 2300161, 149 | "447": 1349135, 150 | "448": 825641, 151 | "449": 665777, 152 | "450": 772098, 153 | "451": 1656260, 154 | "452": 1893663, 155 | "453": 2269293, 156 | "454": 2051305, 157 | "455": 2064893, 158 | "456": 2483143, 159 | "457": 2086367, 160 | "458": 5072133, 161 | "459": 4355532, 162 | "460": 4358043, 163 | "461": 4484663, 164 | "462": 5366896, 165 | "463": 4076307, 166 | "464": 3341425, 167 | "465": 1719413, 168 | "466": 4048105, 169 | "467": 3331599, 170 | "468": 2530447, 171 | "469": 2669321, 172 | "470": 1590109, 173 | "471": 2806467, 174 | "472": 1526049, 175 | "473": 1371119, 176 | "474": 2422849, 177 | "475": 2923576, 178 | "476": 4264108, 179 | "477": 3748361, 180 | "478": 3204617, 181 | "479": 3182661, 182 | "480": 5520436, 183 | "481": 5545876, 184 | "482": 2489697, 185 | "483": 2347115, 186 | "484": 3034805, 187 | "485": 3792109, 188 | "486": 3100937, 189 | "487": 3098722, 190 | "488": 3465299, 191 | "489": 2697678, 192 | "490": 1890121, 193 | "491": 2779707, 194 | "492": 2736659, 195 | "493": 4150747, 196 | "494": 4792562, 197 | "495": 1343537, 198 | "496": 1082251, 199 | "497": 1110565, 200 | "498": 3295775, 201 | "499": 3599227, 202 | "500": 3179217, 203 | "501": 3239193, 204 | "502": 3994119, 205 | "503": 2062285, 206 | "504": 1542438, 207 | "505": 2044010, 208 | "506": 3756774, 209 | "507": 1099543, 210 | "508": 2237923, 211 | "509": 2800643, 212 | "510": 2462254, 213 | "511": 1582077, 214 | "512": 1348585, 215 | "513": 1940304, 216 | "514": 3675035, 217 | "515": 3784457, 218 | "516": 2337627, 219 | "517": 2402749, 220 | "518": 1494167, 221 | "519": 3763293, 222 | "520": 4457303, 223 | "521": 3072995, 224 | "522": 1508733, 225 | "523": 559780, 226 | "524": 1445642, 227 | "525": 1828185, 228 | "526": 4369323, 229 | "527": 3832220, 230 | "528": 2861522, 231 | "529": 1058406, 232 | "530": 2188823, 233 | "531": 1502397, 234 | "532": 539744, 235 | "533": 1661081, 236 | "534": 1239785, 237 | "535": 3221609, 238 | "536": 2487713, 239 | "537": 1825201, 240 | "538": 2170176, 241 | "539": 2875770, 242 | "540": 5147223, 243 | "541": 5468785, 244 | "542": 4058905, 245 | "543": 2706717, 246 | "544": 3466285, 247 | "545": 1982101, 248 | "546": 2524645, 249 | "547": 2921699, 250 | "548": 1758127, 251 | "549": 3355080, 252 | "550": 4977954, 253 | "551": 3411467, 254 | "552": 5300090, 255 | "553": 6561735, 256 | "554": 6275135, 257 | "555": 3500431, 258 | "556": 6196447, 259 | "557": 3266951, 260 | "558": 5801543, 261 | "559": 3661047, 262 | "560": 3115277, 263 | "561": 2874193, 264 | "562": 2279299, 265 | "563": 3667955, 266 | "564": 3332335, 267 | "565": 2331643, 268 | "566": 3702409, 269 | "567": 3662966, 270 | "568": 5316000, 271 | "569": 4408330, 272 | "570": 3713560, 273 | "571": 3588399, 274 | "572": 2988593, 275 | "573": 5188277, 276 | "574": 3045789, 277 | "575": 1929347, 278 | "576": 1531815, 279 | "577": 1606727, 280 | "578": 2366677, 281 | "579": 2339177, 282 | "580": 1375007, 283 | "581": 1266803, 284 | "582": 1364475, 285 | "583": 940601, 286 | "584": 1112617, 287 | "585": 2209881, 288 | "586": 3580435, 289 | "587": 1776359, 290 | "588": 909337, 291 | "589": 951143, 292 | "590": 822899, 293 | "591": 1106051, 294 | "592": 1579545, 295 | "593": 2525027, 296 | "594": 2075540, 297 | "595": 2145364, 298 | "596": 2227110, 299 | "597": 1977023, 300 | "598": 2545081, 301 | "599": 1443449, 302 | "600": 1757421, 303 | "601": 2566091, 304 | "602": 2930546, 305 | "603": 3141468, 306 | "604": 2370772, 307 | "605": 2427918, 308 | "606": 2404920, 309 | "607": 1824228, 310 | "608": 3147816, 311 | "609": 2670331, 312 | "610": 2813628, 313 | "611": 1628862, 314 | "612": 1096964, 315 | "613": 739689, 316 | "614": 1552960, 317 | "615": 822066, 318 | "616": 1920495, 319 | "617": 1759353, 320 | "618": 2118534, 321 | "619": 3123219, 322 | "620": 3217332, 323 | "621": 3685796, 324 | "622": 2486764, 325 | "623": 2766690, 326 | "624": 2465073, 327 | "625": 3620455, 328 | "626": 4143594, 329 | "627": 2986522, 330 | "628": 3323172, 331 | "629": 2593719, 332 | "630": 2689922, 333 | "631": 3076248, 334 | "632": 4594650, 335 | "633": 3620738, 336 | "634": 3649178, 337 | "635": 4490857, 338 | "636": 4002308, 339 | "637": 3526601, 340 | "638": 2281715, 341 | "639": 3318575, 342 | "640": 3185902, 343 | "641": 3511825, 344 | "642": 3455784, 345 | "643": 4593330, 346 | "644": 2911372, 347 | "645": 1755770, 348 | "646": 2026541, 349 | "647": 2683872, 350 | "648": 3731044, 351 | "649": 2015894, 352 | "650": 1669351, 353 | "651": 1980946, 354 | "652": 2980580, 355 | "653": 4237842, 356 | "654": 4933428, 357 | "655": 4333326, 358 | "656": 3731773, 359 | "657": 6093447, 360 | "658": 3893100, 361 | "659": 4248264, 362 | "660": 2236063, 363 | "661": 3201625, 364 | "662": 5970474, 365 | "663": 5888611, 366 | "664": 5072418, 367 | "665": 6014198, 368 | "666": 6401458, 369 | "667": 5347123, 370 | "668": 5053144, 371 | "669": 2760895, 372 | "670": 2746404, 373 | "671": 4393221, 374 | "672": 5706440, 375 | "673": 4388396, 376 | "674": 4334828, 377 | "675": 3950192, 378 | "676": 3840790, 379 | "677": 3538649, 380 | "678": 2787481, 381 | "679": 3043976, 382 | "680": 3533316, 383 | "681": 3971654, 384 | "682": 2415512, 385 | "683": 2503156, 386 | "684": 3623536, 387 | "685": 3847994, 388 | "686": 3319850, 389 | "687": 3067636, 390 | "688": 1773333, 391 | "689": 1130727, 392 | "690": 2697662, 393 | "691": 1671201, 394 | "692": 1610592, 395 | "693": 1201424, 396 | "694": 1108185, 397 | "695": 659598, 398 | "696": 1445583, 399 | "697": 2137032, 400 | "698": 2285324, 401 | "699": 2249300, 402 | "700": 3955206, 403 | "701": 3575184, 404 | "702": 3328597, 405 | "703": 2948486, 406 | "704": 2383684, 407 | "705": 3597390, 408 | "706": 4827086, 409 | "707": 6586990, 410 | "708": 5449314, 411 | "709": 3758954, 412 | "710": 2831160, 413 | "711": 3199453, 414 | "712": 1558677, 415 | "713": 2118262, 416 | "714": 1066488, 417 | "715": 474183, 418 | "716": 2925797, 419 | "717": 3002848, 420 | "718": 2922704, 421 | "719": 2511367, 422 | "720": 4145796, 423 | "721": 4152958, 424 | "722": 6037075, 425 | "723": 5038227, 426 | "724": 2736018, 427 | "725": 2502018, 428 | "726": 1994980, 429 | "727": 2855617, 430 | "728": 3243506, 431 | "729": 2975432, 432 | "730": 3220426, 433 | "731": 3677490, 434 | "732": 3965412, 435 | "733": 3290711, 436 | "734": 1982098, 437 | "735": 2406717, 438 | "736": 1709944, 439 | "737": 2953075, 440 | "738": 3519022, 441 | "739": 4210724, 442 | "740": 4500082, 443 | "741": 4390098, 444 | "742": 3498360, 445 | "743": 2948152, 446 | "744": 2567086, 447 | "745": 3494071, 448 | "746": 3857810, 449 | "747": 3312076, 450 | "748": 2800014, 451 | "749": 3138352, 452 | "750": 3402111, 453 | "751": 2813918, 454 | "752": 5581176, 455 | "753": 3770200, 456 | "754": 3775806, 457 | "755": 3675607, 458 | "756": 2952058, 459 | "757": 1408747, 460 | "758": 296598, 461 | "759": 286533, 462 | "760": 759180, 463 | "761": 1213847, 464 | "762": 1504509, 465 | "763": 1286780, 466 | "764": 1297538, 467 | "765": 1144424, 468 | "766": 3251692, 469 | "767": 2148486, 470 | "768": 1511194, 471 | "769": 1576848, 472 | "770": 1197855, 473 | "771": 767784, 474 | "772": 578019, 475 | "773": 851569, 476 | "774": 521691, 477 | "775": 30174, 478 | "776": 454096, 479 | "777": 502800, 480 | "778": 227270, 481 | "779": 155941, 482 | "780": 2475238, 483 | "781": 4035469, 484 | "782": 3060882, 485 | "783": 2296136, 486 | "784": 3213646, 487 | "785": 1382033, 488 | "786": 1689074, 489 | "787": 2111642, 490 | "788": 2115936, 491 | "789": 2506608, 492 | "790": 2609132, 493 | "791": 3118626, 494 | "792": 3351892, 495 | "793": 4073816, 496 | "794": 4404954, 497 | "795": 4521490, 498 | "796": 4541842, 499 | "797": 4150986, 500 | "798": 5878772, 501 | "799": 5274802, 502 | "800": 4931352, 503 | "801": 5290990, 504 | "802": 4051550, 505 | "803": 6335646, 506 | "804": 4823702, 507 | "805": 5838041, 508 | "806": 5406335, 509 | "807": 7198689, 510 | "808": 4681141, 511 | "809": 4639499, 512 | "810": 3575359, 513 | "811": 3607212, 514 | "812": 1517519, 515 | "813": 2555829, 516 | "814": 1266627, 517 | "815": 1750049, 518 | "816": 1186589, 519 | "817": 1616690, 520 | "818": 1469510, 521 | "819": 2818784, 522 | "820": 1749330, 523 | "821": 2412000, 524 | "822": 4607772, 525 | "823": 4857084, 526 | "824": 1725002, 527 | "825": 971559, 528 | "826": 1487529, 529 | "827": 2090306, 530 | "828": 2641966, 531 | "829": 1759627, 532 | "830": 2241764, 533 | "831": 2878786, 534 | "832": 1448696, 535 | "833": 1703701, 536 | "834": 1579990, 537 | "835": 2299477, 538 | "836": 1321940, 539 | "837": 1373828, 540 | "838": 2224948, 541 | "839": 2481959, 542 | "840": 3711372, 543 | "841": 2849758, 544 | "842": 1630686, 545 | "843": 2148226, 546 | "844": 1466955, 547 | "845": 1709840, 548 | "846": 3791451, 549 | "847": 3272957, 550 | "848": 3360321, 551 | "849": 2107202, 552 | "850": 1127834, 553 | "851": 923391, 554 | "852": 845782, 555 | "853": 533320, 556 | "854": 229086, 557 | "855": 379536, 558 | "856": 1203988, 559 | "857": 2978565, 560 | "858": 1530165, 561 | "859": 1279979, 562 | "860": 2042958, 563 | "861": 2098358, 564 | "862": 1658492, 565 | "863": 2416707, 566 | "864": 2101769, 567 | "865": 2122288, 568 | "866": 1736228, 569 | "867": 2279454, 570 | "868": 2103212, 571 | "869": 3247181, 572 | "870": 2219559, 573 | "871": 2008328, 574 | "872": 3717194, 575 | "873": 3365490, 576 | "874": 1539264, 577 | "875": 1112191, 578 | "876": 708982, 579 | "877": 1593862, 580 | "878": 3247116, 581 | "879": 2735930, 582 | "880": 4005868, 583 | "881": 2706152, 584 | "882": 1116950, 585 | "883": 1334004, 586 | "884": 2444868, 587 | "885": 3651532, 588 | "886": 3248138, 589 | "887": 2075186, 590 | "888": 2762466, 591 | "889": 2759116, 592 | "890": 3077844, 593 | "891": 3156634, 594 | "892": 3300949, 595 | "893": 4170056, 596 | "894": 2535882, 597 | "895": 4289858, 598 | "896": 4865460, 599 | "897": 6993496, 600 | "898": 5065850, 601 | "899": 1772754 602 | } -------------------------------------------------------------------------------- /.github/workflows/run_all_steps_output/tornet.plot.data/round_trip_time.exit.json: -------------------------------------------------------------------------------- 1 | [ 2 | 0.688594, 3 | 0.384936, 4 | 0.572839, 5 | 0.716, 6 | 0.469484, 7 | 0.740644, 8 | 0.64869, 9 | 0.405279, 10 | 0.57, 11 | 0.404316, 12 | 0.433528, 13 | 0.572839, 14 | 1.004279, 15 | 0.469484, 16 | 0.484402, 17 | 0.379954, 18 | 0.367716, 19 | 0.641055, 20 | 0.39433, 21 | 0.645915, 22 | 0.429673, 23 | 0.416657, 24 | 0.644993, 25 | 0.484402, 26 | 0.716622, 27 | 0.544788, 28 | 0.581, 29 | 0.543, 30 | 0.384936, 31 | 0.683494, 32 | 0.375004, 33 | 0.628064, 34 | 0.676725, 35 | 0.412, 36 | 0.683008, 37 | 0.467374, 38 | 0.543, 39 | 0.433528, 40 | 0.639152, 41 | 0.668684, 42 | 0.638064, 43 | 0.721233, 44 | 0.706598, 45 | 0.683008, 46 | 0.500164, 47 | 0.383684, 48 | 0.645915, 49 | 0.649873, 50 | 0.375004, 51 | 0.567154, 52 | 0.706922, 53 | 0.933, 54 | 0.545788, 55 | 0.581, 56 | 0.543, 57 | 0.557472, 58 | 0.402, 59 | 0.705861, 60 | 0.427346, 61 | 0.696922, 62 | 0.411, 63 | 0.545788, 64 | 0.641055, 65 | 0.195, 66 | 0.228528, 67 | 0.369674, 68 | 0.221, 69 | 0.443971, 70 | 0.353315, 71 | 0.358, 72 | 0.340384, 73 | 0.320718, 74 | 0.22, 75 | 0.228528, 76 | 0.370933, 77 | 0.357, 78 | 0.191569, 79 | 0.345962, 80 | 0.403, 81 | 0.174964, 82 | 0.32056, 83 | 0.25, 84 | 0.481, 85 | 0.234925, 86 | 0.396, 87 | 0.263972, 88 | 0.48431, 89 | 0.763279, 90 | 0.212572, 91 | 0.309129 92 | ] -------------------------------------------------------------------------------- /.github/workflows/run_all_steps_output/tornet.plot.data/round_trip_time.onionservice.json: -------------------------------------------------------------------------------- 1 | [ 2 | 0.4262, 3 | 0.442866, 4 | 0.706722, 5 | 0.344953, 6 | 0.780173, 7 | 0.841881, 8 | 0.761902, 9 | 0.697657, 10 | 0.715248, 11 | 0.654185, 12 | 0.931939, 13 | 0.819882, 14 | 0.587279, 15 | 0.56027, 16 | 0.57922, 17 | 0.640845, 18 | 0.644, 19 | 0.622877, 20 | 0.773, 21 | 0.500279, 22 | 0.574276, 23 | 0.719073, 24 | 0.986, 25 | 0.551432, 26 | 1.1, 27 | 0.896379, 28 | 0.943, 29 | 0.507388, 30 | 0.395, 31 | 0.674956, 32 | 0.655, 33 | 0.964, 34 | 0.480397, 35 | 0.829743, 36 | 0.911118, 37 | 0.986361, 38 | 0.461415, 39 | 0.551155, 40 | 0.489486, 41 | 0.709575, 42 | 0.645382, 43 | 0.529482, 44 | 0.763972, 45 | 0.416328, 46 | 0.944, 47 | 0.798, 48 | 0.535239, 49 | 0.723099, 50 | 0.551327, 51 | 0.949088, 52 | 0.712342 53 | ] -------------------------------------------------------------------------------- /.github/workflows/run_all_steps_output/tornet.plot.data/simulation_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "net_scale": 0.0001, 3 | "num_dir_authorities": 3, 4 | "num_exit_circuits_ten_minutes": 150, 5 | "num_hs_circuits_ten_minutes": 40, 6 | "num_public_relays": 6793, 7 | "num_sampled_relays": 4, 8 | "num_tgen_exit_emulated_users": 75.05, 9 | "num_tgen_exit_markov_clients": 75, 10 | "num_tgen_exit_perf_clients": 10, 11 | "num_tgen_exit_servers": 8, 12 | "num_tgen_hs_emulated_users": 19.75, 13 | "num_tgen_hs_markov_clients": 20, 14 | "num_tgen_hs_perf_clients": 10, 15 | "num_tgen_onionservice_servers": 2, 16 | "tornettools_generate_seed": 1 17 | } -------------------------------------------------------------------------------- /.github/workflows/run_all_steps_output/tornet.plot.data/time_to_first_byte_recv.exit.json: -------------------------------------------------------------------------------- 1 | { 2 | "1048576": [ 3 | 0.716, 4 | 0.749994, 5 | 0.65869, 6 | 0.469484, 7 | 0.484402, 8 | 0.716622, 9 | 0.543, 10 | 0.384936, 11 | 0.375004, 12 | 0.716, 13 | 0.383684, 14 | 0.649873, 15 | 0.545788, 16 | 0.481 17 | ], 18 | "51200": [ 19 | 0.688594, 20 | 0.384936, 21 | 0.572839, 22 | 0.469484, 23 | 0.405279, 24 | 0.58, 25 | 0.404316, 26 | 0.433528, 27 | 0.572839, 28 | 1.004279, 29 | 0.379954, 30 | 0.367716, 31 | 0.641055, 32 | 0.403399, 33 | 0.645915, 34 | 0.429673, 35 | 0.416657, 36 | 0.484402, 37 | 0.544788, 38 | 0.581, 39 | 0.693437, 40 | 0.696018, 41 | 0.412, 42 | 0.683008, 43 | 0.467374, 44 | 0.543, 45 | 0.433528, 46 | 0.649873, 47 | 0.697567, 48 | 0.638064, 49 | 0.730801, 50 | 0.683008, 51 | 0.500164, 52 | 0.375004, 53 | 0.567154, 54 | 0.706922, 55 | 0.944, 56 | 0.581, 57 | 0.543, 58 | 0.566751, 59 | 0.402, 60 | 0.716, 61 | 0.427346, 62 | 0.707643, 63 | 0.411, 64 | 0.545788, 65 | 0.641055, 66 | 0.195, 67 | 0.228528, 68 | 0.369674, 69 | 0.221, 70 | 0.453641, 71 | 0.353315, 72 | 0.358, 73 | 0.349322, 74 | 0.320718, 75 | 0.22, 76 | 0.228528, 77 | 0.370933, 78 | 0.357, 79 | 0.191569, 80 | 0.345962, 81 | 0.403, 82 | 0.174964, 83 | 0.32056, 84 | 0.25, 85 | 0.234925, 86 | 0.396, 87 | 0.263972, 88 | 0.763279, 89 | 0.212572, 90 | 0.309129 91 | ], 92 | "5242880": [ 93 | 0.644993, 94 | 0.638064, 95 | 0.645915, 96 | 0.48431 97 | ], 98 | "ALL": [ 99 | 0.688594, 100 | 0.384936, 101 | 0.572839, 102 | 0.469484, 103 | 0.405279, 104 | 0.58, 105 | 0.716, 106 | 0.749994, 107 | 0.65869, 108 | 0.404316, 109 | 0.433528, 110 | 0.572839, 111 | 1.004279, 112 | 0.379954, 113 | 0.367716, 114 | 0.641055, 115 | 0.469484, 116 | 0.484402, 117 | 0.403399, 118 | 0.645915, 119 | 0.429673, 120 | 0.416657, 121 | 0.484402, 122 | 0.544788, 123 | 0.581, 124 | 0.716622, 125 | 0.644993, 126 | 0.693437, 127 | 0.696018, 128 | 0.412, 129 | 0.683008, 130 | 0.467374, 131 | 0.543, 132 | 0.384936, 133 | 0.375004, 134 | 0.638064, 135 | 0.543, 136 | 0.433528, 137 | 0.649873, 138 | 0.697567, 139 | 0.638064, 140 | 0.730801, 141 | 0.683008, 142 | 0.500164, 143 | 0.716, 144 | 0.375004, 145 | 0.567154, 146 | 0.706922, 147 | 0.944, 148 | 0.581, 149 | 0.383684, 150 | 0.649873, 151 | 0.545788, 152 | 0.645915, 153 | 0.543, 154 | 0.566751, 155 | 0.402, 156 | 0.716, 157 | 0.427346, 158 | 0.707643, 159 | 0.411, 160 | 0.545788, 161 | 0.641055, 162 | 0.195, 163 | 0.228528, 164 | 0.369674, 165 | 0.221, 166 | 0.453641, 167 | 0.353315, 168 | 0.358, 169 | 0.349322, 170 | 0.320718, 171 | 0.22, 172 | 0.228528, 173 | 0.370933, 174 | 0.357, 175 | 0.191569, 176 | 0.345962, 177 | 0.403, 178 | 0.174964, 179 | 0.32056, 180 | 0.25, 181 | 0.234925, 182 | 0.396, 183 | 0.263972, 184 | 0.763279, 185 | 0.212572, 186 | 0.309129, 187 | 0.481, 188 | 0.48431 189 | ] 190 | } -------------------------------------------------------------------------------- /.github/workflows/run_all_steps_output/tornet.plot.data/time_to_first_byte_recv.onionservice.json: -------------------------------------------------------------------------------- 1 | { 2 | "1048576": [ 3 | 0.4262, 4 | 0.780173, 5 | 0.697657, 6 | 0.819882, 7 | 0.587279, 8 | 0.943, 9 | 0.480397, 10 | 0.709575, 11 | 0.944 12 | ], 13 | "51200": [ 14 | 0.706722, 15 | 0.344953, 16 | 0.841881, 17 | 0.761902, 18 | 0.654185, 19 | 0.931939, 20 | 0.56027, 21 | 0.57922, 22 | 0.644, 23 | 0.622877, 24 | 0.773, 25 | 0.500279, 26 | 0.574276, 27 | 0.551432, 28 | 1.1, 29 | 0.896379, 30 | 0.507388, 31 | 0.395, 32 | 0.674956, 33 | 0.964, 34 | 0.829743, 35 | 0.911118, 36 | 0.986361, 37 | 0.461415, 38 | 0.551155, 39 | 0.645382, 40 | 0.529482, 41 | 0.763972, 42 | 0.416328, 43 | 0.798, 44 | 0.535239, 45 | 0.723099, 46 | 0.551327, 47 | 0.949088, 48 | 0.712342 49 | ], 50 | "5242880": [ 51 | 0.442866, 52 | 0.715248, 53 | 0.640845, 54 | 0.719073, 55 | 0.986, 56 | 0.655, 57 | 0.489486 58 | ], 59 | "ALL": [ 60 | 0.706722, 61 | 0.344953, 62 | 0.841881, 63 | 0.4262, 64 | 0.780173, 65 | 0.442866, 66 | 0.761902, 67 | 0.654185, 68 | 0.931939, 69 | 0.697657, 70 | 0.819882, 71 | 0.715248, 72 | 0.56027, 73 | 0.57922, 74 | 0.644, 75 | 0.587279, 76 | 0.640845, 77 | 0.622877, 78 | 0.773, 79 | 0.500279, 80 | 0.574276, 81 | 0.719073, 82 | 0.986, 83 | 0.551432, 84 | 1.1, 85 | 0.896379, 86 | 0.507388, 87 | 0.943, 88 | 0.395, 89 | 0.674956, 90 | 0.964, 91 | 0.480397, 92 | 0.655, 93 | 0.829743, 94 | 0.911118, 95 | 0.986361, 96 | 0.461415, 97 | 0.551155, 98 | 0.645382, 99 | 0.709575, 100 | 0.489486, 101 | 0.529482, 102 | 0.763972, 103 | 0.416328, 104 | 0.798, 105 | 0.944, 106 | 0.535239, 107 | 0.723099, 108 | 0.551327, 109 | 0.949088, 110 | 0.712342 111 | ] 112 | } -------------------------------------------------------------------------------- /.github/workflows/run_all_steps_output/tornet.plot.data/time_to_last_byte_recv.exit.json: -------------------------------------------------------------------------------- 1 | { 2 | "1048576": [ 3 | 3.599, 4 | 3.830801, 5 | 3.391, 6 | 2.366484, 7 | 2.875562, 8 | 3.623, 9 | 2.903, 10 | 2.018657, 11 | 1.975, 12 | 3.622, 13 | 2.103, 14 | 3.265873, 15 | 2.788788, 16 | 2.472 17 | ], 18 | "51200": [ 19 | 1.413594, 20 | 0.641657, 21 | 0.836124, 22 | 0.742763, 23 | 0.666788, 24 | 0.937, 25 | 0.681316, 26 | 0.722292, 27 | 0.836124, 28 | 1.267, 29 | 0.600954, 30 | 0.572337, 31 | 1.232107, 32 | 0.681316, 33 | 1.274915, 34 | 0.680295, 35 | 0.629657, 36 | 0.789402, 37 | 0.744788, 38 | 0.937, 39 | 1.316287, 40 | 1.559197, 41 | 0.626, 42 | 1.437008, 43 | 1.053969, 44 | 0.838, 45 | 0.722292, 46 | 1.270873, 47 | 1.562949, 48 | 1.267064, 49 | 1.602793, 50 | 1.437008, 51 | 1.101481, 52 | 0.613, 53 | 0.812556, 54 | 1.581643, 55 | 1.193337, 56 | 0.938, 57 | 0.823, 58 | 0.825476, 59 | 0.653, 60 | 1.59, 61 | 0.674484, 62 | 1.580643, 63 | 0.626, 64 | 0.744788, 65 | 1.232107, 66 | 0.368698, 67 | 0.492, 68 | 0.631276, 69 | 0.431, 70 | 1.082641, 71 | 0.579553, 72 | 0.581, 73 | 0.542384, 74 | 0.904402, 75 | 0.393, 76 | 0.492, 77 | 0.622933, 78 | 0.573, 79 | 0.446, 80 | 0.570962, 81 | 1.319, 82 | 0.388348, 83 | 0.922839, 84 | 0.423, 85 | 0.479, 86 | 0.618, 87 | 0.525251, 88 | 1.280337, 89 | 0.408638, 90 | 0.914129 91 | ], 92 | "5242880": [ 93 | 11.770929, 94 | 11.860064, 95 | 11.807915, 96 | 7.714031 97 | ], 98 | "ALL": [ 99 | 1.413594, 100 | 0.641657, 101 | 0.836124, 102 | 0.742763, 103 | 0.666788, 104 | 0.937, 105 | 3.599, 106 | 3.830801, 107 | 3.391, 108 | 0.681316, 109 | 0.722292, 110 | 0.836124, 111 | 1.267, 112 | 0.600954, 113 | 0.572337, 114 | 1.232107, 115 | 2.366484, 116 | 2.875562, 117 | 0.681316, 118 | 1.274915, 119 | 0.680295, 120 | 0.629657, 121 | 0.789402, 122 | 0.744788, 123 | 0.937, 124 | 3.623, 125 | 11.770929, 126 | 1.316287, 127 | 1.559197, 128 | 0.626, 129 | 1.437008, 130 | 1.053969, 131 | 2.903, 132 | 2.018657, 133 | 1.975, 134 | 11.860064, 135 | 0.838, 136 | 0.722292, 137 | 1.270873, 138 | 1.562949, 139 | 1.267064, 140 | 1.602793, 141 | 1.437008, 142 | 1.101481, 143 | 3.622, 144 | 0.613, 145 | 0.812556, 146 | 1.581643, 147 | 1.193337, 148 | 0.938, 149 | 2.103, 150 | 3.265873, 151 | 2.788788, 152 | 11.807915, 153 | 0.823, 154 | 0.825476, 155 | 0.653, 156 | 1.59, 157 | 0.674484, 158 | 1.580643, 159 | 0.626, 160 | 0.744788, 161 | 1.232107, 162 | 0.368698, 163 | 0.492, 164 | 0.631276, 165 | 0.431, 166 | 1.082641, 167 | 0.579553, 168 | 0.581, 169 | 0.542384, 170 | 0.904402, 171 | 0.393, 172 | 0.492, 173 | 0.622933, 174 | 0.573, 175 | 0.446, 176 | 0.570962, 177 | 1.319, 178 | 0.388348, 179 | 0.922839, 180 | 0.423, 181 | 0.479, 182 | 0.618, 183 | 0.525251, 184 | 1.280337, 185 | 0.408638, 186 | 0.914129, 187 | 2.472, 188 | 7.714031 189 | ] 190 | } -------------------------------------------------------------------------------- /.github/workflows/run_all_steps_output/tornet.plot.data/time_to_last_byte_recv.onionservice.json: -------------------------------------------------------------------------------- 1 | { 2 | "1048576": [ 3 | 2.2402, 4 | 6.355079, 5 | 3.548657, 6 | 10.456882, 7 | 5.002, 8 | 11.52998, 9 | 5.296729, 10 | 11.749147, 11 | 5.925 12 | ], 13 | "51200": [ 14 | 0.962078, 15 | 0.42261, 16 | 1.838881, 17 | 0.878999, 18 | 0.801, 19 | 1.26566, 20 | 0.781837, 21 | 0.666258, 22 | 0.905, 23 | 0.653, 24 | 0.886, 25 | 0.654, 26 | 0.606209, 27 | 0.589469, 28 | 1.663, 29 | 1.179403, 30 | 1.037145, 31 | 0.413, 32 | 0.763532, 33 | 1.26198, 34 | 1.257827, 35 | 1.037, 36 | 1.053911, 37 | 0.544973, 38 | 0.6631, 39 | 1.40666, 40 | 0.56931, 41 | 0.794721, 42 | 0.613328, 43 | 1.049, 44 | 0.566239, 45 | 0.801532, 46 | 0.616674, 47 | 1.477134, 48 | 0.867292 49 | ], 50 | "5242880": [ 51 | 15.782465, 52 | 25.568, 53 | 25.706759, 54 | 21.420073, 55 | 24.165914, 56 | 18.123334, 57 | 21.729893 58 | ], 59 | "ALL": [ 60 | 0.962078, 61 | 0.42261, 62 | 1.838881, 63 | 2.2402, 64 | 6.355079, 65 | 15.782465, 66 | 0.878999, 67 | 0.801, 68 | 1.26566, 69 | 3.548657, 70 | 10.456882, 71 | 25.568, 72 | 0.781837, 73 | 0.666258, 74 | 0.905, 75 | 5.002, 76 | 25.706759, 77 | 0.653, 78 | 0.886, 79 | 0.654, 80 | 0.606209, 81 | 21.420073, 82 | 24.165914, 83 | 0.589469, 84 | 1.663, 85 | 1.179403, 86 | 1.037145, 87 | 11.52998, 88 | 0.413, 89 | 0.763532, 90 | 1.26198, 91 | 5.296729, 92 | 18.123334, 93 | 1.257827, 94 | 1.037, 95 | 1.053911, 96 | 0.544973, 97 | 0.6631, 98 | 1.40666, 99 | 11.749147, 100 | 21.729893, 101 | 0.56931, 102 | 0.794721, 103 | 0.613328, 104 | 1.049, 105 | 5.925, 106 | 0.566239, 107 | 0.801532, 108 | 0.616674, 109 | 1.477134, 110 | 0.867292 111 | ] 112 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | /toolsenv 3 | /build 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DISTRIBUTION STATEMENT 2 | 3 | Approved for public release: distribution unlimited. 4 | 5 | Redistributions of source and binary forms, with or without 6 | modification, are permitted if redistributions retain the above 7 | distribution statement and the following disclaimer. 8 | 9 | DISCLAIMER 10 | 11 | THE SOFTWARE IS SUPPLIED “AS IS” WITHOUT WARRANTY OF ANY KIND. 12 | 13 | AS THE OWNER OF THE SOFTWARE, THE UNITED STATES, THE UNITED STATES 14 | DEPARTMENT OF DEFENSE, AND THEIR EMPLOYEES: (1) DISCLAIM ANY 15 | WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY 16 | IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 17 | PURPOSE, TITLE OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL 18 | LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR 19 | USEFULNESS OF THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF THE 20 | SOFTWARE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS, (4) DO NOT WARRANT 21 | THAT THE SOFTWARE WILL FUNCTION UNINTERRUPTED, THAT IT IS ERROR-FREE 22 | OR THAT ANY ERRORS WILL BE CORRECTED. 23 | 24 | PORTIONS OF THE SOFTWARE RESULTED FROM WORK DEVELOPED BY OR FOR THE 25 | U.S. GOVERNMENT SUBJECT TO THE FOLLOWING LICENSE: THE GOVERNMENT IS 26 | GRANTED FOR ITSELF AND OTHERS ACTING ON ITS BEHALF A PAID-UP, 27 | NONEXCLUSIVE, IRREVOCABLE WORLDWIDE LICENSE IN THIS COMPUTER SOFTWARE 28 | TO REPRODUCE, PREPARE DERIVATIVE WORKS, TO PERFORM OR DISPLAY ANY 29 | PORTION OF THAT WORK, AND TO PERMIT OTHERS TO DO SO FOR GOVERNMENT 30 | PURPOSES. 31 | 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tornettools 2 | 3 | ![](https://github.com/shadow/tornettools/workflows/Build/badge.svg) 4 | 5 | tornettools is a utility to guide you through the Tor network 6 | experimentation process using Shadow, by assisting with the 7 | following experimentation steps: 8 | 9 | - **stage**: Process Tor metrics data for staging network generation 10 | - **generate**: Generate TorNet network configurations 11 | - **simulate**: Run a TorNet simulation in Shadow 12 | - **parse**: Parse useful data from simulation log files 13 | - **plot**: Plot previously parsed data to visualize results 14 | - **archive**: Cleanup and compress Shadow simulation data 15 | 16 | The configuration files that are generated can be run in the 17 | [Shadow network simulator](https://github.com/shadow/shadow); 18 | [NetMirage](https://crysp.uwaterloo.ca/software/netmirage) 19 | and 20 | [Chutney](https://gitweb.torproject.org/chutney.git) 21 | may eventually support the files generated with this tool. 22 | 23 | The generated networks include the use of 24 | [TGen](https://github.com/shadow/tgen) 25 | for the generation of realistic background traffic, and 26 | [OnionTrace](https://github.com/shadow/oniontrace) 27 | for the collection of information from Tor throughout an experiment. 28 | 29 | ### Citation 30 | 31 | This tool was initially created as part of the following research publication. Please cite this paper if you use this tool in your work: 32 | 33 | [Once is Never Enough: Foundations for Sound Statistical Inference in Tor Network Experimentation](https://www.robgjansen.com/publications/neverenough-sec2021.pdf) 34 | _Proceedings of [the 30th USENIX Security Symposium](https://www.usenix.org/conference/usenixsecurity21) (Sec 2021)_ 35 | by [Rob Jansen](https://www.robgjansen.com), Justin Tracey, and [Ian Goldberg](https://cs.uwaterloo.ca/~iang) 36 | 37 | Here is a bibtex entry for latex users: 38 | 39 | ```bibtex 40 | @inproceedings{neverenough-sec2021, 41 | author = {Rob Jansen and Justin Tracey and Ian Goldberg}, 42 | title = {Once is Never Enough: Foundations for Sound Statistical Inference in {Tor} Network Experimentation}, 43 | booktitle = {30th USENIX Security Symposium (Sec)}, 44 | year = {2021}, 45 | note = {See also \url{https://neverenough-sec2021.github.io}}, 46 | } 47 | ``` 48 | 49 | ### Note about versioning 50 | 51 | Development of tornettools is slow and typically focuses on changes that keep the tool in 52 | sync with changes being made in Shadow. Thus, you should generally just ignore the tornettools 53 | version numbers and use the latest version. Sometimes you might want to pin to a specific 54 | version that is known to be compatible with your version of Shadow, e.g., when building 55 | container images, and in this case we recommend pinning to a specific commit hash. 56 | 57 | We do use version numbers, but they have historically been based on vibes rather than semver 58 | and we don't want to introduce the overhead of following a more strict policy than that. 59 | 60 | ### setup is easy with virtualenv and pip 61 | 62 | python3 -m venv toolsenv 63 | source toolsenv/bin/activate 64 | pip install -r requirements.txt 65 | # if you plan to make changes to tornettools, you can add the '--editable' pip install flag 66 | # to avoid the need to re-run 'pip install' after every modification: 67 | # https://pip.pypa.io/en/stable/topics/local-project-installs/#editable-installs 68 | pip install --ignore-installed . 69 | 70 | ### read the help menus 71 | 72 | tornettools -h 73 | tornettools stage -h 74 | tornettools generate -h 75 | tornettools simulate -h 76 | tornettools parse -h 77 | tornettools plot -h 78 | tornettools archive -h 79 | 80 | ### grab the data we need 81 | 82 | wget https://collector.torproject.org/archive/relay-descriptors/consensuses/consensuses-2023-04.tar.xz 83 | wget https://collector.torproject.org/archive/relay-descriptors/server-descriptors/server-descriptors-2023-04.tar.xz 84 | wget https://metrics.torproject.org/userstats-relay-country.csv 85 | wget https://collector.torproject.org/archive/onionperf/onionperf-2023-04.tar.xz 86 | wget -O bandwidth-2023-04.csv "https://metrics.torproject.org/bandwidth.csv?start=2023-04-01&end=2023-04-30" 87 | 88 | ### extract 89 | 90 | tar xaf consensuses-2023-04.tar.xz 91 | tar xaf server-descriptors-2023-04.tar.xz 92 | tar xaf onionperf-2023-04.tar.xz 93 | 94 | ### we also utilize privcount Tor traffic model measurements 95 | 96 | git clone https://github.com/tmodel-ccs2018/tmodel-ccs2018.github.io.git 97 | 98 | ### we also need tor 99 | 100 | sudo apt-get install openssl libssl-dev libevent-dev build-essential automake zlib1g zlib1g-dev 101 | git clone https://git.torproject.org/tor.git 102 | cd tor 103 | ./autogen.sh 104 | ./configure --disable-asciidoc --disable-unittests --disable-manpage --disable-html-manual 105 | make -j$(nproc) 106 | cd .. 107 | 108 | ### install additional executables used by tornettools 109 | 110 | `tornettools` also uses the `faketime`, `dstat`, `free`, and `xz` command-line 111 | tools. On Ubuntu these can be installed with: 112 | 113 | sudo apt-get install faketime dstat procps xz-utils 114 | 115 | ### in order to generate, we need a tor and tor-gencert binaries (to generate relay keys) 116 | 117 | export PATH=${PATH}:`pwd`/tor/src/core/or:`pwd`/tor/src/app:`pwd`/tor/src/tools 118 | 119 | ### stage first, process relay and user info 120 | 121 | tornettools stage \ 122 | consensuses-2023-04 \ 123 | server-descriptors-2023-04 \ 124 | userstats-relay-country.csv \ 125 | tmodel-ccs2018.github.io \ 126 | --onionperf_data_path onionperf-2023-04 \ 127 | --bandwidth_data_path bandwidth-2023-04.csv \ 128 | --geoip_path tor/src/config/geoip 129 | 130 | ### now we can used the staged files to generate many times 131 | 132 | For example, use `--network_scale 0.01` to generate a private Tor network at '1%' the scale of public Tor: 133 | 134 | tornettools generate \ 135 | relayinfo_staging_2023-04-01--2023-04-30.json \ 136 | userinfo_staging_2023-04-01--2023-04-30.json \ 137 | networkinfo_staging.gml \ 138 | tmodel-ccs2018.github.io \ 139 | --network_scale 0.01 \ 140 | --prefix tornet-0.01 141 | 142 | ### now you can run a simulation and process the results 143 | 144 | Make sure you have already installed [shadow](https://github.com/shadow/shadow), [tgen](https://github.com/shadow/tgen), and [oniontrace](https://github.com/shadow/oniontrace). 145 | 146 | Note that simulating a '1%' Tor network for 60 simulation minutes can take as much as 30GiB of RAM. 147 | 148 | tornettools simulate tornet-0.01 149 | tornettools parse tornet-0.01 150 | tornettools plot \ 151 | tornet-0.01 \ 152 | --tor_metrics_path tor_metrics_2023-04-01--2023-04-30.json \ 153 | --prefix pdfs 154 | tornettools archive tornet-0.01 155 | 156 | Performance metrics are plotted in the graph files in the pdfs directory. 157 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | matplotlib 2 | networkx 3 | numpy 4 | pyyaml 5 | scipy 6 | stem 7 | wheel 8 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from distutils.core import setup 4 | 5 | setup(name='tornettools', 6 | version="2.0.0", 7 | description='A utility to generate private Tor network configurations', 8 | author='Rob Jansen', 9 | url='https://github.com/shadow/tornettools', 10 | packages=['tornettools'], 11 | scripts=['tornettools/tornettools'], 12 | ) 13 | -------------------------------------------------------------------------------- /test/run_integration_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Run from base tornettools directory. 4 | # Assumptions made by this script: 5 | # - you have installed libevent and openssl libraries and header files 6 | # - you have installed the build tools needed to build tor 7 | # - python, git, tar, wget, xz-tools are accessible 8 | # Your machine should have ~30GiB of free RAM to run the simulate command 9 | # at the end of this script. 10 | 11 | python -m venv build/tornettoolsenv 12 | source build/tornettoolsenv/bin/activate 13 | 14 | # must set these options *after* sourcing the above environment 15 | set -euo pipefail 16 | 17 | pip install -r requirements.txt 18 | pip install -I . 19 | 20 | cd build 21 | 22 | wget https://collector.torproject.org/archive/relay-descriptors/consensuses/consensuses-2020-11.tar.xz 23 | wget https://collector.torproject.org/archive/relay-descriptors/server-descriptors/server-descriptors-2020-11.tar.xz 24 | wget https://metrics.torproject.org/userstats-relay-country.csv 25 | wget https://collector.torproject.org/archive/onionperf/onionperf-2020-11.tar.xz 26 | wget -O bandwidth-2020-11.csv "https://metrics.torproject.org/bandwidth.csv?start=2020-11-01&end=2020-11-30" 27 | 28 | tar xaf consensuses-2020-11.tar.xz 29 | tar xaf server-descriptors-2020-11.tar.xz 30 | tar xaf onionperf-2020-11.tar.xz 31 | xz -k -d tmodel-ccs2018.github.io/data/shadow/network/atlas-lossless.201801.shadow113.graphml.xml.xz 32 | 33 | GIT_SSL_NO_VERIFY=1 git clone https://github.com/tmodel-ccs2018/tmodel-ccs2018.github.io.git 34 | 35 | GIT_SSL_NO_VERIFY=1 git clone https://git.torproject.org/tor.git 36 | cd tor 37 | ./autogen.sh 38 | ./configure --disable-asciidoc --disable-unittests --disable-manpage --disable-html-manual 39 | make 40 | cd .. 41 | 42 | export PATH=${PATH}:`pwd`/tor/src/core/or:`pwd`/tor/src/app:`pwd`/tor/src/tools 43 | 44 | tornettools stage \ 45 | consensuses-2020-11 \ 46 | server-descriptors-2020-11 \ 47 | userstats-relay-country.csv \ 48 | --onionperf_data_path onionperf-2020-11 \ 49 | --bandwidth_data_path bandwidth-2020-11.csv \ 50 | --geoip_path tor/src/config/geoip 51 | 52 | for n in 0.01 0.1 53 | do 54 | for p in 0.01 0.1 55 | do 56 | for s in 0.01 0.1 57 | do 58 | for t in 0.001 0.01 59 | do 60 | for l in 0.5 1.0 1.5 61 | do 62 | tornettools generate \ 63 | relayinfo_staging_2020-11-01--2020-12-01.json \ 64 | userinfo_staging_2020-11-01--2020-12-01.json \ 65 | tmodel-ccs2018.github.io \ 66 | --network_scale ${n} \ 67 | --process_scale ${p} \ 68 | --server_scale ${s} \ 69 | --torperf_scale ${t} \ 70 | --load_scale ${l} \ 71 | --atlas tmodel-ccs2018.github.io/data/shadow/network/atlas-lossless.201801.shadow113.graphml.xml \ 72 | --prefix tornet-${n}n-${p}p-${s}s-${t}t-${l}l 73 | done 74 | done 75 | done 76 | done 77 | done 78 | 79 | tornettools simulate tornet-0.01n-0.01p-0.01s-0.01t-1.0l 80 | tornettools parse tornet-0.01n-0.01p-0.01s-0.01t-1.0l 81 | tornettools plot tornet-0.01n-0.01p-0.01s-0.01t-1.0l --tor_metrics_path tor_metrics_2020-11-01--2020-11-30.json --prefix pdfs 82 | tornettools archive tornet-0.01n-0.01p-0.01s-0.01t-1.0l 83 | -------------------------------------------------------------------------------- /tornettools/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | tornettools 3 | Authored by Rob Jansen, 2019-2020 4 | See LICENSE for licensing information 5 | ''' 6 | 7 | __all__ = [ 8 | 'stage', 9 | 'generate', 10 | 'generate_defaults', 11 | 'generate_tgen', 12 | 'generate_tor', 13 | 'simulate', 14 | 'parse', 15 | 'parse_oniontrace', 16 | 'parse_rusage', 17 | 'parse_tgen', 18 | 'parse_onionperf', 19 | 'plot', 20 | 'plot_oniontrace', 21 | 'plot_tgen', 22 | 'archive', 23 | 'util', 24 | 'util_geoip', 25 | '_version', 26 | ] 27 | -------------------------------------------------------------------------------- /tornettools/_version.py: -------------------------------------------------------------------------------- 1 | __version__ = "2.0.0" 2 | -------------------------------------------------------------------------------- /tornettools/archive.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | import shutil 4 | import subprocess 5 | 6 | from tornettools.util import which, cmdsplit 7 | 8 | def run(args): 9 | logging.info("Starting to archive simulation results now.") 10 | 11 | if which('xz') is None or which('tar') is None or which('dd') is None: 12 | logging.warning("We require the tar, xz, and dd tools to archive the results.") 13 | logging.critical("Unable to archive with missing tools.") 14 | return 15 | 16 | try: 17 | shutil.copy2(f"{args.prefix}/shadow.data/hosts/4uthority1/cached-consensus", f"{args.prefix}/consensus") 18 | except FileNotFoundError as e: 19 | logging.debug("Could not copy 'cached-consensus': %s", e) 20 | 21 | logging.info("Compressing consensus.") 22 | __xz_parallel(args, "consensus") 23 | logging.info("Compressing shadow config.") 24 | __xz_parallel(args, "shadow.config.xml") # for shadow v1.15.x 25 | __xz_parallel(args, "shadow.config.yaml") # for shadow v2.x.x 26 | logging.info("Compressing dstat log.") 27 | __xz_parallel(args, "dstat.log") 28 | logging.info("Compressing free log.") 29 | __xz_parallel(args, "free.log") 30 | logging.info("Compressing shadow log.") 31 | __xz_parallel(args, "shadow.log") 32 | 33 | logging.info("Compressing conf dir.") 34 | if __tar_xz_parallel(args, "conf"): 35 | shutil.rmtree(f"{args.prefix}/conf") 36 | 37 | logging.info("Compressing shadow template dir.") 38 | if __tar_xz_parallel(args, "shadow.data.template"): 39 | shutil.rmtree(f"{args.prefix}/shadow.data.template") 40 | 41 | logging.info("Compressing shadow data dir.") 42 | if __tar_xz_parallel(args, "shadow.data", excludes=['cached-*', 'diff-cache', 'keys', 'lock']): 43 | shutil.rmtree(f"{args.prefix}/shadow.data") 44 | 45 | logging.info("Compressing remaining log files.") 46 | for name in os.listdir(args.prefix): 47 | if name.endswith(".log"): 48 | __xz_parallel(args, name) 49 | 50 | def __xz_parallel(args, filename): 51 | path = f"{args.prefix}/{filename}" 52 | if os.path.exists(path): 53 | xz_cmd = cmdsplit(f"xz -9 --threads={args.nprocesses} {path}") 54 | comproc = subprocess.run(xz_cmd, cwd=args.prefix, stdout=subprocess.DEVNULL) 55 | if comproc.returncode == 0: 56 | return True 57 | return False 58 | 59 | def __tar_xz_parallel(args, dirname, excludes=[]): 60 | dirpath = f"{args.prefix}/{dirname}" 61 | if not os.path.exists(dirpath): 62 | return False 63 | 64 | # we are basically trying to do something like: 65 | # tar cf - FLAGS dirname | xz -8e --threads=N > dirname.tar.xz 66 | 67 | flags = [f"--exclude='{e}'" for e in excludes] 68 | flag_str = ' '.join(flags) 69 | 70 | tar_cmd = cmdsplit(f"tar cf - {flag_str} {dirname}") 71 | tarproc = subprocess.Popen(tar_cmd, cwd=args.prefix, stdout=subprocess.PIPE) 72 | 73 | xz_cmd = cmdsplit(f"xz -9 --threads={args.nprocesses} -") 74 | xzproc = subprocess.Popen(xz_cmd, cwd=args.prefix, stdin=tarproc.stdout, stdout=subprocess.PIPE) 75 | 76 | dd_cmd = cmdsplit(f"dd of={dirname}.tar.xz") 77 | ddproc = subprocess.Popen(dd_cmd, cwd=args.prefix, stdin=xzproc.stdout, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) 78 | 79 | # wait for the above and collec the return codes 80 | tar_rc = tarproc.wait() 81 | xz_rc = xzproc.wait() 82 | dd_rc = ddproc.wait() 83 | 84 | if tar_rc == 0 and xz_rc == 0 and dd_rc == 0: 85 | return True 86 | else: 87 | return False 88 | -------------------------------------------------------------------------------- /tornettools/generate_defaults.py: -------------------------------------------------------------------------------- 1 | # a relay must have been running longer than this to be considered 2 | RUN_FREQ_THRESH = 0.01 3 | 4 | # the bootstrap length should be greater than 60 seconds 5 | BOOTSTRAP_LENGTH_SECONDS = 300 6 | SIMULATION_LENGTH_SECONDS = 3600 7 | 8 | SHADOW_DATA_PATH = "shadow.data" 9 | SHADOW_TEMPLATE_PATH = "{}.template".format(SHADOW_DATA_PATH) 10 | CONFIG_DIRNAME = "conf" 11 | SHADOW_INSTALL_PREFIX = "~/.local" 12 | SHADOW_HOSTS_PATH = "hosts" 13 | SHADOW_CONFIG_FILENAME = "shadow.config.yaml" 14 | 15 | RESOLV_FILENAME = "shadowresolv.conf" 16 | BW_AUTHORITY_NAME = "bwauthority" 17 | 18 | TORRC_HOST_FILENAME = "torrc" 19 | TORRC_DEFAULTS_HOST_FILENAME = "torrc-defaults" 20 | 21 | TORRC_COMMON_FILENAME = "tor.common.torrc" 22 | TORRC_RELAY_FILENAME = "tor.relay.torrc" 23 | TORRC_RELAY_AUTHORITY_FILENAME = "tor.relay.authority.torrc" 24 | TORRC_RELAY_EXITONLY_FILENAME = "tor.relay.exitonly.torrc" 25 | TORRC_RELAY_EXITGUARD_FILENAME = "tor.relay.exitguard.torrc" 26 | TORRC_RELAY_GUARDONLY_FILENAME = "tor.relay.guardonly.torrc" 27 | TORRC_RELAY_OTHER_FILENAME = "tor.relay.other.torrc" 28 | TORRC_CLIENT_FILENAME = "tor.client.torrc" 29 | TORRC_CLIENT_MARKOV_FILENAME = "tor.client.markov.torrc" 30 | TORRC_CLIENT_PERF_FILENAME = "tor.client.perf.torrc" 31 | TORRC_ONIONSERVICE_FILENAME = "tor.onionservice.torrc" 32 | 33 | TOR_SOCKS_PORT = 9050 34 | TOR_CONTROL_PORT = 9051 35 | TOR_OR_PORT = 9001 36 | TOR_DIR_PORT = 8080 37 | TOR_GUARD_MIN_CONSBW = 2000 38 | TOR_ONIONSERVICE_DIR = "hs" 39 | 40 | # country codes where we can place directory authority tor hosts 41 | DIRAUTH_COUNTRY_CODES = ["US", "DE", "NL", "FR", "SE"] 42 | 43 | # this number of data equals 1 MBit 44 | BW_1MBIT_BYTES = int(round(1000 * 1000 / 8)) 45 | BW_1MBIT_KIB = int(round(BW_1MBIT_BYTES / 1024)) 46 | BW_1MBIT_KBIT = 1000 47 | # this number of data equals 1 GBit 48 | BW_1GBIT_BYTES = int(round(1000 * 1000 * 1000 / 8)) 49 | BW_1GBIT_KIB = int(round(BW_1GBIT_BYTES / 1024)) 50 | BW_1GBIT_KBIT = 1000 * 1000 51 | BW_RATE_MIN = 102400 52 | 53 | # country codes where we can place onionperf client hosts 54 | ONIONPERF_COUNTRY_CODES = ['HK', 'NL', 'AB', 'US'] 55 | # Minimum tgen count for each tgen type (exit or onion) 56 | TGEN_CLIENT_MIN_COUNT = 100 57 | PRIVCOUNT_PERIODS_PER_DAY = 144.0 58 | TGEN_SERVER_PORT = 80 59 | TGEN_ONIONSERVICE_PORT = 8080 60 | 61 | TGENRC_SERVER_FILENAME = "tgen-server.tgenrc.graphml" 62 | TGENRC_PERFCLIENT_EXIT_FILENAME = "tgen-perf-exit.tgenrc.graphml" 63 | TGENRC_PERFCLIENT_HS_FILENAME = "tgen-perf-hs.tgenrc.graphml" 64 | TGENRC_MARKOVCLIENT_FILENAME = "tgenrc.graphml" 65 | TGENRC_FLOWMODEL_FILENAME_FMT = "tgen.tor-flowmodel-{}usec.graphml" 66 | 67 | TMODEL_STREAMMODEL_FILENAME = "tgen.tor-streammodel-ccs2018.graphml" 68 | TMODEL_PACKETMODEL_FILENAME = "tgen.tor-packetmodel-ccs2018.graphml" 69 | TMODEL_TOPOLOGY_FILENAME = "atlas_v201801.shadow_v2.gml" 70 | 71 | # Timestamp passed to faketime(1) when generating certificates. Should be < 1 72 | # year before simulation start (which is currently hard-coded in shadow to 73 | # 2000-01-01). 74 | CERT_FAKETIMESTAMP = "1999-12-01" 75 | 76 | def get_host_rel_conf_path(rc_filename, rc_subdirname=None): 77 | if rc_subdirname is None: 78 | return f"../../../{CONFIG_DIRNAME}/{rc_filename}" 79 | else: 80 | return f"../../../{CONFIG_DIRNAME}/{rc_subdirname}/{rc_filename}" 81 | -------------------------------------------------------------------------------- /tornettools/parse.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import re 4 | 5 | from tornettools.parse_oniontrace import parse_oniontrace_logs, extract_oniontrace_plot_data 6 | from tornettools.parse_tgen import parse_tgen_logs, extract_tgen_plot_data 7 | from tornettools.parse_rusage import parse_resource_usage_logs, extract_resource_usage_plot_data 8 | from tornettools.util import open_readable_file, dump_json_data 9 | 10 | def run(args): 11 | logging.info("Parsing simulation output from {}".format(args.prefix)) 12 | 13 | logging.info("Parsing tgen logs.") 14 | if args.skip_raw or parse_tgen_logs(args): 15 | logging.info("Extracting tgen plot data.") 16 | extract_tgen_plot_data(args) 17 | else: 18 | logging.warning("Parsing tgen logs failed, so we cannot extract tgen plot data.") 19 | 20 | logging.info("Parsing oniontrace logs.") 21 | if args.skip_raw or parse_oniontrace_logs(args): 22 | logging.info("Extracting oniontrace plot data.") 23 | extract_oniontrace_plot_data(args) 24 | else: 25 | logging.warning("Parsing oniontrace logs failed, so we cannot extract oniontrace plot data.") 26 | 27 | logging.info("Parsing resource usage logs.") 28 | if args.skip_raw or parse_resource_usage_logs(args): 29 | logging.info("Extracting resource usage plot data.") 30 | extract_resource_usage_plot_data(args) 31 | else: 32 | logging.warning("Parsing resource usage logs failed, so we cannot extract resource usage plot data.") 33 | 34 | __parse_tornettools_log(args) 35 | 36 | logging.info("Done parsing!") 37 | 38 | def __parse_tornettools_log(args): 39 | gen_logs = [f for f in os.listdir(args.prefix) if f.startswith('tornettools.generate.')] 40 | if len(gen_logs) == 0: 41 | logging.warning("Unable to find simulation info in tornettools.generate.log file") 42 | return 43 | 44 | info = {} 45 | gen_log_path = f"{args.prefix}/{gen_logs[-1]}" 46 | with open_readable_file(gen_log_path) as inf: 47 | for line in inf: 48 | 49 | match = re.search(r'Seeded standard and numpy PRNGs with seed=(\d+)', line) 50 | if match: 51 | info['tornettools_generate_seed'] = int(match.groups()[0]) 52 | continue 53 | match = re.search(r'Chose (\d+) of (\d+) relays using scale factor (\S+)', line) 54 | if match: 55 | info['num_sampled_relays'] = int(match.groups()[0]) 56 | info['num_public_relays'] = int(match.groups()[1]) 57 | info['net_scale'] = float(match.groups()[2]) 58 | continue 59 | match = re.search(r'Generated fingerprints and keys for (\d+) Tor nodes \((\d+) authorities and (\d+) relays', line) 60 | if match: 61 | info['num_dir_authorities'] = int(match.groups()[1]) 62 | continue 63 | match = re.search(r'We will use (\d+) TGen client processes to emulate (\S+) Tor exit users and create (\d+) exit circuits', line) 64 | if match: 65 | info['num_tgen_exit_markov_clients'] = int(match.groups()[0]) 66 | info['num_tgen_exit_emulated_users'] = float(match.groups()[1]) 67 | info['num_exit_circuits_ten_minutes'] = int(match.groups()[2]) 68 | continue 69 | match = re.search(r'We will use (\d+) TGen client processes to emulate (\S+) Tor onion-service users and create (\d+) onion-service circuits', line) 70 | if match: 71 | info['num_tgen_hs_markov_clients'] = int(match.groups()[0]) 72 | info['num_tgen_hs_emulated_users'] = float(match.groups()[1]) 73 | info['num_hs_circuits_ten_minutes'] = int(match.groups()[2]) 74 | continue 75 | match = re.search(r'We will use (\d+) exit perf nodes to benchmark Tor exit performance', line) 76 | if match: 77 | info['num_tgen_exit_perf_clients'] = int(match.groups()[0]) 78 | continue 79 | match = re.search(r'We will use (\d+) onion-service perf nodes to benchmark Tor onion-service performance', line) 80 | if match: 81 | info['num_tgen_hs_perf_clients'] = int(match.groups()[0]) 82 | continue 83 | match = re.search(r'We will use (\d+) TGen exit servers and (\d+) TGen onion-service servers', line) 84 | if match: 85 | info['num_tgen_exit_servers'] = int(match.groups()[0]) 86 | info['num_tgen_onionservice_servers'] = int(match.groups()[1]) 87 | continue 88 | 89 | outpath = f"{args.prefix}/tornet.plot.data/simulation_info.json" 90 | dump_json_data(info, outpath, compress=False) 91 | -------------------------------------------------------------------------------- /tornettools/parse_onionperf.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import logging 4 | 5 | from tornettools.util import dump_json_data, open_readable_file, aka_int, tgen_stream_seconds_at_bytes 6 | 7 | # This code parses onionperf metrics data. 8 | # The output is in a format that the `plot` command can use to compare 9 | # shadow results to Tor metrics results. 10 | # see https://metrics.torproject.org/reproducible-metrics.html#performance 11 | 12 | def run(args): 13 | db = {"circuit_rtt": [], "client_goodput": [], "client_goodput_5MiB": [], 14 | "circuit_build_times": [], "download_times": {}, "daily_counts": {}, 15 | "relay_goodput": {}} 16 | 17 | if args.bandwidth_data_path is not None: 18 | logging.info(f"Parsing bandwidth data stored in '{args.bandwidth_data_path}'") 19 | db['relay_goodput'] = __parse_bandwidth_data(args.bandwidth_data_path) 20 | logging.info("Finished parsing bandwidth data") 21 | 22 | if args.onionperf_data_path is not None: 23 | logging.info(f"Extracting onionperf data stored in '{args.onionperf_data_path}'") 24 | __extract_onionperf_data(args, db) 25 | logging.info("Finished extracting onionperf data") 26 | 27 | # format we want for filename: tor_metrics_2020-01-01--2020-01-31.json 28 | days = [] 29 | days.extend(db['daily_counts'].keys()) 30 | days.extend(db['relay_goodput'].keys()) 31 | days.sort() 32 | 33 | out_path = f"{args.prefix}/tor_metrics_{days[0]}--{days[-1]}.json" 34 | logging.info(f"Saving parsed Tor metrics data to {out_path}") 35 | dump_json_data(db, out_path, compress=False) 36 | 37 | def __parse_bandwidth_data(bw_data_path): 38 | bw = {} 39 | with open_readable_file(bw_data_path) as inf: 40 | for line in inf: 41 | if '20' == line[0:2]: 42 | parts = line.strip().split(',') 43 | day, relay_bw_hist = parts[0], parts[2] 44 | if len(relay_bw_hist) > 0: 45 | bw[day] = float(relay_bw_hist) 46 | return bw 47 | 48 | def __extract_onionperf_data(args, db): 49 | # parse the files 50 | for root, dirs, files in os.walk(args.onionperf_data_path): 51 | for file in files: 52 | if 'onionperf' not in file: 53 | continue 54 | 55 | fullpath = os.path.join(root, file) 56 | 57 | parts = os.path.basename(fullpath).split('.') 58 | day = parts[0] 59 | 60 | logging.info("Processing onionperf file: '{}'".format(fullpath)) 61 | 62 | with open_readable_file(fullpath) as fin: 63 | data = json.load(fin) 64 | __handle_json_data(db, data, day) 65 | 66 | logging.info(f"We processed {len(db['circuit_rtt'])} downloads") 67 | 68 | def __handle_json_data(db, data, day): 69 | if 'data' in data: 70 | for name in data['data']: 71 | if 'tgen' in data['data'][name]: 72 | if 'streams' in data['data'][name]['tgen']: 73 | for stream_id in data['data'][name]['tgen']['streams']: 74 | stream = data['data'][name]['tgen']['streams'][stream_id] 75 | __handle_stream(db, stream, day) 76 | if 'tor' in data['data'][name]: 77 | if 'circuits' in data['data'][name]['tor']: 78 | for circ_id in data['data'][name]['tor']['circuits']: 79 | circuit = data['data'][name]['tor']['circuits'][circ_id] 80 | __handle_circuit(db, circuit) 81 | 82 | def __handle_circuit(db, circuit): 83 | if 'buildtime_seconds' in circuit: 84 | cbt = float(circuit['buildtime_seconds']) 85 | db['circuit_build_times'].append(cbt) 86 | 87 | def __handle_stream(db, stream, day): 88 | # filter out onion service circuit downloads for now 89 | if 'transport_info' in stream: 90 | if 'remote' in stream['transport_info']: 91 | if 'onion' in stream['transport_info']['remote']: 92 | return 93 | 94 | transfer_size_actual = int(stream['byte_info']['payload-bytes-recv']) 95 | transfer_size_target = int(stream['stream_info']['recvsize']) 96 | timeout_limit = __get_timeout_limit(transfer_size_target) 97 | 98 | cmd = int(stream['time_info']['usecs-to-command']) 99 | rsp = int(stream['time_info']['usecs-to-response']) 100 | lb = int(stream['time_info']['usecs-to-last-byte-recv']) 101 | 102 | ttlb = (lb - cmd) / 1000000.0 # usecs to seconds 103 | 104 | # count number of attemps and timeouts/failures 105 | db["daily_counts"].setdefault(day, {"requests": 0, "timeouts": 0, "failures": 0}) 106 | db["daily_counts"][day]["requests"] += 1 107 | if stream['is_error']: 108 | se = stream['stream_info']['error'] 109 | if se.upper() == "TIMEOUT" or se.upper() == "STALLOUT": 110 | db["daily_counts"][day]["timeouts"] += 1 111 | else: 112 | db["daily_counts"][day]["failures"] += 1 113 | return 114 | if lb > 0 and cmd > 0 and ttlb > timeout_limit: 115 | db["daily_counts"][day]["timeouts"] += 1 116 | return 117 | if transfer_size_actual < transfer_size_target: 118 | db["daily_counts"][day]["failures"] += 1 119 | return 120 | 121 | # download was successful 122 | assert stream['is_success'] 123 | 124 | # circuit rtt 125 | if rsp > 0 and cmd > 0: 126 | rtt = (rsp - cmd) / 1000000.0 # usecs to seconds 127 | db['circuit_rtt'].append(rtt) 128 | 129 | # download times, client download 'goodput' and client download 'goodput' for 130 | # the last MiB of 5MiB downloads. 131 | # Tor computs goodput based on the time between the .5 MiB byte to the 1 MiB byte. 132 | # Ie to cut out circuit build and other startup costs. 133 | # https://metrics.torproject.org/reproducible-metrics.html#performance 134 | # 135 | # For 5 MiB downloads we extract the time (in seconds) elapsed between 136 | # receiving the 4MiB byte and 5MiB byte, which is a total amount of 1 MiB or 137 | # 8 Mib. 138 | 139 | if 'elapsed_seconds' in stream and 'payload_bytes_recv' in stream['elapsed_seconds']: 140 | # download times 141 | for (transfer_size, time_to_size) in stream['elapsed_seconds']['payload_bytes_recv'].items(): 142 | if time_to_size > 0 and cmd > 0: 143 | transfer_time_secs = time_to_size - (cmd / 1e6) # usecs to seconds 144 | __store_transfer_time(db, transfer_size, transfer_time_secs) 145 | 146 | # goodput between 500 kibibytes and 1 mebibyte. Old way of calcuting throughput. 147 | # https://metrics.torproject.org/reproducible-metrics.html#performance 148 | goodput = __goodput_bps( 149 | stream, aka_int(512000, 500 * 2**10), aka_int(1048576, 2**20)) 150 | if goodput is not None: 151 | db['client_goodput'].append(goodput) 152 | 153 | # goodput of the 5th Mebibyte. metrics.torproject uses this as of ~ April 2021. 154 | # https://gitlab.torproject.org/tpo/network-health/metrics/statistics/-/issues/40005 155 | # https://gitlab.torproject.org/tpo/network-health/metrics/statistics/-/issues/40020 156 | # https://metrics.torproject.org/reproducible-metrics.html#performance 157 | goodput = __goodput_bps( 158 | stream, aka_int(4194304, 4 * 2**20), aka_int(5242880, 5 * 2**20)) 159 | if goodput is not None: 160 | db['client_goodput_5MiB'].append(goodput) 161 | 162 | elif lb > 0 and cmd > 0: 163 | __store_transfer_time(db, transfer_size_target, ttlb) 164 | 165 | def __goodput_bps(stream, start_bytes, end_bytes): 166 | start_time = tgen_stream_seconds_at_bytes(stream, start_bytes) 167 | if start_time is None: 168 | return None 169 | end_time = tgen_stream_seconds_at_bytes(stream, end_bytes) 170 | if end_time is None or end_time <= start_time: 171 | return None 172 | return (end_bytes - start_bytes) * 8.0 / (end_time - start_time) 173 | 174 | def __store_transfer_time(db, transfer_size, transfer_time): 175 | db['download_times'].setdefault('ALL', []) 176 | db['download_times']['ALL'].append(transfer_time) 177 | db['download_times'].setdefault(str(transfer_size), []) 178 | db['download_times'][str(transfer_size)].append(transfer_time) 179 | 180 | def __get_timeout_limit(num_bytes): 181 | # we compute timeouts based on our tgen configured timeout times 182 | # so that we can keep shadow and tor metrics consistent 183 | # timeouts are in seconds 184 | if num_bytes == 51200: 185 | return 15.0 186 | elif num_bytes == 1048576: 187 | return 60.0 188 | elif num_bytes == 5242880: 189 | return 120.0 190 | else: 191 | return 3600.0 192 | -------------------------------------------------------------------------------- /tornettools/parse_oniontrace.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | import datetime 4 | import re 5 | import subprocess 6 | 7 | from tornettools.util import which, open_writeable_file, load_json_data, dump_json_data 8 | 9 | def parse_oniontrace_logs(args): 10 | otracetools_exe = which('oniontracetools') 11 | 12 | if otracetools_exe is None: 13 | logging.warning("Cannot find oniontracetools in your PATH. Is your python venv active? Do you have oniontracetools installed?") 14 | logging.warning("Unable to parse oniontrace simulation data.") 15 | return 16 | 17 | # oniontracetools supports a list of expressions that are used to search for oniontrace log filenames 18 | # the first -e expression matches the log file names for Shadow v3.x.x 19 | # the second -e expression matches the log file names for Shadow v2.x.x 20 | # and the third -e expression matches the log file names for Shadow v1.x.x 21 | cmd = [otracetools_exe, 22 | 'parse', 23 | '-m', str(args.nprocesses), 24 | '-e', r'/oniontrace\.[0-9]+\.stdout$', 25 | '-e', r'.*\.oniontrace\.[0-9]+\.stdout', 26 | '-e', r'stdout.*\.oniontrace\.[0-9]+\.log', 27 | 'shadow.data/hosts'] 28 | 29 | datestr = datetime.datetime.now().strftime("%Y-%m-%d.%H:%M:%S") 30 | 31 | with open_writeable_file(f"{args.prefix}/oniontracetools.parse.{datestr}.log") as outf: 32 | logging.info("Parsing oniontrace log data with oniontracetools now...") 33 | comproc = subprocess.run(cmd, cwd=args.prefix, stdout=outf, stderr=subprocess.STDOUT) 34 | logging.info(f"oniontracetools returned code {comproc.returncode}") 35 | 36 | return comproc.returncode == 0 37 | 38 | def extract_oniontrace_plot_data(args): 39 | json_path = f"{args.prefix}/oniontrace.analysis.json" 40 | 41 | if not os.path.exists(json_path): 42 | json_path += ".xz" 43 | 44 | if not os.path.exists(json_path): 45 | logging.warning(f"Unable to find oniontrace analysis data at {json_path}.") 46 | return 47 | 48 | data = load_json_data(json_path) 49 | 50 | # parse performance stats only after the network has reached steady state 51 | startts, stopts = args.converge_time, -1 if args.run_time < 0 else args.converge_time + args.run_time 52 | 53 | for circuittype in ('exit', 'onionservice'): 54 | __extract_circuit_build_times(args, circuittype, data, startts, stopts) 55 | 56 | __extract_relay_tput(args, data, startts, stopts) 57 | 58 | def __extract_circuit_build_times(args, circuittype, data, startts, stopts): 59 | cbt = __get_perfclient_cbt(data, circuittype, startts, stopts) 60 | outpath = f"{args.prefix}/tornet.plot.data/perfclient_circuit_build_time.{circuittype}.json" 61 | dump_json_data(cbt, outpath, compress=False) 62 | 63 | def __extract_relay_tput(args, data, startts, stopts): 64 | tput = __get_relay_tput(data, startts, stopts) 65 | outpath = f"{args.prefix}/tornet.plot.data/relay_goodput.json" 66 | dump_json_data(tput, outpath, compress=False) 67 | 68 | def __get_perfclient_cbt(data, circuittype, startts, stopts): 69 | perf_cbt = [] 70 | 71 | # cbts can differ by microseconds. 72 | # TODO: use. 73 | # resolution = 1.0 / 1000000.0 74 | 75 | pattern = re.compile(r'perfclient\d+' + circuittype) 76 | 77 | if 'data' in data: 78 | for name in data['data']: 79 | if pattern.match(name) is None: 80 | continue 81 | 82 | circ = data['data'][name]['oniontrace']['circuit'] 83 | key = 'build_time' 84 | if circ is None or key not in circ: 85 | continue 86 | 87 | cbt = circ[key] 88 | 89 | for secstr in cbt: 90 | sec = int(secstr) - 946684800 91 | if sec >= startts and (stopts < 0 or sec < stopts): 92 | for val in cbt[secstr]: 93 | #item = [val, resolution] 94 | item = val 95 | perf_cbt.append(item) 96 | 97 | return perf_cbt 98 | 99 | def __get_relay_tput(data, startts, stopts): 100 | net_tput_sec = {} 101 | 102 | # resolution in 1 byte. 103 | # TODO: Use. 104 | # resolution = 1 105 | 106 | if 'data' in data: 107 | for name in data['data']: 108 | if 'relay' not in name and '4uthority' not in name: 109 | continue 110 | 111 | bw = data['data'][name]['oniontrace']['bandwidth'] 112 | key = 'bytes_written' 113 | if bw is None or key not in bw: 114 | continue 115 | 116 | tput = bw[key] 117 | 118 | for secstr in tput: 119 | sec = int(secstr) - 946684800 120 | if sec >= startts and (stopts < 0 or sec < stopts): 121 | bytes = int(tput[secstr]) 122 | net_tput_sec.setdefault(sec, 0) 123 | net_tput_sec[sec] += bytes 124 | 125 | return net_tput_sec 126 | -------------------------------------------------------------------------------- /tornettools/parse_rusage.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | import datetime 4 | import re 5 | 6 | from numpy import mean 7 | 8 | from tornettools.util import open_readable_file, load_json_data, dump_json_data 9 | 10 | def parse_resource_usage_logs(args): 11 | logging.info("Parsing resource usage from free log") 12 | if __parse_free_rusage(args): 13 | logging.info("Parsing resource usage from shadow log") 14 | return __parse_shadow_rusage(args) 15 | else: 16 | return False 17 | 18 | def __parse_free_rusage(args): 19 | free_filepath = f"{args.prefix}/free.log" 20 | if not os.path.exists(free_filepath): 21 | free_filepath += ".xz" 22 | 23 | if not os.path.exists(free_filepath): 24 | logging.warning(f"Unable to find memory usage data at {free_filepath}") 25 | return False 26 | 27 | rusage = {} 28 | 29 | last_ts = None 30 | mem_header = None 31 | with open_readable_file(free_filepath) as inf: 32 | for line in inf: 33 | if "UTC" in line: 34 | parts = line.strip().split() 35 | if len(parts) >= 1: 36 | ts = float(parts[0]) 37 | #dt = datetime.datetime.fromtimestamp(ts) 38 | #last_ts = dt.timestamp() 39 | last_ts = ts 40 | elif 'total' in line and mem_header is None: 41 | mem_header = [p.strip() for p in line.strip().split()] 42 | elif "Mem:" in line: 43 | parts = [p.strip() for p in line.strip().split()] 44 | mem_counts = [int(p) for p in parts[1:]] 45 | 46 | memd = {f"mem_{mem_header[i]}": mem_counts[i] for i in range(len(mem_counts))} 47 | 48 | rusage.setdefault(last_ts, memd) 49 | 50 | if len(rusage) > 0: 51 | outpath = f"{args.prefix}/free_rusage.json.xz" 52 | dump_json_data(rusage, outpath, compress=True) 53 | return True 54 | else: 55 | logging.warning(f"Unable to parse memory usage data from {free_filepath}.") 56 | return False 57 | 58 | def __parse_shadow_rusage(args): 59 | shadow_filepath = f"{args.prefix}/shadow.log" 60 | if not os.path.exists(shadow_filepath): 61 | shadow_filepath += ".xz" 62 | 63 | if not os.path.exists(shadow_filepath): 64 | logging.warning(f"Unable to find cpu usage data at {shadow_filepath}") 65 | return False 66 | 67 | rusage = {} 68 | # shadow's C code didn't capitalize "process", shadow's rust code does 69 | heartbeat = re.compile("[Pp]rocess resource usage at simtime") 70 | with open_readable_file(shadow_filepath) as inf: 71 | for line in inf: 72 | if heartbeat.search(line) is not None: 73 | parts = line.strip().split() 74 | if len(parts) >= 13: 75 | sim_time = float(parts[12]) # nanos e.g. 2000000000 76 | std = datetime.timedelta(microseconds=sim_time / 1000.0) 77 | sim_secs = std.total_seconds() 78 | 79 | if sim_secs not in rusage: 80 | real_time = parts[0] # time e.g. 00:00:15.436056 81 | rt_parts = real_time.split(':') 82 | rtd = datetime.timedelta(hours=int(rt_parts[0]), minutes=int(rt_parts[1]), seconds=float(rt_parts[2])) 83 | 84 | rund = {keyval.split('=')[0]: keyval.split('=')[1] for keyval in parts if '=' in keyval} 85 | rund['real_time'] = rtd.total_seconds() 86 | 87 | rusage[sim_secs] = rund 88 | 89 | if len(rusage) > 0: 90 | outpath = f"{args.prefix}/shadow_rusage.json.xz" 91 | dump_json_data(rusage, outpath, compress=True) 92 | return True 93 | else: 94 | logging.warning(f"Unable to parse resource usage data from {shadow_filepath}.") 95 | return False 96 | 97 | def extract_resource_usage_plot_data(args): 98 | free_json_path = f"{args.prefix}/free_rusage.json" 99 | 100 | if not os.path.exists(free_json_path): 101 | free_json_path += ".xz" 102 | 103 | if not os.path.exists(free_json_path): 104 | logging.warning(f"Unable to find memory resource usage data at {free_json_path}.") 105 | return 106 | 107 | shadow_json_path = f"{args.prefix}/shadow_rusage.json" 108 | 109 | if not os.path.exists(shadow_json_path): 110 | shadow_json_path += ".xz" 111 | 112 | if not os.path.exists(shadow_json_path): 113 | logging.warning(f"Unable to find memory resource usage data at {shadow_json_path}.") 114 | return 115 | 116 | free_data = load_json_data(free_json_path) 117 | shadow_data = load_json_data(shadow_json_path) 118 | 119 | __extract_resource_usage(args, free_data, shadow_data) 120 | 121 | def __extract_resource_usage(args, free_data, shadow_data): 122 | rusage = {"ram": __get_ram_usage(free_data), "run_time": __get_run_time(shadow_data)} 123 | outpath = f"{args.prefix}/tornet.plot.data/resource_usage.json" 124 | dump_json_data(rusage, outpath, compress=False) 125 | 126 | def __get_ram_usage(data): 127 | # get the ram used by the os during the simulation. 128 | # the best estimate is total-avail, but free may not always provide avail. 129 | some_key = next(iter(data)) 130 | if "mem_available" in data[some_key]: 131 | used = {float(ts): data[ts]["mem_total"] - data[ts]["mem_available"] for ts in data} 132 | else: 133 | logging.warning("The available memory data is missing, so we are computing memory usage " 134 | "with the used memory data instead (which is less precise and may not " 135 | "match the way usage was calculated for other experiments).") 136 | used = {float(ts): data[ts]["mem_used"] for ts in data} 137 | 138 | ts_start = min(used.keys()) 139 | mem_start = used[ts_start] # mem used by OS, i.e., before starting shadow 140 | mem_max = max(used.values()) 141 | 142 | # subtract mem used by OS, get time offset from beginning of simulation 143 | gib_used_per_second = {int(ts - ts_start): (used[ts] - mem_start) / (1024.0**3) for ts in used} 144 | bytes_used_max = mem_max - mem_start 145 | gib_used_max = bytes_used_max / (1024.0**3) 146 | 147 | gib_minute_bins = {} 148 | for second in gib_used_per_second: 149 | gib_minute_bins.setdefault(int(second / 60), []).append(gib_used_per_second[second]) 150 | 151 | gib_used_per_minute = {minute: mean(gib_minute_bins[minute]) for minute in gib_minute_bins} 152 | 153 | return {"bytes_used_max": bytes_used_max, "gib_used_max": gib_used_max, "gib_used_per_minute": gib_used_per_minute} 154 | 155 | def __get_run_time(data): 156 | real_seconds_per_sim_second = {float(sim_sec): float(data[sim_sec]["real_time"]) for sim_sec in data} 157 | times = list(real_seconds_per_sim_second.values()) 158 | runtime = datetime.timedelta(seconds=max(times)) 159 | 160 | return {"human": str(runtime), 161 | "seconds": runtime.total_seconds(), 162 | "minutes": runtime.total_seconds() / 60.0, 163 | "hours": runtime.total_seconds() / 3600.0, 164 | "real_seconds_per_sim_second": real_seconds_per_sim_second} 165 | -------------------------------------------------------------------------------- /tornettools/parse_tgen.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | import datetime 4 | import subprocess 5 | import re 6 | 7 | from tornettools.util import which, open_writeable_file, load_json_data, dump_json_data, aka_int, tgen_stream_seconds_at_bytes 8 | 9 | def parse_tgen_logs(args): 10 | tgentools_exe = which('tgentools') 11 | 12 | if tgentools_exe is None: 13 | logging.warning("Cannot find tgentools in your PATH. Is your python venv active? Do you have tgentools installed?") 14 | logging.warning("Unable to parse tgen simulation data.") 15 | return 16 | 17 | # tgentools supports a list of expressions that are used to search for oniontrace log filenames 18 | # the first -e expression matches the log file names for Shadow v3.x.x 19 | # the second -e expression matches the log file names for Shadow v2.x.x 20 | # and the third -e expression matches the log file names for Shadow v1.x.x 21 | cmd = [tgentools_exe, 22 | 'parse', 23 | '-m', str(args.nprocesses), 24 | '-e', r'perfclient[0-9]+(exit|onionservice)?/tgen\.[0-9]+\.stdout$', 25 | '-e', r'perfclient[0-9]+(exit|onionservice)?\.tgen\.[0-9]+\.stdout', 26 | '-e', r'stdout\.*perfclient[0-9]+\.tgen\.[0-9]+\.log', 27 | '--complete', 28 | 'shadow.data/hosts'] 29 | 30 | datestr = datetime.datetime.now().strftime("%Y-%m-%d.%H:%M:%S") 31 | 32 | with open_writeable_file(f"{args.prefix}/tgentools.parse.{datestr}.log") as outf: 33 | logging.info("Parsing tgen log data with tgentools now...") 34 | comproc = subprocess.run(cmd, cwd=args.prefix, stdout=outf, stderr=subprocess.STDOUT) 35 | logging.info(f"tgentools returned code {comproc.returncode}") 36 | 37 | return comproc.returncode == 0 38 | 39 | def extract_tgen_plot_data(args): 40 | json_path = f"{args.prefix}/tgen.analysis.json" 41 | 42 | if not os.path.exists(json_path): 43 | json_path += ".xz" 44 | 45 | if not os.path.exists(json_path): 46 | logging.warning(f"Unable to find tgen analysis data at {json_path}.") 47 | return 48 | 49 | data = load_json_data(json_path) 50 | 51 | # parse performance stats only after the network has reached steady state 52 | startts, stopts = args.converge_time, -1 if args.run_time < 0 else args.converge_time + args.run_time 53 | 54 | for circuittype in ('exit', 'onionservice'): 55 | __extract_round_trip_time(args, data, circuittype, startts, stopts) 56 | __extract_download_time(args, data, circuittype, startts, stopts) 57 | __extract_error_rate(args, data, circuittype, startts, stopts) 58 | __extract_client_goodput(args, data, circuittype, startts, stopts) 59 | __extract_client_goodput_5MiB(args, data, circuittype, startts, stopts) 60 | 61 | def __extract_round_trip_time(args, data, circuittype, startts, stopts): 62 | rtt = __get_round_trip_time(data, circuittype, startts, stopts) 63 | outpath = f"{args.prefix}/tornet.plot.data/round_trip_time.{circuittype}.json" 64 | dump_json_data(rtt, outpath, compress=False) 65 | 66 | def __extract_download_time(args, data, circuittype, startts, stopts): 67 | key = "time_to_first_byte_recv" 68 | dt = __get_download_time(data, circuittype, startts, stopts, key) 69 | outpath = f"{args.prefix}/tornet.plot.data/{key}.{circuittype}.json" 70 | dump_json_data(dt, outpath, compress=False) 71 | 72 | key = "time_to_last_byte_recv" 73 | dt = __get_download_time(data, circuittype, startts, stopts, key) 74 | outpath = f"{args.prefix}/tornet.plot.data/{key}.{circuittype}.json" 75 | dump_json_data(dt, outpath, compress=False) 76 | 77 | def __extract_error_rate(args, data, circuittype, startts, stopts): 78 | errrate_per_client = __get_error_rate(data, circuittype, startts, stopts) 79 | outpath = f"{args.prefix}/tornet.plot.data/error_rate.{circuittype}.json" 80 | dump_json_data(errrate_per_client, outpath, compress=False) 81 | 82 | def __extract_client_goodput(args, data, circuittype, startts, stopts): 83 | # goodput between 500 kibibytes and 1 mebibyte. Old way of calcuting throughput. 84 | # https://metrics.torproject.org/reproducible-metrics.html#performance 85 | client_goodput = __get_client_goodput( 86 | data, circuittype, startts, stopts, 87 | aka_int(512000, 500 * 2**10), 88 | aka_int(1048576, 2**20)) 89 | outpath = f"{args.prefix}/tornet.plot.data/perfclient_goodput.{circuittype}.json" 90 | dump_json_data(client_goodput, outpath, compress=False) 91 | 92 | def __extract_client_goodput_5MiB(args, data, circuittype, startts, stopts): 93 | # goodput of the 5th Mebibyte. metrics.torproject uses this as of ~ April 2021. 94 | # https://gitlab.torproject.org/tpo/network-health/metrics/statistics/-/issues/40005 95 | # https://gitlab.torproject.org/tpo/network-health/metrics/statistics/-/issues/40020 96 | # https://metrics.torproject.org/reproducible-metrics.html#performance 97 | client_goodput = __get_client_goodput( 98 | data, circuittype, startts, stopts, 99 | aka_int(4194304, 4 * 2**20), 100 | aka_int(5242880, 5 * 2**20)) 101 | outpath = f"{args.prefix}/tornet.plot.data/perfclient_goodput_5MiB.{circuittype}.json" 102 | dump_json_data(client_goodput, outpath, compress=False) 103 | 104 | def __get_download_time(data, circuittype, startts, stopts, bytekey): 105 | dt = {'ALL': []} 106 | 107 | # download times can differ by microseconds in tgen. 108 | # TODO: use 109 | # resolution = 1.0 / 1000000.0 110 | 111 | pattern = re.compile(r'perfclient\d+' + circuittype) 112 | if 'data' in data: 113 | for name in data['data']: 114 | if pattern.match(name) is None: 115 | continue 116 | db = data['data'][name] 117 | ss = db['tgen']['stream_summary'] 118 | if bytekey in ss: 119 | for header in ss[bytekey]: 120 | for secstr in ss[bytekey][header]: 121 | sec = int(secstr) - 946684800 122 | if sec >= startts and (stopts < 0 or sec < stopts): 123 | #mydlcount += len(data['nodes'][name]['lastbyte'][header][secstr]) 124 | for dl in ss[bytekey][header][secstr]: 125 | seconds = float(dl) 126 | #item = [seconds, resolution] 127 | item = seconds 128 | dt['ALL'].append(item) 129 | dt.setdefault(header, []).append(item) 130 | return dt 131 | 132 | def __get_round_trip_time(data, circuittype, startts, stopts): 133 | rtt = [] 134 | 135 | # rtts can differ by microseconds in tgen. 136 | # TODO: use 137 | # resolution = 1.0 / 1000000.0 138 | 139 | pattern = re.compile(r'perfclient\d+' + circuittype) 140 | if 'data' in data: 141 | for name in data['data']: 142 | if pattern.match(name) is None: 143 | continue 144 | 145 | db = data['data'][name] 146 | ss = db['tgen']['stream_summary'] 147 | 148 | if 'round_trip_time' in ss: 149 | for secstr in ss['round_trip_time']: 150 | sec = int(secstr) - 946684800 151 | if sec >= startts and (stopts < 0 or sec < stopts): 152 | for val in ss['round_trip_time'][secstr]: 153 | #item = [val, resolution] 154 | item = val 155 | rtt.append(item) 156 | 157 | return rtt 158 | 159 | def __get_error_rate(data, circuittype, startts, stopts): 160 | errors_per_client = {'ALL': []} 161 | 162 | pattern = re.compile(r'perfclient\d+' + circuittype) 163 | 164 | if 'data' in data: 165 | for name in data['data']: 166 | if pattern.match(name) is None: 167 | continue 168 | db = data['data'][name] 169 | ss = db['tgen']['stream_summary'] 170 | 171 | mydlcount = 0 172 | errtype_counts = {'ALL': 0} 173 | 174 | key = 'time_to_last_byte_recv' 175 | if key in ss: 176 | for header in ss[key]: 177 | for secstr in ss[key][header]: 178 | sec = int(secstr) - 946684800 179 | if sec >= startts and (stopts < 0 or sec < stopts): 180 | mydlcount += len(ss[key][header][secstr]) 181 | 182 | key = 'errors' 183 | if key in ss: 184 | for errtype in ss[key]: 185 | for secstr in ss[key][errtype]: 186 | sec = int(secstr) - 946684800 187 | if sec >= startts and (stopts < 0 or sec < stopts): 188 | num_err = len(ss[key][errtype][secstr]) 189 | errtype_counts.setdefault(errtype, 0) 190 | errtype_counts[errtype] += num_err 191 | errtype_counts['ALL'] += num_err 192 | 193 | attempted_dl_count = mydlcount + errtype_counts['ALL'] 194 | 195 | #logging.info("attempted {} downloads, {} completed, {} failed".format(attempted_dl_count, mydlcount, errtype_counts['ALL'])) 196 | 197 | if attempted_dl_count > 0: 198 | errcount = float(errtype_counts['ALL']) 199 | dlcount = float(attempted_dl_count) 200 | 201 | error_rate = 100.0 * errcount / dlcount 202 | resolution = 100.0 / dlcount 203 | errors_per_client['ALL'].append([error_rate, resolution]) 204 | 205 | for errtype in errtype_counts: 206 | errcount = float(errtype_counts[errtype]) 207 | error_rate = 100.0 * errcount / dlcount 208 | resolution = 100.0 / dlcount 209 | errors_per_client.setdefault(errtype, []).append([error_rate, resolution]) 210 | 211 | return errors_per_client 212 | 213 | def __get_client_goodput(data, circuittype, startts, stopts, start_bytes, end_bytes): 214 | goodput = [] 215 | 216 | # TODO: goodput would be in bits/second 217 | # resolution = 0.0 218 | 219 | pattern = re.compile(r'perfclient\d+' + circuittype) 220 | 221 | # example json format 222 | #['data']['perfclient1']['tgen']['streams']["blah:blah:localhost:etc"]['elapsed_seconds']['payload_bytes_recv']['512000'] = 3.4546 223 | 224 | if 'data' in data: 225 | for name in data['data']: 226 | if pattern.match(name) is None: 227 | continue 228 | db = data['data'][name] 229 | streams = db['tgen']['streams'] 230 | 231 | for sid in streams: 232 | stream = streams[sid] 233 | start_time = tgen_stream_seconds_at_bytes(stream, start_bytes) 234 | end_time = tgen_stream_seconds_at_bytes(stream, end_bytes) 235 | if start_time is not None and end_time is not None and end_time > start_time: 236 | bps = (end_bytes - start_bytes) * 8.0 / (end_time - start_time) 237 | # We ultimately want to graph Mbps, but for compatibility 238 | # with old data sets, we record Mibi-bps. This is 239 | # converted to Mbps in the `plot` step. 240 | Mibps = bps / 2**20 241 | goodput.append(Mibps) 242 | 243 | return goodput 244 | -------------------------------------------------------------------------------- /tornettools/plot_common.py: -------------------------------------------------------------------------------- 1 | from tornettools.util import which 2 | from numpy import array as nparray, log10 as nplog10 3 | from numpy import ma, mean, median, std, quantile, sqrt, linspace, var 4 | from scipy.stats import scoreatpercentile as score, t 5 | from matplotlib import rcParams 6 | from matplotlib.ticker import FixedFormatter, FixedLocator 7 | from matplotlib import transforms as mtransforms 8 | from matplotlib import scale as mscale 9 | from matplotlib import use as mp_use 10 | mp_use('Agg') # for systems without X11 11 | 12 | 13 | DEFAULT_COLORS = ['C0', 'C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8', 'C9', 'C10', 'C11'] 14 | DEFAULT_LINESTYLES = ['-', '--', '-.', ':'] 15 | 16 | class TailLog(mscale.ScaleBase): 17 | name = 'taillog' 18 | 19 | def __init__(self, axis, **kwargs): 20 | mscale.ScaleBase.__init__(self, axis) 21 | self.nines = kwargs.get('nines', 2) 22 | 23 | def get_transform(self): 24 | return self.Transform(self.nines) 25 | 26 | def set_default_locators_and_formatters(self, axis): 27 | # axis.set_major_locator(FixedLocator( 28 | # nparray([1-10**(-k) for k in range(1+self.nines)]))) 29 | # axis.set_major_formatter(FixedFormatter( 30 | # [str(1-10**(-k)) for k in range(1+self.nines)])) 31 | 32 | #majloc = [10**(-1*self.nines)*k for k in range(100) if k >= 90 or k % 10 == 0] 33 | majloc = [0.0, 0.9, 0.99] 34 | majloc = [round(k, self.nines) for k in majloc] 35 | axis.set_major_locator(FixedLocator(nparray(majloc))) 36 | axis.set_major_formatter(FixedFormatter([str(k) for k in majloc])) 37 | 38 | minloc = [10**(-1 * self.nines) * k for k in range(100) if k not in [0, 90, 99] and (k > 90 or k % 10 == 0)] 39 | minloc = [round(k, self.nines) for k in minloc] 40 | axis.set_minor_locator(FixedLocator(nparray(minloc))) 41 | axis.set_minor_formatter(FixedFormatter([str(k) for k in minloc])) 42 | 43 | def limit_range_for_scale(self, vmin, vmax, minpos): 44 | return vmin, min(1 - 10**(-self.nines), vmax) 45 | 46 | class Transform(mtransforms.Transform): 47 | input_dims = 1 48 | output_dims = 1 49 | is_separable = True 50 | 51 | def __init__(self, nines): 52 | mtransforms.Transform.__init__(self) 53 | self.nines = nines 54 | 55 | def transform_non_affine(self, a): 56 | masked = ma.masked_where(a > 1 - 10**(-1 - self.nines), a) 57 | if masked.mask.any(): 58 | return -ma.log10(1 - a) 59 | else: 60 | return -nplog10(1 - a) 61 | 62 | def inverted(self): 63 | return TailLog.InvertedTransform(self.nines) 64 | 65 | class InvertedTransform(mtransforms.Transform): 66 | input_dims = 1 67 | output_dims = 1 68 | is_separable = True 69 | 70 | def __init__(self, nines): 71 | mtransforms.Transform.__init__(self) 72 | self.nines = nines 73 | 74 | def transform_non_affine(self, a): 75 | return 1. - 10**(-a) 76 | 77 | def inverted(self): 78 | return TailLog.Transform(self.nines) 79 | 80 | 81 | mscale.register_scale(TailLog) 82 | 83 | # The scipy t.ppf distribution is for one-sided hypothesis tests. 84 | # If we want to compute a two-sided error, then we must convert a two-sided 85 | # confidence level to an equivalent one-side confidence level for scipy. 86 | def __two_to_one_sided_confidence_level(two_sided_level): 87 | return two_sided_level / 2 + 0.5 88 | 89 | def __get_error_factor(k, confidence): 90 | level = __two_to_one_sided_confidence_level(confidence) 91 | return t.ppf(level, k - 1) / sqrt(k - 1) 92 | 93 | def __compute_sample_mean_and_error(bucket_list, confidence): 94 | means, mins, maxs = [], [], [] 95 | z_cache = {} 96 | 97 | for i, bucket in enumerate(bucket_list): 98 | # get the error factor from the student's t distribution 99 | # and cache the result to minimize the number of ppf lookups 100 | k = len(bucket) 101 | z = z_cache.setdefault(k, __get_error_factor(k, confidence)) 102 | 103 | # bucket will be a list of items, each of which will either 104 | # be the value (a number), or a list of two numbers (the 105 | # value and the resolution). If it's just a value, the 106 | # correspinding resolution is 0. Create the list of values 107 | # and the list of resolutions. 108 | emp_sample = [getfirstorself(item) for item in bucket] 109 | resolutions = [getsecondorzero(item) for item in bucket] 110 | 111 | # The resolution variance is 1/12 of the sum of the squares 112 | # of the resolutions 113 | resolution_variance = sum([res**2 for res in resolutions]) / 12 114 | 115 | m, v = mean(emp_sample), var(emp_sample) 116 | assert k == len(emp_sample) 117 | s = sqrt(v + resolution_variance / k) 118 | e = z * s 119 | 120 | means.append(m) 121 | mins.append(max(0, m - e)) 122 | maxs.append(m + e) 123 | 124 | return means, mins, maxs 125 | 126 | # computes bins in the range [0,1] for a cdf plot, but applies a scale so that 127 | # the bins have consistent sizes on the final plot 128 | # scale is {"linear", "log", "symlog", "logit", ...} or ScaleBase 129 | # axis can be any plot axis (used for building a new scale) 130 | # num is the number of values to return 131 | def __calc_cdf_bins(scale, axis, num=1000): 132 | if not isinstance(scale, mscale.ScaleBase): 133 | # > For back-compatibility reasons, scales take an Axis object as first 134 | # > argument. However, this argument should not be used: [...] 135 | # https://matplotlib.org/stable/api/scale_api.html#matplotlib.scale.ScaleBase 136 | scale = mscale.scale_factory(scale, axis) 137 | 138 | # get the largest range within [0,1] that is allowed by the scale 139 | limits = scale.get_transform().transform(scale.limit_range_for_scale(0, 1, 0)) 140 | 141 | # get a linear distribution within this range 142 | y = linspace(limits[0], limits[1], num=num) 143 | 144 | # apply the inverse scale transformation 145 | return scale.get_transform().inverted().transform(y) 146 | 147 | # compute a cdf with confidence intervals based on the dataset and plot it on axis 148 | # dataset is a list of data 149 | # confidence is the confidence interval level (eg 0.95 for 95% CIs) 150 | # kwargs is passed to the plot function 151 | # each data may be a list of values, or a list of [value, resolution] items 152 | def draw_cdf_ci(axis, dataset, confidence=0.95, yscale=None, **kwargs): 153 | if yscale is None: 154 | yscale = axis.gca().get_yscale() 155 | 156 | y = __calc_cdf_bins(yscale, axis.gca().get_yaxis()) 157 | quantile_buckets = {q: [] for q in y} 158 | 159 | # we should have one empirical value for each simulation (ie data) for each quantile 160 | total_num_items = 0 161 | for data in dataset: 162 | num_items = len(data) 163 | total_num_items += num_items 164 | if num_items == 0: 165 | continue 166 | 167 | data.sort(key=getfirstorself) 168 | 169 | for q in quantile_buckets: 170 | val_at_q = data[int((num_items - 1) * q)] 171 | quantile_buckets[q].append(val_at_q) 172 | 173 | if total_num_items == 0: 174 | # No data; avoid divide-by-zero in __compute_sample_mean_and_error 175 | x, y, x_min, x_max = [], [], [], [] 176 | else: 177 | # compute the confidence intervals for each quantile 178 | bucket_list = [quantile_buckets[q] for _, q in enumerate(y)] 179 | x, x_min, x_max = __compute_sample_mean_and_error(bucket_list, confidence) 180 | 181 | # for debugging 182 | #axis.plot(x_min, y, label=f"k={k}", color=colors[l%len(colors)], linestyle=linestyle) 183 | #axis.plot(x_max, y, label=f"k={k}", color=colors[l%len(colors)], linestyle=linestyle) 184 | 185 | # if we wanted a ccdf 186 | #y = [1-q for q in y] 187 | 188 | plot_line = axis.plot(x, y, **kwargs) 189 | 190 | kwargs['alpha'] = 0.5 191 | kwargs['linestyle'] = '-' 192 | 193 | axis.fill_betweenx(y, x_min, x_max, **kwargs) 194 | fill_line = axis.fill(0, 0, color=kwargs['color'], alpha=kwargs['alpha']) 195 | 196 | return (plot_line[0], fill_line[0]) 197 | 198 | # compute a cdf from the data and plot it on the axis 199 | # data may be a list of values, or a list of [value, resolution] items 200 | def draw_cdf(axis, data, yscale=None, **kwargs): 201 | if yscale is None: 202 | yscale = axis.gca().get_yscale() 203 | 204 | d = [getfirstorself(item) for item in data] 205 | y = __calc_cdf_bins(yscale, axis.gca().get_yaxis()) 206 | 207 | # the 'interpolation' parameter name is deprecated and replaced with 208 | # 'method', but this change is recent so we'll stick with the deprecated 209 | # name for now 210 | # https://numpy.org/doc/stable/reference/generated/numpy.quantile.html 211 | # the 'lower' is used to match the behaviour of 'draw_cdf_ci()' above 212 | # https://github.com/shadow/tornettools/issues/76 213 | x = quantile(d, y, interpolation='lower') 214 | 215 | plot_line = axis.plot(x, y, **kwargs) 216 | return plot_line[0] 217 | 218 | # plot a line with error bars 219 | # x is a list of x-coordinates 220 | # ydata is a list of 'datas' for each x-coordinate 221 | # each 'data' may be a list of values, or a list of [value, resolution] items 222 | def draw_line_ci(axis, x, ydata, confidence=0.95, **kwargs): 223 | # compute the confidence intervals for each x-coordinate 224 | bucket_list = [ydata[i] for i, _ in enumerate(x)] 225 | y, y_min, y_max = __compute_sample_mean_and_error(bucket_list, confidence) 226 | 227 | plot_line = axis.plot(x, y, **kwargs) 228 | 229 | kwargs['alpha'] = 0.5 230 | kwargs['linestyle'] = '-' 231 | 232 | axis.fill_between(x, y_min, y_max, **kwargs) 233 | fill_line = axis.fill(0, 0, color=kwargs['color'], alpha=kwargs['alpha']) 234 | 235 | return (plot_line[0], fill_line[0]) 236 | 237 | # plot a line with error bars 238 | # x is a list of x-coordinates 239 | # ydata may be a list of values, or a list of [value, resolution] items (one for each x-coordinate) 240 | def draw_line(axis, x, ydata, **kwargs): 241 | y = [] 242 | for i, _ in enumerate(x): 243 | data = [getfirstorself(item) for item in ydata[i]] 244 | y.append(data) 245 | plot_line = axis.plot(x, y, **kwargs) 246 | return plot_line[0] 247 | 248 | # helper - if the passed item is a list, return its first 249 | # element; otherwise, return the item itself 250 | def getfirstorself(item): 251 | if isinstance(item, list): 252 | return item[0] 253 | return item 254 | 255 | # helper - if the passed item is a list, return its second 256 | # element; otherwise, return 0 257 | def getsecondorzero(item): 258 | if isinstance(item, list): 259 | return item[1] 260 | return 0 261 | 262 | def log_stats(filename, msg, dist): 263 | #from numpy import mean, median, std 264 | #from scipy.stats import scoreatpercentile as score 265 | b = sorted(dist) # .values() 266 | b = [getfirstorself(item) for item in b] 267 | with open(filename, 'a') as outf: 268 | print(msg, file=outf) 269 | print("min={} q1={} median={} q3={} max={} mean={} stddev={}".format(min(b), score(b, 25), median(b), score(b, 75), max(b), mean(b), std(b)), file=outf) 270 | 271 | def set_plot_options(): 272 | options = { 273 | # 'backend': 'PDF', 274 | 'font.size': 12, 275 | 'figure.figsize': (5, 4), 276 | 'figure.dpi': 150.0, 277 | 'grid.color': '0.1', 278 | 'grid.linestyle': ':', 279 | 'grid.linewidth': 0.5, 280 | 'axes.grid': True, 281 | # 'axes.grid.axis' : 'y', 282 | # 'axes.axisbelow': True, 283 | 'axes.titlesize': 14, 284 | 'axes.labelsize': 10, 285 | 'axes.formatter.limits': (-4, 4), 286 | 'xtick.labelsize': 8, 287 | 'ytick.labelsize': 8, 288 | 'lines.linewidth': 2.0, 289 | 'lines.markeredgewidth': 0.5, 290 | 'lines.markersize': 10, 291 | 'legend.fontsize': 10, 292 | 'legend.fancybox': False, 293 | 'legend.shadow': False, 294 | 'legend.borderaxespad': 0.5, 295 | 'legend.columnspacing': 1.0, 296 | 'legend.numpoints': 1, 297 | 'legend.handletextpad': 0.25, 298 | 'legend.handlelength': 2.0, 299 | 'legend.labelspacing': 0.25, 300 | 'legend.markerscale': 1.0, 301 | } 302 | 303 | options_latex = { 304 | # turn on the following to embedd fonts; requires latex 305 | 'ps.useafm': True, 306 | 'pdf.use14corefonts': True, 307 | 'text.usetex': True, 308 | # 'text.latex.preamble': r'\boldmath', 309 | 'text.latex.preamble': r'\usepackage{amsmath}', 310 | } 311 | 312 | rcParams.update(options) 313 | 314 | if which("latex") is not None: 315 | rcParams.update(options_latex) 316 | 317 | if 'figure.max_num_figures' in rcParams: 318 | rcParams['figure.max_num_figures'] = 50 319 | if 'figure.max_open_warning' in rcParams: 320 | rcParams['figure.max_open_warning'] = 50 321 | if 'legend.ncol' in rcParams: 322 | rcParams['legend.ncol'] = 50 323 | -------------------------------------------------------------------------------- /tornettools/plot_oniontrace.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | import datetime 4 | import subprocess 5 | 6 | from tornettools.util import which, cmdsplit, find_matching_files_in_dir, open_writeable_file 7 | 8 | def plot_oniontrace(args): 9 | oniontracetools_exe = which('oniontracetools') 10 | 11 | if oniontracetools_exe is None: 12 | logging.warning("Cannot find oniontracetools in your PATH. Is your python venv active? Do you have oniontracetools installed?") 13 | logging.warning("Unable to plot oniontrace data.") 14 | return 15 | 16 | # plot the tgen simulation data for each tgen json file in the tornet path 17 | cmd_prefix_str = f"{oniontracetools_exe} plot --expression 'relay|4uthority' --prefix 'relays'" 18 | for collection in args.tornet_collection_path: 19 | for json_path in find_matching_files_in_dir(collection, "oniontrace.analysis.json"): 20 | dir_path = os.path.dirname(json_path) 21 | dir_name = os.path.basename(dir_path) 22 | 23 | cmd_str = f"{cmd_prefix_str} --data {json_path} {dir_name}" 24 | cmd = cmdsplit(cmd_str) 25 | 26 | datestr = datetime.datetime.now().strftime("%Y-%m-%d.%H:%M:%S") 27 | 28 | with open_writeable_file(f"{dir_path}/oniontracetools.plot.{datestr}.log") as outf: 29 | logging.info(f"Using oniontracetools to plot data from {json_path} now...") 30 | comproc = subprocess.run(cmd, cwd=dir_path, stdout=outf, stderr=subprocess.STDOUT) 31 | logging.info(f"oniontracetools returned code {comproc.returncode}") 32 | -------------------------------------------------------------------------------- /tornettools/plot_tgen.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | import datetime 4 | import subprocess 5 | 6 | from tornettools.util import which, cmdsplit, find_matching_files_in_dir, open_writeable_file 7 | 8 | def plot_tgen(args): 9 | tgentools_exe = which('tgentools') 10 | 11 | if tgentools_exe is None: 12 | logging.warning("Cannot find tgentools in your PATH. Is your python venv active? Do you have tgentools installed?") 13 | logging.warning("Unable to plot tgen data.") 14 | return 15 | 16 | # plot the tgen simulation data for each tgen json file in the tornet path 17 | for circuittype in ('exit', 'onionservice'): 18 | cmd_prefix_str = f"{tgentools_exe} plot --expression 'perfclient\\d+'{circuittype} --bytes --prefix perf.{circuittype}" 19 | for collection in args.tornet_collection_path: 20 | for json_path in find_matching_files_in_dir(collection, "tgen.analysis.json"): 21 | dir_path = os.path.dirname(json_path) 22 | dir_name = os.path.basename(dir_path) 23 | 24 | cmd_str = f"{cmd_prefix_str} --data {json_path} {dir_name}" 25 | cmd = cmdsplit(cmd_str) 26 | 27 | datestr = datetime.datetime.now().strftime("%Y-%m-%d.%H:%M:%S") 28 | 29 | with open_writeable_file(f"{dir_path}/tgentools.plot.{circuittype}.{datestr}.log") as outf: 30 | logging.info(f"Using tgentools to plot data from {json_path} now...") 31 | comproc = subprocess.run(cmd, cwd=dir_path, stdout=outf, stderr=subprocess.STDOUT) 32 | logging.info(f"tgentools returned code {comproc.returncode}") 33 | -------------------------------------------------------------------------------- /tornettools/simulate.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import subprocess 3 | import threading 4 | 5 | from time import sleep 6 | 7 | from tornettools.util import which, cmdsplit, open_writeable_file 8 | 9 | def run(args): 10 | logging.info("Starting a simulation from tornet prefix {}".format(args.prefix)) 11 | 12 | logging.info("Starting dstat") 13 | dstat_subp = __start_dstat(args) 14 | 15 | logging.info("Starting free loop") 16 | free_stop_event = threading.Event() 17 | free_thread = threading.Thread(target=__run_free_loop, args=(args, free_stop_event)) 18 | free_thread.start() 19 | 20 | try: 21 | logging.info("Starting shadow") 22 | comproc = __run_shadow(args) 23 | 24 | logging.info("Cleaning up") 25 | __cleanup_subprocess(dstat_subp) 26 | finally: 27 | free_stop_event.set() 28 | free_thread.join() 29 | 30 | if comproc is None: 31 | logging.warning("Simulation was not started") 32 | return 1 33 | 34 | logging.info(f"Done simulating; shadow returned code '{comproc.returncode}'") 35 | 36 | if comproc.returncode != 0: 37 | logging.error("Shadow simulation did not complete successfully") 38 | 39 | return comproc.returncode 40 | 41 | def __run_shadow(args): 42 | if args.shadow_exe is None: 43 | logging.warning("Cannot find shadow in your PATH. Do you have shadow installed? Did you update your PATH?") 44 | logging.warning("Unable to run simulation without shadow.") 45 | return None 46 | 47 | shadow_cmd_str = f"{args.shadow_exe} {args.shadow_args} {args.shadow_config}" 48 | 49 | if args.use_realtime: 50 | # chrt manipulates the real-time attributes of a process (see `man chrt`) 51 | chrt_exe_path = which('chrt') 52 | 53 | if chrt_exe_path is None: 54 | logging.warning("Cannot find chrt in your PATH. Do you have chrt installed?") 55 | logging.warning("Unable to run simulation with realtime scheduling without chrt.") 56 | return None 57 | 58 | # --fifo sets realtime scheduling policy to SCHED_FIFO 59 | shadow_cmd_str = f"{chrt_exe_path} --fifo 1 {shadow_cmd_str}" 60 | 61 | with open_writeable_file(f"{args.prefix}/shadow.log", compress=args.do_compress) as outf: 62 | shadow_cmd = cmdsplit(shadow_cmd_str) 63 | comproc = subprocess.run(shadow_cmd, cwd=args.prefix, stdout=outf) 64 | 65 | return comproc 66 | 67 | def __run_free_loop(args, stop_event): 68 | date_exe_path = which('date') 69 | free_exe_path = which('free') 70 | 71 | with open(f"{args.prefix}/free.log", 'w') as outf: 72 | while not stop_event.is_set(): 73 | if date_exe_path is not None: 74 | date_cmd = cmdsplit(f"{date_exe_path} --utc '+%s.%N %Z seconds since epoch'") 75 | subprocess.run(date_cmd, cwd=args.prefix, stdout=outf, stderr=subprocess.STDOUT) 76 | 77 | if free_exe_path is not None: 78 | free_cmd = cmdsplit(f"{free_exe_path} -w -b -l") 79 | subprocess.run(free_cmd, cwd=args.prefix, stdout=outf, stderr=subprocess.STDOUT) 80 | 81 | sleep(1) 82 | 83 | def __start_dstat(args): 84 | dstat_exe_path = which('dstat') 85 | 86 | if dstat_exe_path is None: 87 | return None 88 | 89 | dstat_cmd = cmdsplit(f"{dstat_exe_path} -cmstTy --fs --output dstat.log") 90 | dstat_subp = subprocess.Popen(dstat_cmd, cwd=args.prefix, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) 91 | 92 | return dstat_subp 93 | 94 | def __cleanup_subprocess(subp): 95 | # if subp exists but has yet to receive a return code, then we kill it 96 | if subp is not None and subp.poll() is None: 97 | subp.terminate() 98 | subp.wait() 99 | -------------------------------------------------------------------------------- /tornettools/stage.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import logging 4 | import lzma 5 | 6 | from tornettools.generate_defaults import TMODEL_TOPOLOGY_FILENAME 7 | from tornettools.util import dump_json_data 8 | from tornettools.util_geoip import GeoIP 9 | 10 | from multiprocessing import Pool, cpu_count 11 | from statistics import median 12 | from datetime import datetime, timezone 13 | 14 | from stem import Flag 15 | from stem.descriptor import parse_file 16 | 17 | import networkx as nx 18 | 19 | # this is parsed from the consensus files 20 | class Relay(): 21 | def __init__(self, fingerprint, address): 22 | self.fingerprint = fingerprint 23 | self.address = address 24 | # the length of this list indicates the number of consensuses the relay appeared in 25 | self.weights = [] 26 | # a count of the number of consensuses in which the relay had the exit flag 27 | self.num_exit = 0 28 | # a count of the number of consensuses in which the relay had the guard flag 29 | self.num_guard = 0 30 | # bandwidth information parsed from server descriptor files 31 | self.bandwidths = Bandwidths(fingerprint) 32 | 33 | # this is parsed from the server descriptor files 34 | class Bandwidths(): 35 | def __init__(self, fingerprint): 36 | self.fingerprint = fingerprint 37 | self.max_obs_bw = 0 38 | self.bw_rates = [] 39 | self.bw_bursts = [] 40 | 41 | def run(args): 42 | min_unix_time, max_unix_time = stage_relays(args) 43 | stage_users(args, min_unix_time, max_unix_time) 44 | stage_graph(args) 45 | 46 | # this function parses a userstats-relay-country.csv file from 47 | # https://metrics.torproject.org/userstats-relay-country.csv 48 | def stage_users(args, min_unix_time, max_unix_time): 49 | codes_by_unix_time = {} 50 | 51 | logging.info("Processing user file from {}...".format(args.user_stats_path)) 52 | 53 | with open(args.user_stats_path, 'r') as infile: 54 | for line in infile: 55 | # skip the header; the first 4 chars are the year, e.g., '2011' 56 | if line[0:2] != '20': 57 | continue 58 | 59 | parts = line.strip().split(',') 60 | 61 | date = str(parts[0]) # like '2019-01-01' 62 | country_code = str(parts[1]) # like 'us' 63 | # At least one float has been observed in the file: 64 | # 65 | user_count = int(float(parts[2])) # like '14714' or '2e+05' 66 | 67 | dt = datetime.strptime(date, "%Y-%m-%d").replace(tzinfo=timezone.utc) 68 | unix_time = int(dt.strftime("%s")) # returns stamp like 1548910800 69 | 70 | if unix_time < min_unix_time or unix_time > max_unix_time: 71 | continue 72 | 73 | filter = set(['', 'a1', 'a2', '??']) 74 | if country_code in filter: 75 | continue 76 | 77 | codes_by_unix_time.setdefault(unix_time, {}).setdefault(country_code, 0) 78 | codes_by_unix_time[unix_time][country_code] += user_count 79 | 80 | # compute probs of each country over time 81 | probs_by_country_code = {} 82 | for unix_time in codes_by_unix_time: 83 | total_user_count = float(sum(codes_by_unix_time[unix_time].values())) 84 | 85 | for country_code in codes_by_unix_time[unix_time]: 86 | prob = codes_by_unix_time[unix_time][country_code] / total_user_count 87 | probs_by_country_code.setdefault(country_code, []).append(prob) 88 | 89 | # get median country prob for each 90 | output = {} 91 | for country_code in probs_by_country_code: 92 | probs = probs_by_country_code[country_code] 93 | med_prob = median(probs) if len(probs) > 0 else 0.0 94 | output.setdefault(country_code, med_prob) 95 | 96 | # re-normalize 97 | total_prob = float(sum(output.values())) 98 | for country_code in output: 99 | output[country_code] = output[country_code] / total_prob 100 | 101 | timesuffix = get_time_suffix(min_unix_time, max_unix_time) 102 | user_info_path = f"{args.prefix}/userinfo_staging_{timesuffix}.json" 103 | logging.info("Writing user info to {}".format(user_info_path)) 104 | dump_json_data(output, user_info_path, compress=False) 105 | 106 | # this function parses consensus and server descriptor files from, e.g., 107 | # https://collector.torproject.org/archive/relay-descriptors/consensuses/consensuses-2019-01.tar.xz 108 | # https://collector.torproject.org/archive/relay-descriptors/server-descriptors/server-descriptors-2019-01.tar.xz 109 | def stage_relays(args): 110 | num_processes = args.nprocesses if args.nprocesses > 0 else cpu_count() 111 | 112 | logging.info("Starting to process Tor metrics data using {} processes".format(num_processes)) 113 | 114 | consensus_paths = get_file_list(args.consensus_path) 115 | logging.info("Processing {} consensus files from {}...".format(len(consensus_paths), args.consensus_path)) 116 | relays, min_unix_time, max_unix_time, network_stats = process(num_processes, consensus_paths, parse_consensus, combine_parsed_consensus_results) 117 | 118 | servdesc_paths = get_file_list(args.server_descriptor_path) 119 | logging.info("Processing {} server descriptor files from {}...".format(len(servdesc_paths), args.server_descriptor_path)) 120 | sdesc_args = [[p, min_unix_time, max_unix_time] for p in servdesc_paths] 121 | bandwidths = process(num_processes, sdesc_args, parse_serverdesc, combine_parsed_serverdesc_results) 122 | 123 | found_bandwidths = 0 124 | for fingerprint in relays: 125 | if fingerprint in bandwidths: 126 | # overwrite empty bandwidth with parsed bandwidth info 127 | relays[fingerprint].bandwidths = bandwidths[fingerprint] 128 | found_bandwidths += 1 129 | 130 | logging.info("We found bandwidth information for {} of {} relays".format(found_bandwidths, len(relays))) 131 | # for (k, v) in sorted(relays.items(), key=lambda kv: kv[1].bandwidths.max_obs_bw): 132 | # logging.info("fp={} capacity={}".format(k, v.bandwidths.max_obs_bw)) 133 | 134 | geo = None 135 | if args.geoip_path is not None: 136 | geo = GeoIP(args.geoip_path) 137 | 138 | output = { 139 | 'min_unix_time': min_unix_time, 140 | 'max_unix_time': max_unix_time, 141 | 'network_stats': network_stats, 142 | 'relays': {} 143 | } 144 | 145 | for fingerprint in relays: 146 | r = relays[fingerprint] 147 | 148 | output['relays'][fingerprint] = { 149 | 'fingerprint': r.fingerprint, 150 | 'address': r.address, 151 | 'running_frequency': float(len(r.weights)) / float(len(consensus_paths)), # frac consensuses in which relay appeared 152 | 'guard_frequency': float(r.num_guard) / float(len(r.weights)), # when running, frac consensuses with exit flag 153 | 'exit_frequency': float(r.num_exit) / float(len(r.weights)), # when running, frac consensuses with guard flag 154 | 'weight': float(median(r.weights)) if len(r.weights) > 0 else 0.0, 155 | 'bandwidth_capacity': int(r.bandwidths.max_obs_bw), 156 | 'bandwidth_rate': int(median(r.bandwidths.bw_rates)) if len(r.bandwidths.bw_rates) > 0 else 0, 157 | 'bandwidth_burst': int(median(r.bandwidths.bw_bursts)) if len(r.bandwidths.bw_bursts) > 0 else 0, 158 | } 159 | 160 | if geo is not None: 161 | output['relays'][fingerprint]['country_code'] = geo.ip_to_country_code(r.address) 162 | 163 | timesuffix = get_time_suffix(min_unix_time, max_unix_time) 164 | relay_info_path = f"{args.prefix}/relayinfo_staging_{timesuffix}.json" 165 | logging.info("Writing relay info to {}".format(relay_info_path)) 166 | dump_json_data(output, relay_info_path, compress=False) 167 | 168 | return min_unix_time, max_unix_time 169 | 170 | def stage_graph(args): 171 | atlas_path = os.path.join(args.tmodel_git_path, "data/shadow/network/", TMODEL_TOPOLOGY_FILENAME + ".xz") 172 | 173 | with lzma.open(atlas_path) as f: 174 | logging.info(f"Reading compressed network graph {atlas_path}") 175 | network = nx.readwrite.gml.read_gml(f, label='id') 176 | logging.info("Finished reading network graph") 177 | 178 | # create new graph and copy only the nodes 179 | network = nx.classes.function.create_empty_copy(network) 180 | 181 | # it takes networkx a few minutes to read the atlas graph, so we save a smaller graph containing 182 | # only the atlas graph nodes so that the 'generate' step can read these nodes much quicker 183 | network_info_path = f"{args.prefix}/networkinfo_staging.gml" 184 | nx.readwrite.gml.write_gml(network, network_info_path) 185 | 186 | def get_file_list(dir_path): 187 | file_paths = [] 188 | for root, _, filenames in os.walk(dir_path): 189 | for filename in filenames: 190 | file_paths.append(os.path.join(root, filename)) 191 | return file_paths 192 | 193 | def get_time_suffix(min_unix_time, max_unix_time): 194 | min_str = datetime.fromtimestamp(min_unix_time, timezone.utc).strftime("%Y-%m-%d") 195 | max_str = datetime.fromtimestamp(max_unix_time, timezone.utc).strftime("%Y-%m-%d") 196 | return "{}--{}".format(min_str, max_str) 197 | 198 | def process(num_processes, file_paths, map_func, reduce_func): 199 | results = [] 200 | 201 | if num_processes > 1: 202 | p = Pool(num_processes) 203 | try: 204 | async_result = p.map_async(map_func, file_paths) 205 | while not async_result.ready(): 206 | async_result.wait(1) 207 | results = async_result.get() 208 | except KeyboardInterrupt: 209 | print("interrupted, terminating process pool", file=sys.stderr) 210 | p.terminate() 211 | p.join() 212 | sys.exit(1) 213 | else: 214 | for path in file_paths: 215 | results.append(map_func(path)) 216 | 217 | return reduce_func(results) 218 | 219 | def parse_consensus(path): 220 | net_status = next(parse_file(path, document_handler='DOCUMENT', validate=False)) 221 | 222 | relays = {} 223 | weights = {"total": 0, "exit": 0, "guard": 0, "exitguard": 0, "middle": 0} 224 | counts = {"total": 0, "exit": 0, "guard": 0, "exitguard": 0, "middle": 0} 225 | 226 | for (fingerprint, router_entry) in net_status.routers.items(): 227 | if Flag.BADEXIT in router_entry.flags or Flag.RUNNING not in router_entry.flags or Flag.VALID not in router_entry.flags: 228 | continue 229 | 230 | relays.setdefault(fingerprint, {}) 231 | 232 | relays[fingerprint]['address'] = router_entry.address 233 | relays[fingerprint]['weight'] = router_entry.bandwidth 234 | 235 | if Flag.GUARD in router_entry.flags and Flag.FAST in router_entry.flags and Flag.STABLE in router_entry.flags: 236 | relays[fingerprint]['is_guard'] = True 237 | else: 238 | relays[fingerprint]['is_guard'] = False 239 | 240 | if Flag.EXIT in router_entry.flags and router_entry.exit_policy.is_exiting_allowed(): 241 | relays[fingerprint]['is_exit'] = True 242 | else: 243 | relays[fingerprint]['is_exit'] = False 244 | 245 | # fill in the weights 246 | bw_weight = float(router_entry.bandwidth) 247 | 248 | weights["total"] += bw_weight 249 | counts["total"] += 1 250 | if relays[fingerprint]['is_guard'] and relays[fingerprint]['is_exit']: 251 | weights["exitguard"] += bw_weight 252 | counts["exitguard"] += 1 253 | elif relays[fingerprint]['is_guard']: 254 | weights["guard"] += bw_weight 255 | counts["guard"] += 1 256 | elif relays[fingerprint]['is_exit']: 257 | weights["exit"] += bw_weight 258 | counts["exit"] += 1 259 | else: 260 | weights["middle"] += bw_weight 261 | counts["middle"] += 1 262 | 263 | # weights are normalized on a per-consensus basis 264 | for fingerprint in relays: 265 | relays[fingerprint]['weight'] /= weights["total"] 266 | for position_type in weights: 267 | if position_type == "total": 268 | continue 269 | weights[position_type] /= weights["total"] 270 | 271 | result = { 272 | 'type': 'consensus', 273 | 'pub_dt': net_status.valid_after, # valid_after is for V3 descriptors, V2 use net_status.published 274 | 'relays': relays, 275 | 'weights': weights, 276 | 'counts': counts, 277 | } 278 | 279 | return result 280 | 281 | def combine_parsed_consensus_results(results): 282 | relays = {} 283 | network_stats = {} 284 | min_unix_time, max_unix_time = None, None 285 | 286 | counts_t, counts_eg, counts_e, counts_g, counts_m = [], [], [], [], [] 287 | weights_t, weights_eg, weights_e, weights_g, weights_m = [], [], [], [], [] 288 | 289 | for result in results: 290 | if result is None: 291 | continue 292 | 293 | if result['type'] != 'consensus': 294 | continue 295 | 296 | if result['pub_dt'] is not None: 297 | unix_time = result['pub_dt'].replace(tzinfo=timezone.utc).timestamp() 298 | if min_unix_time is None or unix_time < min_unix_time: 299 | min_unix_time = unix_time 300 | if max_unix_time is None or unix_time > max_unix_time: 301 | max_unix_time = unix_time 302 | 303 | weights_t.append(result['weights']['total']) 304 | weights_eg.append(result['weights']['exitguard']) 305 | weights_g.append(result['weights']['guard']) 306 | weights_e.append(result['weights']['exit']) 307 | weights_m.append(result['weights']['middle']) 308 | 309 | counts_t.append(result['counts']['total']) 310 | counts_eg.append(result['counts']['exitguard']) 311 | counts_g.append(result['counts']['guard']) 312 | counts_e.append(result['counts']['exit']) 313 | counts_m.append(result['counts']['middle']) 314 | 315 | for fingerprint in result['relays']: 316 | relays.setdefault(fingerprint, Relay(fingerprint, result['relays'][fingerprint]['address'])) 317 | 318 | r = relays[fingerprint] 319 | 320 | r.weights.append(result['relays'][fingerprint]['weight']) 321 | 322 | if result['relays'][fingerprint]['is_exit']: 323 | r.num_exit += 1 324 | if result['relays'][fingerprint]['is_guard']: 325 | r.num_guard += 1 326 | 327 | network_stats = { 328 | # the counts are whole numbers 329 | 'med_count_exitguard': int(round(median(counts_eg))), 330 | 'med_count_guard': int(round(median(counts_g))), 331 | 'med_count_exit': int(round(median(counts_e))), 332 | 'med_count_middle': int(round(median(counts_m))), 333 | 'med_count_total': int(round(median(counts_t))), 334 | # the weights are normalized (fractional) 335 | 'med_weight_exitguard': float(median(weights_eg)), 336 | 'med_weight_guard': float(median(weights_g)), 337 | 'med_weight_exit': float(median(weights_e)), 338 | 'med_weight_middle': float(median(weights_m)), 339 | 'med_weight_total': 1.0, # for completeness 340 | } 341 | 342 | timestr = get_time_suffix(min_unix_time, max_unix_time) 343 | logging.info("Found {} total unique relays during {} with a median network size of {} relays".format(len(relays), timestr, network_stats['med_count_total'])) 344 | 345 | return relays, min_unix_time, max_unix_time, network_stats 346 | 347 | # this func is run by helper processes in process pool 348 | def parse_serverdesc(args): 349 | path, min_time, max_time = args 350 | relay = next(parse_file(path, document_handler='DOCUMENT', descriptor_type='server-descriptor 1.0', validate=False)) 351 | 352 | if relay is None: 353 | return None 354 | 355 | pub_ts = relay.published.replace(tzinfo=timezone.utc).timestamp() 356 | if pub_ts < min_time or pub_ts > max_time: 357 | return None 358 | 359 | if relay.observed_bandwidth is None: 360 | return None 361 | 362 | advertised_bw = relay.observed_bandwidth 363 | 364 | avg_bw = relay.average_bandwidth 365 | bst_bw = relay.burst_bandwidth 366 | 367 | if avg_bw is not None and avg_bw < advertised_bw: 368 | advertised_bw = avg_bw 369 | if bst_bw is not None and bst_bw < advertised_bw: 370 | advertised_bw = bst_bw 371 | 372 | result = { 373 | 'type': 'serverdesc', 374 | 'pub_dt': relay.published, 375 | 'fprint': relay.fingerprint, 376 | 'address': relay.address, 377 | 'bw_obs': relay.observed_bandwidth, 378 | 'bw_rate': avg_bw if avg_bw is not None else 0, 379 | 'bw_burst': bst_bw if bst_bw is not None else 0, 380 | 'bw_adv': advertised_bw, 381 | } 382 | 383 | return result 384 | 385 | def combine_parsed_serverdesc_results(results): 386 | bandwidths = {} 387 | 388 | for result in results: 389 | if result is None: 390 | continue 391 | 392 | if result['type'] != 'serverdesc': 393 | continue 394 | 395 | bandwidths.setdefault(result['fprint'], Bandwidths(result['fprint'])) 396 | 397 | b = bandwidths[result['fprint']] 398 | 399 | b.max_obs_bw = max(b.max_obs_bw, result['bw_obs']) 400 | b.bw_rates.append(result['bw_rate']) 401 | b.bw_bursts.append(result['bw_burst']) 402 | 403 | return bandwidths 404 | 405 | def parse_extrainfo(path): # unused right now, but might be useful 406 | xinfo = next(parse_file(path, document_handler='DOCUMENT', descriptor_type='extra-info 1.0', validate=False)) 407 | 408 | read_max_rate, read_avg_rate = 0, 0 409 | if xinfo.read_history_values is not None and xinfo.read_history_interval is not None: 410 | read_max_rate = int(max(xinfo.read_history_values) / xinfo.read_history_interval) 411 | read_avg_rate = int((sum(xinfo.read_history_values) / len(xinfo.read_history_values)) / xinfo.read_history_interval) 412 | 413 | write_max_rate, write_avg_rate = 0, 0 414 | if xinfo.write_history_values is not None and xinfo.write_history_interval is not None: 415 | write_max_rate = int(max(xinfo.write_history_values) / xinfo.write_history_interval) 416 | write_avg_rate = int((sum(xinfo.write_history_values) / len(xinfo.write_history_values)) / xinfo.write_history_interval) 417 | 418 | result = { 419 | 'type': type, 420 | 'pub_dt': xinfo.published, 421 | 'fprint': xinfo.fingerprint, 422 | 'nickname': xinfo.nickname, 423 | 'bytes_read_max': read_max_rate, 424 | 'bytes_read_avg': read_avg_rate, 425 | 'bytes_write_max': write_max_rate, 426 | 'bytes_write_avg': write_avg_rate, 427 | } 428 | 429 | return result 430 | -------------------------------------------------------------------------------- /tornettools/util.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | import json 4 | import lzma 5 | import re 6 | import shutil 7 | import shlex 8 | 9 | def make_directories(path): 10 | p = os.path.abspath(os.path.expanduser(path)) 11 | d = os.path.dirname(p) 12 | if not os.path.exists(d): 13 | os.makedirs(d) 14 | 15 | # test if program is in path 16 | def which(program): 17 | # returns None if not found 18 | return shutil.which(program) 19 | 20 | def cmdsplit(cmd_str): 21 | return shlex.split(cmd_str) 22 | 23 | def open_writeable_file(filepath, compress=False): 24 | make_directories(filepath) 25 | if compress: 26 | if not filepath.endswith(".xz"): 27 | filepath += ".xz" 28 | outfile = lzma.open(filepath, 'wt') 29 | else: 30 | outfile = open(filepath, 'w') 31 | return outfile 32 | 33 | def open_readable_file(filepath): 34 | if not os.path.exists(filepath) and not filepath.endswith('.xz'): 35 | filepath += ".xz" # look for the compressed version 36 | if filepath.endswith('.xz'): 37 | infile = lzma.open(filepath, 'rt') 38 | else: 39 | infile = open(filepath, 'r') 40 | return infile 41 | 42 | def dump_json_data(output, outfile_path, compress=False): 43 | with open_writeable_file(outfile_path, compress) as outfile: 44 | json.dump(output, outfile, sort_keys=True, separators=(',', ': '), indent=2) 45 | 46 | def load_json_data(infile_path): 47 | with open_readable_file(infile_path) as infile: 48 | data = json.load(infile) 49 | return data 50 | 51 | def find_matching_files_in_dir(search_dir, filepattern): 52 | if isinstance(filepattern, str): 53 | # Interpret as a literal string 54 | logging.info(f"Searching for files containing {filepattern} in directory tree at {search_dir}") 55 | filepattern = re.compile('.*' + re.escape(filepattern) + '.*') 56 | else: 57 | logging.info(f"Searching for files matching {filepattern.pattern} in directory tree at {search_dir}") 58 | found = [] 59 | for root, dirs, files in os.walk(search_dir): 60 | for name in files: 61 | if filepattern.match(name): 62 | p = os.path.join(root, name) 63 | logging.info("Found {}".format(p)) 64 | found.append(p) 65 | logging.info(f"Found {len(found)} total files") 66 | return found 67 | 68 | # Useful for spelling integer constants multiple ways. e.g. it can be useful 69 | # to spell 1 Mebi as 1048576 if that's how it's spelled out in other 70 | # documentation, *and* as 2 ** 20 to be able to easily verify that it's really 71 | # exactly 1 Mebi and not something slightly different. 72 | # e.g.: 73 | # start_bytes = aka_int(2**20, 1048576) 74 | def aka_int(x, y): 75 | assert x == y 76 | return x 77 | 78 | # Looks for the given data point, first in 79 | # stream['elapsed_seconds']['payload_bytes_recv'], and then falls back to 80 | # stream['elapsed_seconds']['payload_progress_recv']. Returns None if not found 81 | # in either. 82 | # 83 | # This is useful because, e.g., tgen data currently doesn't have 4 MiB in 84 | # `payload_bytes_recv`, but *does* have progress 0.8 of 5 MiB streams in 85 | # `payload_progress_recv`. 86 | def tgen_stream_seconds_at_bytes(stream, num_bytes): 87 | es = stream.get('elapsed_seconds') 88 | if es is None: 89 | return None 90 | seconds = es['payload_bytes_recv'].get(str(num_bytes)) 91 | if seconds is not None: 92 | return float(seconds) 93 | progress = num_bytes / float(stream['stream_info']['recvsize']) 94 | seconds = es['payload_progress_recv'].get(str(progress)) 95 | if seconds is not None: 96 | return float(seconds) 97 | return None 98 | -------------------------------------------------------------------------------- /tornettools/util_geoip.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from bisect import bisect 4 | 5 | class GeoIP(): 6 | # geoip_path is the path to a geoip file distributed with the tor source code 7 | # e.g.: path/to/tor/src/config/geoip 8 | def __init__(self, geoip_path): 9 | self.geoip_path = geoip_path 10 | 11 | self.boundaries = [] 12 | self.codes = {} 13 | 14 | # parse the geoip file 15 | # see here for supported range queries using bisection 16 | # https://stackoverflow.com/questions/23639361/fast-checking-of-ranges-in-python 17 | if os.path.exists(self.geoip_path): 18 | with open(self.geoip_path, "r") as f: 19 | for line in f: 20 | # ignore comment lines 21 | if line[0] == "#": 22 | continue 23 | # normal lines contain ranges and country code, e.g.: 123,125,US 24 | # the geoip file may contain the same number twice, e.g.: 123,123,US 25 | # so I assume the range are already half-open intervals 26 | parts = line.strip().split(',') 27 | low, high, code = int(parts[0]), int(parts[1]), parts[2] 28 | # enforce assumption that the data is sorted 29 | if len(self.boundaries) > 0: 30 | assert self.boundaries[-1] <= low 31 | assert low <= high 32 | # add the half-open interval boundaries 33 | self.boundaries.append(low) 34 | self.boundaries.append(high) 35 | # make sure we can look up the code later 36 | self.codes[low] = code 37 | 38 | def ip_to_country_code(self, ip_address): 39 | # Convert a IPv4 address into a 32-bit integer. 40 | ip_array = ip_address.split('.') 41 | if len(ip_array) == 4: 42 | ipnum = (int(ip_array[0]) * 16777216) + (int(ip_array[1]) * 65536) + (int(ip_array[2]) * 256) + int(ip_array[3]) 43 | # check if ip is in an interval from the parsed geoip data 44 | b = bisect(self.boundaries, ipnum) 45 | if (b % 2) == 1: 46 | # the ip is in a known interval defined in the geoip file. 47 | # b holds the index of the right side of the interval. 48 | # right_index = b 49 | left_index = b - 1 50 | low = self.boundaries[left_index] 51 | code = self.codes[low] 52 | return "{}".format(code) 53 | else: 54 | # ip is not in an interval defined in the geoip file 55 | pass 56 | # we don't know the country code 57 | return "AP" 58 | --------------------------------------------------------------------------------