Add three NAT type buckets to the snowflake broker
At the moment we use two buckets for NATs, labelled "restricted" and "unrestricted". See the wiki for more information on this: https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/wikis/NAT-matching
There's an edge case that clients with a port-restricted NAT pull proxies from the "restricted NAT" bucket, but aren't compatible with symmetric NATs (see the table in the documentation). We allowed this edge case to exist after testing snowflake with port-restriced NATs and having good performance and compatibility with > 80% of proxies. However, the increase of mobile phone proxies and the exhaustion of IPv4 might change that.
Let's add a third NAT bucket (and also redo our naming scheme because it's confusing at the moment). How about the following:
We can define the following three NAT types (inspiration taken from the Xbox naming scheme):
-
open
: no NAT, full cone NAT, restricted cone NAT -
moderate
: port-restricted cone NAT -
strict
: symmetric NAT
Then, clients will report their NAT type, and the matching algorithm/pseudocode will be as follows:
switch NATType {
case NATOpen:
// can be matched with any NAT type, try to match with proxies with the most aggressive NATs first
var snowflakeHeap *SnowflakeHeap
if len(strictHeap) > 0 {
snowflakeHeap = strictHeap
else if len(moderateHeap) > 0 {
snowflakeHeap = moderateHeap
} else if len(openHeap) > 0 {
snowflakeHeap = openHeap
} else {
// there are no snowflake available
}
snowflake := heap.Pop(snowflakeHeap)
case NATModerate:
// can be matched with open or moderate NATs, try to match with proxies with the most aggressive NATs first
var snowflakeHeap *SnowflakeHeap
if len(moderateHeap) > 0 {
snowflakeHeap = moderateHeap
} else if len(openHeap) > 0 {
snowflakeHeap = openHeap
} else {
// there are no snowflake available
return
}
snowflake := heap.Pop(snowflakeHeap)
case NATStrict:
// can only be matched with open NATs
if len(openHeap) > 0 {
snowflakeHeap = openHeap
} else {
// there are no snowflake available
return
}
snowflake := heap.Pop(snowflakeHeap)
}
We'll have to update our metrics (CollecTor and prometheus) as well.