Too many proxies with unknown NAT (NAT check often fails in iPtProxy)
This is a spin-off of #40071 (closed)
According to broker metrics, proxies with "unknown" NAT type account for almost 50% of all proxies:
snowflake-ips-nat-restricted 88141
snowflake-ips-nat-unrestricted 7201
snowflake-ips-nat-unknown 64974
And I think I have an idea why this could be happening. As I wrote in #40071 (comment 3073155), standalone proxies check their NAT type before making the first request for a client. But this is not the case for browser proxies! And what we do with our metrics is only record NAT type of the very first poll request of the day. We do not override the previously reported NAT type for the proxy's IP address: https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/blob/f64f234eeb0820f9b6e20811536e4848cd2e9b7c/broker/metrics.go#L118-120 So, since browser proxies don't usually run 24/7 and restart ~every day or even more often, they make their first poll request with "unknown" NAT type, and the metrics retain that.
So, in this particular case, this is purely a metrics problem, it doesn't affect the functional aspect of Snowflake.
Here is a patch to add tests that catch this
diff --git a/broker/snowflake-broker_test.go b/broker/snowflake-broker_test.go
index bb4360e..909dafd 100644
--- a/broker/snowflake-broker_test.go
+++ b/broker/snowflake-broker_test.go
@@ -940,7 +940,7 @@ snowflake-ips-nat-unknown 0
//Test NAT types
Convey("proxy counts by NAT type", func() {
w := httptest.NewRecorder()
- data := bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.2","Type":"unknown","NAT":"restricted"}`))
+ data := bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.2","Type":"unknown","NAT":"unknown"}`))
r, err := http.NewRequest("POST", "snowflake.broker/proxy", data)
r.RemoteAddr = "129.97.208.23:8888" //CA geoip
So(err, ShouldBeNil)
@@ -953,9 +953,9 @@ snowflake-ips-nat-unknown 0
<-done
ctx.metrics.printMetrics()
- So(buf.String(), ShouldContainSubstring, "snowflake-ips-nat-restricted 1\nsnowflake-ips-nat-unrestricted 0\nsnowflake-ips-nat-unknown 0")
+ So(buf.String(), ShouldContainSubstring, "snowflake-ips-nat-restricted 0\nsnowflake-ips-nat-unrestricted 0\nsnowflake-ips-nat-unknown 1")
- data = bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.2","Type":"unknown","NAT":"unrestricted"}`))
+ data = bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.2","Type":"unknown","NAT":"restricted"}`))
r, err = http.NewRequest("POST", "snowflake.broker/proxy", data)
if err != nil {
log.Printf("unable to get NewRequest with error: %v", err)
@@ -969,6 +969,23 @@ snowflake-ips-nat-unknown 0
p.offerChannel <- nil
<-done
+ ctx.metrics.printMetrics()
+ So(buf.String(), ShouldContainSubstring, "snowflake-ips-nat-restricted 1\nsnowflake-ips-nat-unrestricted 0\nsnowflake-ips-nat-unknown 1")
+
+ // A proxy with the a previously unknown NAT type determined its
+ // NAT type and made another poll.
+ data = bytes.NewReader([]byte(`{"Sid":"ymbcCMto7KHNGYlp","Version":"1.2","Type":"unknown","NAT":"unrestricted"}`))
+ r, err = http.NewRequest("POST", "snowflake.broker/proxy", data)
+ So(err, ShouldBeNil)
+ r.RemoteAddr = "129.97.208.23:8888" // IP of the first proxy
+ go func(i *IPC) {
+ proxyPolls(i, w, r)
+ done <- true
+ }(i)
+ p = <-ctx.proxyPolls //manually unblock poll
+ p.offerChannel <- nil
+ <-done
+
ctx.metrics.printMetrics()
So(buf.String(), ShouldContainSubstring, "snowflake-ips-nat-restricted 1\nsnowflake-ips-nat-unrestricted 1\nsnowflake-ips-nat-unknown 0")
})