From 8976cdd9be1bb70d3457bf2551971e12ee253ce1 Mon Sep 17 00:00:00 2001 From: Karsten Loesing Date: Fri, 18 Dec 2020 12:02:55 +0100 Subject: [PATCH] Parse new NAT-based Snowflake lines. Implements #40002. --- CHANGELOG.md | 5 +- .../torproject/descriptor/SnowflakeStats.java | 54 +++++++++++++ .../org/torproject/descriptor/impl/Key.java | 5 ++ .../descriptor/impl/SnowflakeStatsImpl.java | 75 +++++++++++++++++++ .../impl/SnowflakeStatsImplTest.java | 50 +++++++++++++ 5 files changed, 188 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac865a9..68ad2a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ -# Changes in version 2.??.? - 2020-??-?? +# Changes in version 2.16.0 - 2020-??-?? + + * Medium changes + - Parse new NAT-based Snowflake lines. # Changes in version 2.15.0 - 2020-12-11 diff --git a/src/main/java/org/torproject/descriptor/SnowflakeStats.java b/src/main/java/org/torproject/descriptor/SnowflakeStats.java index 2fe78df..967d061 100644 --- a/src/main/java/org/torproject/descriptor/SnowflakeStats.java +++ b/src/main/java/org/torproject/descriptor/SnowflakeStats.java @@ -103,6 +103,30 @@ public interface SnowflakeStats extends Descriptor { */ Optional clientDeniedCount(); + /** + * Return a count of the number of times a client with a restricted or unknown + * NAT type has requested a proxy from the broker but no proxies were + * available, rounded up to the nearest multiple of 8. + * + * @return Count of the number of times a client with a restricted or unknown + * NAT type has requested a proxy from the broker but no proxies were + * available, rounded up to the nearest multiple of 8. + * @since 2.16.0 + */ + Optional clientRestrictedDeniedCount(); + + /** + * Return a count of the number of times a client with an unrestricted NAT + * type has requested a proxy from the broker but no proxies were available, + * rounded up to the nearest multiple of 8. + * + * @return Count of the number of times a client with an unrestricted NAT type + * has requested a proxy from the broker but no proxies were available, + * rounded up to the nearest multiple of 8. + * @since 2.16.0 + */ + Optional clientUnrestrictedDeniedCount(); + /** * Return a count of the number of times a client successfully received a * proxy from the broker, rounded up to the nearest multiple of 8. @@ -112,5 +136,35 @@ public interface SnowflakeStats extends Descriptor { * @since 2.7.0 */ Optional clientSnowflakeMatchCount(); + + /** + * Return a count of the total number of unique IP addresses of snowflake + * proxies that have a restricted NAT type. + * + * @return Count of the total number of unique IP addresses of snowflake + * proxies that have a restricted NAT type. + * @since 2.16.0 + */ + Optional snowflakeIpsNatRestricted(); + + /** + * Return a count of the total number of unique IP addresses of snowflake + * proxies that have an unrestricted NAT type. + * + * @return Count of the total number of unique IP addresses of snowflake + * proxies that have an unrestricted NAT type. + * @since 2.16.0 + */ + Optional snowflakeIpsNatUnrestricted(); + + /** + * Return a count of the total number of unique IP addresses of snowflake + * proxies that have an unknown NAT type. + * + * @return Count of the total number of unique IP addresses of snowflake + * proxies that have an unknown NAT type. + * @since 2.16.0 + */ + Optional snowflakeIpsNatUnknown(); } diff --git a/src/main/java/org/torproject/descriptor/impl/Key.java b/src/main/java/org/torproject/descriptor/impl/Key.java index b02d96e..410cef6 100644 --- a/src/main/java/org/torproject/descriptor/impl/Key.java +++ b/src/main/java/org/torproject/descriptor/impl/Key.java @@ -36,7 +36,9 @@ public enum Key { CELL_STATS_END("cell-stats-end"), CELL_TIME_IN_QUEUE("cell-time-in-queue"), CLIENT_DENIED_COUNT("client-denied-count"), + CLIENT_RESTRICTED_DENIED_COUNT("client-restricted-denied-count"), CLIENT_SNOWFLAKE_MATCH_COUNT("client-snowflake-match-count"), + CLIENT_UNRESTRICTED_DENIED_COUNT("client-unrestricted-denied-count"), CLIENT_VERSIONS("client-versions"), CONN_BI_DIRECT("conn-bi-direct"), CONSENSUS_METHOD("consensus-method"), @@ -149,6 +151,9 @@ public enum Key { SNOWFLAKE_IDLE_COUNT("snowflake-idle-count"), SNOWFLAKE_IPS("snowflake-ips"), SNOWFLAKE_IPS_BADGE("snowflake-ips-badge"), + SNOWFLAKE_IPS_NAT_RESTRICTED("snowflake-ips-nat-restricted"), + SNOWFLAKE_IPS_NAT_UNKNOWN("snowflake-ips-nat-unknown"), + SNOWFLAKE_IPS_NAT_UNRESTRICTED("snowflake-ips-nat-unrestricted"), SNOWFLAKE_IPS_STANDALONE("snowflake-ips-standalone"), SNOWFLAKE_IPS_TOTAL("snowflake-ips-total"), SNOWFLAKE_IPS_WEBEXT("snowflake-ips-webext"), diff --git a/src/main/java/org/torproject/descriptor/impl/SnowflakeStatsImpl.java b/src/main/java/org/torproject/descriptor/impl/SnowflakeStatsImpl.java index f52281e..2af40b5 100644 --- a/src/main/java/org/torproject/descriptor/impl/SnowflakeStatsImpl.java +++ b/src/main/java/org/torproject/descriptor/impl/SnowflakeStatsImpl.java @@ -80,9 +80,24 @@ public class SnowflakeStatsImpl extends DescriptorImpl case CLIENT_DENIED_COUNT: this.parseClientDeniedCount(line, parts); break; + case CLIENT_RESTRICTED_DENIED_COUNT: + this.parseClientRestrictedDeniedCount(line, parts); + break; + case CLIENT_UNRESTRICTED_DENIED_COUNT: + this.parseClientUnrestrictedDeniedCount(line, parts); + break; case CLIENT_SNOWFLAKE_MATCH_COUNT: this.parseClientSnowflakeMatchCount(line, parts); break; + case SNOWFLAKE_IPS_NAT_RESTRICTED: + this.parseSnowflakeIpsNatRestricted(line, parts); + break; + case SNOWFLAKE_IPS_NAT_UNRESTRICTED: + this.parseSnowflakeIpsNatUnrestricted(line, parts); + break; + case SNOWFLAKE_IPS_NAT_UNKNOWN: + this.parseSnowflakeIpsNatUnknown(line, parts); + break; case INVALID: default: ParseHelper.parseKeyword(line, parts[0]); @@ -142,11 +157,36 @@ public class SnowflakeStatsImpl extends DescriptorImpl this.clientDeniedCount = ParseHelper.parseLong(line, parts, 1); } + private void parseClientRestrictedDeniedCount(String line, String[] parts) + throws DescriptorParseException { + this.clientRestrictedDeniedCount = ParseHelper.parseLong(line, parts, 1); + } + + private void parseClientUnrestrictedDeniedCount(String line, String[] parts) + throws DescriptorParseException { + this.clientUnrestrictedDeniedCount = ParseHelper.parseLong(line, parts, 1); + } + private void parseClientSnowflakeMatchCount(String line, String[] parts) throws DescriptorParseException { this.clientSnowflakeMatchCount = ParseHelper.parseLong(line, parts, 1); } + private void parseSnowflakeIpsNatRestricted(String line, String[] parts) + throws DescriptorParseException { + this.snowflakeIpsNatRestricted = ParseHelper.parseLong(line, parts, 1); + } + + private void parseSnowflakeIpsNatUnrestricted(String line, String[] parts) + throws DescriptorParseException { + this.snowflakeIpsNatUnrestricted = ParseHelper.parseLong(line, parts, 1); + } + + private void parseSnowflakeIpsNatUnknown(String line, String[] parts) + throws DescriptorParseException { + this.snowflakeIpsNatUnknown = ParseHelper.parseLong(line, parts, 1); + } + private LocalDateTime snowflakeStatsEnd; @Override @@ -210,11 +250,46 @@ public class SnowflakeStatsImpl extends DescriptorImpl return Optional.ofNullable(this.clientDeniedCount); } + private Long clientRestrictedDeniedCount; + + @Override + public Optional clientRestrictedDeniedCount() { + return Optional.ofNullable(this.clientRestrictedDeniedCount); + } + + private Long clientUnrestrictedDeniedCount; + + @Override + public Optional clientUnrestrictedDeniedCount() { + return Optional.ofNullable(this.clientUnrestrictedDeniedCount); + } + private Long clientSnowflakeMatchCount; @Override public Optional clientSnowflakeMatchCount() { return Optional.ofNullable(this.clientSnowflakeMatchCount); } + + private Long snowflakeIpsNatRestricted; + + @Override + public Optional snowflakeIpsNatRestricted() { + return Optional.ofNullable(this.snowflakeIpsNatRestricted); + } + + private Long snowflakeIpsNatUnrestricted; + + @Override + public Optional snowflakeIpsNatUnrestricted() { + return Optional.ofNullable(this.snowflakeIpsNatUnrestricted); + } + + private Long snowflakeIpsNatUnknown; + + @Override + public Optional snowflakeIpsNatUnknown() { + return Optional.ofNullable(this.snowflakeIpsNatUnknown); + } } diff --git a/src/test/java/org/torproject/descriptor/impl/SnowflakeStatsImplTest.java b/src/test/java/org/torproject/descriptor/impl/SnowflakeStatsImplTest.java index 4051b74..8d03511 100644 --- a/src/test/java/org/torproject/descriptor/impl/SnowflakeStatsImplTest.java +++ b/src/test/java/org/torproject/descriptor/impl/SnowflakeStatsImplTest.java @@ -42,6 +42,36 @@ public class SnowflakeStatsImplTest { "client-denied-count 0", "client-snowflake-match-count 864" }; + /** + * Snowflake statistics written on 2020-12-16 at 19:24:38 as obtained from + * CollecTor. + */ + private static final String[] snowflakeStats20201216192438 = new String[] { + "@type snowflake-stats 1.0", + "snowflake-stats-end 2020-12-16 19:24:38 (86400 s)", + "snowflake-ips US=1716,SE=109,PH=97,EC=5,FO=1,AU=121,SA=12,PK=8,IR=12," + + "GF=2,UZ=1,BY=12,BE=39,MT=2,BA=2,SC=1,MM=3,FR=272,PS=7,LT=12," + + "NL=209,CY=4,TW=26,GA=4,IL=25,MX=38,HU=35,HR=21,AE=6,PY=5,PT=50," + + "FI=48,RO=116,DE=877,MY=27,CA=223,IQ=1,CZ=46,SI=7,RS=14,KN=3,JO=2," + + "TN=3,LB=2,PE=6,ID=28,MK=2,AT=70,MV=1,BR=149,TR=19,JP=513,CH=151," + + "NZ=36,VN=18,MD=2,GR=56,UA=33,AZ=3,CN=74,RU=113,LV=23,EE=10,TH=63," + + "BG=10,??=15,HK=22,CR=3,SD=2,GB=372,DK=37,BD=5,ZA=22,LU=24,KR=26," + + "LK=3,IS=3,PR=1,MO=1,PL=165,NO=49,CL=15,IE=24,KE=1,MA=2,GT=1,ES=74," + + "EG=16,PA=3,IN=142,CO=5,GI=1,DZ=12,KZ=1,AR=24,UY=3,NP=8,SN=2,SG=45," + + "TZ=1,SK=20,TG=8,BZ=5,IT=172,BF=2", + "snowflake-ips-total 6943", + "snowflake-ips-standalone 32", + "snowflake-ips-badge 27", + "snowflake-ips-webext 6882", + "snowflake-idle-count 956568", + "client-denied-count 640", + "client-restricted-denied-count 640", + "client-unrestricted-denied-count 0", + "client-snowflake-match-count 11456", + "snowflake-ips-nat-restricted 3140", + "snowflake-ips-nat-unrestricted 29", + "snowflake-ips-nat-unknown 3768" }; + @Test public void testExampleMetricsLog() throws DescriptorParseException { SnowflakeStats snowflakeStats = new SnowflakeStatsImpl( @@ -145,5 +175,25 @@ public class SnowflakeStatsImplTest { new SnowflakeStatsImpl(new TestDescriptorBuilder( "snowflake-stats-end 2019-08-07 19:52:11 (0 s)").build(), null); } + + @Test + public void testNatBasedSnowflakeLines() throws DescriptorParseException { + SnowflakeStats snowflakeStats = new SnowflakeStatsImpl( + new TestDescriptorBuilder(snowflakeStats20201216192438).build(), null); + assertTrue(snowflakeStats.clientRestrictedDeniedCount().isPresent()); + assertEquals((Long) 640L, + snowflakeStats.clientRestrictedDeniedCount().get()); + assertTrue(snowflakeStats.clientUnrestrictedDeniedCount().isPresent()); + assertEquals((Long) 0L, + snowflakeStats.clientUnrestrictedDeniedCount().get()); + assertTrue(snowflakeStats.snowflakeIpsNatRestricted().isPresent()); + assertEquals((Long) 3140L, + snowflakeStats.snowflakeIpsNatRestricted().get()); + assertTrue(snowflakeStats.snowflakeIpsNatUnrestricted().isPresent()); + assertEquals((Long) 29L, + snowflakeStats.snowflakeIpsNatUnrestricted().get()); + assertTrue(snowflakeStats.snowflakeIpsNatUnknown().isPresent()); + assertEquals((Long) 3768L, snowflakeStats.snowflakeIpsNatUnknown().get()); + } }