Skip to content
Snippets Groups Projects
Commit 2cab2b49 authored by Karsten Loesing's avatar Karsten Loesing
Browse files

Add ipv6.py script for tpo/metrics/trac#40002.

parent ff414dc6
No related branches found
No related tags found
No related merge requests found
# Script that Counts IPv6 Relays in the Consensus
See `ipv6.py` for usage information and [tpo/metrics/trac#40002](https://gitlab.torproject.org/tpo/metrics/trac/-/issues/40002) for context.
#!/usr/bin/env python3
"""
----------------------------------------------------------------------------
This script implements Section 3 of Proposal 313:
3. Monitoring IPv6 Relays in the Consensus
We propose writing a script that calculates:
* the number of relays, and
* the consensus weight fraction of relays,
in the consensus that:
* have an IPv6 ORPort,
* support IPv6 reachability checks,
* support IPv6 clients, and
* support IPv6 reachability checks, and IPv6 clients.
In order to provide easy access to these statistics, we propose
that the script should:
* download a consensus (or read an existing consensus), and
* calculate and report these statistics.
The following consensus weight fractions should divide by the total
consensus weight:
* have an IPv6 ORPort (all relays have an IPv4 ORPort), and
* support IPv6 reachability checks (all relays support IPv4 reachability).
The following consensus weight fractions should divide by the
"usable Guard" consensus weight:
* support IPv6 clients, and
* support IPv6 reachability checks and IPv6 clients.
"Usable Guards" have the Guard flag, but do not have the Exit flag. If the
Guard also has the BadExit flag, the Exit flag should be ignored.
Note that this definition of "Usable Guards" is only valid when the
consensus contains many more guards than exits. That is, Wgd must be 0 in
the consensus. (See the [Tor Directory Protocol] for more details.)
Therefore, the script should check that Wgd is 0. If it is not, the script
should log a warning about the accuracy of the "Usable Guard" statistics.
----------------------------------------------------------------------------
Copyright 2020 The Tor Project
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the names of the copyright owners nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------------
"""
import sys, stem, stem.version, stem.descriptor
partial_support_version = stem.version.Version('0.4.4')
full_support_version = stem.version.Version('0.4.5')
def read(consensus_filename):
parsed_consensus = stem.descriptor.parse_file(consensus_filename,
descriptor_type = 'network-status-consensus-3 1.0')
valid_after = None
wgd = None
relays = []
relays_ipv6 = []
relays_partial = []
relays_full = []
guards = []
guards_ipv6 = []
guards_partial = []
guards_full = []
for relay in parsed_consensus:
if not valid_after:
valid_after = relay.document.valid_after
wgd = relay.document.bandwidth_weights["Wgd"]
if relay.bandwidth is None:
continue
has_ipv6_address = False
if relay.or_addresses:
for or_address in relay.or_addresses:
if len(or_address) == 3 and or_address[2]:
has_ipv6_address = True
is_usable_guard = False
if relay.flags:
if "Guard" in relay.flags and (
"Exit" not in relay.flags or "BadExit" in relay.flags):
is_usable_guard = True
has_partial_support = False
has_full_support = False
if relay.version:
if relay.version >= full_support_version:
has_full_support = True
if relay.version >= partial_support_version:
has_partial_support = True
relays.append(relay.bandwidth)
if has_ipv6_address:
relays_ipv6.append(relay.bandwidth)
if has_partial_support:
relays_partial.append(relay.bandwidth)
if has_full_support:
relays_full.append(relay.bandwidth)
if is_usable_guard:
guards.append(relay.bandwidth)
if has_ipv6_address:
guards_ipv6.append(relay.bandwidth)
if has_partial_support:
guards_partial.append(relay.bandwidth)
if has_full_support:
guards_full.append(relay.bandwidth)
print("\nParsed consensus with valid-after time {0}:".format(valid_after))
print(" - Consensus contains {0} relays:".format(len(relays)))
print(" - {0} of these ({1:.2f}% TCW) have an IPv6 ORPort.".
format(len(relays_ipv6), 100 * sum(relays_ipv6) / sum(relays)))
print(" - {0} of these ({1:.2f}% TCW) support at least partial IPv6 reachability checks.".
format(len(relays_partial), 100 * sum(relays_partial) / sum(relays)))
print(" - {0} of these ({1:.2f}% TCW) support full IPv6 reachability checks.".
format(len(relays_full), 100 * sum(relays_full) / sum(relays)))
print(" - Consensus contains {0} \"usable guards\" and Wgd={1}.".
format(len(guards), wgd))
print(" - {0} of these ({1:.2f}% UGCW) support IPv6 clients.".
format(len(guards_ipv6), 100 * sum(guards_ipv6) / sum(guards)))
print(" - {0} of these ({1:.2f}% UGCW) support at least partial IPv6 reachability checks.".
format(len(guards_partial), 100 * sum(guards_partial) / sum(guards)))
print(" - {0} of these ({1:.2f}% UGCW) support full IPv6 reachability checks.".
format(len(guards_full), 100 * sum(guards_full) / sum(guards)))
if wgd != 0:
print(" - WARNING: \"usable guards\" definition is only valid when Wgd=0!")
def main():
if len(sys.argv) < 2:
print("Please provide one or more downloaded consensus files as arguments.")
else:
for argv in sorted(sys.argv[1:]):
read(argv)
print()
if __name__ == '__main__':
sys.exit(main())
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment