Add a num-turbotunnel server transport option.

Replaces the hardcoded numKCPInstances.
This commit is contained in:
David Fifield 2022-12-11 17:51:09 -07:00
parent c6fabb212d
commit 936a1f8138
3 changed files with 45 additions and 7 deletions

View file

@ -70,6 +70,26 @@ setcap 'cap_net_bind_service=+ep' /usr/local/bin/snowflake-server
``` ```
# Multiple KCP state machines
The server internally uses a network protocol called KCP
to manage and persist client sessions.
Each KCP scheduler runs on a single thread.
When there are many simultaneous users (thousands),
a single KCP scheduler can be a bottleneck.
The `num-turbotunnel` pluggable transport option
lets you control the number of KCP instances,
which can help with CPU scaling:
https://bugs.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/40200
There is currently no way to set this option automatically.
You have to tune it manually.
```
ServerTransportOptions snowflake num-turbotunnel=2
```
# Controlling source addresses # Controlling source addresses
Use the `orport-srcaddr` pluggable transport option to control what source addresses Use the `orport-srcaddr` pluggable transport option to control what source addresses
@ -83,6 +103,11 @@ Use `ServerTransportOptions` in torrc to set the option:
ServerTransportOptions snowflake orport-srcaddr=127.0.2.0/24 ServerTransportOptions snowflake orport-srcaddr=127.0.2.0/24
``` ```
You can combine it with other options:
```
ServerTransportOptions snowflake num-turbotunnel=2 orport-srcaddr=127.0.2.0/24
```
Specifying a source address range other than the default 127.0.0.1 Specifying a source address range other than the default 127.0.0.1
can help with conserving localhost ephemeral ports on servers can help with conserving localhost ephemeral ports on servers
that receive a lot of connections: that receive a lot of connections:

View file

@ -55,11 +55,6 @@ const (
WindowSize = 65535 WindowSize = 65535
// StreamSize controls the maximum amount of in flight data between a client and server. // StreamSize controls the maximum amount of in flight data between a client and server.
StreamSize = 1048576 //1MB StreamSize = 1048576 //1MB
// numKCPInstances is the number of parallel KCP state machines to run.
// Clients are assigned to a particular KCP instance by a hash of their
// ClientID.
numKCPInstances = 2
) )
// Transport is a structure with methods that conform to the Go PT v2.1 API // Transport is a structure with methods that conform to the Go PT v2.1 API
@ -76,7 +71,7 @@ func NewSnowflakeServer(getCertificate func(*tls.ClientHelloInfo) (*tls.Certific
// Listen starts a listener on addr that will accept both turbotunnel // Listen starts a listener on addr that will accept both turbotunnel
// and legacy Snowflake connections. // and legacy Snowflake connections.
func (t *Transport) Listen(addr net.Addr) (*SnowflakeListener, error) { func (t *Transport) Listen(addr net.Addr, numKCPInstances int) (*SnowflakeListener, error) {
listener := &SnowflakeListener{ listener := &SnowflakeListener{
addr: addr, addr: addr,
queue: make(chan net.Conn, 65534), queue: make(chan net.Conn, 65534),

View file

@ -14,6 +14,7 @@ import (
"os" "os"
"os/signal" "os/signal"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"sync" "sync"
"syscall" "syscall"
@ -280,7 +281,24 @@ func main() {
orPortSrcAddr = ipnet orPortSrcAddr = ipnet
} }
ln, err := transport.Listen(bindaddr.Addr) numKCPInstances := 1
// Are we requested to run a certain number of KCP state
// machines?
if value, ok := bindaddr.Options.Get("num-turbotunnel"); ok {
n, err := strconv.Atoi(value)
if err == nil && n < 1 {
err = fmt.Errorf("cannot be less than 1")
}
if err != nil {
err = fmt.Errorf("parsing num-turbotunnel: %w", err)
log.Println(err)
pt.SmethodError(bindaddr.MethodName, err.Error())
continue
}
numKCPInstances = n
}
ln, err := transport.Listen(bindaddr.Addr, numKCPInstances)
if err != nil { if err != nil {
log.Printf("error opening listener: %s", err) log.Printf("error opening listener: %s", err)
pt.SmethodError(bindaddr.MethodName, err.Error()) pt.SmethodError(bindaddr.MethodName, err.Error())